diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs
index 32a0e4878d..ba1a7965fd 100644
--- a/src/QsCompiler/CommandLineTool/Commands/Build.cs
+++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs
@@ -131,7 +131,8 @@ public static int Run(BuildOptions options, ConsoleLogger logger)
ProjectName = options.ProjectName,
GenerateFunctorSupport = true,
SkipSyntaxTreeTrimming = options.TrimLevel == 0,
- AttemptFullPreEvaluation = options.TrimLevel > 1,
+ ConvertClassicalControl = options.TrimLevel >= 2,
+ AttemptFullPreEvaluation = options.TrimLevel > 2,
DocumentationOutputFolder = options.DocFolder,
BuildOutputFolder = options.OutputFolder ?? (usesPlugins ? "." : null),
DllOutputPath = options.EmitDll ? " " : null, // set to e.g. an empty space to generate the dll in the same location as the .bson file
diff --git a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs
index 3d718ff0ac..6fc6433f27 100644
--- a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs
+++ b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs
@@ -221,7 +221,8 @@ public static int Run(DiagnoseOptions options, ConsoleLogger logger)
{
GenerateFunctorSupport = true,
SkipSyntaxTreeTrimming = options.TrimLevel == 0,
- AttemptFullPreEvaluation = options.TrimLevel > 1,
+ ConvertClassicalControl = options.TrimLevel >= 2,
+ AttemptFullPreEvaluation = options.TrimLevel > 2,
RewriteSteps = options.Plugins?.Select(step => (step, (string)null)) ?? ImmutableArray<(string, string)>.Empty,
EnableAdditionalChecks = true
};
diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs
index eaf79450e5..200969bb40 100644
--- a/src/QsCompiler/Compiler/CompilationLoader.cs
+++ b/src/QsCompiler/Compiler/CompilationLoader.cs
@@ -1,905 +1,924 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-using System;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Reflection.Metadata;
-using System.Reflection.PortableExecutable;
-using Microsoft.Quantum.QsCompiler.BuiltInRewriteSteps;
-using Microsoft.Quantum.QsCompiler.CompilationBuilder;
-using Microsoft.Quantum.QsCompiler.DataTypes;
-using Microsoft.Quantum.QsCompiler.Diagnostics;
-using Microsoft.Quantum.QsCompiler.Documentation;
-using Microsoft.Quantum.QsCompiler.ReservedKeywords;
-using Microsoft.Quantum.QsCompiler.Serialization;
-using Microsoft.Quantum.QsCompiler.SyntaxTree;
-using Microsoft.Quantum.QsCompiler.Transformations.BasicTransformations;
-using Microsoft.VisualStudio.LanguageServer.Protocol;
-using Newtonsoft.Json.Bson;
-using MetadataReference = Microsoft.CodeAnalysis.MetadataReference;
-
-
-namespace Microsoft.Quantum.QsCompiler
-{
- public class CompilationLoader
- {
- ///
- /// Represents the type of a task event.
- ///
- public enum CompilationTaskEventType
- {
- Start,
- End
- }
-
- ///
- /// Represents the arguments associated to a task event.
- ///
- public class CompilationTaskEventArgs : EventArgs
- {
- public CompilationTaskEventType Type;
- public string ParentTaskName;
- public string TaskName;
-
- public CompilationTaskEventArgs(CompilationTaskEventType type, string parentTaskName, string taskName)
- {
- ParentTaskName = parentTaskName;
- TaskName = taskName;
- Type = type;
- }
- }
-
- ///
- /// Defines the handler for compilation task events.
- ///
- public delegate void CompilationTaskEventHandler(object sender, CompilationTaskEventArgs args);
- ///
- /// Given a load function that loads the content of a sequence of files from disk,
- /// returns the content for all sources to compile.
- ///
- public delegate ImmutableDictionary SourceLoader(Func, ImmutableDictionary> loadFromDisk);
- ///
- /// Given a load function that loads the content of a sequence of referenced assemblies from disk,
- /// returns the loaded references for the compilation.
- ///
- public delegate References ReferenceLoader(Func, References> loadFromDisk);
- ///
- /// Used to raise a compilation task event.
- ///
- public static event CompilationTaskEventHandler CompilationTaskEvent;
- ///
- /// If LoadAssembly is not null, it will be used to load the dlls that are search for classes defining rewrite steps.
- ///
- public static Func LoadAssembly { get; set; }
-
-
- ///
- /// may be specified via configuration (or project) file in the future
- ///
- public struct Configuration
- {
- ///
- /// The name of the project. Used as assembly name in the generated dll.
- /// The name of the project with a suitable extension will also be used as the name of the generated binary file.
- ///
- public string ProjectName;
- ///
- /// If set to true, the syntax tree rewrite step that replaces all generation directives
- /// for all functor specializations is executed during compilation.
- ///
- public bool GenerateFunctorSupport;
- ///
- /// Unless this is set to true, the syntax tree rewrite step that eliminates selective abstractions is executed during compilation.
- /// In particular, all conjugations are inlined.
- ///
- public bool SkipSyntaxTreeTrimming;
- ///
- /// If set to true, the compiler attempts to pre-evaluate the built compilation as much as possible.
- /// This is an experimental feature that will change over time.
- ///
- public bool AttemptFullPreEvaluation;
- ///
- /// Unless this is set to true, all usages of type-parameterized callables are replaced with
- /// the concrete callable instantiation if an entry point is specified for the compilation.
- /// Removes all type-parameterizations in the syntax tree.
- ///
- public bool SkipMonomorphization;
- ///
- /// If the output folder is not null,
- /// documentation is generated in the specified folder based on doc comments in the source code.
- ///
- public string DocumentationOutputFolder;
- ///
- /// Directory where the compiled binaries will be generated.
- /// No binaries will be written to disk unless this path is specified and valid.
- ///
- public string BuildOutputFolder;
- ///
- /// Output path for the dll containing the compiled binaries.
- /// No dll will be generated unless this path is specified and valid.
- ///
- public string DllOutputPath;
- ///
- /// If set to true, then referenced dlls will be loaded purely based on attributes in the contained C# code.
- /// Any Q# resources will be ignored.
- ///
- public bool LoadReferencesBasedOnGeneratedCsharp;
- ///
- /// Contains a sequence of tuples with the path to a dotnet dll containing one or more rewrite steps
- /// (i.e. classes implementing IRewriteStep) and the corresponding output folder.
- /// The contained rewrite steps will be executed in the defined order and priority at the end of the compilation.
- ///
- public IEnumerable<(string, string)> RewriteSteps;
- ///
- /// If set to true, the post-condition for loaded rewrite steps is checked if the corresponding verification is implemented.
- /// Otherwise post-condition verifications are skipped.
- ///
- public bool EnableAdditionalChecks;
- ///
- /// Handle to pass arbitrary constants with which to populate the corresponding dictionary for loaded rewrite steps.
- /// These values will take precedence over any already existing values that the default constructor sets.
- /// However, the compiler may overwrite the assembly constants defined for the Q# compilation unit in the dictionary of the loaded step.
- /// The given dictionary in this configuration is left unchanged in any case.
- ///
- public IReadOnlyDictionary AssemblyConstants;
-
- ///
- /// Indicates whether a serialization of the syntax tree needs to be generated.
- /// This is the case if either the build output folder is specified or the dll output path is specified.
- ///
- internal bool SerializeSyntaxTree =>
- BuildOutputFolder != null || DllOutputPath != null;
-
- ///
- /// If the ProjectName does not have an ending "proj", appends a .qsproj ending to the project name.
- /// Returns null if the project name is null.
- ///
- internal string ProjectNameWithExtension =>
- this.ProjectName == null ? null :
- this.ProjectName.EndsWith("proj") ? this.ProjectName :
- $"{this.ProjectName}.qsproj";
-
- ///
- /// If the ProjectName does have an extension ending with "proj", returns the project name without that extension.
- /// Returns null if the project name is null.
- ///
- internal string ProjectNameWithoutExtension =>
- this.ProjectName == null ? null :
- Path.GetExtension(this.ProjectName).EndsWith("proj") ? Path.GetFileNameWithoutExtension(this.ProjectName) :
- this.ProjectName;
- }
-
- ///
- /// used to indicate the status of individual compilation steps
- ///
- public enum Status { NotRun = -1, Succeeded = 0, Failed = 1 }
-
- private class ExecutionStatus
- {
- internal Status SourceFileLoading = Status.NotRun;
- internal Status ReferenceLoading = Status.NotRun;
- internal Status PluginLoading = Status.NotRun;
- internal Status Validation = Status.NotRun;
- internal Status FunctorSupport = Status.NotRun;
- internal Status PreEvaluation = Status.NotRun;
- internal Status TreeTrimming = Status.NotRun;
- internal Status Monomorphization = Status.NotRun;
- internal Status Documentation = Status.NotRun;
- internal Status Serialization = Status.NotRun;
- internal Status BinaryFormat = Status.NotRun;
- internal Status DllGeneration = Status.NotRun;
- internal Status[] LoadedRewriteSteps;
-
- internal ExecutionStatus(IEnumerable externalRewriteSteps) =>
- this.LoadedRewriteSteps = externalRewriteSteps.Select(_ => Status.NotRun).ToArray();
-
- private bool WasSuccessful(bool run, Status code) =>
- (run && code == Status.Succeeded) || (!run && code == Status.NotRun);
-
- internal bool Success(Configuration options, bool isExe) =>
- this.SourceFileLoading <= 0 &&
- this.ReferenceLoading <= 0 &&
- WasSuccessful(true, this.Validation) &&
- WasSuccessful(true, this.PluginLoading) &&
- WasSuccessful(options.GenerateFunctorSupport, this.FunctorSupport) &&
- WasSuccessful(options.AttemptFullPreEvaluation, this.PreEvaluation) &&
- WasSuccessful(!options.SkipSyntaxTreeTrimming, this.TreeTrimming) &&
- WasSuccessful(isExe && !options.SkipMonomorphization, this.Monomorphization) &&
- WasSuccessful(options.DocumentationOutputFolder != null, this.Documentation) &&
- WasSuccessful(options.SerializeSyntaxTree, this.Serialization) &&
- WasSuccessful(options.BuildOutputFolder != null, this.BinaryFormat) &&
- WasSuccessful(options.DllOutputPath != null, this.DllGeneration) &&
- this.LoadedRewriteSteps.All(status => WasSuccessful(true, status));
- }
-
- ///
- /// Indicates whether all source files were loaded successfully.
- /// Source file loading may not be executed if the content was preloaded using methods outside this class.
- ///
- public Status SourceFileLoading => this.CompilationStatus.SourceFileLoading;
- ///
- /// Indicates whether all references were loaded successfully.
- /// The loading may not be executed if all references were preloaded using methods outside this class.
- ///
- public Status ReferenceLoading => this.CompilationStatus.ReferenceLoading;
- ///
- /// Indicates whether all external dlls specifying e.g. rewrite steps
- /// to perform as part of the compilation have been loaded successfully.
- /// The status indicates a successful execution if no such external dlls have been specified.
- ///
- public Status PluginLoading => this.CompilationStatus.PluginLoading;
- ///
- /// Indicates whether the compilation unit passed the compiler validation
- /// that is executed before invoking further rewrite and/or generation steps.
- ///
- public Status Validation => this.CompilationStatus.Validation;
- ///
- /// Indicates whether all specializations were generated successfully.
- /// This rewrite step is only executed if the corresponding configuration is specified.
- ///
- public Status FunctorSupport => this.CompilationStatus.FunctorSupport;
- ///
- /// Indicates whether the pre-evaluation step executed successfully.
- /// This rewrite step is only executed if the corresponding configuration is specified.
- ///
- public Status PreEvaluation => this.CompilationStatus.PreEvaluation;
- ///
- /// Indicates whether all the type-parameterized callables were resolved to concrete callables.
- /// This rewrite step is only executed if the corresponding configuration is specified.
- ///
- public Status Monomorphization => this.CompilationStatus.Monomorphization;
- ///
- /// Indicates whether documentation for the compilation was generated successfully.
- /// This step is only executed if the corresponding configuration is specified.
- ///
- public Status Documentation => this.CompilationStatus.Documentation;
- ///
- /// Indicates whether the built compilation could be serialized successfully.
- /// This step is only executed if either the binary representation or a dll is emitted.
- ///
- public Status Serialization => this.CompilationStatus.Serialization;
- ///
- /// Indicates whether a binary representation for the generated syntax tree has been generated successfully.
- /// This step is only executed if the corresponding configuration is specified.
- ///
- public Status BinaryFormat => this.CompilationStatus.BinaryFormat;
- ///
- /// Indicates whether a dll containing the compiled binary has been generated successfully.
- /// This step is only executed if the corresponding configuration is specified.
- ///
- public Status DllGeneration => this.CompilationStatus.DllGeneration;
-
- ///
- /// Indicates whether all rewrite steps with the given name and loaded from the given source executed successfully.
- /// The source, if specified, is the path to the dll in which the step is specified.
- /// Returns a status NotRun if no such step was found or executed.
- /// Execution is considered successful if the precondition and transformation (if any) returned true.
- ///
- public Status LoadedRewriteStep(string name, string source = null)
- {
- var uri = String.IsNullOrWhiteSpace(source) ? null : new Uri(Path.GetFullPath(source));
- bool MatchesQuery(int index) => this.ExternalRewriteSteps[index].Name == name && (source == null || this.ExternalRewriteSteps[index].Origin == uri);
- var statuses = this.CompilationStatus.LoadedRewriteSteps.Where((s, i) => MatchesQuery(i)).ToArray();
- return statuses.All(s => s == Status.Succeeded) ? Status.Succeeded : statuses.Any(s => s == Status.Failed) ? Status.Failed : Status.NotRun;
- }
- ///
- /// Indicates the overall status of all rewrite step from external dlls.
- /// The status is indicated as success if none of these steps failed.
- ///
- public Status AllLoadedRewriteSteps => this.CompilationStatus.LoadedRewriteSteps.Any(s => s == Status.Failed) ? Status.Failed : Status.Succeeded;
- ///
- /// Indicates the overall success of all compilation steps.
- /// The compilation is indicated as having been successful if all steps that were configured to execute completed successfully.
- ///
- public bool Success => this.CompilationStatus.Success(this.Config, this.CompilationOutput?.EntryPoints.Length != 0);
-
-
- ///
- /// Logger used to log all diagnostic events during compilation.
- ///
- private readonly ILogger Logger;
- ///
- /// Configuration specifying the compilation steps to execute.
- ///
- private readonly Configuration Config;
- ///
- /// Used to track the status of individual compilation steps.
- ///
- private readonly ExecutionStatus CompilationStatus;
- ///
- /// Contains all loaded rewrite steps found in the specified plugin dlls,
- /// where configurable properties such as the output folder have already been initialized to suitable values.
- ///
- private readonly ImmutableArray ExternalRewriteSteps;
-
- ///
- /// Contains all diagnostics generated upon source file and reference loading.
- /// All other diagnostics can be accessed via the VerifiedCompilation.
- ///
- public ImmutableArray LoadDiagnostics;
- ///
- /// Contains the initial compilation built by the compilation unit manager after verification.
- ///
- public readonly CompilationUnitManager.Compilation VerifiedCompilation;
- ///
- /// Contains the built compilation including the syntax tree after executing all configured rewrite steps.
- ///
- public readonly QsCompilation CompilationOutput;
- ///
- /// Contains the absolute path where the binary representation of the generated syntax tree has been written to disk.
- ///
- public readonly string PathToCompiledBinary;
- ///
- /// Contains the absolute path where the generated dll containing the compiled binary has been written to disk.
- ///
- public readonly string DllOutputPath;
-
- ///
- /// Contains the full Q# syntax tree after executing all configured rewrite steps, including the content of loaded references.
- ///
- public IEnumerable GeneratedSyntaxTree =>
- this.CompilationOutput?.Namespaces;
-
- ///
- /// Contains the Uri and names of all rewrite steps loaded from the specified dlls
- /// in the order in which they are executed.
- ///
- public ImmutableArray<(Uri, string)> LoadedRewriteSteps =>
- this.ExternalRewriteSteps.Select(step => (step.Origin, step.Name)).ToImmutableArray();
-
-
- ///
- /// Builds the compilation for the source files and references loaded by the given loaders,
- /// executing the compilation steps specified by the given options.
- /// Uses the specified logger to log all diagnostic events.
- /// Throws an ArgumentNullException if either one of the given loaders is null or returns null.
- ///
- public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReferences, Configuration? options = null, ILogger logger = null)
- {
- RaiseCompilationTaskStart(null, "OverallCompilation");
-
- // loading the content to compiler
-
- this.Logger = logger;
- this.LoadDiagnostics = ImmutableArray.Empty;
- this.Config = options ?? new Configuration();
-
- Status rewriteStepLoading = Status.Succeeded;
- this.ExternalRewriteSteps = RewriteSteps.Load(this.Config,
- d => this.LogAndUpdateLoadDiagnostics(ref rewriteStepLoading, d),
- ex => this.LogAndUpdate(ref rewriteStepLoading, ex));
- this.PrintLoadedRewriteSteps(this.ExternalRewriteSteps);
- this.CompilationStatus = new ExecutionStatus(this.ExternalRewriteSteps);
- this.CompilationStatus.PluginLoading = rewriteStepLoading;
-
- RaiseCompilationTaskStart("OverallCompilation", "SourcesLoading");
- var sourceFiles = loadSources?.Invoke(this.LoadSourceFiles)
- ?? throw new ArgumentNullException("unable to load source files");
- RaiseCompilationTaskEnd("OverallCompilation", "SourcesLoading");
- RaiseCompilationTaskStart("OverallCompilation", "ReferenceLoading");
- var references = loadReferences?.Invoke(refs => this.LoadAssemblies(refs, this.Config.LoadReferencesBasedOnGeneratedCsharp))
- ?? throw new ArgumentNullException("unable to load referenced binary files");
- RaiseCompilationTaskEnd("OverallCompilation", "ReferenceLoading");
-
- // building the compilation
-
- RaiseCompilationTaskStart("OverallCompilation", "Build");
- this.CompilationStatus.Validation = Status.Succeeded;
- var files = CompilationUnitManager.InitializeFileManagers(sourceFiles, null, this.OnCompilerException); // do *not* live track (i.e. use publishing) here!
- var compilationManager = new CompilationUnitManager(this.OnCompilerException);
- compilationManager.UpdateReferencesAsync(references);
- compilationManager.AddOrUpdateSourceFilesAsync(files);
- this.VerifiedCompilation = compilationManager.Build();
- this.CompilationOutput = this.VerifiedCompilation?.BuiltCompilation;
- compilationManager.Dispose();
-
- foreach (var diag in this.VerifiedCompilation?.Diagnostics() ?? Enumerable.Empty())
- { this.LogAndUpdate(ref this.CompilationStatus.Validation, diag); }
-
- // executing the specified rewrite steps
-
- if (!this.Config.SkipMonomorphization && this.CompilationOutput?.EntryPoints.Length != 0)
- {
- if (!Uri.TryCreate(Assembly.GetExecutingAssembly().CodeBase, UriKind.Absolute, out Uri thisDllUri))
- { thisDllUri = new Uri(Path.GetFullPath(".", "CompilationLoader.cs")); }
- var rewriteStep = new RewriteSteps.LoadedStep(new Monomorphization(), typeof(IRewriteStep), thisDllUri);
- this.CompilationStatus.Monomorphization = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out this.CompilationOutput);
- }
-
- if (this.Config.GenerateFunctorSupport)
- {
- this.CompilationStatus.FunctorSupport = Status.Succeeded;
- void onException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.FunctorSupport, ex);
- var generated = this.CompilationOutput != null && CodeGeneration.GenerateFunctorSpecializations(this.CompilationOutput, out this.CompilationOutput, onException);
- if (!generated) this.LogAndUpdate(ref this.CompilationStatus.FunctorSupport, ErrorCode.FunctorGenerationFailed, Enumerable.Empty());
- }
-
- if (!this.Config.SkipSyntaxTreeTrimming)
- {
- this.CompilationStatus.TreeTrimming = Status.Succeeded;
- void onException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.TreeTrimming, ex);
- var trimmed = this.CompilationOutput != null && this.CompilationOutput.InlineConjugations(out this.CompilationOutput, onException);
- if (!trimmed) this.LogAndUpdate(ref this.CompilationStatus.TreeTrimming, ErrorCode.TreeTrimmingFailed, Enumerable.Empty());
- }
-
- if (this.Config.AttemptFullPreEvaluation)
- {
- this.CompilationStatus.PreEvaluation = Status.Succeeded;
- void onException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.PreEvaluation, ex);
- var evaluated = this.CompilationOutput != null && this.CompilationOutput.PreEvaluateAll(out this.CompilationOutput, onException);
- if (!evaluated) this.LogAndUpdate(ref this.CompilationStatus.PreEvaluation, ErrorCode.PreEvaluationFailed, Enumerable.Empty());
- }
-
- RaiseCompilationTaskEnd("OverallCompilation", "Build");
-
- // generating the compiled binary and dll
-
- RaiseCompilationTaskStart("OverallCompilation", "OutputGeneration");
- using (var ms = new MemoryStream())
- {
- RaiseCompilationTaskStart("OutputGeneration", "SyntaxTreeSerialization");
- var serialized = this.Config.SerializeSyntaxTree && this.SerializeSyntaxTree(ms);
- RaiseCompilationTaskEnd("OutputGeneration", "SyntaxTreeSerialization");
- if (serialized && this.Config.BuildOutputFolder != null)
- {
- RaiseCompilationTaskStart("OutputGeneration", "BinaryGeneration");
- this.PathToCompiledBinary = this.GenerateBinary(ms);
- RaiseCompilationTaskEnd("OutputGeneration", "BinaryGeneration");
- }
- if (serialized && this.Config.DllOutputPath != null)
- {
- RaiseCompilationTaskStart("OutputGeneration", "DllGeneration");
- this.DllOutputPath = this.GenerateDll(ms);
- RaiseCompilationTaskEnd("OutputGeneration", "DllGeneration");
- }
- }
-
- // executing the specified generation steps
-
- if (this.Config.DocumentationOutputFolder != null)
- {
- RaiseCompilationTaskStart("OutputGeneration", "DocumentationGeneration");
- this.CompilationStatus.Documentation = Status.Succeeded;
- var docsFolder = Path.GetFullPath(String.IsNullOrWhiteSpace(this.Config.DocumentationOutputFolder) ? "." : this.Config.DocumentationOutputFolder);
- void onDocException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.Documentation, ex);
- var docsGenerated = this.VerifiedCompilation != null && DocBuilder.Run(docsFolder, this.VerifiedCompilation.SyntaxTree.Values, this.VerifiedCompilation.SourceFiles, onException: onDocException);
- if (!docsGenerated) this.LogAndUpdate(ref this.CompilationStatus.Documentation, ErrorCode.DocGenerationFailed, Enumerable.Empty());
- RaiseCompilationTaskEnd("OutputGeneration", "DocumentationGeneration");
- }
-
- RaiseCompilationTaskEnd("OverallCompilation", "OutputGeneration");
-
- // invoking rewrite steps in external dlls
-
- RaiseCompilationTaskStart("OverallCompilation", "RewriteSteps");
- for (int i = 0; i < this.ExternalRewriteSteps.Length; i++)
- {
- if (this.CompilationOutput == null) continue;
- var executed = this.ExecuteRewriteStep(this.ExternalRewriteSteps[i], this.CompilationOutput, out var transformed);
- if (executed == Status.Succeeded) this.CompilationOutput = transformed;
- this.CompilationStatus.LoadedRewriteSteps[i] = executed;
- }
-
- RaiseCompilationTaskEnd("OverallCompilation", "RewriteSteps");
- RaiseCompilationTaskEnd(null, "OverallCompilation");
- }
-
- ///
- /// Executes the given rewrite step on the given compilation, returning a transformed compilation as an out parameter.
- /// Catches and logs any thrown exception. Returns the status of the rewrite step.
- /// Throws an ArgumentNullException if the rewrite step to execute or the given compilation is null.
- ///
- private Status ExecuteRewriteStep(RewriteSteps.LoadedStep rewriteStep, QsCompilation compilation, out QsCompilation transformed)
- {
- if (rewriteStep == null) throw new ArgumentNullException(nameof(rewriteStep));
- if (compilation == null) throw new ArgumentNullException(nameof(compilation));
-
- string GetDiagnosticsCode(DiagnosticSeverity severity) =>
- rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Error ? Errors.Code(ErrorCode.CsharpGenerationGeneratedError) :
- rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Warning ? Warnings.Code(WarningCode.CsharpGenerationGeneratedWarning) :
- rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Information ? Informations.Code(InformationCode.CsharpGenerationGeneratedInfo) :
- null;
-
- Status LogDiagnostics(Status status = Status.Succeeded)
- {
- try
- {
- foreach (var diagnostic in rewriteStep.GeneratedDiagnostics ?? ImmutableArray.Empty)
- { this.LogAndUpdate(ref status, RewriteSteps.LoadedStep.ConvertDiagnostic(diagnostic, GetDiagnosticsCode)); }
- }
- catch { this.LogAndUpdate(ref status, Warning(WarningCode.RewriteStepDiagnosticsGenerationFailed, new[] { rewriteStep.Name })); }
- return status;
- }
-
- var status = Status.Succeeded;
- var messageSource = ProjectManager.MessageSource(rewriteStep.Origin);
- Diagnostic Warning(WarningCode code, params string[] args) => Warnings.LoadWarning(code, args, messageSource);
- try
- {
- transformed = compilation;
- var preconditionFailed = rewriteStep.ImplementsPreconditionVerification && !rewriteStep.PreconditionVerification(compilation);
- if (preconditionFailed)
- {
- LogDiagnostics();
- this.LogAndUpdate(ref status, Warning(WarningCode.PreconditionVerificationFailed, new[] { rewriteStep.Name, messageSource }));
- return status;
- }
-
- var transformationFailed = rewriteStep.ImplementsTransformation && !rewriteStep.Transformation(compilation, out transformed);
- var postconditionFailed = this.Config.EnableAdditionalChecks && rewriteStep.ImplementsPostconditionVerification && !rewriteStep.PostconditionVerification(transformed);
- LogDiagnostics();
-
- if (transformationFailed) this.LogAndUpdate(ref status, ErrorCode.RewriteStepExecutionFailed, new[] { rewriteStep.Name, messageSource });
- if (postconditionFailed) this.LogAndUpdate(ref status, ErrorCode.PostconditionVerificationFailed, new[] { rewriteStep.Name, messageSource });
- return status;
- }
- catch (Exception ex)
- {
- this.LogAndUpdate(ref status, ex);
- var isLoadException = ex is FileLoadException || ex.InnerException is FileLoadException;
- if (isLoadException) this.LogAndUpdate(ref status, ErrorCode.FileNotFoundDuringPluginExecution, new[] { rewriteStep.Name, messageSource });
- else this.LogAndUpdate(ref status, ErrorCode.PluginExecutionFailed, new[] { rewriteStep.Name, messageSource });
- transformed = null;
- }
- return status;
- }
-
- ///
- /// Builds the compilation of the specified source files and references,
- /// executing the compilation steps specified by the given options.
- /// Uses the specified logger to log all diagnostic events.
- ///
- public CompilationLoader(IEnumerable sources, IEnumerable references, Configuration? options = null, ILogger logger = null)
- : this(load => load(sources), load => load(references), options, logger) { }
-
- ///
- /// Builds the compilation of the specified source files and the loaded references returned by the given loader,
- /// executing the compilation steps specified by the given options.
- /// Uses the specified logger to log all diagnostic events.
- /// Throws an ArgumentNullException if the given loader is null or returns null.
- ///
- public CompilationLoader(IEnumerable sources, ReferenceLoader loadReferences, Configuration? options = null, ILogger logger = null)
- : this(load => load(sources), loadReferences, options, logger) { }
-
- ///
- /// Builds the compilation of the content returned by the given loader and the specified references,
- /// executing the compilation steps specified by the given options.
- /// Uses the specified logger to log all diagnostic events.
- /// Throws an ArgumentNullException if the given loader is null or returns null.
- ///
- public CompilationLoader(SourceLoader loadSources, IEnumerable references, Configuration? options = null, ILogger logger = null)
- : this(loadSources, load => load(references), options, logger) { }
-
-
- // private routines used for logging and status updates
-
- ///
- /// Logs the given diagnostic and updates the status passed as reference accordingly.
- /// Throws an ArgumentNullException if the given diagnostic is null.
- ///
- private void LogAndUpdate(ref Status current, Diagnostic d)
- {
- this.Logger?.Log(d);
- if (d.IsError()) current = Status.Failed;
- }
-
- ///
- /// Logs the given exception and updates the status passed as reference accordingly.
- ///
- private void LogAndUpdate(ref Status current, Exception ex)
- {
- this.Logger?.Log(ex);
- current = Status.Failed;
- }
-
- ///
- /// Logs an error with the given error code and message parameters, and updates the status passed as reference accordingly.
- ///
- private void LogAndUpdate(ref Status current, ErrorCode code, IEnumerable args)
- {
- this.Logger?.Log(code, args);
- current = Status.Failed;
- }
-
- ///
- /// Logs the given diagnostic and updates the status passed as reference accordingly.
- /// Adds the given diagnostic to the tracked load diagnostics.
- /// Throws an ArgumentNullException if the given diagnostic is null.
- ///
- private void LogAndUpdateLoadDiagnostics(ref Status current, Diagnostic d)
- {
- this.LoadDiagnostics = this.LoadDiagnostics.Add(d);
- this.LogAndUpdate(ref current, d);
- }
-
- ///
- /// Logs an UnexpectedCompilerException error as well as the given exception, and updates the validation status accordingly.
- ///
- private void OnCompilerException(Exception ex)
- {
- this.LogAndUpdate(ref this.CompilationStatus.Validation, ErrorCode.UnexpectedCompilerException, Enumerable.Empty());
- this.LogAndUpdate(ref this.CompilationStatus.Validation, ex);
- }
-
- ///
- /// Logs the names of the given source files as Information.
- /// Does nothing if the given argument is null.
- ///
- private void PrintResolvedFiles(IEnumerable sourceFiles)
- {
- if (sourceFiles == null) return;
- var args = sourceFiles.Any()
- ? sourceFiles.Select(f => f?.LocalPath).ToArray()
- : new string[] { "(none)" };
- this.Logger?.Log(InformationCode.CompilingWithSourceFiles, Enumerable.Empty(), messageParam: Formatting.Indent(args).ToArray());
- }
-
- ///
- /// Logs the names of the given assemblies as Information.
- /// Does nothing if the given argument is null.
- ///
- private void PrintResolvedAssemblies(IEnumerable> assemblies)
- {
- if (assemblies == null) return;
- var args = assemblies.Any()
- ? assemblies.Select(name => name.Value).ToArray()
- : new string[] { "(none)" };
- this.Logger?.Log(InformationCode.CompilingWithAssemblies, Enumerable.Empty(), messageParam: Formatting.Indent(args).ToArray());
- }
-
- ///
- /// Logs the names and origins of the given rewrite steps as Information.
- /// Does nothing if the given argument is null.
- ///
- private void PrintLoadedRewriteSteps(IEnumerable rewriteSteps)
- {
- if (rewriteSteps == null) return;
- var args = rewriteSteps.Any()
- ? rewriteSteps.Select(step => $"{step.Name} ({step.Origin})").ToArray()
- : new string[] { "(none)" };
- this.Logger?.Log(InformationCode.LoadedRewriteSteps, Enumerable.Empty(), messageParam: Formatting.Indent(args).ToArray());
- }
-
-
- // routines for loading from and dumping to files
-
- ///
- /// Used to load the content of the specified source files from disk.
- /// Returns a dictionary mapping the file uri to its content.
- /// Logs suitable diagnostics in the process and modifies the compilation status accordingly.
- /// Prints all loaded files using PrintResolvedFiles.
- ///
- private ImmutableDictionary LoadSourceFiles(IEnumerable sources)
- {
- this.CompilationStatus.SourceFileLoading = 0;
- if (sources == null) this.LogAndUpdate(ref this.CompilationStatus.SourceFileLoading, ErrorCode.SourceFilesMissing, Enumerable.Empty());
- void onException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.SourceFileLoading, ex);
- void onDiagnostic(Diagnostic d) => this.LogAndUpdateLoadDiagnostics(ref this.CompilationStatus.SourceFileLoading, d);
- var sourceFiles = ProjectManager.LoadSourceFiles(sources ?? Enumerable.Empty(), onDiagnostic, onException);
- this.PrintResolvedFiles(sourceFiles.Keys);
- return sourceFiles;
- }
-
- ///
- /// Used to load the content of the specified assembly references from disk.
- /// Returns the loaded content of the references.
- /// Logs suitable diagnostics in the process and modifies the compilation status accordingly.
- /// Prints all loaded files using PrintResolvedAssemblies.
- ///
- private References LoadAssemblies(IEnumerable refs, bool ignoreDllResources)
- {
- this.CompilationStatus.ReferenceLoading = 0;
- if (refs == null) this.Logger?.Log(WarningCode.ReferencesSetToNull, Enumerable.Empty());
- void onException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.ReferenceLoading, ex);
- void onDiagnostic(Diagnostic d) => this.LogAndUpdateLoadDiagnostics(ref this.CompilationStatus.ReferenceLoading, d);
- var headers = ProjectManager.LoadReferencedAssemblies(refs ?? Enumerable.Empty(), onDiagnostic, onException, ignoreDllResources);
- var projId = this.Config.ProjectName == null ? null : Path.ChangeExtension(Path.GetFullPath(this.Config.ProjectNameWithExtension), "qsproj");
- var references = new References(headers, (code, args) => onDiagnostic(Errors.LoadError(code, args, projId)));
- this.PrintResolvedAssemblies(references.Declarations.Keys);
- return references;
- }
-
- ///
- /// Writes a binary representation of the built Q# compilation output to the given memory stream.
- /// Logs suitable diagnostics in the process and modifies the compilation status accordingly.
- /// Does *not* close the given memory stream, and
- /// returns true if the serialization has been successfully generated.
- /// Throws an ArgumentNullException if the given memory stream is null.
- ///
- private bool SerializeSyntaxTree(MemoryStream ms)
- {
- if (ms == null) throw new ArgumentNullException(nameof(ms));
- bool ErrorAndReturn()
- {
- this.LogAndUpdate(ref this.CompilationStatus.Serialization, ErrorCode.SerializationFailed, Enumerable.Empty());
- return false;
- }
- this.CompilationStatus.Serialization = 0;
- if (this.CompilationOutput == null) ErrorAndReturn();
-
- using var writer = new BsonDataWriter(ms) { CloseOutput = false };
- var fromSources = this.CompilationOutput.Namespaces.Select(ns => FilterBySourceFile.Apply(ns, s => s.Value.EndsWith(".qs")));
- var compilation = new QsCompilation(fromSources.ToImmutableArray(), this.CompilationOutput.EntryPoints);
- try { Json.Serializer.Serialize(writer, compilation); }
- catch (Exception ex)
- {
- this.LogAndUpdate(ref this.CompilationStatus.Serialization, ex);
- ErrorAndReturn();
- }
- return true;
- }
-
- ///
- /// Backtracks to the beginning of the given memory stream and writes its content to disk,
- /// generating a suitable bson file in the specified build output folder using the project name as file name.
- /// Generates a file name at random if no project name is specified.
- /// Logs suitable diagnostics in the process and modifies the compilation status accordingly.
- /// Returns the absolute path of the file where the binary representation has been generated.
- /// Returns null if the binary file could not be generated.
- /// Does *not* close the given memory stream.
- /// Throws an ArgumentNullException if the given memory stream is null.
- ///
- private string GenerateBinary(MemoryStream serialization)
- {
- if (serialization == null) throw new ArgumentNullException(nameof(serialization));
- this.CompilationStatus.BinaryFormat = 0;
-
- var projId = NonNullable.New(Path.GetFullPath(this.Config.ProjectNameWithExtension ?? Path.GetRandomFileName()));
- var outFolder = Path.GetFullPath(String.IsNullOrWhiteSpace(this.Config.BuildOutputFolder) ? "." : this.Config.BuildOutputFolder);
- var target = GeneratedFile(projId, outFolder, ".bson", "");
-
- try
- {
- serialization.Seek(0, SeekOrigin.Begin);
- using (var file = new FileStream(target, FileMode.Create, FileAccess.Write))
- { serialization.WriteTo(file); }
- return target;
- }
- catch (Exception ex)
- {
- this.LogAndUpdate(ref this.CompilationStatus.BinaryFormat, ex);
- this.LogAndUpdate(ref this.CompilationStatus.BinaryFormat, ErrorCode.GeneratingBinaryFailed, Enumerable.Empty());
- return null;
- }
- }
-
- ///
- /// Backtracks to the beginning of the given memory stream and,
- /// assuming the given memory stream contains a serialization of the compiled syntax tree,
- /// generates a dll containing the compiled binary at the specified dll output path.
- /// Logs suitable diagnostics in the process and modifies the compilation status accordingly.
- /// Returns the absolute path of the file where the dll has been generated.
- /// Returns null if the dll could not be generated.
- /// Does *not* close the given memory stream.
- /// Throws an ArgumentNullException if the given memory stream is null.
- ///
- private string GenerateDll(MemoryStream serialization)
- {
- if (serialization == null) throw new ArgumentNullException(nameof(serialization));
- this.CompilationStatus.DllGeneration = 0;
-
- var fallbackFileName = (this.PathToCompiledBinary ?? this.Config.ProjectNameWithExtension) ?? Path.GetRandomFileName();
- var outputPath = Path.GetFullPath(String.IsNullOrWhiteSpace(this.Config.DllOutputPath) ? fallbackFileName : this.Config.DllOutputPath);
- outputPath = Path.ChangeExtension(outputPath, "dll");
-
- MetadataReference CreateReference(string file, int id) =>
- MetadataReference.CreateFromFile(file)
- .WithAliases(new string[] { $"{DotnetCoreDll.ReferenceAlias}{id}" }); // referenced Q# dlls are recognized based on this alias
-
- // We need to force the inclusion of references despite that we do not include C# code that depends on them.
- // This is done via generating a certain handle in all dlls built via this compilation loader.
- // This checks if that handle is available to merely generate a warning if we can't include the reference.
- bool CanBeIncluded(NonNullable dll)
- {
- try // no need to throw in case this fails - ignore the reference instead
- {
- using var stream = File.OpenRead(dll.Value);
- using var assemblyFile = new PEReader(stream);
- var metadataReader = assemblyFile.GetMetadataReader();
- return metadataReader.TypeDefinitions
- .Select(metadataReader.GetTypeDefinition)
- .Any(t => metadataReader.GetString(t.Namespace) == DotnetCoreDll.MetadataNamespace);
- }
- catch { return false; }
- }
-
- try
- {
- var referencePaths = GetSourceFiles.Apply(this.CompilationOutput.Namespaces) // we choose to keep only Q# references that have been used
- .Where(file => file.Value.EndsWith(".dll"));
- var references = referencePaths.Select((dll, id) => (dll, CreateReference(dll.Value, id), CanBeIncluded(dll))).ToImmutableArray();
- var csharpTree = MetadataGeneration.GenerateAssemblyMetadata(references.Where(r => r.Item3).Select(r => r.Item2));
- foreach (var (dropped, _, _) in references.Where(r => !r.Item3))
- {
- var warning = Warnings.LoadWarning(WarningCode.ReferenceCannotBeIncludedInDll, new[] { dropped.Value }, null);
- this.LogAndUpdate(ref this.CompilationStatus.DllGeneration, warning);
- }
-
- var compilation = CodeAnalysis.CSharp.CSharpCompilation.Create(
- this.Config.ProjectNameWithoutExtension ?? Path.GetFileNameWithoutExtension(outputPath),
- syntaxTrees: new[] { csharpTree },
- references: references.Select(r => r.Item2).Append(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)), // if System.Object can't be found a warning is generated
- options: new CodeAnalysis.CSharp.CSharpCompilationOptions(outputKind: CodeAnalysis.OutputKind.DynamicallyLinkedLibrary)
- );
-
- using var outputStream = File.OpenWrite(outputPath);
- serialization.Seek(0, SeekOrigin.Begin);
- var astResource = new CodeAnalysis.ResourceDescription(DotnetCoreDll.ResourceName, () => serialization, true);
- var result = compilation.Emit(outputStream,
- options: new CodeAnalysis.Emit.EmitOptions(),
- manifestResources: new CodeAnalysis.ResourceDescription[] { astResource }
- );
-
- var errs = result.Diagnostics.Where(d => d.Severity >= CodeAnalysis.DiagnosticSeverity.Error);
- if (errs.Any()) throw new Exception($"error(s) on emitting dll: {Environment.NewLine}{String.Join(Environment.NewLine, errs.Select(d => d.GetMessage()))}");
- return outputPath;
- }
- catch (Exception ex)
- {
- this.LogAndUpdate(ref this.CompilationStatus.DllGeneration, ex);
- this.LogAndUpdate(ref this.CompilationStatus.DllGeneration, ErrorCode.GeneratingDllFailed, Enumerable.Empty());
- return null;
- }
- }
-
- ///
- /// Given the path to a Q# binary file, reads the content of that file and returns the corresponding compilation as out parameter.
- /// Throws the corresponding exception if the given path does not correspond to a suitable binary file.
- ///
- public static bool ReadBinary(string file, out QsCompilation syntaxTree) =>
- ReadBinary(new MemoryStream(File.ReadAllBytes(Path.GetFullPath(file))), out syntaxTree);
-
- ///
- /// Given a stream with the content of a Q# binary file, returns the corresponding compilation as out parameter.
- /// Throws an ArgumentNullException if the given stream is null.
- ///
- public static bool ReadBinary(Stream stream, out QsCompilation syntaxTree) =>
- AssemblyLoader.LoadSyntaxTree(stream, out syntaxTree);
-
- ///
- /// Given a file id assigned by the Q# compiler, computes the corresponding path in the specified output folder.
- /// Returns the computed absolute path for a file with the specified ending.
- /// If the content for that file is specified, writes that content to disk.
- /// Throws an ArgumentException if the given file id is incompatible with and id assigned by the Q# compiler.
- /// Throws the corresponding exception any of the path operations fails or if the writing fails.
- ///
- public static string GeneratedFile(NonNullable fileId, string outputFolder, string fileEnding, string content = null)
- {
- if (!CompilationUnitManager.TryGetUri(fileId, out var file))
- { throw new ArgumentException("the given file id is not consistent with and id generated by the Q# compiler"); }
- string FullDirectoryName(string dir) =>
- Path.GetFullPath(dir.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar);
-
- outputFolder = String.IsNullOrWhiteSpace(outputFolder) ? "." : outputFolder;
- var outputUri = new Uri(FullDirectoryName(outputFolder));
- var currentDir = new Uri(FullDirectoryName("."));
- var relFilePath = currentDir.MakeRelativeUri(file);
- var filePath = Uri.UnescapeDataString(new Uri(outputUri, relFilePath).LocalPath);
- var fileDir = filePath.StartsWith(outputUri.LocalPath)
- ? Path.GetDirectoryName(filePath)
- : Path.GetDirectoryName(outputUri.LocalPath);
- var targetFile = Path.GetFullPath(Path.Combine(fileDir, Path.GetFileNameWithoutExtension(filePath) + fileEnding));
-
- if (content == null) return targetFile;
- if (!Directory.Exists(fileDir)) Directory.CreateDirectory(fileDir);
- File.WriteAllText(targetFile, content);
- return targetFile;
- }
-
- ///
- /// Raises a compilation task start event.
- ///
- private void RaiseCompilationTaskStart (string parentTaskName, string taskName) =>
- CompilationTaskEvent?.Invoke(this, new CompilationTaskEventArgs(CompilationTaskEventType.Start, parentTaskName, taskName));
-
- ///
- /// Raises a compilation task end event.
- ///
- private void RaiseCompilationTaskEnd(string parentTaskName, string taskName) =>
- CompilationTaskEvent?.Invoke(this, new CompilationTaskEventArgs(CompilationTaskEventType.End, parentTaskName, taskName));
- }
-}
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using Microsoft.Quantum.QsCompiler.BuiltInRewriteSteps;
+using Microsoft.Quantum.QsCompiler.CompilationBuilder;
+using Microsoft.Quantum.QsCompiler.DataTypes;
+using Microsoft.Quantum.QsCompiler.Diagnostics;
+using Microsoft.Quantum.QsCompiler.Documentation;
+using Microsoft.Quantum.QsCompiler.ReservedKeywords;
+using Microsoft.Quantum.QsCompiler.Serialization;
+using Microsoft.Quantum.QsCompiler.SyntaxTree;
+using Microsoft.Quantum.QsCompiler.Transformations.BasicTransformations;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+using Newtonsoft.Json.Bson;
+using MetadataReference = Microsoft.CodeAnalysis.MetadataReference;
+
+
+namespace Microsoft.Quantum.QsCompiler
+{
+ public class CompilationLoader
+ {
+ ///
+ /// Represents the type of a task event.
+ ///
+ public enum CompilationTaskEventType
+ {
+ Start,
+ End
+ }
+
+ ///
+ /// Represents the arguments associated to a task event.
+ ///
+ public class CompilationTaskEventArgs : EventArgs
+ {
+ public CompilationTaskEventType Type;
+ public string ParentTaskName;
+ public string TaskName;
+
+ public CompilationTaskEventArgs(CompilationTaskEventType type, string parentTaskName, string taskName)
+ {
+ ParentTaskName = parentTaskName;
+ TaskName = taskName;
+ Type = type;
+ }
+ }
+
+ ///
+ /// Defines the handler for compilation task events.
+ ///
+ public delegate void CompilationTaskEventHandler(object sender, CompilationTaskEventArgs args);
+ ///
+ /// Given a load function that loads the content of a sequence of files from disk,
+ /// returns the content for all sources to compile.
+ ///
+ public delegate ImmutableDictionary SourceLoader(Func, ImmutableDictionary> loadFromDisk);
+ ///
+ /// Given a load function that loads the content of a sequence of referenced assemblies from disk,
+ /// returns the loaded references for the compilation.
+ ///
+ public delegate References ReferenceLoader(Func, References> loadFromDisk);
+ ///
+ /// Used to raise a compilation task event.
+ ///
+ public static event CompilationTaskEventHandler CompilationTaskEvent;
+ ///
+ /// If LoadAssembly is not null, it will be used to load the dlls that are search for classes defining rewrite steps.
+ ///
+ public static Func LoadAssembly { get; set; }
+
+
+ ///
+ /// may be specified via configuration (or project) file in the future
+ ///
+ public struct Configuration
+ {
+ ///
+ /// The name of the project. Used as assembly name in the generated dll.
+ /// The name of the project with a suitable extension will also be used as the name of the generated binary file.
+ ///
+ public string ProjectName;
+ ///
+ /// If set to true, the syntax tree rewrite step that replaces all generation directives
+ /// for all functor specializations is executed during compilation.
+ ///
+ public bool GenerateFunctorSupport;
+ ///
+ /// Unless this is set to true, the syntax tree rewrite step that eliminates selective abstractions is executed during compilation.
+ /// In particular, all conjugations are inlined.
+ ///
+ public bool SkipSyntaxTreeTrimming;
+ ///
+ /// If set to true, the compiler attempts to pre-evaluate the built compilation as much as possible.
+ /// This is an experimental feature that will change over time.
+ ///
+ public bool AttemptFullPreEvaluation;
+ ///
+ /// If set to true, the compiler will remove if-statements and replace them with calls to appropriate
+ /// intrinsic operations.
+ ///
+ public bool ConvertClassicalControl;
+ ///
+ /// Unless this is set to true, all usages of type-parameterized callables are replaced with
+ /// the concrete callable instantiation if an entry point is specified for the compilation.
+ /// Removes all type-parameterizations in the syntax tree.
+ ///
+ public bool SkipMonomorphization;
+ ///
+ /// If the output folder is not null,
+ /// documentation is generated in the specified folder based on doc comments in the source code.
+ ///
+ public string DocumentationOutputFolder;
+ ///
+ /// Directory where the compiled binaries will be generated.
+ /// No binaries will be written to disk unless this path is specified and valid.
+ ///
+ public string BuildOutputFolder;
+ ///
+ /// Output path for the dll containing the compiled binaries.
+ /// No dll will be generated unless this path is specified and valid.
+ ///
+ public string DllOutputPath;
+ ///
+ /// If set to true, then referenced dlls will be loaded purely based on attributes in the contained C# code.
+ /// Any Q# resources will be ignored.
+ ///
+ public bool LoadReferencesBasedOnGeneratedCsharp;
+ ///
+ /// Contains a sequence of tuples with the path to a dotnet dll containing one or more rewrite steps
+ /// (i.e. classes implementing IRewriteStep) and the corresponding output folder.
+ /// The contained rewrite steps will be executed in the defined order and priority at the end of the compilation.
+ ///
+ public IEnumerable<(string, string)> RewriteSteps;
+ ///
+ /// If set to true, the post-condition for loaded rewrite steps is checked if the corresponding verification is implemented.
+ /// Otherwise post-condition verifications are skipped.
+ ///
+ public bool EnableAdditionalChecks;
+ ///
+ /// Handle to pass arbitrary constants with which to populate the corresponding dictionary for loaded rewrite steps.
+ /// These values will take precedence over any already existing values that the default constructor sets.
+ /// However, the compiler may overwrite the assembly constants defined for the Q# compilation unit in the dictionary of the loaded step.
+ /// The given dictionary in this configuration is left unchanged in any case.
+ ///
+ public IReadOnlyDictionary AssemblyConstants;
+
+ ///
+ /// Indicates whether a serialization of the syntax tree needs to be generated.
+ /// This is the case if either the build output folder is specified or the dll output path is specified.
+ ///
+ internal bool SerializeSyntaxTree =>
+ BuildOutputFolder != null || DllOutputPath != null;
+
+ ///
+ /// If the ProjectName does not have an ending "proj", appends a .qsproj ending to the project name.
+ /// Returns null if the project name is null.
+ ///
+ internal string ProjectNameWithExtension =>
+ this.ProjectName == null ? null :
+ this.ProjectName.EndsWith("proj") ? this.ProjectName :
+ $"{this.ProjectName}.qsproj";
+
+ ///
+ /// If the ProjectName does have an extension ending with "proj", returns the project name without that extension.
+ /// Returns null if the project name is null.
+ ///
+ internal string ProjectNameWithoutExtension =>
+ this.ProjectName == null ? null :
+ Path.GetExtension(this.ProjectName).EndsWith("proj") ? Path.GetFileNameWithoutExtension(this.ProjectName) :
+ this.ProjectName;
+ }
+
+ ///
+ /// used to indicate the status of individual compilation steps
+ ///
+ public enum Status { NotRun = -1, Succeeded = 0, Failed = 1 }
+
+ private class ExecutionStatus
+ {
+ internal Status SourceFileLoading = Status.NotRun;
+ internal Status ReferenceLoading = Status.NotRun;
+ internal Status PluginLoading = Status.NotRun;
+ internal Status Validation = Status.NotRun;
+ internal Status FunctorSupport = Status.NotRun;
+ internal Status PreEvaluation = Status.NotRun;
+ internal Status TreeTrimming = Status.NotRun;
+ internal Status ConvertClassicalControl = Status.NotRun;
+ internal Status Monomorphization = Status.NotRun;
+ internal Status Documentation = Status.NotRun;
+ internal Status Serialization = Status.NotRun;
+ internal Status BinaryFormat = Status.NotRun;
+ internal Status DllGeneration = Status.NotRun;
+ internal Status[] LoadedRewriteSteps;
+
+ internal ExecutionStatus(IEnumerable externalRewriteSteps) =>
+ this.LoadedRewriteSteps = externalRewriteSteps.Select(_ => Status.NotRun).ToArray();
+
+ private bool WasSuccessful(bool run, Status code) =>
+ (run && code == Status.Succeeded) || (!run && code == Status.NotRun);
+
+ internal bool Success(Configuration options, bool isExe) =>
+ this.SourceFileLoading <= 0 &&
+ this.ReferenceLoading <= 0 &&
+ WasSuccessful(true, this.Validation) &&
+ WasSuccessful(true, this.PluginLoading) &&
+ WasSuccessful(options.GenerateFunctorSupport, this.FunctorSupport) &&
+ WasSuccessful(options.AttemptFullPreEvaluation, this.PreEvaluation) &&
+ WasSuccessful(!options.SkipSyntaxTreeTrimming, this.TreeTrimming) &&
+ WasSuccessful(options.ConvertClassicalControl, this.ConvertClassicalControl) &&
+
+ WasSuccessful(isExe && !options.SkipMonomorphization, this.Monomorphization) &&
+ WasSuccessful(options.DocumentationOutputFolder != null, this.Documentation) &&
+ WasSuccessful(options.SerializeSyntaxTree, this.Serialization) &&
+ WasSuccessful(options.BuildOutputFolder != null, this.BinaryFormat) &&
+ WasSuccessful(options.DllOutputPath != null, this.DllGeneration) &&
+ this.LoadedRewriteSteps.All(status => WasSuccessful(true, status));
+ }
+
+ ///
+ /// Indicates whether all source files were loaded successfully.
+ /// Source file loading may not be executed if the content was preloaded using methods outside this class.
+ ///
+ public Status SourceFileLoading => this.CompilationStatus.SourceFileLoading;
+ ///
+ /// Indicates whether all references were loaded successfully.
+ /// The loading may not be executed if all references were preloaded using methods outside this class.
+ ///
+ public Status ReferenceLoading => this.CompilationStatus.ReferenceLoading;
+ ///
+ /// Indicates whether all external dlls specifying e.g. rewrite steps
+ /// to perform as part of the compilation have been loaded successfully.
+ /// The status indicates a successful execution if no such external dlls have been specified.
+ ///
+ public Status PluginLoading => this.CompilationStatus.PluginLoading;
+ ///
+ /// Indicates whether the compilation unit passed the compiler validation
+ /// that is executed before invoking further rewrite and/or generation steps.
+ ///
+ public Status Validation => this.CompilationStatus.Validation;
+ ///
+ /// Indicates whether all specializations were generated successfully.
+ /// This rewrite step is only executed if the corresponding configuration is specified.
+ ///
+ public Status FunctorSupport => this.CompilationStatus.FunctorSupport;
+ ///
+ /// Indicates whether the pre-evaluation step executed successfully.
+ /// This rewrite step is only executed if the corresponding configuration is specified.
+ ///
+ public Status PreEvaluation => this.CompilationStatus.PreEvaluation;
+ ///
+ /// Indicates whether all the type-parameterized callables were resolved to concrete callables.
+ /// This rewrite step is only executed if the corresponding configuration is specified.
+ ///
+ public Status Monomorphization => this.CompilationStatus.Monomorphization;
+ ///
+ /// Indicates whether documentation for the compilation was generated successfully.
+ /// This step is only executed if the corresponding configuration is specified.
+ ///
+ public Status Documentation => this.CompilationStatus.Documentation;
+ ///
+ /// Indicates whether the built compilation could be serialized successfully.
+ /// This step is only executed if either the binary representation or a dll is emitted.
+ ///
+ public Status Serialization => this.CompilationStatus.Serialization;
+ ///
+ /// Indicates whether a binary representation for the generated syntax tree has been generated successfully.
+ /// This step is only executed if the corresponding configuration is specified.
+ ///
+ public Status BinaryFormat => this.CompilationStatus.BinaryFormat;
+ ///
+ /// Indicates whether a dll containing the compiled binary has been generated successfully.
+ /// This step is only executed if the corresponding configuration is specified.
+ ///
+ public Status DllGeneration => this.CompilationStatus.DllGeneration;
+
+ ///
+ /// Indicates whether all rewrite steps with the given name and loaded from the given source executed successfully.
+ /// The source, if specified, is the path to the dll in which the step is specified.
+ /// Returns a status NotRun if no such step was found or executed.
+ /// Execution is considered successful if the precondition and transformation (if any) returned true.
+ ///
+ public Status LoadedRewriteStep(string name, string source = null)
+ {
+ var uri = String.IsNullOrWhiteSpace(source) ? null : new Uri(Path.GetFullPath(source));
+ bool MatchesQuery(int index) => this.ExternalRewriteSteps[index].Name == name && (source == null || this.ExternalRewriteSteps[index].Origin == uri);
+ var statuses = this.CompilationStatus.LoadedRewriteSteps.Where((s, i) => MatchesQuery(i)).ToArray();
+ return statuses.All(s => s == Status.Succeeded) ? Status.Succeeded : statuses.Any(s => s == Status.Failed) ? Status.Failed : Status.NotRun;
+ }
+ ///
+ /// Indicates the overall status of all rewrite step from external dlls.
+ /// The status is indicated as success if none of these steps failed.
+ ///
+ public Status AllLoadedRewriteSteps => this.CompilationStatus.LoadedRewriteSteps.Any(s => s == Status.Failed) ? Status.Failed : Status.Succeeded;
+ ///
+ /// Indicates the overall success of all compilation steps.
+ /// The compilation is indicated as having been successful if all steps that were configured to execute completed successfully.
+ ///
+ public bool Success => this.CompilationStatus.Success(this.Config, this.CompilationOutput?.EntryPoints.Length != 0);
+
+
+ ///
+ /// Logger used to log all diagnostic events during compilation.
+ ///
+ private readonly ILogger Logger;
+ ///
+ /// Configuration specifying the compilation steps to execute.
+ ///
+ private readonly Configuration Config;
+ ///
+ /// Used to track the status of individual compilation steps.
+ ///
+ private readonly ExecutionStatus CompilationStatus;
+ ///
+ /// Contains all loaded rewrite steps found in the specified plugin dlls,
+ /// where configurable properties such as the output folder have already been initialized to suitable values.
+ ///
+ private readonly ImmutableArray ExternalRewriteSteps;
+
+ ///
+ /// Contains all diagnostics generated upon source file and reference loading.
+ /// All other diagnostics can be accessed via the VerifiedCompilation.
+ ///
+ public ImmutableArray LoadDiagnostics;
+ ///
+ /// Contains the initial compilation built by the compilation unit manager after verification.
+ ///
+ public readonly CompilationUnitManager.Compilation VerifiedCompilation;
+ ///
+ /// Contains the built compilation including the syntax tree after executing all configured rewrite steps.
+ ///
+ public readonly QsCompilation CompilationOutput;
+ ///
+ /// Contains the absolute path where the binary representation of the generated syntax tree has been written to disk.
+ ///
+ public readonly string PathToCompiledBinary;
+ ///
+ /// Contains the absolute path where the generated dll containing the compiled binary has been written to disk.
+ ///
+ public readonly string DllOutputPath;
+
+ ///
+ /// Contains the full Q# syntax tree after executing all configured rewrite steps, including the content of loaded references.
+ ///
+ public IEnumerable GeneratedSyntaxTree =>
+ this.CompilationOutput?.Namespaces;
+
+ ///
+ /// Contains the Uri and names of all rewrite steps loaded from the specified dlls
+ /// in the order in which they are executed.
+ ///
+ public ImmutableArray<(Uri, string)> LoadedRewriteSteps =>
+ this.ExternalRewriteSteps.Select(step => (step.Origin, step.Name)).ToImmutableArray();
+
+
+ ///
+ /// Builds the compilation for the source files and references loaded by the given loaders,
+ /// executing the compilation steps specified by the given options.
+ /// Uses the specified logger to log all diagnostic events.
+ /// Throws an ArgumentNullException if either one of the given loaders is null or returns null.
+ ///
+ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReferences, Configuration? options = null, ILogger logger = null)
+ {
+ RaiseCompilationTaskStart(null, "OverallCompilation");
+
+ // loading the content to compiler
+
+ this.Logger = logger;
+ this.LoadDiagnostics = ImmutableArray.Empty;
+ this.Config = options ?? new Configuration();
+
+ Status rewriteStepLoading = Status.Succeeded;
+ this.ExternalRewriteSteps = RewriteSteps.Load(this.Config,
+ d => this.LogAndUpdateLoadDiagnostics(ref rewriteStepLoading, d),
+ ex => this.LogAndUpdate(ref rewriteStepLoading, ex));
+ this.PrintLoadedRewriteSteps(this.ExternalRewriteSteps);
+ this.CompilationStatus = new ExecutionStatus(this.ExternalRewriteSteps);
+ this.CompilationStatus.PluginLoading = rewriteStepLoading;
+
+ RaiseCompilationTaskStart("OverallCompilation", "SourcesLoading");
+ var sourceFiles = loadSources?.Invoke(this.LoadSourceFiles)
+ ?? throw new ArgumentNullException("unable to load source files");
+ RaiseCompilationTaskEnd("OverallCompilation", "SourcesLoading");
+ RaiseCompilationTaskStart("OverallCompilation", "ReferenceLoading");
+ var references = loadReferences?.Invoke(refs => this.LoadAssemblies(refs, this.Config.LoadReferencesBasedOnGeneratedCsharp))
+ ?? throw new ArgumentNullException("unable to load referenced binary files");
+ RaiseCompilationTaskEnd("OverallCompilation", "ReferenceLoading");
+
+ // building the compilation
+
+ RaiseCompilationTaskStart("OverallCompilation", "Build");
+ this.CompilationStatus.Validation = Status.Succeeded;
+ var files = CompilationUnitManager.InitializeFileManagers(sourceFiles, null, this.OnCompilerException); // do *not* live track (i.e. use publishing) here!
+ var compilationManager = new CompilationUnitManager(this.OnCompilerException);
+ compilationManager.UpdateReferencesAsync(references);
+ compilationManager.AddOrUpdateSourceFilesAsync(files);
+ this.VerifiedCompilation = compilationManager.Build();
+ this.CompilationOutput = this.VerifiedCompilation?.BuiltCompilation;
+ compilationManager.Dispose();
+
+ foreach (var diag in this.VerifiedCompilation?.Diagnostics() ?? Enumerable.Empty())
+ { this.LogAndUpdate(ref this.CompilationStatus.Validation, diag); }
+
+ // executing the specified rewrite steps
+
+ if (!Uri.TryCreate(Assembly.GetExecutingAssembly().CodeBase, UriKind.Absolute, out Uri thisDllUri))
+ { thisDllUri = new Uri(Path.GetFullPath(".", "CompilationLoader.cs")); }
+
+ QsCompilation ExecuteAsAtomicTransformation(RewriteSteps.LoadedStep rewriteStep, ref Status status)
+ {
+ status = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out var transformed);
+ return status == Status.Succeeded ? transformed : this.CompilationOutput;
+ }
+
+ if (this.Config.ConvertClassicalControl)
+ {
+ var rewriteStep = new RewriteSteps.LoadedStep(new ClassicallyControlled(), typeof(IRewriteStep), thisDllUri);
+ this.CompilationOutput = ExecuteAsAtomicTransformation(rewriteStep, ref this.CompilationStatus.ConvertClassicalControl);
+ }
+
+ if (!this.Config.SkipMonomorphization && this.CompilationOutput?.EntryPoints.Length != 0)
+ {
+ var rewriteStep = new RewriteSteps.LoadedStep(new Monomorphization(), typeof(IRewriteStep), thisDllUri);
+ this.CompilationOutput = ExecuteAsAtomicTransformation(rewriteStep, ref this.CompilationStatus.Monomorphization);
+ }
+
+ if (this.Config.GenerateFunctorSupport)
+ {
+ this.CompilationStatus.FunctorSupport = Status.Succeeded;
+ void onException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.FunctorSupport, ex);
+ var generated = this.CompilationOutput != null && CodeGeneration.GenerateFunctorSpecializations(this.CompilationOutput, out this.CompilationOutput, onException);
+ if (!generated) this.LogAndUpdate(ref this.CompilationStatus.FunctorSupport, ErrorCode.FunctorGenerationFailed, Enumerable.Empty());
+ }
+
+ if (!this.Config.SkipSyntaxTreeTrimming)
+ {
+ this.CompilationStatus.TreeTrimming = Status.Succeeded;
+ void onException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.TreeTrimming, ex);
+ var trimmed = this.CompilationOutput != null && this.CompilationOutput.InlineConjugations(out this.CompilationOutput, onException);
+ if (!trimmed) this.LogAndUpdate(ref this.CompilationStatus.TreeTrimming, ErrorCode.TreeTrimmingFailed, Enumerable.Empty());
+ }
+
+ if (this.Config.AttemptFullPreEvaluation)
+ {
+ this.CompilationStatus.PreEvaluation = Status.Succeeded;
+ void onException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.PreEvaluation, ex);
+ var evaluated = this.CompilationOutput != null && this.CompilationOutput.PreEvaluateAll(out this.CompilationOutput, onException);
+ if (!evaluated) this.LogAndUpdate(ref this.CompilationStatus.PreEvaluation, ErrorCode.PreEvaluationFailed, Enumerable.Empty());
+ }
+
+ RaiseCompilationTaskEnd("OverallCompilation", "Build");
+
+ // generating the compiled binary and dll
+
+ RaiseCompilationTaskStart("OverallCompilation", "OutputGeneration");
+ using (var ms = new MemoryStream())
+ {
+ RaiseCompilationTaskStart("OutputGeneration", "SyntaxTreeSerialization");
+ var serialized = this.Config.SerializeSyntaxTree && this.SerializeSyntaxTree(ms);
+ RaiseCompilationTaskEnd("OutputGeneration", "SyntaxTreeSerialization");
+ if (serialized && this.Config.BuildOutputFolder != null)
+ {
+ RaiseCompilationTaskStart("OutputGeneration", "BinaryGeneration");
+ this.PathToCompiledBinary = this.GenerateBinary(ms);
+ RaiseCompilationTaskEnd("OutputGeneration", "BinaryGeneration");
+ }
+ if (serialized && this.Config.DllOutputPath != null)
+ {
+ RaiseCompilationTaskStart("OutputGeneration", "DllGeneration");
+ this.DllOutputPath = this.GenerateDll(ms);
+ RaiseCompilationTaskEnd("OutputGeneration", "DllGeneration");
+ }
+ }
+
+ // executing the specified generation steps
+
+ if (this.Config.DocumentationOutputFolder != null)
+ {
+ RaiseCompilationTaskStart("OutputGeneration", "DocumentationGeneration");
+ this.CompilationStatus.Documentation = Status.Succeeded;
+ var docsFolder = Path.GetFullPath(String.IsNullOrWhiteSpace(this.Config.DocumentationOutputFolder) ? "." : this.Config.DocumentationOutputFolder);
+ void onDocException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.Documentation, ex);
+ var docsGenerated = this.VerifiedCompilation != null && DocBuilder.Run(docsFolder, this.VerifiedCompilation.SyntaxTree.Values, this.VerifiedCompilation.SourceFiles, onException: onDocException);
+ if (!docsGenerated) this.LogAndUpdate(ref this.CompilationStatus.Documentation, ErrorCode.DocGenerationFailed, Enumerable.Empty());
+ RaiseCompilationTaskEnd("OutputGeneration", "DocumentationGeneration");
+ }
+
+ RaiseCompilationTaskEnd("OverallCompilation", "OutputGeneration");
+
+ // invoking rewrite steps in external dlls
+
+ RaiseCompilationTaskStart("OverallCompilation", "RewriteSteps");
+ for (int i = 0; i < this.ExternalRewriteSteps.Length; i++)
+ {
+ if (this.CompilationOutput == null) continue;
+ this.CompilationOutput = ExecuteAsAtomicTransformation(this.ExternalRewriteSteps[i], ref this.CompilationStatus.LoadedRewriteSteps[i]);
+ }
+
+ RaiseCompilationTaskEnd("OverallCompilation", "RewriteSteps");
+ RaiseCompilationTaskEnd(null, "OverallCompilation");
+ }
+
+ ///
+ /// Executes the given rewrite step on the given compilation, returning a transformed compilation as an out parameter.
+ /// Catches and logs any thrown exception. Returns the status of the rewrite step.
+ /// Throws an ArgumentNullException if the rewrite step to execute or the given compilation is null.
+ ///
+ private Status ExecuteRewriteStep(RewriteSteps.LoadedStep rewriteStep, QsCompilation compilation, out QsCompilation transformed)
+ {
+ if (rewriteStep == null) throw new ArgumentNullException(nameof(rewriteStep));
+ if (compilation == null) throw new ArgumentNullException(nameof(compilation));
+
+ string GetDiagnosticsCode(DiagnosticSeverity severity) =>
+ rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Error ? Errors.Code(ErrorCode.CsharpGenerationGeneratedError) :
+ rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Warning ? Warnings.Code(WarningCode.CsharpGenerationGeneratedWarning) :
+ rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Information ? Informations.Code(InformationCode.CsharpGenerationGeneratedInfo) :
+ null;
+
+ Status LogDiagnostics(Status status = Status.Succeeded)
+ {
+ try
+ {
+ foreach (var diagnostic in rewriteStep.GeneratedDiagnostics ?? ImmutableArray.Empty)
+ { this.LogAndUpdate(ref status, RewriteSteps.LoadedStep.ConvertDiagnostic(diagnostic, GetDiagnosticsCode)); }
+ }
+ catch { this.LogAndUpdate(ref status, Warning(WarningCode.RewriteStepDiagnosticsGenerationFailed, new[] { rewriteStep.Name })); }
+ return status;
+ }
+
+ var status = Status.Succeeded;
+ var messageSource = ProjectManager.MessageSource(rewriteStep.Origin);
+ Diagnostic Warning(WarningCode code, params string[] args) => Warnings.LoadWarning(code, args, messageSource);
+ try
+ {
+ transformed = compilation;
+ var preconditionFailed = rewriteStep.ImplementsPreconditionVerification && !rewriteStep.PreconditionVerification(compilation);
+ if (preconditionFailed)
+ {
+ LogDiagnostics();
+ this.LogAndUpdate(ref status, Warning(WarningCode.PreconditionVerificationFailed, new[] { rewriteStep.Name, messageSource }));
+ return status;
+ }
+
+ var transformationFailed = rewriteStep.ImplementsTransformation && !rewriteStep.Transformation(compilation, out transformed);
+ var postconditionFailed = this.Config.EnableAdditionalChecks && rewriteStep.ImplementsPostconditionVerification && !rewriteStep.PostconditionVerification(transformed);
+ LogDiagnostics();
+
+ if (transformationFailed) this.LogAndUpdate(ref status, ErrorCode.RewriteStepExecutionFailed, new[] { rewriteStep.Name, messageSource });
+ if (postconditionFailed) this.LogAndUpdate(ref status, ErrorCode.PostconditionVerificationFailed, new[] { rewriteStep.Name, messageSource });
+ return status;
+ }
+ catch (Exception ex)
+ {
+ this.LogAndUpdate(ref status, ex);
+ var isLoadException = ex is FileLoadException || ex.InnerException is FileLoadException;
+ if (isLoadException) this.LogAndUpdate(ref status, ErrorCode.FileNotFoundDuringPluginExecution, new[] { rewriteStep.Name, messageSource });
+ else this.LogAndUpdate(ref status, ErrorCode.PluginExecutionFailed, new[] { rewriteStep.Name, messageSource });
+ transformed = null;
+ }
+ return status;
+ }
+
+ ///
+ /// Builds the compilation of the specified source files and references,
+ /// executing the compilation steps specified by the given options.
+ /// Uses the specified logger to log all diagnostic events.
+ ///
+ public CompilationLoader(IEnumerable sources, IEnumerable references, Configuration? options = null, ILogger logger = null)
+ : this(load => load(sources), load => load(references), options, logger) { }
+
+ ///
+ /// Builds the compilation of the specified source files and the loaded references returned by the given loader,
+ /// executing the compilation steps specified by the given options.
+ /// Uses the specified logger to log all diagnostic events.
+ /// Throws an ArgumentNullException if the given loader is null or returns null.
+ ///
+ public CompilationLoader(IEnumerable sources, ReferenceLoader loadReferences, Configuration? options = null, ILogger logger = null)
+ : this(load => load(sources), loadReferences, options, logger) { }
+
+ ///
+ /// Builds the compilation of the content returned by the given loader and the specified references,
+ /// executing the compilation steps specified by the given options.
+ /// Uses the specified logger to log all diagnostic events.
+ /// Throws an ArgumentNullException if the given loader is null or returns null.
+ ///
+ public CompilationLoader(SourceLoader loadSources, IEnumerable references, Configuration? options = null, ILogger logger = null)
+ : this(loadSources, load => load(references), options, logger) { }
+
+
+ // private routines used for logging and status updates
+
+ ///
+ /// Logs the given diagnostic and updates the status passed as reference accordingly.
+ /// Throws an ArgumentNullException if the given diagnostic is null.
+ ///
+ private void LogAndUpdate(ref Status current, Diagnostic d)
+ {
+ this.Logger?.Log(d);
+ if (d.IsError()) current = Status.Failed;
+ }
+
+ ///
+ /// Logs the given exception and updates the status passed as reference accordingly.
+ ///
+ private void LogAndUpdate(ref Status current, Exception ex)
+ {
+ this.Logger?.Log(ex);
+ current = Status.Failed;
+ }
+
+ ///
+ /// Logs an error with the given error code and message parameters, and updates the status passed as reference accordingly.
+ ///
+ private void LogAndUpdate(ref Status current, ErrorCode code, IEnumerable args)
+ {
+ this.Logger?.Log(code, args);
+ current = Status.Failed;
+ }
+
+ ///
+ /// Logs the given diagnostic and updates the status passed as reference accordingly.
+ /// Adds the given diagnostic to the tracked load diagnostics.
+ /// Throws an ArgumentNullException if the given diagnostic is null.
+ ///
+ private void LogAndUpdateLoadDiagnostics(ref Status current, Diagnostic d)
+ {
+ this.LoadDiagnostics = this.LoadDiagnostics.Add(d);
+ this.LogAndUpdate(ref current, d);
+ }
+
+ ///
+ /// Logs an UnexpectedCompilerException error as well as the given exception, and updates the validation status accordingly.
+ ///
+ private void OnCompilerException(Exception ex)
+ {
+ this.LogAndUpdate(ref this.CompilationStatus.Validation, ErrorCode.UnexpectedCompilerException, Enumerable.Empty());
+ this.LogAndUpdate(ref this.CompilationStatus.Validation, ex);
+ }
+
+ ///
+ /// Logs the names of the given source files as Information.
+ /// Does nothing if the given argument is null.
+ ///
+ private void PrintResolvedFiles(IEnumerable sourceFiles)
+ {
+ if (sourceFiles == null) return;
+ var args = sourceFiles.Any()
+ ? sourceFiles.Select(f => f?.LocalPath).ToArray()
+ : new string[] { "(none)" };
+ this.Logger?.Log(InformationCode.CompilingWithSourceFiles, Enumerable.Empty(), messageParam: Formatting.Indent(args).ToArray());
+ }
+
+ ///
+ /// Logs the names of the given assemblies as Information.
+ /// Does nothing if the given argument is null.
+ ///
+ private void PrintResolvedAssemblies(IEnumerable> assemblies)
+ {
+ if (assemblies == null) return;
+ var args = assemblies.Any()
+ ? assemblies.Select(name => name.Value).ToArray()
+ : new string[] { "(none)" };
+ this.Logger?.Log(InformationCode.CompilingWithAssemblies, Enumerable.Empty(), messageParam: Formatting.Indent(args).ToArray());
+ }
+
+ ///
+ /// Logs the names and origins of the given rewrite steps as Information.
+ /// Does nothing if the given argument is null.
+ ///
+ private void PrintLoadedRewriteSteps(IEnumerable rewriteSteps)
+ {
+ if (rewriteSteps == null) return;
+ var args = rewriteSteps.Any()
+ ? rewriteSteps.Select(step => $"{step.Name} ({step.Origin})").ToArray()
+ : new string[] { "(none)" };
+ this.Logger?.Log(InformationCode.LoadedRewriteSteps, Enumerable.Empty(), messageParam: Formatting.Indent(args).ToArray());
+ }
+
+
+ // routines for loading from and dumping to files
+
+ ///
+ /// Used to load the content of the specified source files from disk.
+ /// Returns a dictionary mapping the file uri to its content.
+ /// Logs suitable diagnostics in the process and modifies the compilation status accordingly.
+ /// Prints all loaded files using PrintResolvedFiles.
+ ///
+ private ImmutableDictionary LoadSourceFiles(IEnumerable sources)
+ {
+ this.CompilationStatus.SourceFileLoading = 0;
+ if (sources == null) this.LogAndUpdate(ref this.CompilationStatus.SourceFileLoading, ErrorCode.SourceFilesMissing, Enumerable.Empty());
+ void onException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.SourceFileLoading, ex);
+ void onDiagnostic(Diagnostic d) => this.LogAndUpdateLoadDiagnostics(ref this.CompilationStatus.SourceFileLoading, d);
+ var sourceFiles = ProjectManager.LoadSourceFiles(sources ?? Enumerable.Empty(), onDiagnostic, onException);
+ this.PrintResolvedFiles(sourceFiles.Keys);
+ return sourceFiles;
+ }
+
+ ///
+ /// Used to load the content of the specified assembly references from disk.
+ /// Returns the loaded content of the references.
+ /// Logs suitable diagnostics in the process and modifies the compilation status accordingly.
+ /// Prints all loaded files using PrintResolvedAssemblies.
+ ///
+ private References LoadAssemblies(IEnumerable refs, bool ignoreDllResources)
+ {
+ this.CompilationStatus.ReferenceLoading = 0;
+ if (refs == null) this.Logger?.Log(WarningCode.ReferencesSetToNull, Enumerable.Empty());
+ void onException(Exception ex) => this.LogAndUpdate(ref this.CompilationStatus.ReferenceLoading, ex);
+ void onDiagnostic(Diagnostic d) => this.LogAndUpdateLoadDiagnostics(ref this.CompilationStatus.ReferenceLoading, d);
+ var headers = ProjectManager.LoadReferencedAssemblies(refs ?? Enumerable.Empty(), onDiagnostic, onException, ignoreDllResources);
+ var projId = this.Config.ProjectName == null ? null : Path.ChangeExtension(Path.GetFullPath(this.Config.ProjectNameWithExtension), "qsproj");
+ var references = new References(headers, (code, args) => onDiagnostic(Errors.LoadError(code, args, projId)));
+ this.PrintResolvedAssemblies(references.Declarations.Keys);
+ return references;
+ }
+
+ ///
+ /// Writes a binary representation of the built Q# compilation output to the given memory stream.
+ /// Logs suitable diagnostics in the process and modifies the compilation status accordingly.
+ /// Does *not* close the given memory stream, and
+ /// returns true if the serialization has been successfully generated.
+ /// Throws an ArgumentNullException if the given memory stream is null.
+ ///
+ private bool SerializeSyntaxTree(MemoryStream ms)
+ {
+ if (ms == null) throw new ArgumentNullException(nameof(ms));
+ bool ErrorAndReturn()
+ {
+ this.LogAndUpdate(ref this.CompilationStatus.Serialization, ErrorCode.SerializationFailed, Enumerable.Empty());
+ return false;
+ }
+ this.CompilationStatus.Serialization = 0;
+ if (this.CompilationOutput == null) ErrorAndReturn();
+
+ using var writer = new BsonDataWriter(ms) { CloseOutput = false };
+ var fromSources = this.CompilationOutput.Namespaces.Select(ns => FilterBySourceFile.Apply(ns, s => s.Value.EndsWith(".qs")));
+ var compilation = new QsCompilation(fromSources.ToImmutableArray(), this.CompilationOutput.EntryPoints);
+ try { Json.Serializer.Serialize(writer, compilation); }
+ catch (Exception ex)
+ {
+ this.LogAndUpdate(ref this.CompilationStatus.Serialization, ex);
+ ErrorAndReturn();
+ }
+ return true;
+ }
+
+ ///
+ /// Backtracks to the beginning of the given memory stream and writes its content to disk,
+ /// generating a suitable bson file in the specified build output folder using the project name as file name.
+ /// Generates a file name at random if no project name is specified.
+ /// Logs suitable diagnostics in the process and modifies the compilation status accordingly.
+ /// Returns the absolute path of the file where the binary representation has been generated.
+ /// Returns null if the binary file could not be generated.
+ /// Does *not* close the given memory stream.
+ /// Throws an ArgumentNullException if the given memory stream is null.
+ ///
+ private string GenerateBinary(MemoryStream serialization)
+ {
+ if (serialization == null) throw new ArgumentNullException(nameof(serialization));
+ this.CompilationStatus.BinaryFormat = 0;
+
+ var projId = NonNullable.New(Path.GetFullPath(this.Config.ProjectNameWithExtension ?? Path.GetRandomFileName()));
+ var outFolder = Path.GetFullPath(String.IsNullOrWhiteSpace(this.Config.BuildOutputFolder) ? "." : this.Config.BuildOutputFolder);
+ var target = GeneratedFile(projId, outFolder, ".bson", "");
+
+ try
+ {
+ serialization.Seek(0, SeekOrigin.Begin);
+ using (var file = new FileStream(target, FileMode.Create, FileAccess.Write))
+ { serialization.WriteTo(file); }
+ return target;
+ }
+ catch (Exception ex)
+ {
+ this.LogAndUpdate(ref this.CompilationStatus.BinaryFormat, ex);
+ this.LogAndUpdate(ref this.CompilationStatus.BinaryFormat, ErrorCode.GeneratingBinaryFailed, Enumerable.Empty());
+ return null;
+ }
+ }
+
+ ///
+ /// Backtracks to the beginning of the given memory stream and,
+ /// assuming the given memory stream contains a serialization of the compiled syntax tree,
+ /// generates a dll containing the compiled binary at the specified dll output path.
+ /// Logs suitable diagnostics in the process and modifies the compilation status accordingly.
+ /// Returns the absolute path of the file where the dll has been generated.
+ /// Returns null if the dll could not be generated.
+ /// Does *not* close the given memory stream.
+ /// Throws an ArgumentNullException if the given memory stream is null.
+ ///
+ private string GenerateDll(MemoryStream serialization)
+ {
+ if (serialization == null) throw new ArgumentNullException(nameof(serialization));
+ this.CompilationStatus.DllGeneration = 0;
+
+ var fallbackFileName = (this.PathToCompiledBinary ?? this.Config.ProjectNameWithExtension) ?? Path.GetRandomFileName();
+ var outputPath = Path.GetFullPath(String.IsNullOrWhiteSpace(this.Config.DllOutputPath) ? fallbackFileName : this.Config.DllOutputPath);
+ outputPath = Path.ChangeExtension(outputPath, "dll");
+
+ MetadataReference CreateReference(string file, int id) =>
+ MetadataReference.CreateFromFile(file)
+ .WithAliases(new string[] { $"{DotnetCoreDll.ReferenceAlias}{id}" }); // referenced Q# dlls are recognized based on this alias
+
+ // We need to force the inclusion of references despite that we do not include C# code that depends on them.
+ // This is done via generating a certain handle in all dlls built via this compilation loader.
+ // This checks if that handle is available to merely generate a warning if we can't include the reference.
+ bool CanBeIncluded(NonNullable dll)
+ {
+ try // no need to throw in case this fails - ignore the reference instead
+ {
+ using var stream = File.OpenRead(dll.Value);
+ using var assemblyFile = new PEReader(stream);
+ var metadataReader = assemblyFile.GetMetadataReader();
+ return metadataReader.TypeDefinitions
+ .Select(metadataReader.GetTypeDefinition)
+ .Any(t => metadataReader.GetString(t.Namespace) == DotnetCoreDll.MetadataNamespace);
+ }
+ catch { return false; }
+ }
+
+ try
+ {
+ var referencePaths = GetSourceFiles.Apply(this.CompilationOutput.Namespaces) // we choose to keep only Q# references that have been used
+ .Where(file => file.Value.EndsWith(".dll"));
+ var references = referencePaths.Select((dll, id) => (dll, CreateReference(dll.Value, id), CanBeIncluded(dll))).ToImmutableArray();
+ var csharpTree = MetadataGeneration.GenerateAssemblyMetadata(references.Where(r => r.Item3).Select(r => r.Item2));
+ foreach (var (dropped, _, _) in references.Where(r => !r.Item3))
+ {
+ var warning = Warnings.LoadWarning(WarningCode.ReferenceCannotBeIncludedInDll, new[] { dropped.Value }, null);
+ this.LogAndUpdate(ref this.CompilationStatus.DllGeneration, warning);
+ }
+
+ var compilation = CodeAnalysis.CSharp.CSharpCompilation.Create(
+ this.Config.ProjectNameWithoutExtension ?? Path.GetFileNameWithoutExtension(outputPath),
+ syntaxTrees: new[] { csharpTree },
+ references: references.Select(r => r.Item2).Append(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)), // if System.Object can't be found a warning is generated
+ options: new CodeAnalysis.CSharp.CSharpCompilationOptions(outputKind: CodeAnalysis.OutputKind.DynamicallyLinkedLibrary)
+ );
+
+ using var outputStream = File.OpenWrite(outputPath);
+ serialization.Seek(0, SeekOrigin.Begin);
+ var astResource = new CodeAnalysis.ResourceDescription(DotnetCoreDll.ResourceName, () => serialization, true);
+ var result = compilation.Emit(outputStream,
+ options: new CodeAnalysis.Emit.EmitOptions(),
+ manifestResources: new CodeAnalysis.ResourceDescription[] { astResource }
+ );
+
+ var errs = result.Diagnostics.Where(d => d.Severity >= CodeAnalysis.DiagnosticSeverity.Error);
+ if (errs.Any()) throw new Exception($"error(s) on emitting dll: {Environment.NewLine}{String.Join(Environment.NewLine, errs.Select(d => d.GetMessage()))}");
+ return outputPath;
+ }
+ catch (Exception ex)
+ {
+ this.LogAndUpdate(ref this.CompilationStatus.DllGeneration, ex);
+ this.LogAndUpdate(ref this.CompilationStatus.DllGeneration, ErrorCode.GeneratingDllFailed, Enumerable.Empty());
+ return null;
+ }
+ }
+
+ ///
+ /// Given the path to a Q# binary file, reads the content of that file and returns the corresponding compilation as out parameter.
+ /// Throws the corresponding exception if the given path does not correspond to a suitable binary file.
+ ///
+ public static bool ReadBinary(string file, out QsCompilation syntaxTree) =>
+ ReadBinary(new MemoryStream(File.ReadAllBytes(Path.GetFullPath(file))), out syntaxTree);
+
+ ///
+ /// Given a stream with the content of a Q# binary file, returns the corresponding compilation as out parameter.
+ /// Throws an ArgumentNullException if the given stream is null.
+ ///
+ public static bool ReadBinary(Stream stream, out QsCompilation syntaxTree) =>
+ AssemblyLoader.LoadSyntaxTree(stream, out syntaxTree);
+
+ ///
+ /// Given a file id assigned by the Q# compiler, computes the corresponding path in the specified output folder.
+ /// Returns the computed absolute path for a file with the specified ending.
+ /// If the content for that file is specified, writes that content to disk.
+ /// Throws an ArgumentException if the given file id is incompatible with and id assigned by the Q# compiler.
+ /// Throws the corresponding exception any of the path operations fails or if the writing fails.
+ ///
+ public static string GeneratedFile(NonNullable fileId, string outputFolder, string fileEnding, string content = null)
+ {
+ if (!CompilationUnitManager.TryGetUri(fileId, out var file))
+ { throw new ArgumentException("the given file id is not consistent with and id generated by the Q# compiler"); }
+ string FullDirectoryName(string dir) =>
+ Path.GetFullPath(dir.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar);
+
+ outputFolder = String.IsNullOrWhiteSpace(outputFolder) ? "." : outputFolder;
+ var outputUri = new Uri(FullDirectoryName(outputFolder));
+ var currentDir = new Uri(FullDirectoryName("."));
+ var relFilePath = currentDir.MakeRelativeUri(file);
+ var filePath = Uri.UnescapeDataString(new Uri(outputUri, relFilePath).LocalPath);
+ var fileDir = filePath.StartsWith(outputUri.LocalPath)
+ ? Path.GetDirectoryName(filePath)
+ : Path.GetDirectoryName(outputUri.LocalPath);
+ var targetFile = Path.GetFullPath(Path.Combine(fileDir, Path.GetFileNameWithoutExtension(filePath) + fileEnding));
+
+ if (content == null) return targetFile;
+ if (!Directory.Exists(fileDir)) Directory.CreateDirectory(fileDir);
+ File.WriteAllText(targetFile, content);
+ return targetFile;
+ }
+
+ ///
+ /// Raises a compilation task start event.
+ ///
+ private void RaiseCompilationTaskStart (string parentTaskName, string taskName) =>
+ CompilationTaskEvent?.Invoke(this, new CompilationTaskEventArgs(CompilationTaskEventType.Start, parentTaskName, taskName));
+
+ ///
+ /// Raises a compilation task end event.
+ ///
+ private void RaiseCompilationTaskEnd(string parentTaskName, string taskName) =>
+ CompilationTaskEvent?.Invoke(this, new CompilationTaskEventArgs(CompilationTaskEventType.End, parentTaskName, taskName));
+ }
+}
diff --git a/src/QsCompiler/Compiler/FunctorGeneration.cs b/src/QsCompiler/Compiler/FunctorGeneration.cs
index 71ba0b8c1b..123da37375 100644
--- a/src/QsCompiler/Compiler/FunctorGeneration.cs
+++ b/src/QsCompiler/Compiler/FunctorGeneration.cs
@@ -28,7 +28,7 @@ private static QsTuple> ControlledArg(Qs
: null;
///
- /// Given a sequence of specialziations, returns the implementation of the given kind, or null if no such specialization exists.
+ /// Given a sequence of specializations, returns the implementation of the given kind, or null if no such specialization exists.
/// Throws an ArgumentException if more than one specialization of that kind exist.
/// Throws an ArgumentNullException if the sequence of specializations is null, or contains any entries that are null.
///
@@ -67,7 +67,7 @@ private static (QsTuple>, QsScope)? Body
}
///
- /// Given a Q# callable, evaluates any functor generator directive given for its adjoint specializiation.
+ /// Given a Q# callable, evaluates any functor generator directive given for its adjoint specialization.
/// If the body specialization is either intrinsic or external, return the given callable unchanged.
/// Otherwise returns a new QsCallable with the adjoint specialization set to the generated implementation if it was generated,
/// or set to the original specialization if the specialization was not requested to be auto-generated.
@@ -96,7 +96,7 @@ private static QsCallable BuildAdjoint(this QsCallable callable)
}
///
- /// Given a Q# callable, evaluates any functor generator directive given for its controlled specializiation.
+ /// Given a Q# callable, evaluates any functor generator directive given for its controlled specialization.
/// If the body specialization is either intrinsic or external, return the given callable unchanged.
/// Otherwise returns a new QsCallable with the controlled specialization set to the generated implementation if it was generated,
/// or set to the original specialization if the specialization was not requested to be auto-generated.
@@ -122,7 +122,7 @@ private static QsCallable BuildControlled(this QsCallable callable)
}
///
- /// Given a Q# callable, evaluates any functor generator directive given for its controlled adjoint specializiation.
+ /// Given a Q# callable, evaluates any functor generator directive given for its controlled adjoint specialization.
/// If the body specialization is either intrinsic or external, return the given callable unchanged.
/// Otherwise returns a new QsCallable with the controlled adjoint specialization set to the generated implementation if it was generated,
/// or set to the original specialization if the specialization was not requested to be auto-generated.
@@ -153,10 +153,10 @@ private static QsCallable BuildControlledAdjoint(this QsCallable callable)
{
var ctl = callable.Specializations.GetSpecialization(QsSpecializationKind.QsControlled);
var (ctlArg, ctlImpl) = GetContent(ctl?.Implementation) ?? throw new ArgumentException("no implementation provided for controlled specialization");
- void SetImplementation(QsScope impl) => ctlAdj = ctlAdj.WithImplementation(SpecializationImplementation.NewProvided(ctlArg, impl));
-
- //if (gen.Item.IsSelfInverse) SetImplementation(ctlImpl); -> nothing to do here, we want to keep this information
- if (gen.Item.IsInvert) SetImplementation(ctlImpl.GenerateAdjoint());
+ if (gen.Item.IsInvert)
+ {
+ ctlAdj = ctlAdj.WithImplementation(SpecializationImplementation.NewProvided(ctlArg, ctlImpl.GenerateAdjoint()));
+ }
}
}
return callable.WithSpecializations(specs => specs.Select(s => s.Kind == QsSpecializationKind.QsControlledAdjoint ? ctlAdj : s).ToImmutableArray());
diff --git a/src/QsCompiler/Compiler/PluginInterface.cs b/src/QsCompiler/Compiler/PluginInterface.cs
index 6454c01abc..50dc8bcd1a 100644
--- a/src/QsCompiler/Compiler/PluginInterface.cs
+++ b/src/QsCompiler/Compiler/PluginInterface.cs
@@ -93,7 +93,7 @@ public struct Diagnostic
///
/// Verifies whether a given compilation satisfies the precondition for executing this rewrite step.
/// indicates whether or not this method is implemented.
- /// If the precondition verfication succeeds, then the invocation of an implemented transformation (if any)
+ /// If the precondition verification succeeds, then the invocation of an implemented transformation (if any)
/// with the given compilation should complete without throwing an exception.
/// The precondition verification should never throw an exception,
/// but instead indicate if the precondition is satisfied via the returned value.
diff --git a/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs b/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs
new file mode 100644
index 0000000000..dd7088f28f
--- /dev/null
+++ b/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs
@@ -0,0 +1,72 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Quantum.QsCompiler.DataTypes;
+using Microsoft.Quantum.QsCompiler.SyntaxTree;
+using Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTransformation;
+
+
+namespace Microsoft.Quantum.QsCompiler.BuiltInRewriteSteps
+{
+ internal class ClassicallyControlled : IRewriteStep
+ {
+ public string Name => "ClassicallyControlled";
+ public int Priority => 10; // Not used for built-in transformations like this
+ public IDictionary AssemblyConstants { get; }
+ public IEnumerable GeneratedDiagnostics => null;
+
+ public bool ImplementsTransformation => true;
+ public bool ImplementsPreconditionVerification => true;
+ public bool ImplementsPostconditionVerification => false;
+
+ public ClassicallyControlled()
+ {
+ AssemblyConstants = new Dictionary();
+ }
+
+ public bool Transformation(QsCompilation compilation, out QsCompilation transformed)
+ {
+ transformed = ClassicallyControlledTransformation.Apply(compilation);
+ return true;
+ }
+
+ public bool PreconditionVerification(QsCompilation compilation)
+ {
+ var controlNs = compilation.Namespaces
+ .FirstOrDefault(ns => ns.Name.Equals(BuiltIn.ClassicallyControlledNamespace));
+
+ if (controlNs == null)
+ {
+ return false;
+ }
+
+ var providedOperations = new QsNamespace[] { controlNs }.Callables().Select(c => c.FullName.Name);
+ var requiredBuiltIns = new List>()
+ {
+ BuiltIn.ApplyIfZero.Name,
+ BuiltIn.ApplyIfZeroA.Name,
+ BuiltIn.ApplyIfZeroC.Name,
+ BuiltIn.ApplyIfZeroCA.Name,
+
+ BuiltIn.ApplyIfOne.Name,
+ BuiltIn.ApplyIfOneA.Name,
+ BuiltIn.ApplyIfOneC.Name,
+ BuiltIn.ApplyIfOneCA.Name,
+
+ BuiltIn.ApplyIfElseR.Name,
+ BuiltIn.ApplyIfElseRA.Name,
+ BuiltIn.ApplyIfElseRC.Name,
+ BuiltIn.ApplyIfElseRCA.Name
+ };
+
+ return requiredBuiltIns.All(builtIn => providedOperations.Any(provided => provided.Equals(builtIn)));
+ }
+
+ public bool PostconditionVerification(QsCompilation compilation)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs
index 7cdefcd69d..000e21a2ca 100644
--- a/src/QsCompiler/Core/Dependencies.fs
+++ b/src/QsCompiler/Core/Dependencies.fs
@@ -6,6 +6,7 @@ namespace Microsoft.Quantum.QsCompiler
open System.Collections.Immutable
open Microsoft.Quantum.QsCompiler.DataTypes
open Microsoft.Quantum.QsCompiler.SyntaxTree
+open Microsoft.Quantum.QsCompiler.SyntaxTokens
type BuiltIn = {
@@ -21,6 +22,7 @@ type BuiltIn = {
static member IntrinsicNamespace = NonNullable.New "Microsoft.Quantum.Intrinsic"
static member StandardArrayNamespace = NonNullable.New "Microsoft.Quantum.Arrays"
static member DiagnosticsNamespace = NonNullable.New "Microsoft.Quantum.Diagnostics"
+ static member ClassicallyControlledNamespace = NonNullable.New "Microsoft.Quantum.Simulation.QuantumProcessor.Extensions"
/// Returns the set of namespaces that is automatically opened for each compilation.
static member NamespacesToAutoOpen = ImmutableHashSet.Create (BuiltIn.CoreNamespace)
@@ -85,6 +87,91 @@ type BuiltIn = {
TypeParameters = ImmutableArray.Empty
}
+ // hard dependencies in Microsoft.Quantum.Simulation.QuantumProcessor.Extensions
+
+ // This is expected to have type <'T>((Result, (('T => Unit), 'T)) => Unit)
+ static member ApplyIfZero = {
+ Name = "ApplyIfZero" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New)
+ }
+
+ // This is expected to have type <'T>((Result, (('T => Unit is Adj), 'T)) => Unit is Adj)
+ static member ApplyIfZeroA = {
+ Name = "ApplyIfZeroA" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New)
+ }
+
+ // This is expected to have type <'T>((Result, (('T => Unit is Ctl), 'T)) => Unit is Ctl)
+ static member ApplyIfZeroC = {
+ Name = "ApplyIfZeroC" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New)
+ }
+
+ // This is expected to have type <'T>((Result, (('T => Unit is Adj + Ctl), 'T)) => Unit is Adj + Ctl)
+ static member ApplyIfZeroCA = {
+ Name = "ApplyIfZeroCA" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New)
+ }
+
+ // This is expected to have type <'T>((Result, (('T => Unit), 'T)) => Unit)
+ static member ApplyIfOne = {
+ Name = "ApplyIfOne" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New)
+ }
+
+ // This is expected to have type <'T>((Result, (('T => Unit is Adj), 'T)) => Unit is Adj)
+ static member ApplyIfOneA = {
+ Name = "ApplyIfOneA" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New)
+ }
+
+ // This is expected to have type <'T>((Result, (('T => Unit is Ctl), 'T)) => Unit is Ctl)
+ static member ApplyIfOneC = {
+ Name = "ApplyIfOneC" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New)
+ }
+
+ // This is expected to have type <'T>((Result, (('T => Unit is Adj + Ctl), 'T)) => Unit is Adj + Ctl)
+ static member ApplyIfOneCA = {
+ Name = "ApplyIfOneCA" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New)
+ }
+
+ // This is expected to have type <'T, 'U>((Result, (('T => Unit), 'T), (('U => Unit), 'U)) => Unit)
+ static member ApplyIfElseR = {
+ Name = "ApplyIfElseR" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New)
+ }
+
+ // This is expected to have type <'T, 'U>((Result, (('T => Unit is Adj), 'T), (('U => Unit is Adj), 'U)) => Unit is Adj)
+ static member ApplyIfElseRA = {
+ Name = "ApplyIfElseRA" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New)
+ }
+
+ // This is expected to have type <'T, 'U>((Result, (('T => Unit is Ctl), 'T), (('U => Unit is Ctl), 'U)) => Unit is Ctl)
+ static member ApplyIfElseRC = {
+ Name = "ApplyIfElseRC" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New)
+ }
+
+ // This is expected to have type <'T, 'U>((Result, (('T => Unit is Adj + Ctl), 'T), (('U => Unit is Adj + Ctl), 'U)) => Unit is Adj + Ctl)
+ static member ApplyIfElseRCA = {
+ Name = "ApplyIfElseRCA" |> NonNullable.New
+ Namespace = BuiltIn.ClassicallyControlledNamespace
+ TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New)
+ }
// "weak dependencies" in other namespaces (e.g. things used for code actions)
diff --git a/src/QsCompiler/DataStructures/SyntaxExtensions.fs b/src/QsCompiler/DataStructures/SyntaxExtensions.fs
index 15d8e0ba17..4c71dc94c7 100644
--- a/src/QsCompiler/DataStructures/SyntaxExtensions.fs
+++ b/src/QsCompiler/DataStructures/SyntaxExtensions.fs
@@ -171,21 +171,6 @@ type TypedExpression with
// utils for walking the data structure
- /// Returns true if the expression contains missing expressions.
- /// Returns false otherwise.
- static member public ContainsMissing (ex : TypedExpression) =
- match ex.TupleItems with
- | Some items when items.Length > 1 -> items |> List.exists TypedExpression.ContainsMissing
- | Some [] -> true
- | _ -> false
-
- /// Returns true if the expression is a call-like expression, and the arguments contain a missing expression.
- /// Returns false otherwise.
- static member public IsPartialApplication kind =
- match kind with
- | CallLikeExpression (_, args) -> args |> TypedExpression.ContainsMissing
- | _ -> false
-
/// Returns true if the expression kind does not contain any inner expressions.
static member private IsAtomic (kind : QsExpressionKind<'E, _, _>) =
match kind with
diff --git a/src/QsCompiler/DataStructures/SyntaxTree.fs b/src/QsCompiler/DataStructures/SyntaxTree.fs
index 7f05775360..47fa010d67 100644
--- a/src/QsCompiler/DataStructures/SyntaxTree.fs
+++ b/src/QsCompiler/DataStructures/SyntaxTree.fs
@@ -364,6 +364,17 @@ type TypedExpression = {
member this.TypeParameterResolutions =
this.TypeArguments.ToImmutableDictionary((fun (origin, name, _) -> origin, name), (fun (_,_,t) -> t))
+ /// Returns true if the expression is a call-like expression, and the arguments contain a missing expression.
+ /// Returns false otherwise.
+ static member public IsPartialApplication kind =
+ let rec containsMissing ex =
+ match ex.Expression with
+ | MissingExpr -> true
+ | ValueTuple items -> items |> Seq.exists containsMissing
+ | _ -> false
+ match kind with
+ | CallLikeExpression (_, args) -> args |> containsMissing
+ | _ -> false
/// Fully resolved Q# initializer expression.
diff --git a/src/QsCompiler/Optimizations/OptimizingTransformations/ConstantPropagation.fs b/src/QsCompiler/Optimizations/OptimizingTransformations/ConstantPropagation.fs
index e4c70ef545..7fd900a6b8 100644
--- a/src/QsCompiler/Optimizations/OptimizingTransformations/ConstantPropagation.fs
+++ b/src/QsCompiler/Optimizations/OptimizingTransformations/ConstantPropagation.fs
@@ -22,7 +22,7 @@ type ConstantPropagation(callables) =
/// Returns whether the given expression should be propagated as a constant.
/// For a statement of the form "let x = [expr];", if shouldPropagate(expr) is true,
- /// then we should substitute [expr] for x wherever x occurs in future code.
+ /// then we should substitute x with [expr] wherever x occurs in future code.
let rec shouldPropagate callables (expr : TypedExpression) =
let folder ex sub =
isLiteral callables ex ||
diff --git a/src/QsCompiler/Optimizations/Utils/Evaluation.fs b/src/QsCompiler/Optimizations/Utils/Evaluation.fs
index 8447ebeb5f..e052c745e0 100644
--- a/src/QsCompiler/Optimizations/Utils/Evaluation.fs
+++ b/src/QsCompiler/Optimizations/Utils/Evaluation.fs
@@ -219,7 +219,7 @@ and [] private ExpressionKindEvaluator(callables: ImmutableDictio
let fe = FunctionEvaluator (callables)
return! fe.evaluateFunction qualName arg types stmtsLeft |> Option.map (fun x -> x.Expression)
| CallLikeExpression (baseMethod, partialArg) ->
- do! check (TypedExpression.ContainsMissing partialArg)
+ do! check (TypedExpression.IsPartialApplication method.Expression)
return this.Transform (CallLikeExpression (baseMethod, fillPartialArg (partialArg, arg)))
| _ -> return! None
} |? CallLikeExpression (method, arg)
@@ -229,7 +229,7 @@ and [] private ExpressionKindEvaluator(callables: ImmutableDictio
maybe {
match method.Expression with
| CallLikeExpression (baseMethod, partialArg) ->
- do! check (TypedExpression.ContainsMissing partialArg)
+ do! check (TypedExpression.IsPartialApplication method.Expression)
return this.Transform (CallLikeExpression (baseMethod, fillPartialArg (partialArg, arg)))
| _ -> return! None
} |? CallLikeExpression (method, arg)
@@ -239,7 +239,7 @@ and [] private ExpressionKindEvaluator(callables: ImmutableDictio
maybe {
match method.Expression with
| CallLikeExpression (baseMethod, partialArg) ->
- do! check (TypedExpression.ContainsMissing partialArg)
+ do! check (TypedExpression.IsPartialApplication method.Expression)
return this.Transform (CallLikeExpression (baseMethod, fillPartialArg (partialArg, arg)))
| _ -> return! None
} |? CallLikeExpression (method, arg)
diff --git a/src/QsCompiler/Optimizations/Utils/HelperFunctions.fs b/src/QsCompiler/Optimizations/Utils/HelperFunctions.fs
index 0d38fb6277..44d295f2e1 100644
--- a/src/QsCompiler/Optimizations/Utils/HelperFunctions.fs
+++ b/src/QsCompiler/Optimizations/Utils/HelperFunctions.fs
@@ -190,18 +190,26 @@ and internal defaultValue (bt: TypeKind): ExprKind option =
| _ -> None
+/// Returns true if the expression contains missing expressions.
+/// Returns false otherwise.
+let rec private containsMissing (ex : TypedExpression) =
+ match ex.Expression with
+ | MissingExpr -> true
+ | ValueTuple items -> items |> Seq.exists containsMissing
+ | _ -> false
+
/// Fills a partial argument by replacing MissingExprs with the corresponding values of a tuple
let rec internal fillPartialArg (partialArg: TypedExpression, arg: TypedExpression): TypedExpression =
match partialArg with
| Missing -> arg
| Tuple items ->
let argsList =
- match List.filter TypedExpression.ContainsMissing items, arg with
+ match List.filter containsMissing items, arg with
| [_], _ -> [arg]
| _, Tuple args -> args
| _ -> failwithf "args must be a tuple"
items |> List.mapFold (fun args t1 ->
- if TypedExpression.ContainsMissing t1 then
+ if containsMissing t1 then
match args with
| [] -> failwithf "ran out of args"
| head :: tail -> fillPartialArg (t1, head), tail
diff --git a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs
index 4855f39847..5282c6deb3 100644
--- a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs
+++ b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs
@@ -381,11 +381,11 @@ let private VerifyIdentifier addDiagnostic (symbols : SymbolTracker<_>) (sym, tA
/// Verifies whether an expression of the given argument type can be used as argument to a method (function, operation, or setter)
/// that expects an argument of the given target type. The given target type may contain a missing type (valid for a setter).
-/// Accumulates and returns an array with error codes for the cases where this is not the case, and returns an empyt array otherwise.
+/// Accumulates and returns an array with error codes for the cases where this is not the case, and returns an empty array otherwise.
/// Note that MissingTypes in the argument type should not occur aside from possibly as array base type of the expression.
/// A missing type in the given argument type will cause a verification failure in QsCompilerError.
/// For each type parameter in the target type, calls addTypeParameterResolution with a tuple of the type parameter and the type that is substituted for it.
-/// IMPORTANT: The consistent (i.e. non-ambiguous and non-contraining) resolution of type parameters is *not* verified by this routine
+/// IMPORTANT: The consistent (i.e. non-ambiguous and non-constraining) resolution of type parameters is *not* verified by this routine
/// and needs to be verified in a separate step!
let internal TypeMatchArgument addTypeParameterResolution targetType argType =
let givenAndExpectedType = [argType |> toString; targetType |> toString]
diff --git a/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj b/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj
index 2ac4b52561..40476e0288 100644
--- a/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj
+++ b/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/src/QsCompiler/TestTargets/Simulation/Example/Example.csproj b/src/QsCompiler/TestTargets/Simulation/Example/Example.csproj
index a2bbd3b3a8..b7d89acc9c 100644
--- a/src/QsCompiler/TestTargets/Simulation/Example/Example.csproj
+++ b/src/QsCompiler/TestTargets/Simulation/Example/Example.csproj
@@ -1,9 +1,10 @@
-
+
Detailed
Exe
netcoreapp3.0
+ false
false
diff --git a/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj b/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj
index a87b1f1752..daee5a684b 100644
--- a/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj
+++ b/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs
new file mode 100644
index 0000000000..a8ebac129f
--- /dev/null
+++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs
@@ -0,0 +1,1240 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Microsoft.Quantum.QsCompiler.Testing
+
+open System
+open System.IO
+open Microsoft.Quantum.QsCompiler
+open Microsoft.Quantum.QsCompiler.CompilationBuilder
+open Microsoft.Quantum.QsCompiler.DataTypes
+open Microsoft.Quantum.QsCompiler.SyntaxTree
+open Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTransformation
+open Xunit
+open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput
+open System.Text.RegularExpressions
+open Microsoft.Quantum.QsCompiler.SyntaxTokens
+
+
+type ClassicalControlTests () =
+
+ let compilationManager = new CompilationUnitManager(new Action (fun ex -> failwith ex.Message))
+
+ let getTempFile () = new Uri(Path.GetFullPath(Path.GetRandomFileName()))
+ let getManager uri content = CompilationUnitManager.InitializeFileManager(uri, content, compilationManager.PublishDiagnostics, compilationManager.LogException)
+
+ do let addOrUpdateSourceFile filePath = getManager (new Uri(filePath)) (File.ReadAllText filePath) |> compilationManager.AddOrUpdateSourceFileAsync |> ignore
+ Path.Combine ("TestCases", "LinkingTests", "Core.qs") |> Path.GetFullPath |> addOrUpdateSourceFile
+ Path.Combine ("TestCases", "LinkingTests", "QuantumProcessorExtensions.qs") |> Path.GetFullPath |> addOrUpdateSourceFile
+
+ let ReadAndChunkSourceFile fileName =
+ let sourceInput = Path.Combine ("TestCases", "LinkingTests", fileName) |> File.ReadAllText
+ sourceInput.Split ([|"==="|], StringSplitOptions.RemoveEmptyEntries)
+
+ let BuildContent content =
+
+ let fileId = getTempFile()
+ let file = getManager fileId content
+
+ compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore
+ let compilationDataStructures = compilationManager.Build()
+ compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore
+
+ compilationDataStructures.Diagnostics() |> Seq.exists (fun d -> d.IsError()) |> Assert.False
+ Assert.NotNull compilationDataStructures.BuiltCompilation
+
+ compilationDataStructures
+
+ let CompileClassicalControlTest testNumber =
+ let srcChunks = ReadAndChunkSourceFile "ClassicalControl.qs"
+ srcChunks.Length >= testNumber + 1 |> Assert.True
+ let shared = srcChunks.[0]
+ let compilationDataStructures = BuildContent <| shared + srcChunks.[testNumber]
+ let processedCompilation = ClassicallyControlledTransformation.Apply compilationDataStructures.BuiltCompilation
+ Assert.NotNull processedCompilation
+ Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] processedCompilation
+ processedCompilation
+
+ let GetBodyFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsBody)
+ let GetAdjFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsAdjoint)
+ let GetCtlFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsControlled)
+ let GetCtlAdjFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsControlledAdjoint)
+
+ let GetLinesFromSpecialization specialization =
+ let writer = new SyntaxTreeToQs()
+
+ specialization
+ |> fun x -> match x.Implementation with | Provided (_, body) -> Some body | _ -> None
+ |> Option.get
+ |> writer.Scope.Transform
+ |> ignore
+
+ (writer.Scope :?> ScopeToQs).Output.Split(Environment.NewLine)
+ |> Array.filter (fun str -> str <> String.Empty)
+
+ let CheckIfLineIsCall ``namespace`` name input =
+ let call = sprintf @"(%s\.)?%s" <| Regex.Escape ``namespace`` <| Regex.Escape name
+ let typeArgs = @"(<\s*([^<]*[^<\s])\s*>)?" // Does not support nested type args
+ let args = @"\(\s*(.*[^\s])?\s*\)"
+ let regex = sprintf @"^\s*%s\s*%s\s*%s;$" call typeArgs args
+
+ let regexMatch = Regex.Match(input, regex)
+ if regexMatch.Success then
+ (true, regexMatch.Groups.[3].Value, regexMatch.Groups.[4].Value)
+ else
+ (false, "", "")
+
+ let MakeApplicationRegex (opName : QsQualifiedName) =
+ let call = sprintf @"(%s\.)?%s" <| Regex.Escape opName.Namespace.Value <| Regex.Escape opName.Name.Value
+ let typeArgs = @"(<\s*([^<]*[^<\s])\s*>)?" // Does not support nested type args
+ let args = @"\(\s*(.*[^\s])?\s*\)"
+
+ sprintf @"\(%s\s*%s,\s*%s\)" <| call <| typeArgs <| args
+
+ let IsApplyIfArgMatch input resultVar (opName : QsQualifiedName) =
+ let regexMatch = Regex.Match(input, sprintf @"^\s*%s,\s*%s$" <| Regex.Escape resultVar <| MakeApplicationRegex opName)
+
+ if regexMatch.Success then
+ (true, regexMatch.Groups.[3].Value, regexMatch.Groups.[4].Value)
+ else
+ (false, "", "")
+
+ let IsApplyIfElseArgsMatch input resultVar (opName1 : QsQualifiedName) (opName2 : QsQualifiedName) =
+ let ApplyIfElseRegex = sprintf @"^%s,\s*%s,\s*%s$"
+ <| Regex.Escape resultVar
+ <| MakeApplicationRegex opName1
+ <| MakeApplicationRegex opName2
+
+ let regexMatch = Regex.Match(input, ApplyIfElseRegex)
+ if regexMatch.Success then
+ (true, regexMatch.Groups.[3].Value, regexMatch.Groups.[4].Value, regexMatch.Groups.[7].Value, regexMatch.Groups.[8].Value)
+ else
+ (false, "", "", "", "")
+
+ let CheckIfSpecializationHasCalls specialization (calls : seq) =
+ let lines = GetLinesFromSpecialization specialization
+ Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x, _, _) -> x)) calls
+
+ let AssertSpecializationHasCalls specialization calls =
+ Assert.True(CheckIfSpecializationHasCalls specialization calls, sprintf "Callable %O(%A) did not have expected content" specialization.Parent specialization.Kind)
+
+ let ExpandBuiltInQualifiedSymbol (i, (builtin : BuiltIn)) = (i, builtin.Namespace.Value, builtin.Name.Value)
+
+ let IdentifyGeneratedByCalls generatedCallables calls =
+ let mutable callables = generatedCallables |> Seq.map (fun x -> x, x |> (GetBodyFromCallable >> GetLinesFromSpecialization))
+ let hasCall callable (call : seq) =
+ let (_, lines : string[]) = callable
+ Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x, _, _) -> x)) call
+
+ Assert.True(Seq.length callables = Seq.length calls) // This should be true if this method is called correctly
+
+ let mutable rtrn = Seq.empty
+
+ let removeAt i lst =
+ Seq.append
+ <| Seq.take i lst
+ <| Seq.skip (i+1) lst
+
+ for call in calls do
+ callables
+ |> Seq.tryFindIndex (fun callSig -> hasCall callSig call)
+ |> (fun x ->
+ Assert.True (x <> None, sprintf "Did not find expected generated content")
+ rtrn <- Seq.append rtrn [Seq.item x.Value callables]
+ callables <- removeAt x.Value callables
+ )
+ rtrn |> Seq.map (fun (x,y) -> x)
+
+ let GetCallablesWithSuffix compilation ns (suffix : string) =
+ compilation.Namespaces
+ |> Seq.filter (fun x -> x.Name.Value = ns)
+ |> GlobalCallableResolutions
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith suffix)
+ |> Seq.map (fun x -> x.Value)
+
+ let GetCallableWithName compilation ns name =
+ compilation.Namespaces
+ |> Seq.filter (fun x -> x.Name.Value = ns)
+ |> GlobalCallableResolutions
+ |> Seq.find (fun x -> x.Key.Name.Value = name)
+ |> (fun x -> x.Value)
+
+ let ApplyIfElseTest compilation =
+ let generated = GetCallablesWithSuffix compilation Signatures.ClassicalControlNs "_Foo"
+
+ Assert.True(2 = Seq.length generated) // Should already be asserted by the signature check
+
+ let ifContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let elseContent =
+ [
+ (0, "SubOps", "SubOp2");
+ (1, "SubOps", "SubOp3");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [ifContent; elseContent]
+ let ifOp, elseOp = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens)
+
+ let original = GetCallableWithName compilation Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable
+ let lines = original |> GetLinesFromSpecialization
+
+ Assert.True(2 = Seq.length lines, sprintf "Callable %O(%A) did not have the expected number of statements" original.Parent original.Kind)
+
+ let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfElseR.Namespace.Value BuiltIn.ApplyIfElseR.Name.Value lines.[1]
+ Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent original.Kind)
+
+ args, ifOp.FullName, elseOp.FullName
+
+ let DoesCallSupportFunctors expectedFunctors call =
+ let hasAdjoint = expectedFunctors |> Seq.contains QsFunctor.Adjoint
+ let hasControlled = expectedFunctors |> Seq.contains QsFunctor.Controlled
+
+ // Checks the characteristics match
+ let charMatch = lazy match call.Signature.Information.Characteristics.SupportedFunctors with
+ | Value x -> x.SetEquals(expectedFunctors)
+ | Null -> 0 = Seq.length expectedFunctors
+
+ // Checks that the target specializations are present
+ let adjMatch = lazy if hasAdjoint then
+ call.Specializations
+ |> Seq.tryFind (fun x -> x.Kind = QsSpecializationKind.QsAdjoint)
+ |> function
+ | None -> false
+ | Some x -> match x.Implementation with
+ | SpecializationImplementation.Generated gen -> gen = QsGeneratorDirective.Invert || gen = QsGeneratorDirective.SelfInverse
+ | SpecializationImplementation.Provided _ -> true
+ | _ -> false
+ else true
+
+ let ctlMatch = lazy if hasControlled then
+ call.Specializations
+ |> Seq.tryFind (fun x -> x.Kind = QsSpecializationKind.QsControlled)
+ |> function
+ | None -> false
+ | Some x -> match x.Implementation with
+ | SpecializationImplementation.Generated gen -> gen = QsGeneratorDirective.Distribute
+ | SpecializationImplementation.Provided _ -> true
+ | _ -> false
+ else true
+
+ charMatch.Value && adjMatch.Value && ctlMatch.Value
+
+ let AssertCallSupportsFunctors expectedFunctors call =
+ Assert.True(DoesCallSupportFunctors expectedFunctors call, sprintf "Callable %O did not support the expected functors" call.FullName)
+
+ []
+ []
+ member this.``Basic Hoist`` () =
+ let result = CompileClassicalControlTest 1
+
+ let generated = GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo"
+ |> (fun x -> Assert.True(1 = Seq.length x); Seq.item 0 x |> GetBodyFromCallable)
+
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ (2, "SubOps", "SubOp3");
+ ]
+ |> AssertSpecializationHasCalls generated
+
+ []
+ []
+ member this.``Hoist Loops`` () =
+ CompileClassicalControlTest 2 |> ignore
+
+ []
+ []
+ member this.``Don't Hoist Single Call`` () =
+ // Single calls should not be hoisted into their own operation
+ CompileClassicalControlTest 3 |> ignore
+
+ []
+ []
+ member this.``Hoist Single Non-Call`` () =
+ // Single expressions that are not calls should be hoisted into their own operation
+ CompileClassicalControlTest 4 |> ignore
+
+ []
+ []
+ member this.``Don't Hoist Return Statements`` () =
+ CompileClassicalControlTest 5 |> ignore
+
+ []
+ []
+ member this.``All-Or-None Hoisting`` () =
+ CompileClassicalControlTest 6 |> ignore
+
+ []
+ []
+ member this.``ApplyIfZero And ApplyIfOne`` () =
+ let result = CompileClassicalControlTest 7
+
+ let originalOp = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable
+
+ [
+ (1, BuiltIn.ApplyIfZero);
+ (3, BuiltIn.ApplyIfOne);
+ ]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls originalOp
+
+ []
+ []
+ member this.``Apply If Zero Else One`` () =
+ let (args, ifOp, elseOp) = CompileClassicalControlTest 8 |> ApplyIfElseTest
+ IsApplyIfElseArgsMatch args "r" ifOp elseOp
+ |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyIfElse did not have the correct arguments"))
+
+ []
+ []
+ member this.``Apply If One Else Zero`` () =
+ let (args, ifOp, elseOp) = CompileClassicalControlTest 9 |> ApplyIfElseTest
+ // The operation arguments should be swapped from the previous test
+ IsApplyIfElseArgsMatch args "r" elseOp ifOp
+ |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyIfElse did not have the correct arguments"))
+
+ []
+ []
+ member this.``If Elif`` () =
+ let result = CompileClassicalControlTest 10
+
+ let generated = GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo"
+
+ Assert.True(3 = Seq.length generated) // Should already be asserted by the signature check
+
+ let ifContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let elifContent =
+ [
+ (0, "SubOps", "SubOp3");
+ (1, "SubOps", "SubOp1");
+ ]
+ let elseContent =
+ [
+ (0, "SubOps", "SubOp2");
+ (1, "SubOps", "SubOp3");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [ifContent; elifContent; elseContent]
+ let ifOp, elifOp, elseOp = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens), (Seq.item 2 orderedGens)
+
+ let original = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable
+ let lines = original |> GetLinesFromSpecialization
+
+ Assert.True(2 = Seq.length lines, sprintf "Callable %O(%A) did not have the expected number of statements" original.Parent original.Kind)
+
+ let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfElseR.Namespace.Value BuiltIn.ApplyIfElseR.Name.Value lines.[1]
+ Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent original.Kind)
+
+ let errorMsg = "ApplyIfElse did not have the correct arguments"
+ let (success, _, _, _, subArgs) = IsApplyIfElseArgsMatch args "r" ifOp.FullName { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name }
+ Assert.True(success, errorMsg)
+ IsApplyIfElseArgsMatch subArgs "r" elseOp.FullName elifOp.FullName // elif and else are swapped because second condition is against One
+ |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg))
+
+ []
+ []
+ member this.``And Condition`` () =
+ let (args, ifOp, elseOp) = CompileClassicalControlTest 11 |> ApplyIfElseTest
+
+ let errorMsg = "ApplyIfElse did not have the correct arguments"
+ let (success, _, subArgs, _, _) = IsApplyIfElseArgsMatch args "r" { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } elseOp
+ Assert.True(success, errorMsg)
+ IsApplyIfElseArgsMatch subArgs "r" elseOp ifOp // if and else are swapped because second condition is against One
+ |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg))
+
+ []
+ []
+ member this.``Or Condition`` () =
+ let (args, ifOp, elseOp) = CompileClassicalControlTest 12 |> ApplyIfElseTest
+
+ let errorMsg = "ApplyIfElse did not have the correct arguments"
+ let (success, _, _, _, subArgs) = IsApplyIfElseArgsMatch args "r" ifOp { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name }
+ Assert.True(success, errorMsg)
+ IsApplyIfElseArgsMatch subArgs "r" elseOp ifOp // if and else are swapped because second condition is against One
+ |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg))
+
+ []
+ []
+ member this.``Don't Hoist Functions`` () =
+ CompileClassicalControlTest 13 |> ignore
+
+ []
+ []
+ member this.``Hoist Self-Contained Mutable`` () =
+ CompileClassicalControlTest 14 |> ignore
+
+ []
+ []
+ member this.``Don't Hoist General Mutable`` () =
+ CompileClassicalControlTest 15 |> ignore
+
+ []
+ []
+ member this.``Generics Support`` () =
+ let result = CompileClassicalControlTest 16
+
+ let callables = result.Namespaces
+ |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs)
+ |> GlobalCallableResolutions
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "Foo")
+ |> fun x -> x.Value
+ let generated = callables
+ |> Seq.find (fun x -> x.Key.Name.Value.EndsWith "_Foo")
+ |> fun x -> x.Value
+
+ let GetTypeParams call =
+ call.Signature.TypeParameters
+ |> Seq.choose (function | ValidName str -> Some str.Value | InvalidName -> None)
+
+ let AssertTypeArgsMatch typeArgs1 typeArgs2 =
+ let errorMsg = "The type parameters for the original and generated operations do not match"
+ Assert.True(Seq.length typeArgs1 = Seq.length typeArgs2, errorMsg)
+
+ for pair in Seq.zip typeArgs1 typeArgs2 do
+ Assert.True(fst pair = snd pair, errorMsg)
+
+ // Assert that the generated operation has the same type parameters as the original operation
+ let originalTypeParams = GetTypeParams original
+ let generatedTypeParams = GetTypeParams generated
+ AssertTypeArgsMatch originalTypeParams generatedTypeParams
+
+ // Assert that the original operation calls the generated operation with the appropriate type arguments
+ let lines = GetBodyFromCallable original |> GetLinesFromSpecialization
+ let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfZero.Namespace.Value BuiltIn.ApplyIfZero.Name.Value lines.[1]
+ Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.FullName QsSpecializationKind.QsBody)
+
+ let (success, typeArgs, _) = IsApplyIfArgMatch args "r" generated.FullName
+ Assert.True(success, sprintf "ApplyIfZero did not have the correct arguments")
+
+ AssertTypeArgsMatch originalTypeParams <| typeArgs.Replace("'", "").Replace(" ", "").Split(",")
+
+ []
+ []
+ member this.``Adjoint Support`` () =
+ let result = CompileClassicalControlTest 17
+
+ let callables = result.Namespaces
+ |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs)
+ |> GlobalCallableResolutions
+
+ let selfOp = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "Self")
+ |> fun x -> x.Value
+ let invertOp = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "Invert")
+ |> fun x -> x.Value
+ let providedOp = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "Provided")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZero)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable selfOp)
+
+ [(1, BuiltIn.ApplyIfZeroA)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable invertOp)
+
+ [(1, BuiltIn.ApplyIfZero)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable providedOp)
+
+ let _selfOp = callables
+ |> Seq.find (fun x -> x.Key.Name.Value.EndsWith "_Self")
+ |> fun x -> x.Value
+ let _invertOp = callables
+ |> Seq.find (fun x -> x.Key.Name.Value.EndsWith "_Invert")
+ |> fun x -> x.Value
+ let _providedOps = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_Provided")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(2 = Seq.length _providedOps) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let adjointContent =
+ [
+ (0, "SubOps", "SubOp2");
+ (1, "SubOps", "SubOp3");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls _providedOps [bodyContent; adjointContent]
+ let bodyGen, adjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens)
+
+ AssertCallSupportsFunctors [] _selfOp
+ AssertCallSupportsFunctors [QsFunctor.Adjoint] _invertOp
+ AssertCallSupportsFunctors [] bodyGen
+ AssertCallSupportsFunctors [] adjGen
+
+ []
+ []
+ member this.``Controlled Support`` () =
+ let result = CompileClassicalControlTest 18
+
+ let callables = result.Namespaces
+ |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs)
+ |> GlobalCallableResolutions
+
+ let distributeOp = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "Distribute")
+ |> fun x -> x.Value
+ let providedOp = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "Provided")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZeroC)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable distributeOp)
+
+ [(1, BuiltIn.ApplyIfZero)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable providedOp)
+
+ let _distributeOp = callables
+ |> Seq.find (fun x -> x.Key.Name.Value.EndsWith "_Distribute")
+ |> fun x -> x.Value
+ let _providedOps = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_Provided")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(2 = Seq.length _providedOps) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let controlledContent =
+ [
+ (0, "SubOps", "SubOp2");
+ (1, "SubOps", "SubOp3");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls _providedOps [bodyContent; controlledContent]
+ let bodyGen, ctlGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled] _distributeOp
+ AssertCallSupportsFunctors [] bodyGen
+ AssertCallSupportsFunctors [] ctlGen
+
+ []
+ []
+ member this.``Controlled Adjoint Support - Provided`` () =
+ let result = CompileClassicalControlTest 19
+
+ let callables = result.Namespaces
+ |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs)
+ |> GlobalCallableResolutions
+
+ (*-----------------------------------------*)
+
+ let bodyCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "ProvidedBody")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZeroCA)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetCtlAdjFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_ProvidedBody")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(2 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let ctlAdjContent =
+ [
+ (0, "SubOps", "SubOp2");
+ (1, "SubOps", "SubOp3");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlAdjContent]
+ let bodyGen, ctlAdjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] bodyGen
+ AssertCallSupportsFunctors [] ctlAdjGen
+
+ bodyCheck ()
+
+ (*-----------------------------------------*)
+
+ let controlledCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "ProvidedControlled")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZeroA)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetCtlFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetCtlAdjFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_ProvidedControlled")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(3 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let ctlContent =
+ [
+ (0, "SubOps", "SubOp3");
+ (1, "SubOps", "SubOp1");
+ ]
+ let ctlAdjContent =
+ [
+ (0, "SubOps", "SubOp2");
+ (1, "SubOps", "SubOp3");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent; ctlAdjContent]
+ let bodyGen, ctlGen, ctlAdjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens), (Seq.item 2 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [QsFunctor.Adjoint] bodyGen
+ AssertCallSupportsFunctors [] ctlGen
+ AssertCallSupportsFunctors [] ctlAdjGen
+
+ controlledCheck ()
+
+ (*-----------------------------------------*)
+
+ let adjointCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "ProvidedAdjoint")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZeroC)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetAdjFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetCtlAdjFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_ProvidedAdjoint")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(3 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let adjContent =
+ [
+ (0, "SubOps", "SubOp3");
+ (1, "SubOps", "SubOp1");
+ ]
+ let ctlAdjContent =
+ [
+ (0, "SubOps", "SubOp2");
+ (1, "SubOps", "SubOp3");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; adjContent; ctlAdjContent]
+ let bodyGen, adjGen, ctlAdjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens), (Seq.item 2 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [QsFunctor.Controlled] bodyGen
+ AssertCallSupportsFunctors [] adjGen
+ AssertCallSupportsFunctors [] ctlAdjGen
+
+ adjointCheck ()
+
+ (*-----------------------------------------*)
+
+ let allCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "ProvidedAll")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZero)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetCtlFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetAdjFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetCtlAdjFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_ProvidedAll")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(4 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let ctlContent =
+ [
+ (0, "SubOps", "SubOp3");
+ (1, "SubOps", "SubOp1");
+ ]
+ let adjContent =
+ [
+ (0, "SubOps", "SubOp2");
+ (1, "SubOps", "SubOp3");
+ ]
+ let ctlAdjContent =
+ [
+ (2, "SubOps", "SubOp3");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent; adjContent; ctlAdjContent]
+ let bodyGen, ctlGen, adjGen, ctlAdjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens), (Seq.item 2 orderedGens), (Seq.item 3 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [] bodyGen
+ AssertCallSupportsFunctors [] ctlGen
+ AssertCallSupportsFunctors [] adjGen
+ AssertCallSupportsFunctors [] ctlAdjGen
+
+ allCheck ()
+
+ []
+ []
+ member this.``Controlled Adjoint Support - Distribute`` ()=
+ let result = CompileClassicalControlTest 20
+
+ let callables = result.Namespaces
+ |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs)
+ |> GlobalCallableResolutions
+
+ (*-----------------------------------------*)
+
+ let bodyCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "DistributeBody")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZeroCA)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_DistributeBody")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(1 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+
+ let bodyGen = (Seq.item 0 generated)
+ AssertSpecializationHasCalls (GetBodyFromCallable bodyGen) bodyContent
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] bodyGen
+
+ bodyCheck ()
+
+ (*-----------------------------------------*)
+
+ let controlledCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "DistributeControlled")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZeroCA)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetCtlFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_DistributeControlled")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(2 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let ctlContent =
+ [
+ (0, "SubOps", "SubOp3");
+ (1, "SubOps", "SubOp1");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent]
+ let bodyGen, ctlGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] bodyGen
+ AssertCallSupportsFunctors [] ctlGen
+
+ controlledCheck ()
+
+ (*-----------------------------------------*)
+
+ let adjointCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "DistributeAdjoint")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZeroC)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOneC)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetAdjFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_DistributeAdjoint")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(2 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let adjContent =
+ [
+ (0, "SubOps", "SubOp3");
+ (1, "SubOps", "SubOp1");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; adjContent]
+ let bodyGen, adjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [QsFunctor.Controlled] bodyGen
+ AssertCallSupportsFunctors [QsFunctor.Controlled] adjGen
+
+ adjointCheck ()
+
+ (*-----------------------------------------*)
+
+ let allCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "DistributeAll")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZero)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetCtlFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOneC)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetAdjFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_DistributeAll")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(3 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let ctlContent =
+ [
+ (0, "SubOps", "SubOp3");
+ (1, "SubOps", "SubOp1");
+ ]
+ let adjContent =
+ [
+ (0, "SubOps", "SubOp2");
+ (1, "SubOps", "SubOp3");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent; adjContent]
+ let bodyGen, ctlGen, adjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens), (Seq.item 2 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [] bodyGen
+ AssertCallSupportsFunctors [] ctlGen
+ AssertCallSupportsFunctors [QsFunctor.Controlled] adjGen
+
+ allCheck ()
+
+ []
+ []
+ member this.``Controlled Adjoint Support - Invert`` ()=
+ let result = CompileClassicalControlTest 21
+
+ let callables = result.Namespaces
+ |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs)
+ |> GlobalCallableResolutions
+
+ (*-----------------------------------------*)
+
+ let bodyCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "InvertBody")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZeroCA)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_InvertBody")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(1 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+
+ let bodyGen = (Seq.item 0 generated)
+ AssertSpecializationHasCalls (GetBodyFromCallable bodyGen) bodyContent
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] bodyGen
+
+ bodyCheck ()
+
+ (*-----------------------------------------*)
+
+ let controlledCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "InvertControlled")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZeroA)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOneA)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetCtlFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_InvertControlled")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(2 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let ctlContent =
+ [
+ (0, "SubOps", "SubOp3");
+ (1, "SubOps", "SubOp1");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent]
+ let bodyGen, ctlGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [QsFunctor.Adjoint] bodyGen
+ AssertCallSupportsFunctors [QsFunctor.Adjoint] ctlGen
+
+ controlledCheck ()
+
+ (*-----------------------------------------*)
+
+ let adjointCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "InvertAdjoint")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZeroCA)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetAdjFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_InvertAdjoint")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(2 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let adjContent =
+ [
+ (0, "SubOps", "SubOp3");
+ (1, "SubOps", "SubOp1");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; adjContent]
+ let bodyGen, adjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] bodyGen
+ AssertCallSupportsFunctors [] adjGen
+
+ adjointCheck ()
+
+ (*-----------------------------------------*)
+
+ let allCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "InvertAll")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZero)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOneA)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetCtlFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetAdjFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_InvertAll")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(3 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let ctlContent =
+ [
+ (0, "SubOps", "SubOp3");
+ (1, "SubOps", "SubOp1");
+ ]
+ let adjContent =
+ [
+ (0, "SubOps", "SubOp2");
+ (1, "SubOps", "SubOp3");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent; adjContent]
+ let bodyGen, ctlGen, adjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens), (Seq.item 2 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [] bodyGen
+ AssertCallSupportsFunctors [QsFunctor.Adjoint] ctlGen
+ AssertCallSupportsFunctors [] adjGen
+
+ allCheck ()
+
+ []
+ []
+ member this.``Controlled Adjoint Support - Self`` ()=
+ let result = CompileClassicalControlTest 22
+
+ let callables = result.Namespaces
+ |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs)
+ |> GlobalCallableResolutions
+
+ (*-----------------------------------------*)
+
+ let bodyCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "SelfBody")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZeroC)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_SelfBody")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(1 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+
+ let bodyGen = (Seq.item 0 generated)
+ AssertSpecializationHasCalls (GetBodyFromCallable bodyGen) bodyContent
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [QsFunctor.Controlled] bodyGen
+
+ bodyCheck ()
+
+ (*-----------------------------------------*)
+
+ let controlledCheck () =
+ let original = callables
+ |> Seq.find (fun x -> x.Key.Name.Value = "SelfControlled")
+ |> fun x -> x.Value
+
+ [(1, BuiltIn.ApplyIfZero)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetBodyFromCallable original)
+
+ [(1, BuiltIn.ApplyIfOne)]
+ |> Seq.map ExpandBuiltInQualifiedSymbol
+ |> AssertSpecializationHasCalls (GetCtlFromCallable original)
+
+ let generated = callables
+ |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_SelfControlled")
+ |> Seq.map (fun x -> x.Value)
+
+ Assert.True(2 = Seq.length generated) // Should already be asserted by the signature check
+
+ let bodyContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let ctlContent =
+ [
+ (0, "SubOps", "SubOp3");
+ (1, "SubOps", "SubOp1");
+ ]
+
+ let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent]
+ let bodyGen, ctlGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original
+ AssertCallSupportsFunctors [] bodyGen
+ AssertCallSupportsFunctors [] ctlGen
+
+ controlledCheck ()
+
+ []
+ []
+ member this.``Within Block Support`` () =
+ let result = CompileClassicalControlTest 23
+
+ let original = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable
+ let generated = GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo"
+
+ Assert.True(2 = Seq.length generated) // Should already be asserted by the signature check
+
+ let originalContent =
+ [
+ (2, BuiltIn.ApplyIfZeroA);
+ (5, BuiltIn.ApplyIfOne);
+ ] |> Seq.map ExpandBuiltInQualifiedSymbol
+ let outerContent =
+ [
+ (0, "SubOps", "SubOp1");
+ (1, "SubOps", "SubOp2");
+ ]
+ let innerContent =
+ [
+ (0, "SubOps", "SubOp2");
+ (1, "SubOps", "SubOp3");
+ ]
+
+ AssertSpecializationHasCalls original originalContent
+
+ let orderedGens = IdentifyGeneratedByCalls generated [outerContent; innerContent]
+ let outerOp = (Seq.item 0 orderedGens)
+
+ AssertCallSupportsFunctors [QsFunctor.Adjoint] outerOp
+
+ let lines = GetLinesFromSpecialization original
+ let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfZeroA.Namespace.Value BuiltIn.ApplyIfZeroA.Name.Value lines.[2]
+ Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent QsSpecializationKind.QsBody)
+
+ let (success, _, _) = IsApplyIfArgMatch args "r" outerOp.FullName
+ Assert.True(success, "ApplyIfZeroA did not have the correct arguments")
+
+ []
+ []
+ member this.``Arguments Partially Resolve Type Parameters`` () =
+ let result = CompileClassicalControlTest 24
+
+ let original = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable
+
+ let lines = GetLinesFromSpecialization original
+ let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfZero.Namespace.Value BuiltIn.ApplyIfZero.Name.Value lines.[1]
+ Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent QsSpecializationKind.QsBody)
+
+ let (success, typeArgs, _) = IsApplyIfArgMatch args "r" {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"}
+ Assert.True(success, "ApplyIfZero did not have the correct arguments")
+
+ Assert.True((typeArgs = "Int, Double"), "Bar did not have the correct type arguments")
+
+ []
+ []
+ member this.``Hoist Functor Application`` () =
+ CompileClassicalControlTest 25 |> ignore
+
+ []
+ []
+ member this.``Hoist Partial Application`` () =
+ CompileClassicalControlTest 26 |> ignore
+
+ []
+ []
+ member this.``Hoist Array Item Call`` () =
+ CompileClassicalControlTest 27 |> ignore
\ No newline at end of file
diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs
index 39206b7e61..7956a74edf 100644
--- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs
+++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs
@@ -54,7 +54,7 @@ type LinkingTests (output:ITestOutputHelper) =
tests.Verify (callable.FullName, diag)
member private this.BuildContent content =
-
+
let fileId = getTempFile()
let file = getManager fileId content
@@ -79,16 +79,16 @@ type LinkingTests (output:ITestOutputHelper) =
monomorphicCompilation
member private this.CompileIntrinsicResolution source environment =
-
+
let envDS = this.BuildContent environment
let sourceDS = this.BuildContent source
IntrinsicResolutionTransformation.Apply(envDS.BuiltCompilation, sourceDS.BuiltCompilation)
member private this.RunIntrinsicResolutionTest testNumber =
-
+
let srcChunks = LinkingTests.ReadAndChunkSourceFile "IntrinsicResolution.qs"
- srcChunks.Length >= 2*testNumber |> Assert.True
+ srcChunks.Length >= 2 * testNumber |> Assert.True
let chunckNumber = 2 * (testNumber - 1)
let result = this.CompileIntrinsicResolution srcChunks.[chunckNumber] srcChunks.[chunckNumber+1]
Signatures.SignatureCheck [Signatures.IntrinsicResolutionNs] Signatures.IntrinsicResolutionSignatures.[testNumber-1] result
@@ -113,7 +113,7 @@ type LinkingTests (output:ITestOutputHelper) =
[]
member this.``Monomorphization`` () =
-
+
let filePath = Path.Combine ("TestCases", "LinkingTests", "Generics.qs") |> Path.GetFullPath
let fileId = (new Uri(filePath))
getManager fileId (File.ReadAllText filePath)
@@ -136,7 +136,7 @@ type LinkingTests (output:ITestOutputHelper) =
member this.``Intrinsic Resolution Returns UDT`` () =
this.RunIntrinsicResolutionTest 2
-
+
[]
[]
member this.``Intrinsic Resolution Type Mismatch Error`` () =
diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs
new file mode 100644
index 0000000000..cbe56be9c4
--- /dev/null
+++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs
@@ -0,0 +1,885 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+
+namespace SubOps {
+ operation SubOp1() : Unit is Adj + Ctl { }
+ operation SubOp2() : Unit is Adj + Ctl { }
+ operation SubOp3() : Unit is Adj + Ctl { }
+}
+
+// =================================
+
+// Basic Hoist
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ SubOp3();
+ let temp = 4;
+ using (q = Qubit()) {
+ let temp2 = q;
+ }
+ }
+ }
+
+}
+
+// =================================
+
+// Hoist Loops
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+
+ operation Foo() : Unit {
+ let r = Zero;
+
+ if (r == Zero) {
+ for (index in 0 .. 3) {
+ let temp = index;
+ }
+
+ repeat {
+ let success = true;
+ } until (success)
+ fixup {
+ let temp2 = 0;
+ }
+ }
+ }
+
+}
+
+// =================================
+
+// Don't Hoist Single Call
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+ if (r == Zero) {
+ SubOp1();
+ }
+ }
+
+}
+
+// =================================
+
+// Hoist Single Non-Call
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+
+ if (r == Zero) {
+ let temp = 2;
+ }
+ }
+
+}
+
+// =================================
+
+// Don't Hoist Return Statements
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+ if (r == Zero) {
+ SubOp1();
+ return ();
+ }
+ }
+
+}
+
+// =================================
+
+// All-Or-None Hoisting
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation IfInvalid() : Unit {
+ let r = Zero;
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ return ();
+ } else {
+ SubOp2();
+ SubOp3();
+ }
+ }
+
+ operation ElseInvalid() : Unit {
+ let r = Zero;
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ } else {
+ SubOp2();
+ SubOp3();
+ return ();
+ }
+ }
+
+ operation BothInvalid() : Unit {
+ let r = Zero;
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ return ();
+ } else {
+ SubOp2();
+ SubOp3();
+ return ();
+ }
+ }
+
+}
+
+// =================================
+
+// ApplyIfZero And ApplyIfOne
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ SubOp3();
+ }
+
+ let temp = 0;
+
+ if (r == One) {
+ SubOp2();
+ SubOp3();
+ SubOp1();
+ }
+ }
+
+}
+
+// =================================
+
+// Apply If Zero Else One
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ } else {
+ SubOp2();
+ SubOp3();
+ }
+ }
+
+}
+
+// =================================
+
+// Apply If One Else Zero
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = One;
+
+ if (r == One) {
+ SubOp1();
+ SubOp2();
+ } else {
+ SubOp2();
+ SubOp3();
+ }
+ }
+
+}
+
+// =================================
+
+// If Elif
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ } elif (r == One) {
+ SubOp3();
+ SubOp1();
+ } else {
+ SubOp2();
+ SubOp3();
+ }
+ }
+
+}
+
+// =================================
+
+// And Condition
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+
+ if (r == Zero and r == One) {
+ SubOp1();
+ SubOp2();
+ } else {
+ SubOp2();
+ SubOp3();
+ }
+ }
+
+}
+
+// =================================
+
+// Or Condition
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+
+ if (r == Zero or r == One) {
+ SubOp1();
+ SubOp2();
+ } else {
+ SubOp2();
+ SubOp3();
+ }
+ }
+
+}
+
+// =================================
+
+// Don't Hoist Functions
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+
+ function Foo() : Unit {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubFunc1();
+ SubFunc2();
+ SubFunc3();
+ }
+ }
+
+ function SubFunc1() : Unit { }
+ function SubFunc2() : Unit { }
+ function SubFunc3() : Unit { }
+
+}
+
+// =================================
+
+// Hoist Self-Contained Mutable
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+
+ if (r == Zero) {
+ mutable temp = 3;
+ set temp = 4;
+ }
+ }
+
+}
+
+// =================================
+
+// Don't Hoist General Mutable
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+
+ mutable temp = 3;
+ if (r == Zero) {
+ set temp = 4;
+ }
+ }
+
+}
+
+// =================================
+
+// Generics Support
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo<'A, 'B, 'C>() : Unit {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+}
+
+// =================================
+
+// Adjoint Support
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Provided() : Unit is Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ adjoint (...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp2();
+ SubOp3();
+ }
+ }
+ }
+
+ operation Self() : Unit is Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ adjoint self;
+ }
+
+ operation Invert() : Unit is Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ adjoint invert;
+ }
+
+}
+
+// =================================
+
+// Controlled Support
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Provided() : Unit is Ctl {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled (ctl, ...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp2();
+ SubOp3();
+ }
+ }
+ }
+
+ operation Distribute() : Unit is Ctl {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled distribute;
+ }
+
+}
+
+// =================================
+
+// Controlled Adjoint Support - Provided
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation ProvidedBody() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled adjoint (ctl, ...) {
+ let y = One;
+
+ if (y == One) {
+ SubOp2();
+ SubOp3();
+ }
+ }
+ }
+
+ operation ProvidedAdjoint() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ adjoint (...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp3();
+ SubOp1();
+ }
+ }
+
+ controlled adjoint (ctl, ...) {
+ let y = One;
+
+ if (y == One) {
+ SubOp2();
+ SubOp3();
+ }
+ }
+ }
+
+ operation ProvidedControlled() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled (ctl, ...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp3();
+ SubOp1();
+ }
+ }
+
+ controlled adjoint (ctl, ...) {
+ let y = One;
+
+ if (y == One) {
+ SubOp2();
+ SubOp3();
+ }
+ }
+ }
+
+ operation ProvidedAll() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled (ctl, ...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp3();
+ SubOp1();
+ }
+ }
+
+ adjoint (...) {
+ let y = One;
+
+ if (y == One) {
+ SubOp2();
+ SubOp3();
+ }
+ }
+
+ controlled adjoint (ctl, ...) {
+ let b = One;
+
+ if (b == One) {
+ let temp1 = 0;
+ let temp2 = 0;
+ SubOp3();
+ }
+ }
+ }
+
+}
+
+// =================================
+
+// Controlled Adjoint Support - Distribute
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation DistributeBody() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled adjoint distribute;
+ }
+
+ operation DistributeAdjoint() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ adjoint (...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp3();
+ SubOp1();
+ }
+ }
+
+ controlled adjoint distribute;
+ }
+
+ operation DistributeControlled() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled (ctl, ...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp3();
+ SubOp1();
+ }
+ }
+
+ controlled adjoint distribute;
+ }
+
+ operation DistributeAll() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled (ctl, ...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp3();
+ SubOp1();
+ }
+ }
+
+ adjoint (...) {
+ let y = One;
+
+ if (y == One) {
+ SubOp2();
+ SubOp3();
+ }
+ }
+
+ controlled adjoint distribute;
+ }
+
+}
+
+// =================================
+
+// Controlled Adjoint Support - Invert
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation InvertBody() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled adjoint invert;
+ }
+
+ operation InvertAdjoint() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ adjoint (...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp3();
+ SubOp1();
+ }
+ }
+
+ controlled adjoint invert;
+ }
+
+ operation InvertControlled() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled (ctl, ...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp3();
+ SubOp1();
+ }
+ }
+
+ controlled adjoint invert;
+ }
+
+ operation InvertAll() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled (ctl, ...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp3();
+ SubOp1();
+ }
+ }
+
+ adjoint (...) {
+ let y = One;
+
+ if (y == One) {
+ SubOp2();
+ SubOp3();
+ }
+ }
+
+ controlled adjoint invert;
+ }
+
+}
+
+// =================================
+
+// Controlled Adjoint Support - Self
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation SelfBody() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled adjoint self;
+ }
+
+ operation SelfControlled() : Unit is Ctl + Adj {
+ body (...) {
+ let r = Zero;
+
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ }
+
+ controlled (ctl, ...) {
+ let w = One;
+
+ if (w == One) {
+ SubOp3();
+ SubOp1();
+ }
+ }
+
+ controlled adjoint self;
+ }
+
+}
+
+// =================================
+
+// Within Block Support
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = One;
+ within {
+ if (r == Zero) {
+ SubOp1();
+ SubOp2();
+ }
+ } apply {
+ if (r == One) {
+ SubOp2();
+ SubOp3();
+ }
+ }
+ }
+
+}
+
+// =================================
+
+// Arguments Partially Resolve Type Parameters
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+
+ operation Bar<'Q, 'W> (q : 'Q, w : 'W) : Unit { }
+
+ operation Foo() : Unit {
+ let r = Zero;
+ if (r == Zero) {
+ Bar(1, 1.0);
+ }
+ }
+}
+
+// =================================
+
+// Hoist Functor Application
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let r = Zero;
+ if (r == Zero) {
+ Adjoint SubOp1();
+ }
+ }
+}
+
+// =================================
+
+// Hoist Partial Application
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+
+ operation Bar (q : Int, w : Double) : Unit { }
+
+ operation Foo() : Unit {
+ let r = Zero;
+ if (r == Zero) {
+ (Bar(1, _))(1.0);
+ }
+ }
+}
+
+// =================================
+
+// Hoist Array Item Call
+namespace Microsoft.Quantum.Testing.ClassicalControl {
+ open SubOps;
+
+ operation Foo() : Unit {
+ let f = [SubOp1];
+ let r = Zero;
+ if (r == Zero) {
+ f[0]();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/QuantumProcessorExtensions.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/QuantumProcessorExtensions.qs
new file mode 100644
index 0000000000..f719f2b5f5
--- /dev/null
+++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/QuantumProcessorExtensions.qs
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+
+namespace Microsoft.Quantum.Simulation.QuantumProcessor.Extensions {
+ operation ApplyIfZero<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit), zeroArg : 'T)) : Unit { }
+ operation ApplyIfZeroA<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj), zeroArg : 'T)) : Unit is Adj { }
+ operation ApplyIfZeroC<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl), zeroArg : 'T)) : Unit is Ctl { }
+ operation ApplyIfZeroCA<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl + Adj), zeroArg : 'T)) : Unit is Ctl + Adj { }
+
+ operation ApplyIfOne<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit), oneArg : 'T)) : Unit { }
+ operation ApplyIfOneA<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Adj), oneArg : 'T)) : Unit is Adj { }
+ operation ApplyIfOneC<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Ctl), oneArg : 'T)) : Unit is Ctl { }
+ operation ApplyIfOneCA<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Ctl + Adj), oneArg : 'T)) : Unit is Ctl + Adj { }
+
+ operation ApplyIfElseR<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit), zeroArg : 'T) , (onResultOneOp : ('U => Unit), oneArg : 'U)) : Unit { }
+ operation ApplyIfElseRA<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Adj), oneArg : 'U)) : Unit is Adj { }
+ operation ApplyIfElseRC<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Ctl), oneArg : 'U)) : Unit is Ctl { }
+ operation ApplyIfElseRCA<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj + Ctl), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Adj + Ctl), oneArg : 'U)) : Unit is Ctl + Adj { }
+}
\ No newline at end of file
diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs
index 034e421186..7a183d5d5f 100644
--- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs
+++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs
@@ -14,12 +14,13 @@ open Xunit
let private _BaseTypes =
[|
- "Unit", UnitType;
- "Int", Int;
- "Double", Double;
- "String", String;
- "Qubit", Qubit;
- "Qubit[]", ResolvedType.New Qubit |> ArrayType;
+ "Unit", UnitType
+ "Int", Int
+ "Double", Double
+ "String", String
+ "Result", Result
+ "Qubit", Qubit
+ "Qubit[]", ResolvedType.New Qubit |> ArrayType
|]
let private _MakeTypeMap udts =
@@ -48,6 +49,16 @@ let private _MakeSignatures sigs =
|> Seq.map (fun (types, case) -> Seq.map (fun _sig -> _MakeSig _sig types) case)
|> Seq.toArray
+let _MakeTypeParam originNs originName paramName =
+ originName + "." + paramName, {
+ Origin = {
+ Namespace = NonNullable<_>.New originNs;
+ Name = NonNullable<_>.New originName
+ }
+ TypeName = NonNullable<_>.New paramName
+ Range = Null
+ } |> TypeParameter
+
/// For all given namespaces in checkedNamespaces, checks that there are exactly
/// the callables specified with targetSignatures in the given compilation.
let public SignatureCheck checkedNamespaces targetSignatures compilation =
@@ -57,7 +68,7 @@ let public SignatureCheck checkedNamespaces targetSignatures compilation =
| Some ns -> ns
| None -> sprintf "Expected but did not find namespace: %s" targetNs |> failwith
- let callableSigs =
+ let mutable callableSigs =
checkedNamespaces
|> Seq.map (fun checkedNs -> getNs checkedNs)
|> SyntaxExtensions.Callables
@@ -77,69 +88,76 @@ let public SignatureCheck checkedNamespaces targetSignatures compilation =
| QsTypeKind.UnitType -> "()"
| _ -> args |> (ExpressionToQs () |> ExpressionTypeToQs).Apply
+ let removeAt i lst =
+ Seq.append
+ <| Seq.take i lst
+ <| Seq.skip (i+1) lst
+
(*Tests that all target signatures are present*)
for targetSig in targetSignatures do
let sig_fullName, sig_argType, sig_rtrnType = targetSig
callableSigs
- |> Seq.exists (fun callSig -> doesCallMatchSig callSig targetSig)
- |> (fun x -> Assert.True (x, sprintf "Expected but did not find: %s.%s %s : %A" sig_fullName.Namespace.Value sig_fullName.Name.Value (makeArgsString sig_argType) sig_rtrnType.Resolution))
+ |> Seq.tryFindIndex (fun callSig -> doesCallMatchSig callSig targetSig)
+ |> (fun x ->
+ Assert.True (x <> None, sprintf "Expected but did not find: %s.*%s %s : %A" sig_fullName.Namespace.Value sig_fullName.Name.Value (makeArgsString sig_argType) sig_rtrnType.Resolution)
+ callableSigs <- removeAt x.Value callableSigs
+ )
(*Tests that *only* targeted signatures are present*)
for callSig in callableSigs do
let sig_fullName, sig_argType, sig_rtrnType = callSig
- targetSignatures
- |> Seq.exists (fun targetSig -> doesCallMatchSig callSig targetSig)
- |> (fun x -> Assert.True (x, sprintf "Found unexpected callable: %s.%s %s : %A" sig_fullName.Namespace.Value sig_fullName.Name.Value (makeArgsString sig_argType) sig_rtrnType.Resolution))
+ failwith (sprintf "Found unexpected callable: %O %s : %A" sig_fullName (makeArgsString sig_argType) sig_rtrnType.Resolution)
/// Names of several testing namespaces
let public MonomorphizationNs = "Microsoft.Quantum.Testing.Monomorphization"
let public GenericsNs = "Microsoft.Quantum.Testing.Generics"
let public IntrinsicResolutionNs = "Microsoft.Quantum.Testing.IntrinsicResolution"
+let public ClassicalControlNs = "Microsoft.Quantum.Testing.ClassicalControl"
/// Expected callable signatures to be found when running Monomorphization tests
let public MonomorphizationSignatures =
[|
(_DefaultTypes, [| (*Test Case 1*)
- MonomorphizationNs, "Test1", [||], "Unit";
- GenericsNs, "Test1Main", [||], "Unit";
-
- GenericsNs, "BasicGeneric", [|"Double"; "Int"|], "Unit";
- GenericsNs, "BasicGeneric", [|"String"; "String"|], "Unit";
- GenericsNs, "BasicGeneric", [|"Unit"; "Unit"|], "Unit";
- GenericsNs, "BasicGeneric", [|"String"; "Double"|], "Unit";
- GenericsNs, "BasicGeneric", [|"Int"; "Double"|], "Unit";
- GenericsNs, "NoArgsGeneric", [||], "Double";
- GenericsNs, "ReturnGeneric", [|"Double"; "String"; "Int"|], "Int";
- GenericsNs, "ReturnGeneric", [|"String"; "Int"; "String"|], "String";
- |]);
+ MonomorphizationNs, "Test1", [||], "Unit"
+ GenericsNs, "Test1Main", [||], "Unit"
+
+ GenericsNs, "BasicGeneric", [|"Double"; "Int"|], "Unit"
+ GenericsNs, "BasicGeneric", [|"String"; "String"|], "Unit"
+ GenericsNs, "BasicGeneric", [|"Unit"; "Unit"|], "Unit"
+ GenericsNs, "BasicGeneric", [|"String"; "Double"|], "Unit"
+ GenericsNs, "BasicGeneric", [|"Int"; "Double"|], "Unit"
+ GenericsNs, "NoArgsGeneric", [||], "Double"
+ GenericsNs, "ReturnGeneric", [|"Double"; "String"; "Int"|], "Int"
+ GenericsNs, "ReturnGeneric", [|"String"; "Int"; "String"|], "String"
+ |])
(_DefaultTypes, [| (*Test Case 2*)
- MonomorphizationNs, "Test2", [||], "Unit";
- GenericsNs, "Test2Main", [||], "Unit";
+ MonomorphizationNs, "Test2", [||], "Unit"
+ GenericsNs, "Test2Main", [||], "Unit"
- GenericsNs, "ArrayGeneric", [|"Qubit"; "String"|], "Int";
- GenericsNs, "ArrayGeneric", [|"Qubit"; "Int"|], "Int";
- GenericsNs, "GenericCallsGeneric", [|"Qubit"; "Int"|], "Unit";
- |]);
+ GenericsNs, "ArrayGeneric", [|"Qubit"; "String"|], "Int"
+ GenericsNs, "ArrayGeneric", [|"Qubit"; "Int"|], "Int"
+ GenericsNs, "GenericCallsGeneric", [|"Qubit"; "Int"|], "Unit"
+ |])
(_DefaultTypes, [| (*Test Case 3*)
- MonomorphizationNs, "Test3", [||], "Unit";
- GenericsNs, "Test3Main", [||], "Unit";
-
- GenericsNs, "GenericCallsSpecializations", [|"Double"; "String"; "Qubit[]"|], "Unit";
- GenericsNs, "GenericCallsSpecializations", [|"Double"; "String"; "Double"|], "Unit";
- GenericsNs, "GenericCallsSpecializations", [|"String"; "Int"; "Unit"|], "Unit";
-
- GenericsNs, "BasicGeneric", [|"Qubit[]"; "Qubit[]"|], "Unit";
- GenericsNs, "BasicGeneric", [|"String"; "Qubit[]"|], "Unit";
- GenericsNs, "BasicGeneric", [|"Double"; "String"|], "Unit";
- GenericsNs, "BasicGeneric", [|"Qubit[]"; "Double"|], "Unit";
- GenericsNs, "BasicGeneric", [|"String"; "Double"|], "Unit";
- GenericsNs, "BasicGeneric", [|"Qubit[]"; "Unit"|], "Unit";
- GenericsNs, "BasicGeneric", [|"Int"; "Unit"|], "Unit";
- GenericsNs, "BasicGeneric", [|"String"; "Int"|], "Unit";
-
- GenericsNs, "ArrayGeneric", [|"Qubit"; "Qubit[]"|], "Int";
- GenericsNs, "ArrayGeneric", [|"Qubit"; "Double"|], "Int";
- GenericsNs, "ArrayGeneric", [|"Qubit"; "Unit"|], "Int";
+ MonomorphizationNs, "Test3", [||], "Unit"
+ GenericsNs, "Test3Main", [||], "Unit"
+
+ GenericsNs, "GenericCallsSpecializations", [|"Double"; "String"; "Qubit[]"|], "Unit"
+ GenericsNs, "GenericCallsSpecializations", [|"Double"; "String"; "Double"|], "Unit"
+ GenericsNs, "GenericCallsSpecializations", [|"String"; "Int"; "Unit"|], "Unit"
+
+ GenericsNs, "BasicGeneric", [|"Qubit[]"; "Qubit[]"|], "Unit"
+ GenericsNs, "BasicGeneric", [|"String"; "Qubit[]"|], "Unit"
+ GenericsNs, "BasicGeneric", [|"Double"; "String"|], "Unit"
+ GenericsNs, "BasicGeneric", [|"Qubit[]"; "Double"|], "Unit"
+ GenericsNs, "BasicGeneric", [|"String"; "Double"|], "Unit"
+ GenericsNs, "BasicGeneric", [|"Qubit[]"; "Unit"|], "Unit"
+ GenericsNs, "BasicGeneric", [|"Int"; "Unit"|], "Unit"
+ GenericsNs, "BasicGeneric", [|"String"; "Int"|], "Unit"
+
+ GenericsNs, "ArrayGeneric", [|"Qubit"; "Qubit[]"|], "Int"
+ GenericsNs, "ArrayGeneric", [|"Qubit"; "Double"|], "Int"
+ GenericsNs, "ArrayGeneric", [|"Qubit"; "Unit"|], "Int"
|])
|]
|> _MakeSignatures
@@ -152,33 +170,227 @@ let private _IntrinsicResolutionTypes = _MakeTypeMap [|
let public IntrinsicResolutionSignatures =
[|
(_DefaultTypes, [|
- IntrinsicResolutionNs, "IntrinsicResolutionTest1", [||], "Unit";
- IntrinsicResolutionNs, "LocalIntrinsic", [||], "Unit";
- IntrinsicResolutionNs, "Override", [||], "Unit";
- IntrinsicResolutionNs, "EnvironmentIntrinsic", [||], "Unit";
- |]);
+ IntrinsicResolutionNs, "IntrinsicResolutionTest1", [||], "Unit"
+ IntrinsicResolutionNs, "LocalIntrinsic", [||], "Unit"
+ IntrinsicResolutionNs, "Override", [||], "Unit"
+ IntrinsicResolutionNs, "EnvironmentIntrinsic", [||], "Unit"
+ |])
(_IntrinsicResolutionTypes, [|
- IntrinsicResolutionNs, "IntrinsicResolutionTest2", [||], "Unit";
- IntrinsicResolutionNs, "Override", [||], "TestType";
- IntrinsicResolutionNs, "TestType", [||], "TestType";
- |]);
+ IntrinsicResolutionNs, "IntrinsicResolutionTest2", [||], "Unit"
+ IntrinsicResolutionNs, "Override", [||], "TestType"
+ IntrinsicResolutionNs, "TestType", [||], "TestType"
+ |])
(_IntrinsicResolutionTypes, [|
- IntrinsicResolutionNs, "IntrinsicResolutionTest3", [||], "Unit";
- IntrinsicResolutionNs, "Override", [||], "TestType";
- IntrinsicResolutionNs, "TestType", [||], "TestType";
- |]);
+ IntrinsicResolutionNs, "IntrinsicResolutionTest3", [||], "Unit"
+ IntrinsicResolutionNs, "Override", [||], "TestType"
+ IntrinsicResolutionNs, "TestType", [||], "TestType"
+ |])
(_IntrinsicResolutionTypes, [|
- IntrinsicResolutionNs, "IntrinsicResolutionTest4", [||], "Unit";
- IntrinsicResolutionNs, "Override", [|"TestType"|], "Unit";
- IntrinsicResolutionNs, "TestType", [||], "TestType";
- |]);
+ IntrinsicResolutionNs, "IntrinsicResolutionTest4", [||], "Unit"
+ IntrinsicResolutionNs, "Override", [|"TestType"|], "Unit"
+ IntrinsicResolutionNs, "TestType", [||], "TestType"
+ |])
(_DefaultTypes, [|
- IntrinsicResolutionNs, "IntrinsicResolutionTest5", [||], "Unit";
- IntrinsicResolutionNs, "Override", [||], "Unit";
- |]);
+ IntrinsicResolutionNs, "IntrinsicResolutionTest5", [||], "Unit"
+ IntrinsicResolutionNs, "Override", [||], "Unit"
+ |])
(_DefaultTypes, [|
- IntrinsicResolutionNs, "IntrinsicResolutionTest6", [||], "Unit";
- IntrinsicResolutionNs, "Override", [||], "Unit";
- |]);
+ IntrinsicResolutionNs, "IntrinsicResolutionTest6", [||], "Unit"
+ IntrinsicResolutionNs, "Override", [||], "Unit"
+ |])
+ |]
+ |> _MakeSignatures
+
+let private _TypeParameterTypes = _MakeTypeMap [|
+ _MakeTypeParam ClassicalControlNs "Bar" "Q"
+ _MakeTypeParam ClassicalControlNs "Bar" "W"
+|]
+
+let private _DefaultWithOperation = _MakeTypeMap [|
+ "SubOp1Type[]", ((ResolvedType.New UnitType, ResolvedType.New UnitType), {
+ Characteristics = (ResolvedCharacteristics.FromProperties [OpProperty.Adjointable; OpProperty.Controllable])
+ InferredInformation = InferredCallableInformation.NoInformation
+ }) |> QsTypeKind.Operation |> ResolvedType.New |> ArrayType
+|]
+
+/// Expected callable signatures to be found when running Classical Control tests
+let public ClassicalControlSignatures =
+ [|
+ (_DefaultTypes, [| // Basic Hoist
+ ClassicalControlNs, "Foo", [||], "Unit"; // The original operation
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; // The generated operation
+ |])
+ (_DefaultTypes, [| // Hoist Loops
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Don't Hoist Single Call
+ ClassicalControlNs, "Foo", [||], "Unit"
+ |])
+ (_DefaultTypes, [| // Hoist Single Non-Call
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Don't Hoist Return Statements
+ ClassicalControlNs, "Foo", [||], "Unit"
+ |])
+ (_DefaultTypes, [| // All-Or-None Hoisting
+ ClassicalControlNs, "IfInvalid", [||], "Unit"
+ ClassicalControlNs, "ElseInvalid", [||], "Unit"
+ ClassicalControlNs, "BothInvalid", [||], "Unit"
+ |])
+ (_DefaultTypes, [| // ApplyIfZero And ApplyIfOne
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Apply If Zero Else One
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Apply If One Else Zero
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // If Elif
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // And Condition
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Or Condition
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Don't Hoist Functions
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "SubFunc1", [||], "Unit"
+ ClassicalControlNs, "SubFunc2", [||], "Unit"
+ ClassicalControlNs, "SubFunc3", [||], "Unit"
+ |])
+ (_DefaultTypes, [| // Hoist Self-Contained Mutable
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Don't Hoist General Mutable
+ ClassicalControlNs, "Foo", [||], "Unit"
+ |])
+ (_DefaultTypes, [| // Generics Support
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Adjoint Support
+ ClassicalControlNs, "Provided", [||], "Unit"
+ ClassicalControlNs, "Self", [||], "Unit"
+ ClassicalControlNs, "Invert", [||], "Unit"
+ ClassicalControlNs, "_Provided", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Provided", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Self", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Invert", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Controlled Support
+ ClassicalControlNs, "Provided", [||], "Unit"
+ ClassicalControlNs, "Distribute", [||], "Unit"
+ ClassicalControlNs, "_Provided", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Provided", [|"Result";"Qubit[]";"Unit"|], "Unit"
+ ClassicalControlNs, "_Distribute", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Controlled Adjoint Support - Provided
+ ClassicalControlNs, "ProvidedBody", [||], "Unit"
+ ClassicalControlNs, "ProvidedAdjoint", [||], "Unit"
+ ClassicalControlNs, "ProvidedControlled", [||], "Unit"
+ ClassicalControlNs, "ProvidedAll", [||], "Unit"
+
+ ClassicalControlNs, "_ProvidedBody", [|"Result"|], "Unit"
+ ClassicalControlNs, "_ProvidedBody", [|"Result";"Qubit[]";"Unit"|], "Unit"
+
+ ClassicalControlNs, "_ProvidedAdjoint", [|"Result"|], "Unit"
+ ClassicalControlNs, "_ProvidedAdjoint", [|"Result"|], "Unit"
+ ClassicalControlNs, "_ProvidedAdjoint", [|"Result";"Qubit[]";"Unit"|], "Unit"
+
+ ClassicalControlNs, "_ProvidedControlled", [|"Result"|], "Unit"
+ ClassicalControlNs, "_ProvidedControlled", [|"Result";"Qubit[]";"Unit"|], "Unit"
+ ClassicalControlNs, "_ProvidedControlled", [|"Result";"Qubit[]";"Unit"|], "Unit"
+
+ ClassicalControlNs, "_ProvidedAll", [|"Result"|], "Unit"
+ ClassicalControlNs, "_ProvidedAll", [|"Result"|], "Unit"
+ ClassicalControlNs, "_ProvidedAll", [|"Result";"Qubit[]";"Unit"|], "Unit"
+ ClassicalControlNs, "_ProvidedAll", [|"Result";"Qubit[]";"Unit"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Controlled Adjoint Support - Distribute
+ ClassicalControlNs, "DistributeBody", [||], "Unit"
+ ClassicalControlNs, "DistributeAdjoint", [||], "Unit"
+ ClassicalControlNs, "DistributeControlled", [||], "Unit"
+ ClassicalControlNs, "DistributeAll", [||], "Unit"
+
+ ClassicalControlNs, "_DistributeBody", [|"Result"|], "Unit"
+
+ ClassicalControlNs, "_DistributeAdjoint", [|"Result"|], "Unit"
+ ClassicalControlNs, "_DistributeAdjoint", [|"Result"|], "Unit"
+
+ ClassicalControlNs, "_DistributeControlled", [|"Result"|], "Unit"
+ ClassicalControlNs, "_DistributeControlled", [|"Result";"Qubit[]";"Unit"|], "Unit"
+
+ ClassicalControlNs, "_DistributeAll", [|"Result"|], "Unit"
+ ClassicalControlNs, "_DistributeAll", [|"Result"|], "Unit"
+ ClassicalControlNs, "_DistributeAll", [|"Result";"Qubit[]";"Unit"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Controlled Adjoint Support - Invert
+ ClassicalControlNs, "InvertBody", [||], "Unit"
+ ClassicalControlNs, "InvertAdjoint", [||], "Unit"
+ ClassicalControlNs, "InvertControlled", [||], "Unit"
+ ClassicalControlNs, "InvertAll", [||], "Unit"
+
+ ClassicalControlNs, "_InvertBody", [|"Result"|], "Unit"
+
+ ClassicalControlNs, "_InvertAdjoint", [|"Result"|], "Unit"
+ ClassicalControlNs, "_InvertAdjoint", [|"Result"|], "Unit"
+
+ ClassicalControlNs, "_InvertControlled", [|"Result"|], "Unit"
+ ClassicalControlNs, "_InvertControlled", [|"Result";"Qubit[]";"Unit"|], "Unit"
+
+ ClassicalControlNs, "_InvertAll", [|"Result"|], "Unit"
+ ClassicalControlNs, "_InvertAll", [|"Result"|], "Unit"
+ ClassicalControlNs, "_InvertAll", [|"Result";"Qubit[]";"Unit"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Controlled Adjoint Support - Self
+ ClassicalControlNs, "SelfBody", [||], "Unit"
+ ClassicalControlNs, "SelfControlled", [||], "Unit"
+
+ ClassicalControlNs, "_SelfBody", [|"Result"|], "Unit"
+
+ ClassicalControlNs, "_SelfControlled", [|"Result"|], "Unit"
+ ClassicalControlNs, "_SelfControlled", [|"Result";"Qubit[]";"Unit"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Within Block Support
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_TypeParameterTypes, [| // Arguments Partially Resolve Type Parameters
+ ClassicalControlNs, "Bar", [|"Bar.Q";"Bar.W"|], "Unit"
+ ClassicalControlNs, "Foo", [||], "Unit"
+ |])
+ (_DefaultTypes, [| // Hoist Functor Application
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_DefaultTypes, [| // Hoist Partial Application
+ ClassicalControlNs, "Bar", [|"Int";"Double"|], "Unit"
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"Result"|], "Unit"
+ |])
+ (_DefaultWithOperation, [| // Hoist Array Item Call
+ ClassicalControlNs, "Foo", [||], "Unit"
+ ClassicalControlNs, "_Foo", [|"SubOp1Type[]";"Result"|], "Unit"
+ |])
|]
|> _MakeSignatures
\ No newline at end of file
diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj
index 2f3634b115..b7f2118e59 100644
--- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj
+++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj
@@ -23,6 +23,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
@@ -41,6 +44,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
@@ -131,6 +137,7 @@
+
diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs
new file mode 100644
index 0000000000..3068bdd70e
--- /dev/null
+++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs
@@ -0,0 +1,1094 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.Quantum.QsCompiler.DataTypes;
+using Microsoft.Quantum.QsCompiler.SyntaxTokens;
+using Microsoft.Quantum.QsCompiler.SyntaxTree;
+
+
+namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTransformation
+{
+ using ExpressionKind = QsExpressionKind;
+ using ResolvedTypeKind = QsTypeKind;
+ using TypeArgsResolution = ImmutableArray, ResolvedType>>;
+
+ // This transformation works in two passes.
+ // 1st Pass: Hoist the contents of conditional statements into separate operations, where possible.
+ // 2nd Pass: On the way down the tree, reshape conditional statements to replace Elif's and
+ // top level OR and AND conditions with equivalent nested if-else statements. One the way back up
+ // the tree, convert conditional statements into ApplyIf calls, where possible.
+ // This relies on anything having type parameters must be a global callable.
+ public class ClassicallyControlledTransformation
+ {
+ public static QsCompilation Apply(QsCompilation compilation)
+ {
+ compilation = HoistTransformation.Apply(compilation);
+
+ var filter = new ClassicallyControlledSyntax(compilation);
+ return new QsCompilation(compilation.Namespaces.Select(ns => filter.Transform(ns)).ToImmutableArray(), compilation.EntryPoints);
+ }
+
+ private static TypedExpression CreateIdentifierExpression(Identifier id,
+ TypeArgsResolution typeArgsMapping, ResolvedType resolvedType) =>
+ new TypedExpression
+ (
+ ExpressionKind.NewIdentifier(
+ id,
+ typeArgsMapping.Any()
+ ? QsNullable>.NewValue(typeArgsMapping
+ .Select(argMapping => argMapping.Item3) // This should preserve the order of the type args
+ .ToImmutableArray())
+ : QsNullable>.Null),
+ typeArgsMapping,
+ resolvedType,
+ new InferredExpressionInformation(false, false),
+ QsNullable>.Null
+ );
+
+ private static TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) =>
+ new TypedExpression
+ (
+ ExpressionKind.NewValueTuple(expressions.ToImmutableArray()),
+ TypeArgsResolution.Empty,
+ ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())),
+ new InferredExpressionInformation(false, false),
+ QsNullable>.Null
+ );
+
+ private static (bool, QsResult, TypedExpression) IsConditionedOnResultLiteralExpression(TypedExpression expression)
+ {
+ if (expression.Expression is ExpressionKind.EQ eq)
+ {
+ if (eq.Item1.Expression is ExpressionKind.ResultLiteral exp1)
+ {
+ return (true, exp1.Item, eq.Item2);
+ }
+ else if (eq.Item2.Expression is ExpressionKind.ResultLiteral exp2)
+ {
+ return (true, exp2.Item, eq.Item1);
+ }
+ }
+
+ return (false, null, null);
+ }
+
+ private static (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatement statement)
+ {
+ if (statement.Statement is QsStatementKind.QsConditionalStatement cond)
+ {
+ if (cond.Item.ConditionalBlocks.Length == 1 && (cond.Item.ConditionalBlocks[0].Item1.Expression is ExpressionKind.EQ expression))
+ {
+ var scope = cond.Item.ConditionalBlocks[0].Item2.Body;
+ var defaultScope = cond.Item.Default.ValueOr(null)?.Body;
+
+ var (success, literal, expr) = IsConditionedOnResultLiteralExpression(cond.Item.ConditionalBlocks[0].Item1);
+
+ if (success)
+ {
+ return (true, literal, expr, scope, defaultScope);
+ }
+ }
+ }
+
+ return (false, null, null, null, null);
+ }
+
+ private ClassicallyControlledTransformation() { }
+
+ private class ClassicallyControlledSyntax : SyntaxTreeTransformation
+ {
+ public ClassicallyControlledSyntax(QsCompilation compilation, ClassicallyControlledScope scope = null) : base(scope ?? new ClassicallyControlledScope(compilation)) { }
+
+ public override QsCallable onFunction(QsCallable c) => c; // Prevent anything in functions from being considered
+ }
+
+ private class ClassicallyControlledScope : ScopeTransformation
+ {
+ private QsCompilation _Compilation;
+
+ public ClassicallyControlledScope(QsCompilation compilation, NoExpressionTransformations expr = null) : base(expr ?? new NoExpressionTransformations())
+ {
+ _Compilation = compilation;
+ }
+
+ private TypeArgsResolution GetCombinedType(TypeArgsResolution outer, TypeArgsResolution inner)
+ {
+ var outerDict = outer.ToDictionary(x => (x.Item1, x.Item2), x => x.Item3);
+ return inner.Select(innerRes =>
+ {
+ if (innerRes.Item3.Resolution is ResolvedTypeKind.TypeParameter typeParam &&
+ outerDict.TryGetValue((typeParam.Item.Origin, typeParam.Item.TypeName), out var outerRes))
+ {
+ outerDict.Remove((typeParam.Item.Origin, typeParam.Item.TypeName));
+ return Tuple.Create(innerRes.Item1, innerRes.Item2, outerRes);
+ }
+ else
+ {
+ return innerRes;
+ }
+ }).Concat(outerDict.Select(x => Tuple.Create(x.Key.Item1, x.Key.Item2, x.Value))).ToImmutableArray();
+ }
+
+ private (bool, TypedExpression, TypedExpression) IsValidScope(QsScope scope)
+ {
+ // if the scope has exactly one statement in it and that statement is a call like expression statement
+ if (scope != null
+ && scope.Statements.Length == 1
+ && scope.Statements[0].Statement is QsStatementKind.QsExpressionStatement expr
+ && expr.Item.ResolvedType.Resolution.IsUnitType
+ && expr.Item.Expression is ExpressionKind.CallLikeExpression call
+ && !TypedExpression.IsPartialApplication(expr.Item.Expression)
+ && call.Item1.Expression is ExpressionKind.Identifier)
+ {
+ // We are dissolving the application of arguments here, so the call's type argument
+ // resolutions have to be moved to the 'identifier' sub expression.
+
+ var callTypeArguments = expr.Item.TypeArguments;
+ var idTypeArguments = call.Item1.TypeArguments;
+ var combinedTypeArguments = GetCombinedType(callTypeArguments, idTypeArguments);
+
+ // This relies on anything having type parameters must be a global callable.
+ var newExpr1 = call.Item1;
+ if (combinedTypeArguments.Any()
+ && newExpr1.Expression is ExpressionKind.Identifier id
+ && id.Item1 is Identifier.GlobalCallable global)
+ {
+ var globalCallable = _Compilation.Namespaces
+ .Where(ns => ns.Name.Equals(global.Item.Namespace))
+ .Callables()
+ .FirstOrDefault(c => c.FullName.Name.Equals(global.Item.Name));
+
+ QsCompilerError.Verify(globalCallable != null, $"Could not find the global reference {global.Item.Namespace.Value + "." + global.Item.Name.Value}");
+
+ var callableTypeParameters = globalCallable.Signature.TypeParameters
+ .Select(x => x as QsLocalSymbol.ValidName);
+
+ QsCompilerError.Verify(callableTypeParameters.All(x => x != null), $"Invalid type parameter names.");
+
+ newExpr1 = new TypedExpression(
+ ExpressionKind.NewIdentifier(
+ id.Item1,
+ QsNullable>.NewValue(
+ callableTypeParameters
+ .Select(x => combinedTypeArguments.First(y => y.Item2.Equals(x.Item)).Item3).ToImmutableArray())),
+ combinedTypeArguments,
+ call.Item1.ResolvedType,
+ call.Item1.InferredInformation,
+ call.Item1.Range);
+ }
+
+ return (true, newExpr1, call.Item2);
+ }
+
+ return (false, null, null);
+ }
+
+ private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, TypeArgsResolution typeRes) =>
+ new TypedExpression
+ (
+ ExpressionKind.NewCallLikeExpression(id, args),
+ typeRes,
+ ResolvedType.New(ResolvedTypeKind.UnitType),
+ new InferredExpressionInformation(false, true),
+ QsNullable>.Null
+ );
+
+ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope conditionScope, QsScope defaultScope)
+ {
+ var controlCall = GetApplyIfExpression(result, conditionExpression, conditionScope, defaultScope);
+
+ if (controlCall != null)
+ {
+ return new QsStatement(
+ QsStatementKind.NewQsExpressionStatement(controlCall),
+ statement.SymbolDeclarations,
+ QsNullable.Null,
+ statement.Comments);
+ }
+ else
+ {
+ // ToDo: add diagnostic message here
+ return statement; // If the blocks can't be converted, return the original
+ }
+ }
+
+ private static ResolvedType GetApplyIfResolvedType(IEnumerable props, ResolvedType argumentType)
+ {
+ var characteristics = new CallableInformation(
+ ResolvedCharacteristics.FromProperties(props),
+ InferredCallableInformation.NoInformation);
+
+ return ResolvedType.New(ResolvedTypeKind.NewOperation(
+ Tuple.Create(argumentType, ResolvedType.New(ResolvedTypeKind.UnitType)),
+ characteristics));
+ }
+
+ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression conditionExpression, QsScope conditionScope, QsScope defaultScope)
+ {
+ var (isCondValid, condId, condArgs) = IsValidScope(conditionScope);
+ var (isDefaultValid, defaultId, defaultArgs) = IsValidScope(defaultScope);
+
+ BuiltIn controlOpInfo;
+ TypedExpression controlArgs;
+ ImmutableArray targetArgs;
+
+ var props = ImmutableHashSet.Empty;
+
+ if (isCondValid)
+ {
+ // Get characteristic properties from global id
+ if (condId.ResolvedType.Resolution is ResolvedTypeKind.Operation op)
+ {
+ props = op.Item2.Characteristics.GetProperties();
+ }
+
+ (bool adj, bool ctl) = (props.Contains(OpProperty.Adjointable), props.Contains(OpProperty.Controllable));
+
+ if (isDefaultValid)
+ {
+ if (adj && ctl)
+ {
+ controlOpInfo = BuiltIn.ApplyIfElseRCA;
+ }
+ else if (adj)
+ {
+ controlOpInfo = BuiltIn.ApplyIfElseRA;
+ }
+ else if (ctl)
+ {
+ controlOpInfo = BuiltIn.ApplyIfElseRC;
+ }
+ else
+ {
+ controlOpInfo = BuiltIn.ApplyIfElseR;
+ }
+
+ var (zeroOpArg, oneOpArg) = (result == QsResult.Zero)
+ ? (CreateValueTupleExpression(condId, condArgs), CreateValueTupleExpression(defaultId, defaultArgs))
+ : (CreateValueTupleExpression(defaultId, defaultArgs), CreateValueTupleExpression(condId, condArgs));
+
+ controlArgs = CreateValueTupleExpression(conditionExpression, zeroOpArg, oneOpArg);
+
+ targetArgs = ImmutableArray.Create(condArgs.ResolvedType, defaultArgs.ResolvedType);
+ }
+ else if (defaultScope == null)
+ {
+ if (adj && ctl)
+ {
+ controlOpInfo = (result == QsResult.Zero)
+ ? BuiltIn.ApplyIfZeroCA
+ : BuiltIn.ApplyIfOneCA;
+ }
+ else if (adj)
+ {
+ controlOpInfo = (result == QsResult.Zero)
+ ? BuiltIn.ApplyIfZeroA
+ : BuiltIn.ApplyIfOneA;
+ }
+ else if (ctl)
+ {
+ controlOpInfo = (result == QsResult.Zero)
+ ? BuiltIn.ApplyIfZeroC
+ : BuiltIn.ApplyIfOneC;
+ }
+ else
+ {
+ controlOpInfo = (result == QsResult.Zero)
+ ? BuiltIn.ApplyIfZero
+ : BuiltIn.ApplyIfOne;
+ }
+
+ controlArgs = CreateValueTupleExpression(
+ conditionExpression,
+ CreateValueTupleExpression(condId, condArgs));
+
+ targetArgs = ImmutableArray.Create(condArgs.ResolvedType);
+ }
+ else
+ {
+ return null; // ToDo: Diagnostic message - default body exists, but is not valid
+ }
+
+ }
+ else
+ {
+ return null; // ToDo: Diagnostic message - cond body not valid
+ }
+
+ // Build the surrounding apply-if call
+ var controlOpId = CreateIdentifierExpression(
+ Identifier.NewGlobalCallable(new QsQualifiedName(controlOpInfo.Namespace, controlOpInfo.Name)),
+ targetArgs
+ .Zip(controlOpInfo.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(controlOpInfo.Namespace, controlOpInfo.Name), param, type))
+ .ToImmutableArray(),
+ GetApplyIfResolvedType(props, controlArgs.ResolvedType));
+
+ // Creates identity resolutions for the call expression
+ var opTypeArgResolutions = targetArgs
+ .SelectMany(x =>
+ x.Resolution is ResolvedTypeKind.TupleType tup
+ ? tup.Item
+ : ImmutableArray.Create(x))
+ .Where(x => x.Resolution.IsTypeParameter)
+ .Select(x => (x.Resolution as ResolvedTypeKind.TypeParameter).Item)
+ .GroupBy(x => (x.Origin, x.TypeName))
+ .Select(group =>
+ {
+ var typeParam = group.First();
+ return Tuple.Create(typeParam.Origin, typeParam.TypeName, ResolvedType.New(ResolvedTypeKind.NewTypeParameter(typeParam)));
+ })
+ .ToImmutableArray();
+
+ return CreateApplyIfCall(controlOpId, controlArgs, opTypeArgResolutions);
+ }
+
+ private (bool, QsConditionalStatement) ProcessElif(QsConditionalStatement cond)
+ {
+ if (cond.ConditionalBlocks.Length < 2) return (false, cond);
+
+ var subCond = new QsConditionalStatement(cond.ConditionalBlocks.RemoveAt(0), cond.Default);
+ var secondCondBlock = cond.ConditionalBlocks[1].Item2;
+
+ var subIfStatment = new QsStatement
+ (
+ QsStatementKind.NewQsConditionalStatement(subCond),
+ LocalDeclarations.Empty,
+ secondCondBlock.Location,
+ secondCondBlock.Comments
+ );
+
+ var newDefault = QsNullable.NewValue(new QsPositionedBlock(
+ new QsScope(ImmutableArray.Create(subIfStatment), secondCondBlock.Body.KnownSymbols),
+ secondCondBlock.Location,
+ QsComments.Empty));
+
+ return (true, new QsConditionalStatement(ImmutableArray.Create(cond.ConditionalBlocks[0]), newDefault));
+ }
+
+ private (bool, QsConditionalStatement) ProcessOR(QsConditionalStatement cond)
+ {
+ // This method expects elif blocks to have been abstracted out
+ if (cond.ConditionalBlocks.Length != 1) return (false, cond);
+
+ var (condition, block) = cond.ConditionalBlocks[0];
+
+ if (condition.Expression is ExpressionKind.OR orCond)
+ {
+ var subCond = new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(orCond.Item2, block)), cond.Default);
+ var subIfStatment = new QsStatement
+ (
+ QsStatementKind.NewQsConditionalStatement(subCond),
+ LocalDeclarations.Empty,
+ block.Location,
+ QsComments.Empty
+ );
+ var newDefault = QsNullable.NewValue(new QsPositionedBlock(
+ new QsScope(ImmutableArray.Create(subIfStatment), block.Body.KnownSymbols),
+ block.Location,
+ QsComments.Empty));
+
+ return (true, new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(orCond.Item1, block)), newDefault));
+ }
+ else
+ {
+ return (false, cond);
+ }
+ }
+
+ private (bool, QsConditionalStatement) ProcessAND(QsConditionalStatement cond)
+ {
+ // This method expects elif blocks to have been abstracted out
+ if (cond.ConditionalBlocks.Length != 1) return (false, cond);
+
+ var (condition, block) = cond.ConditionalBlocks[0];
+
+ if (condition.Expression is ExpressionKind.AND andCond)
+ {
+ var subCond = new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(andCond.Item2, block)), cond.Default);
+ var subIfStatment = new QsStatement
+ (
+ QsStatementKind.NewQsConditionalStatement(subCond),
+ LocalDeclarations.Empty,
+ block.Location,
+ QsComments.Empty
+ );
+ var newBlock = new QsPositionedBlock(
+ new QsScope(ImmutableArray.Create(subIfStatment), block.Body.KnownSymbols),
+ block.Location,
+ QsComments.Empty);
+
+ return (true, new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(andCond.Item1, newBlock)), cond.Default));
+ }
+ else
+ {
+ return (false, cond);
+ }
+ }
+
+ private QsStatement ReshapeConditional(QsStatement statement)
+ {
+ if (statement.Statement is QsStatementKind.QsConditionalStatement cond)
+ {
+ var stm = cond.Item;
+ (_, stm) = ProcessElif(stm);
+ bool wasOrProcessed, wasAndProcessed;
+ do
+ {
+ (wasOrProcessed, stm) = ProcessOR(stm);
+ (wasAndProcessed, stm) = ProcessAND(stm);
+ } while (wasOrProcessed || wasAndProcessed);
+
+ return new QsStatement
+ (
+ QsStatementKind.NewQsConditionalStatement(stm),
+ statement.SymbolDeclarations,
+ statement.Location,
+ statement.Comments
+ );
+ }
+ return statement;
+ }
+
+ public override QsScope Transform(QsScope scope)
+ {
+ var parentSymbols = this.onLocalDeclarations(scope.KnownSymbols);
+ var statements = new List();
+
+ foreach (var statement in scope.Statements)
+ {
+ if (statement.Statement is QsStatementKind.QsConditionalStatement)
+ {
+ var stm = ReshapeConditional(statement);
+ stm = this.onStatement(stm);
+
+ var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(stm);
+
+ if (isCondition)
+ {
+ statements.Add(CreateApplyIfStatement(stm, result, conditionExpression, conditionScope, defaultScope));
+ }
+ else
+ {
+ statements.Add(stm);
+ }
+ }
+ else
+ {
+ statements.Add(this.onStatement(statement));
+ }
+ }
+
+ return new QsScope(statements.ToImmutableArray(), parentSymbols);
+ }
+ }
+
+ // Transformation that updates the contents of newly generated operations by:
+ // 1. Rerouting the origins of type parameter references to the new operation
+ // 2. Changes the IsMutable info on variable that used to be mutable, but are now immutable params to the operation
+ private class UpdateGeneratedOpTransformation
+ {
+ private bool _IsRecursiveIdentifier = false;
+ private ImmutableArray>> _Parameters;
+ private QsQualifiedName _OldName;
+ private QsQualifiedName _NewName;
+
+ public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName)
+ {
+ var filter = new SyntaxTreeTransformation>(
+ new ScopeTransformation(
+ new UpdateGeneratedOpExpression(
+ new UpdateGeneratedOpTransformation(parameters, oldName, newName))));
+
+ return filter.onCallableImplementation(qsCallable);
+ }
+
+ private UpdateGeneratedOpTransformation(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName)
+ {
+ _Parameters = parameters;
+ _OldName = oldName;
+ _NewName = newName;
+ }
+
+ private class UpdateGeneratedOpExpression : ExpressionTransformation
+ {
+ private UpdateGeneratedOpTransformation _super;
+
+ public UpdateGeneratedOpExpression(UpdateGeneratedOpTransformation super) :
+ base(expr => new UpdateGeneratedOpExpressionKind(super, expr as UpdateGeneratedOpExpression),
+ expr => new UpdateGeneratedOpExpressionType(super, expr as UpdateGeneratedOpExpression))
+ { _super = super; }
+
+ public override ImmutableDictionary>, ResolvedType> onTypeParamResolutions(ImmutableDictionary>, ResolvedType> typeParams)
+ {
+ // Prevent keys from having their names updated
+ return typeParams.ToImmutableDictionary(kvp => kvp.Key, kvp => this.Type.Transform(kvp.Value));
+ }
+
+ public override TypedExpression Transform(TypedExpression ex)
+ {
+ // Checks if expression is mutable identifier that is in parameter list
+ if (ex.InferredInformation.IsMutable &&
+ ex.Expression is ExpressionKind.Identifier id &&
+ id.Item1 is Identifier.LocalVariable variable &&
+ _super._Parameters.Any(x => x.VariableName.Equals(variable)))
+ {
+ // Set the mutability to false
+ ex = new TypedExpression(
+ ex.Expression,
+ ex.TypeArguments,
+ ex.ResolvedType,
+ new InferredExpressionInformation(false, ex.InferredInformation.HasLocalQuantumDependency),
+ ex.Range);
+ }
+
+ // Prevent _IsRecursiveIdentifier from propagating beyond the typed expression it is referring to
+ var isRecursiveIdentifier = _super._IsRecursiveIdentifier;
+ var rtrn = base.Transform(ex);
+ _super._IsRecursiveIdentifier = isRecursiveIdentifier;
+ return rtrn;
+ }
+ }
+
+ private class UpdateGeneratedOpExpressionKind : ExpressionKindTransformation
+ {
+ private UpdateGeneratedOpTransformation _super;
+
+ public UpdateGeneratedOpExpressionKind(UpdateGeneratedOpTransformation super, UpdateGeneratedOpExpression expr) : base(expr) { _super = super; }
+
+ public override ExpressionKind onIdentifier(Identifier sym, QsNullable> tArgs)
+ {
+ var rtrn = base.onIdentifier(sym, tArgs);
+
+ // Then check if this is a recursive identifier
+ // In this context, that is a call back to the original callable from the newly generated operation
+ if (sym is Identifier.GlobalCallable callable && _super._OldName.Equals(callable.Item))
+ {
+ // Setting this flag will prevent the rerouting logic from processing the resolved type of the recursive identifier expression.
+ // This is necessary because we don't want any type parameters from the original callable from being rerouted to the new generated
+ // operation's type parameters in the definition of the identifier.
+ _super._IsRecursiveIdentifier = true;
+ }
+ return rtrn;
+ }
+ }
+
+ private class UpdateGeneratedOpExpressionType : ExpressionTypeTransformation
+ {
+ private UpdateGeneratedOpTransformation _super;
+
+ public UpdateGeneratedOpExpressionType(UpdateGeneratedOpTransformation super, UpdateGeneratedOpExpression expr) : base(expr) { _super = super; }
+
+ public override ResolvedTypeKind onTypeParameter(QsTypeParameter tp)
+ {
+ // Reroute a type parameter's origin to the newly generated operation
+ if (!_super._IsRecursiveIdentifier && _super._OldName.Equals(tp.Origin))
+ {
+ tp = new QsTypeParameter(_super._NewName, tp.TypeName, tp.Range);
+ }
+
+ return base.onTypeParameter(tp);
+ }
+ }
+ }
+
+ // Transformation handling the first pass task of hoisting of the contents of conditional statements.
+ // If blocks are first validated to see if they can safely be hoisted into their own operation.
+ // Validation requirements are that there are no return statements and that there are no set statements
+ // on mutables declared outside the block. Setting mutables declared inside the block is valid.
+ // If the block is valid, and there is more than one statement in the block, a new operation with the
+ // block's contents is generated, having all the same type parameters as the calling context
+ // and all known variables at the start of the block become parameters to the new operation.
+ // The contents of the conditional block are then replaced with a call to the new operation with all
+ // the type parameters and known variables being forwarded to the new operation as arguments.
+ private class HoistTransformation
+ {
+ private bool _IsValidScope = true;
+ private List _ControlOperations;
+ private ImmutableArray>> _CurrentHoistParams =
+ ImmutableArray>>.Empty;
+ private bool _ContainsHoistParamRef = false;
+
+ private class CallableDetails
+ {
+ public QsCallable Callable;
+ public QsSpecialization Adjoint;
+ public QsSpecialization Controlled;
+ public QsSpecialization ControlledAdjoint;
+ public QsNullable> TypeParamTypes;
+
+ public CallableDetails(QsCallable callable)
+ {
+ Callable = callable;
+ Adjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsAdjoint);
+ Controlled = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlled);
+ ControlledAdjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlledAdjoint);
+ TypeParamTypes = callable.Signature.TypeParameters.Any(param => param.IsValidName)
+ ? QsNullable>.NewValue(callable.Signature.TypeParameters
+ .Where(param => param.IsValidName)
+ .Select(param =>
+ ResolvedType.New(ResolvedTypeKind.NewTypeParameter(new QsTypeParameter(
+ callable.FullName,
+ ((QsLocalSymbol.ValidName)param).Item,
+ QsNullable>.Null
+ ))))
+ .ToImmutableArray())
+ : QsNullable>.Null;
+ }
+ }
+
+ private CallableDetails _CurrentCallable = null;
+ private bool _InBody = false;
+ private bool _InAdjoint = false;
+ private bool _InControlled = false;
+
+ private bool _InWithinBlock = false;
+
+ public static QsCompilation Apply(QsCompilation compilation)
+ {
+ var filter = new HoistSyntax(new HoistTransformation());
+
+ return new QsCompilation(compilation.Namespaces.Select(ns => filter.Transform(ns)).ToImmutableArray(), compilation.EntryPoints);
+ }
+
+ private (ResolvedSignature, IEnumerable) MakeSpecializations(QsQualifiedName callableName, ResolvedType argsType, SpecializationImplementation bodyImplementation)
+ {
+ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature, SpecializationImplementation impl) =>
+ new QsSpecialization(
+ kind,
+ callableName,
+ ImmutableArray.Empty,
+ _CurrentCallable.Callable.SourceFile,
+ QsNullable.Null,
+ QsNullable>.Null,
+ signature,
+ impl,
+ ImmutableArray.Empty,
+ QsComments.Empty);
+
+ var adj = _CurrentCallable.Adjoint;
+ var ctl = _CurrentCallable.Controlled;
+ var ctlAdj = _CurrentCallable.ControlledAdjoint;
+
+ bool addAdjoint = false;
+ bool addControlled = false;
+
+ if (_InWithinBlock)
+ {
+ addAdjoint = true;
+ addControlled = false;
+ }
+ else if (_InBody)
+ {
+ if (adj != null && adj.Implementation is SpecializationImplementation.Generated adjGen) addAdjoint = adjGen.Item.IsInvert;
+ if (ctl != null && ctl.Implementation is SpecializationImplementation.Generated ctlGen) addControlled = ctlGen.Item.IsDistribute;
+ if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated ctlAdjGen)
+ {
+ addAdjoint = addAdjoint || ctlAdjGen.Item.IsInvert && ctl.Implementation.IsGenerated;
+ addControlled = addControlled || ctlAdjGen.Item.IsDistribute && adj.Implementation.IsGenerated;
+ }
+ }
+ else if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated gen)
+ {
+ addControlled = _InAdjoint && gen.Item.IsDistribute;
+ addAdjoint = _InControlled && gen.Item.IsInvert;
+ }
+
+ var props = new List();
+ if (addAdjoint) props.Add(OpProperty.Adjointable);
+ if (addControlled) props.Add(OpProperty.Controllable);
+ var newSig = new ResolvedSignature(
+ _CurrentCallable.Callable.Signature.TypeParameters,
+ argsType,
+ ResolvedType.New(ResolvedTypeKind.UnitType),
+ new CallableInformation(ResolvedCharacteristics.FromProperties(props), InferredCallableInformation.NoInformation));
+
+ var controlledSig = new ResolvedSignature(
+ newSig.TypeParameters,
+ ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Create(
+ ResolvedType.New(ResolvedTypeKind.NewArrayType(ResolvedType.New(ResolvedTypeKind.Qubit))),
+ newSig.ArgumentType))),
+ newSig.ReturnType,
+ newSig.Information);
+
+ var specializations = new List() { MakeSpec(QsSpecializationKind.QsBody, newSig, bodyImplementation) };
+
+ if (addAdjoint)
+ {
+ specializations.Add(MakeSpec(
+ QsSpecializationKind.QsAdjoint,
+ newSig,
+ SpecializationImplementation.NewGenerated(QsGeneratorDirective.Invert)));
+ }
+
+ if (addControlled)
+ {
+ specializations.Add(MakeSpec(
+ QsSpecializationKind.QsControlled,
+ controlledSig,
+ SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute)));
+ }
+
+ if (addAdjoint && addControlled)
+ {
+ specializations.Add(MakeSpec(
+ QsSpecializationKind.QsControlledAdjoint,
+ controlledSig,
+ SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute)));
+ }
+
+ return (newSig, specializations);
+ }
+
+ private (QsCallable, ResolvedType) GenerateOperation(QsScope contents)
+ {
+ var newName = new QsQualifiedName(
+ _CurrentCallable.Callable.FullName.Namespace,
+ NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _CurrentCallable.Callable.FullName.Name.Value));
+
+ var knownVariables = contents.KnownSymbols.IsEmpty
+ ? ImmutableArray>>.Empty
+ : contents.KnownSymbols.Variables;
+
+ var parameters = QsTuple>.NewQsTuple(knownVariables
+ .Select(var => QsTuple>.NewQsTupleItem(new LocalVariableDeclaration(
+ QsLocalSymbol.NewValidName(var.VariableName),
+ var.Type,
+ var.InferredInformation,
+ var.Position,
+ var.Range)))
+ .ToImmutableArray());
+
+ var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType);
+ if (knownVariables.Length == 1)
+ {
+ paramTypes = knownVariables.First().Type;
+ }
+ else if (knownVariables.Length > 1)
+ {
+ paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables
+ .Select(var => var.Type)
+ .ToImmutableArray()));
+ }
+
+ var (signature, specializations) = MakeSpecializations(newName, paramTypes, SpecializationImplementation.NewProvided(parameters, contents));
+
+ var controlCallable = new QsCallable(
+ QsCallableKind.Operation,
+ newName,
+ ImmutableArray.Empty,
+ _CurrentCallable.Callable.SourceFile,
+ QsNullable.Null,
+ signature,
+ parameters,
+ specializations.ToImmutableArray(),
+ ImmutableArray.Empty,
+ QsComments.Empty);
+
+ var updatedCallable = UpdateGeneratedOpTransformation.Apply(controlCallable, knownVariables, _CurrentCallable.Callable.FullName, newName);
+
+ return (updatedCallable, signature.ArgumentType);
+ }
+
+ private HoistTransformation() { }
+
+ private class HoistSyntax : SyntaxTreeTransformation>
+ {
+ private HoistTransformation _super;
+
+ public HoistSyntax(HoistTransformation super, ScopeTransformation scope = null) :
+ base(scope ?? new ScopeTransformation(
+ scopeTransform => new HoistStatementKind(super, scopeTransform),
+ new HoistExpression(super)))
+ { _super = super; }
+
+ public override QsCallable onCallableImplementation(QsCallable c)
+ {
+ _super._CurrentCallable = new CallableDetails(c);
+ return base.onCallableImplementation(c);
+ }
+
+ public override QsSpecialization onBodySpecialization(QsSpecialization spec)
+ {
+ _super._InBody = true;
+ var rtrn = base.onBodySpecialization(spec);
+ _super._InBody = false;
+ return rtrn;
+ }
+
+ public override QsSpecialization onAdjointSpecialization(QsSpecialization spec)
+ {
+ _super._InAdjoint = true;
+ var rtrn = base.onAdjointSpecialization(spec);
+ _super._InAdjoint = false;
+ return rtrn;
+ }
+
+ public override QsSpecialization onControlledSpecialization(QsSpecialization spec)
+ {
+ _super._InControlled = true;
+ var rtrn = base.onControlledSpecialization(spec);
+ _super._InControlled = false;
+ return rtrn;
+ }
+
+ public override QsCallable onFunction(QsCallable c) => c; // Prevent anything in functions from being hoisted
+
+ public override QsNamespace Transform(QsNamespace ns)
+ {
+ // Control operations list will be populated in the transform
+ _super._ControlOperations = new List();
+ return base.Transform(ns)
+ .WithElements(elems => elems.AddRange(_super._ControlOperations.Select(op => QsNamespaceElement.NewQsCallable(op))));
+ }
+ }
+
+ private class HoistStatementKind : StatementKindTransformation>
+ {
+ private HoistTransformation _super;
+
+ public HoistStatementKind(HoistTransformation super, ScopeTransformation scope) : base(scope) { _super = super; }
+
+ private (QsCallable, QsStatement) HoistIfContents(QsScope contents)
+ {
+ var (targetOp, originalArgumentType) = _super.GenerateOperation(contents);
+ var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation(
+ Tuple.Create(
+ originalArgumentType,
+ ResolvedType.New(ResolvedTypeKind.UnitType)),
+ targetOp.Signature.Information));
+
+ var targetTypeArgTypes = _super._CurrentCallable.TypeParamTypes;
+ var targetOpId = new TypedExpression
+ (
+ ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetOp.FullName), targetTypeArgTypes),
+ targetTypeArgTypes.IsNull
+ ? TypeArgsResolution.Empty
+ : targetTypeArgTypes.Item
+ .Select(type => Tuple.Create(targetOp.FullName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type))
+ .ToImmutableArray(),
+ targetOpType,
+ new InferredExpressionInformation(false, false),
+ QsNullable>.Null
+ );
+
+ var knownSymbols = contents.KnownSymbols.Variables;
+
+ TypedExpression targetArgs = null;
+ if (knownSymbols.Any())
+ {
+ targetArgs = CreateValueTupleExpression(knownSymbols.Select(var => CreateIdentifierExpression(
+ Identifier.NewLocalVariable(var.VariableName),
+ TypeArgsResolution.Empty,
+ var.Type))
+ .ToArray());
+ }
+ else
+ {
+ targetArgs = new TypedExpression
+ (
+ ExpressionKind.UnitValue,
+ TypeArgsResolution.Empty,
+ ResolvedType.New(ResolvedTypeKind.UnitType),
+ new InferredExpressionInformation(false, false),
+ QsNullable>.Null
+ );
+ }
+
+ var call = new TypedExpression
+ (
+ ExpressionKind.NewCallLikeExpression(targetOpId, targetArgs),
+ targetTypeArgTypes.IsNull
+ ? TypeArgsResolution.Empty
+ : targetTypeArgTypes.Item
+ .Select(type => Tuple.Create(_super._CurrentCallable.Callable.FullName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type))
+ .ToImmutableArray(),
+ ResolvedType.New(ResolvedTypeKind.UnitType),
+ new InferredExpressionInformation(false, true),
+ QsNullable>.Null
+ );
+
+ return (targetOp, new QsStatement(
+ QsStatementKind.NewQsExpressionStatement(call),
+ LocalDeclarations.Empty,
+ QsNullable.Null,
+ QsComments.Empty));
+ }
+
+ private bool IsScopeSingleCall(QsScope contents)
+ {
+ if (contents.Statements.Length != 1) return false;
+
+ return contents.Statements[0].Statement is QsStatementKind.QsExpressionStatement expr
+ && expr.Item.Expression is ExpressionKind.CallLikeExpression call
+ && !TypedExpression.IsPartialApplication(expr.Item.Expression)
+ && call.Item1.Expression is ExpressionKind.Identifier;
+ }
+
+ public override QsStatementKind onConjugation(QsConjugation stm)
+ {
+ var superInWithinBlock = _super._InWithinBlock;
+ _super._InWithinBlock = true;
+ var (_, outer) = this.onPositionedBlock(QsNullable.Null, stm.OuterTransformation);
+ _super._InWithinBlock = superInWithinBlock;
+
+ var (_, inner) = this.onPositionedBlock(QsNullable.Null, stm.InnerTransformation);
+
+ return QsStatementKind.NewQsConjugation(new QsConjugation(outer, inner));
+ }
+
+ public override QsStatementKind onReturnStatement(TypedExpression ex)
+ {
+ _super._IsValidScope = false;
+ return base.onReturnStatement(ex);
+ }
+
+ public override QsStatementKind onValueUpdate(QsValueUpdate stm)
+ {
+ // If lhs contains an identifier found in the scope's known variables (variables from the super-scope), the scope is not valid
+ var lhs = this.ExpressionTransformation(stm.Lhs);
+
+ if (_super._ContainsHoistParamRef)
+ {
+ _super._IsValidScope = false;
+ }
+
+ var rhs = this.ExpressionTransformation(stm.Rhs);
+ return QsStatementKind.NewQsValueUpdate(new QsValueUpdate(lhs, rhs));
+ }
+
+ public override QsStatementKind onConditionalStatement(QsConditionalStatement stm)
+ {
+ var contextValidScope = _super._IsValidScope;
+ var contextHoistParams = _super._CurrentHoistParams;
+
+ var isHoistValid = true;
+
+ var newConditionBlocks = new List>();
+ var generatedOperations = new List