From 27be5924904e9aef249da5536a40ec4f5cf736e7 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 22 Nov 2019 19:42:12 -0800 Subject: [PATCH 01/53] Added Andres's basic implementation of the Classically Controlled transformation. --- src/QsCompiler/Core/Dependencies.fs | 15 ++ .../ClassicallyControlledTransformation.cs | 170 ++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index 1b03d6d4d7..5a7101d015 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -21,6 +21,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.ClassicallyControlled" /// Returns the set of namespaces that is automatically opened for each compilation. static member NamespacesToAutoOpen = ImmutableHashSet.Create (BuiltIn.CoreNamespace) @@ -85,6 +86,20 @@ type BuiltIn = { TypeParameters = ImmutableArray.Empty } + // 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), `T)) => Unit) + static member ApplyIfZero = { + Name = "ApplyIfZero" |> NonNullable.New + Namespace = BuiltIn.ClassicallyControlledNamespace + TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + } + // "weak dependencies" in other namespaces (e.g. things used for code actions) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs new file mode 100644 index 0000000000..ea51b27082 --- /dev/null +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -0,0 +1,170 @@ +// 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; + + public class ClassicallyControlledTransformation + { + public static void Apply(QsCompilation compilation) + { + var filter = new ClassicallyControlledSyntax(); + + foreach (var ns in compilation.Namespaces) + { + filter.Transform(ns); + } + } + + private class ClassicallyControlledSyntax : SyntaxTreeTransformation + { + public ClassicallyControlledSyntax(ClassicallyControlledScope scope = null) : base(scope ?? new ClassicallyControlledScope()) { } + } + + private class ClassicallyControlledScope : ScopeTransformation + { + public ClassicallyControlledScope(NoExpressionTransformations expr = null) : base (expr ?? new NoExpressionTransformations()) { } + + public (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatementKind statement) + { + if (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; + + if (expression.Item1.Expression is ExpressionKind.ResultLiteral exp1) + { + return (true, exp1.Item, expression.Item2, scope, defaultScope); + } + else if (expression.Item2.Expression is ExpressionKind.ResultLiteral exp2) + { + return (true, exp2.Item, expression.Item1, scope, defaultScope); + } + } + } + + return (false, null, null, null, null); + } + + public bool AreSimpleCallStatements(IEnumerable stmts) => + stmts.Select(s => IsSimpleCallStatement(s.Statement).Item1).All(b => b); + + public (bool, TypedExpression, TypedExpression) IsSimpleCallStatement(QsStatementKind statement) + { + if (statement is QsStatementKind.QsExpressionStatement expr) + { + var returnType = expr.Item.ResolvedType; + + if (returnType.Resolution.IsUnitType && expr.Item.Expression is ExpressionKind.CallLikeExpression call) + { + return (true, call.Item1, call.Item2); + } + } + + return (false, null, null); + } + + public TypedExpression CreateTypedExpression(ExpressionKind expression) => + CreateTypedExpression(expression, ResolvedType.New(ResolvedTypeKind.UnitType)); + + public TypedExpression CreateTypedExpression(ExpressionKind expression, ResolvedType returnType) + { + var inferredInfo = new InferredExpressionInformation(isMutable: false, hasLocalQuantumDependency: true); + var nullRange = QsNullable>.Null; + var emptyTypes = ImmutableArray, ResolvedType>>.Empty; + + return new TypedExpression(expression, emptyTypes, returnType, inferredInfo, nullRange); + } + + public QsStatement CreateApplyIfStatement(QsResult result, TypedExpression conditionExpression, QsStatement s) + { + var (_, op, originalArgs) = IsSimpleCallStatement(s.Statement); + + var nullTypes = QsNullable>.Null; // ToDo: support callables that take arguments + + var originalCall = CreateTypedExpression(ExpressionKind.NewValueTuple(new TypedExpression[] { op, originalArgs }.ToImmutableArray())); + + var opType = (result == QsResult.One) + ? BuiltIn.ApplyIfOne + : BuiltIn.ApplyIfOne; + var applyIfOp = Identifier.NewGlobalCallable(new QsQualifiedName(opType.Namespace, opType.Name)) as Identifier.GlobalCallable; + var id = CreateTypedExpression(ExpressionKind.NewIdentifier(applyIfOp, nullTypes)); + + var args = CreateTypedExpression(ExpressionKind.NewValueTuple(new TypedExpression[] { conditionExpression, originalCall }.ToImmutableArray())); + var call = CreateTypedExpression(ExpressionKind.NewCallLikeExpression(id, args)); + + return new QsStatement(QsStatementKind.NewQsExpressionStatement(call), s.SymbolDeclarations, s.Location, s.Comments); + } + + private static int _varCount = 0; + + public (QsStatement, TypedExpression) CreateNewConditionVariable(TypedExpression value, QsStatement condStatement) + { + _varCount++; + var name = $"__classic_ctrl{_varCount}__"; + + // The typed expression with the identifier of the variable we just created: + var idExpression = CreateTypedExpression(ExpressionKind.NewIdentifier(Identifier.NewLocalVariable(NonNullable.New(name)), QsNullable>.Null)); + + // The actual binding statement: + var binding = new QsBinding(QsBindingKind.ImmutableBinding, SymbolTuple.NewVariableName(NonNullable.New(name)), value); + var stmt = new QsStatement(QsStatementKind.NewQsVariableDeclaration(binding), condStatement.SymbolDeclarations, condStatement.Location, condStatement.Comments); + + return (stmt, idExpression); + } + + public override QsScope Transform(QsScope scope) + { + var statements = ImmutableArray.CreateBuilder(); + foreach (var statement in scope.Statements) + { + var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(statement.Statement); + + if (isCondition && AreSimpleCallStatements(conditionScope.Statements) && (defaultScope == null || AreSimpleCallStatements(defaultScope.Statements))) + { + // The condition must be an identifier, otherwise we'll call it multiple times. + // If not, create a new variable and use that: + if (!(conditionExpression.Expression is ExpressionKind.Identifier)) + { + var (letStmt, idExpression) = CreateNewConditionVariable(conditionExpression, statement); + statements.Add(letStmt); + conditionExpression = idExpression; + } + + foreach (var stmt in conditionScope.Statements) + { + statements.Add(CreateApplyIfStatement(result, conditionExpression, stmt)); + } + + if (defaultScope != null) + { + foreach (var stmt in defaultScope.Statements) + { + statements.Add(CreateApplyIfStatement(result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, stmt)); + } + } + } + else + { + statements.Add(this.onStatement(statement)); + } + } + + return new QsScope(statements.ToImmutableArray(), scope.KnownSymbols); + } + } + } +} From 3dc34f69ed19ccbf9f31fc5b50167f868c40f7f1 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 3 Dec 2019 11:20:04 -0800 Subject: [PATCH 02/53] Added rewrite step --- .../RewriteSteps/ClassicallyControlled.cs | 48 +++++++++++++++++++ .../Libraries/Library1/Library1.csproj | 2 +- .../TestTargets/Simulation/Target/Program.cs | 2 +- .../Simulation/Target/Simulation.csproj | 2 +- .../ClassicallyControlledTransformation.cs | 13 +++-- 5 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs diff --git a/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs b/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs new file mode 100644 index 0000000000..2ac40e6da3 --- /dev/null +++ b/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.Quantum.QsCompiler.SyntaxTree; +using Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTransformation; + + +namespace Microsoft.Quantum.QsCompiler.BuiltInRewriteSteps +{ + internal class ClassicallyControlled : IRewriteStep + { + public string Name { get; } + public int Priority { get; } + public IDictionary AssemblyConstants { get; } + + public bool ImplementsTransformation { get; } + public bool ImplementsPreconditionVerification { get; } + public bool ImplementsPostconditionVerification { get; } + + public ClassicallyControlled() + { + Name = "ClassicallyControlled"; + Priority = 10; // Not used for built-in transformations like this + AssemblyConstants = new Dictionary(); + ImplementsTransformation = true; + ImplementsPreconditionVerification = false; + ImplementsPostconditionVerification = false; + } + + public bool Transformation(QsCompilation compilation, out QsCompilation transformed) + { + transformed = ClassicallyControlledTransformation.Apply(compilation); + return true; + } + + public bool PreconditionVerification(QsCompilation compilation) + { + throw new System.NotImplementedException(); + } + + public bool PostconditionVerification(QsCompilation compilation) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj b/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj index a121c9089b..fc4716768b 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/Target/Program.cs b/src/QsCompiler/TestTargets/Simulation/Target/Program.cs index ad8ae35195..2233a00ef9 100644 --- a/src/QsCompiler/TestTargets/Simulation/Target/Program.cs +++ b/src/QsCompiler/TestTargets/Simulation/Target/Program.cs @@ -40,7 +40,7 @@ public bool Transformation(QsCompilation compilation, out QsCompilation transfor .Where(s => !Path.GetFileName(s.Value).StartsWith("Microsoft.Quantum")); foreach (var source in allSources) { - var content = SimulationCode.generate(source, compilation.Namespaces); + var content = SimulationCode.generate(source, CodegenContext.Create(compilation.Namespaces)); try { CompilationLoader.GeneratedFile(source, outputFolder ?? this.Name, ".g.cs", content); } catch { success = false; } } diff --git a/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj b/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj index ee6bd60ccd..fbc4f60b5f 100644 --- a/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj +++ b/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index ea51b27082..38c24a10ed 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -17,14 +17,11 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTran public class ClassicallyControlledTransformation { - public static void Apply(QsCompilation compilation) + public static QsCompilation Apply(QsCompilation compilation) { var filter = new ClassicallyControlledSyntax(); - foreach (var ns in compilation.Namespaces) - { - filter.Transform(ns); - } + return new QsCompilation(compilation.Namespaces.Select(ns => filter.Transform(ns)).ToImmutableArray(), compilation.EntryPoints); } private class ClassicallyControlledSyntax : SyntaxTreeTransformation @@ -93,13 +90,13 @@ public QsStatement CreateApplyIfStatement(QsResult result, TypedExpression condi { var (_, op, originalArgs) = IsSimpleCallStatement(s.Statement); - var nullTypes = QsNullable>.Null; // ToDo: support callables that take arguments + var nullTypes = QsNullable>.Null; var originalCall = CreateTypedExpression(ExpressionKind.NewValueTuple(new TypedExpression[] { op, originalArgs }.ToImmutableArray())); var opType = (result == QsResult.One) ? BuiltIn.ApplyIfOne - : BuiltIn.ApplyIfOne; + : BuiltIn.ApplyIfZero; var applyIfOp = Identifier.NewGlobalCallable(new QsQualifiedName(opType.Namespace, opType.Name)) as Identifier.GlobalCallable; var id = CreateTypedExpression(ExpressionKind.NewIdentifier(applyIfOp, nullTypes)); @@ -128,6 +125,8 @@ public QsStatement CreateApplyIfStatement(QsResult result, TypedExpression condi public override QsScope Transform(QsScope scope) { + scope = base.Transform(scope); // process sub-scopes first + var statements = ImmutableArray.CreateBuilder(); foreach (var statement in scope.Statements) { From 97a730b5299d07cbc14d52a0716041209fcb5149 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Wed, 4 Dec 2019 15:00:13 -0800 Subject: [PATCH 03/53] Filled in type information for injected control flow operation calls. --- src/QsCompiler/Compiler/CompilationLoader.cs | 21 ++++- src/QsCompiler/Core/Dependencies.fs | 40 ++++++++- .../ClassicallyControlledTransformation.cs | 85 +++++++++++++------ 3 files changed, 112 insertions(+), 34 deletions(-) diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index b209ecf0ab..0d89d0c28e 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -65,6 +65,11 @@ public struct Configuration /// 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. @@ -149,6 +154,7 @@ private class ExecutionStatus 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; @@ -354,14 +360,21 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference 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")); } + + // executing the specified rewrite steps + + if (this.Config.ConvertClassicalControl) + { + var rewriteStep = new RewriteSteps.LoadedStep(new ClassicallyControlled(), typeof(IRewriteStep), thisDllUri); + this.CompilationStatus.ConvertClassicalControl = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out this.CompilationOutput); + } 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); + this.CompilationStatus.Monomorphization = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out this.CompilationOutput); } if (this.Config.GenerateFunctorSupport) diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index e6aeec2732..f4fd877ff3 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 = { @@ -86,20 +87,55 @@ type BuiltIn = { TypeParameters = ImmutableArray.Empty } - // This is expected to have type <`T>((Result, ((`T => Unit), `T)) => Unit) + static member private _MakeResolvedType builtIn = + let typeParamT = + { + Origin = {Namespace = builtIn.Namespace; Name = builtIn.Name}; + TypeName = builtIn.TypeParameters.[0]; + Range = QsRangeInfo.Null + } |> TypeParameter |> ResolvedType.New + + let args = + [ + Result |> ResolvedType.New; + [ + ( + ( + typeParamT, + UnitType |> ResolvedType.New + ), + CallableInformation.NoInformation + ) |> Operation |> ResolvedType.New; + typeParamT + ].ToImmutableArray() |> TupleType |> ResolvedType.New + ].ToImmutableArray() |> TupleType |> ResolvedType.New + + ( + ( + args, + ResolvedType.New UnitType + ), + CallableInformation.NoInformation + ) |> Operation |> ResolvedType.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), `T)) => Unit) + static member ApplyIfOneResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfOne + + // 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) } + static member ApplyIfZeroResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfZero + // "weak dependencies" in other namespaces (e.g. things used for code actions) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 38c24a10ed..7a2b7daf0f 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -74,34 +74,51 @@ public bool AreSimpleCallStatements(IEnumerable stmts) => return (false, null, null); } - public TypedExpression CreateTypedExpression(ExpressionKind expression) => - CreateTypedExpression(expression, ResolvedType.New(ResolvedTypeKind.UnitType)); - - public TypedExpression CreateTypedExpression(ExpressionKind expression, ResolvedType returnType) - { - var inferredInfo = new InferredExpressionInformation(isMutable: false, hasLocalQuantumDependency: true); - var nullRange = QsNullable>.Null; - var emptyTypes = ImmutableArray, ResolvedType>>.Empty; - - return new TypedExpression(expression, emptyTypes, returnType, inferredInfo, nullRange); - } + public TypedExpression CreateIdentifierExpression(Identifier id, + QsNullable> typeParams, ResolvedType resolvedType) => + new TypedExpression + ( + ExpressionKind.NewIdentifier(id, typeParams), + ImmutableArray, ResolvedType>>.Empty, + resolvedType, + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + public TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => + new TypedExpression + ( + ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), + ImmutableArray, ResolvedType>>.Empty, + ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + public TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, BuiltIn controlOp, ResolvedType opTypeParamResolution) => + new TypedExpression + ( + ExpressionKind.NewCallLikeExpression(id, args), + ImmutableArray.Create(Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), controlOp.TypeParameters.First(), opTypeParamResolution)), + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, true), + QsNullable>.Null + ); public QsStatement CreateApplyIfStatement(QsResult result, TypedExpression conditionExpression, QsStatement s) { var (_, op, originalArgs) = IsSimpleCallStatement(s.Statement); - var nullTypes = QsNullable>.Null; - - var originalCall = CreateTypedExpression(ExpressionKind.NewValueTuple(new TypedExpression[] { op, originalArgs }.ToImmutableArray())); - - var opType = (result == QsResult.One) - ? BuiltIn.ApplyIfOne - : BuiltIn.ApplyIfZero; - var applyIfOp = Identifier.NewGlobalCallable(new QsQualifiedName(opType.Namespace, opType.Name)) as Identifier.GlobalCallable; - var id = CreateTypedExpression(ExpressionKind.NewIdentifier(applyIfOp, nullTypes)); - - var args = CreateTypedExpression(ExpressionKind.NewValueTuple(new TypedExpression[] { conditionExpression, originalCall }.ToImmutableArray())); - var call = CreateTypedExpression(ExpressionKind.NewCallLikeExpression(id, args)); + var (controlOp, opType) = (result == QsResult.One) + ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) + : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); + var applyIfOp = Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)) as Identifier.GlobalCallable; + + var id = CreateIdentifierExpression(applyIfOp, QsNullable>.Null, opType); + var originalCall = CreateValueTupleExpression(op, originalArgs); + var args = CreateValueTupleExpression(conditionExpression, originalCall); + + var call = CreateApplyIfCall(id, args, controlOp, originalArgs.ResolvedType); return new QsStatement(QsStatementKind.NewQsExpressionStatement(call), s.SymbolDeclarations, s.Location, s.Comments); } @@ -111,15 +128,27 @@ public QsStatement CreateApplyIfStatement(QsResult result, TypedExpression condi public (QsStatement, TypedExpression) CreateNewConditionVariable(TypedExpression value, QsStatement condStatement) { _varCount++; - var name = $"__classic_ctrl{_varCount}__"; + var name = NonNullable.New($"__classic_ctrl{_varCount}__"); // The typed expression with the identifier of the variable we just created: - var idExpression = CreateTypedExpression(ExpressionKind.NewIdentifier(Identifier.NewLocalVariable(NonNullable.New(name)), QsNullable>.Null)); + var idExpression = CreateIdentifierExpression(Identifier.NewLocalVariable(name), QsNullable>.Null, value.ResolvedType); // The actual binding statement: - var binding = new QsBinding(QsBindingKind.ImmutableBinding, SymbolTuple.NewVariableName(NonNullable.New(name)), value); - var stmt = new QsStatement(QsStatementKind.NewQsVariableDeclaration(binding), condStatement.SymbolDeclarations, condStatement.Location, condStatement.Comments); - + var binding = new QsBinding(QsBindingKind.ImmutableBinding, SymbolTuple.NewVariableName(name), value); + var symbDecl = new LocalDeclarations(condStatement.SymbolDeclarations.Variables.Add(new LocalVariableDeclaration> + ( + name, + value.ResolvedType, + new InferredExpressionInformation(false, false), + condStatement.Location.IsValue + ? QsNullable>.NewValue(condStatement.Location.Item.Offset) + : QsNullable>.Null, + condStatement.Location.IsValue + ? condStatement.Location.Item.Range + : Tuple.Create(QsPositionInfo.Zero, QsPositionInfo.Zero) + ))); + var stmt = new QsStatement(QsStatementKind.NewQsVariableDeclaration(binding), symbDecl, condStatement.Location, condStatement.Comments); + return (stmt, idExpression); } From 374172cba895a4315e18a4e5854e6b8c3a9a1d91 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Mon, 9 Dec 2019 16:04:35 -0800 Subject: [PATCH 04/53] Added logic to hoist sub-scopes into generated operators. Logic is currently disabled by comments. --- .../ClassicallyControlledTransformation.cs | 138 ++++++++++++++++-- 1 file changed, 125 insertions(+), 13 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 7a2b7daf0f..1a3465d940 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -17,23 +17,50 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTran public class ClassicallyControlledTransformation { + private List _ControlOperators; + private QsCallable _CurrentCallable = null; + public static QsCompilation Apply(QsCompilation compilation) { - var filter = new ClassicallyControlledSyntax(); + var filter = new ClassicallyControlledSyntax(new ClassicallyControlledTransformation()); return new QsCompilation(compilation.Namespaces.Select(ns => filter.Transform(ns)).ToImmutableArray(), compilation.EntryPoints); } + private ClassicallyControlledTransformation() { } + private class ClassicallyControlledSyntax : SyntaxTreeTransformation { - public ClassicallyControlledSyntax(ClassicallyControlledScope scope = null) : base(scope ?? new ClassicallyControlledScope()) { } + private ClassicallyControlledTransformation _super; + + public ClassicallyControlledSyntax(ClassicallyControlledTransformation super, ClassicallyControlledScope scope = null) : base(scope ?? new ClassicallyControlledScope(super)) + { + _super = super; + } + + public override QsCallable onCallableImplementation(QsCallable c) + { + _super._CurrentCallable = c; + return base.onCallableImplementation(c); + } + + public override QsNamespace Transform(QsNamespace ns) + { + // Control operators list will be populated in the transform + _super._ControlOperators = new List(); + return base.Transform(ns) + .WithElements(elems => elems.AddRange(_super._ControlOperators.Select(op => QsNamespaceElement.NewQsCallable(op)))); + } } private class ClassicallyControlledScope : ScopeTransformation { - public ClassicallyControlledScope(NoExpressionTransformations expr = null) : base (expr ?? new NoExpressionTransformations()) { } + private ClassicallyControlledTransformation _super; + + public ClassicallyControlledScope(ClassicallyControlledTransformation super, NoExpressionTransformations expr = null) + : base (expr ?? new NoExpressionTransformations()) { _super = super; } - public (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatementKind statement) + private (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatementKind statement) { if (statement is QsStatementKind.QsConditionalStatement cond) { @@ -56,10 +83,10 @@ private class ClassicallyControlledScope : ScopeTransformation stmts) => + private bool AreSimpleCallStatements(IEnumerable stmts) => stmts.Select(s => IsSimpleCallStatement(s.Statement).Item1).All(b => b); - public (bool, TypedExpression, TypedExpression) IsSimpleCallStatement(QsStatementKind statement) + private (bool, TypedExpression, TypedExpression) IsSimpleCallStatement(QsStatementKind statement) { if (statement is QsStatementKind.QsExpressionStatement expr) { @@ -74,7 +101,7 @@ public bool AreSimpleCallStatements(IEnumerable stmts) => return (false, null, null); } - public TypedExpression CreateIdentifierExpression(Identifier id, + private TypedExpression CreateIdentifierExpression(Identifier id, QsNullable> typeParams, ResolvedType resolvedType) => new TypedExpression ( @@ -85,7 +112,7 @@ public TypedExpression CreateIdentifierExpression(Identifier id, QsNullable>.Null ); - public TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => + private TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => new TypedExpression ( ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), @@ -95,7 +122,7 @@ public TypedExpression CreateValueTupleExpression(params TypedExpression[] expre QsNullable>.Null ); - public TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, BuiltIn controlOp, ResolvedType opTypeParamResolution) => + private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, BuiltIn controlOp, ResolvedType opTypeParamResolution) => new TypedExpression ( ExpressionKind.NewCallLikeExpression(id, args), @@ -105,7 +132,7 @@ public TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression arg QsNullable>.Null ); - public QsStatement CreateApplyIfStatement(QsResult result, TypedExpression conditionExpression, QsStatement s) + private QsStatement CreateApplyIfStatement(QsResult result, TypedExpression conditionExpression, QsStatement s) { var (_, op, originalArgs) = IsSimpleCallStatement(s.Statement); @@ -113,19 +140,54 @@ public QsStatement CreateApplyIfStatement(QsResult result, TypedExpression condi ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); var applyIfOp = Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)) as Identifier.GlobalCallable; - + var id = CreateIdentifierExpression(applyIfOp, QsNullable>.Null, opType); var originalCall = CreateValueTupleExpression(op, originalArgs); var args = CreateValueTupleExpression(conditionExpression, originalCall); - + var call = CreateApplyIfCall(id, args, controlOp, originalArgs.ResolvedType); return new QsStatement(QsStatementKind.NewQsExpressionStatement(call), s.SymbolDeclarations, s.Location, s.Comments); } + private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) + { + // Hoist the scope to its own operator + var targetName = GenerateControlOperator(contents, statement.Comments); + var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( + Tuple.Create( + ResolvedType.New(ResolvedTypeKind.UnitType), // ToDo: add generic params to operator + ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes + CallableInformation.NoInformation + )); + var targetOpId = CreateIdentifierExpression( + Identifier.NewGlobalCallable(new QsQualifiedName(targetName.Namespace, targetName.Name)), + QsNullable>.Null, // ToDo: allow for type params to be passed in from super-scope + targetOpType); + var targetArgs = CreateValueTupleExpression(); // ToDo: add generic params to operator + + // Build the surrounding apply-if call + var (controlOp, controlOpType) = (result == QsResult.One) + ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) + : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); + var controlOpId = CreateIdentifierExpression( + Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)), + QsNullable>.Null, + controlOpType); + var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); + + var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, targetArgs.ResolvedType); + + return new QsStatement( + QsStatementKind.NewQsExpressionStatement(controlCall), + statement.SymbolDeclarations, + QsNullable.Null, + statement.Comments); + } + private static int _varCount = 0; - public (QsStatement, TypedExpression) CreateNewConditionVariable(TypedExpression value, QsStatement condStatement) + private (QsStatement, TypedExpression) CreateNewConditionVariable(TypedExpression value, QsStatement condStatement) { _varCount++; var name = NonNullable.New($"__classic_ctrl{_varCount}__"); @@ -152,6 +214,52 @@ public QsStatement CreateApplyIfStatement(QsResult result, TypedExpression condi return (stmt, idExpression); } + private QsQualifiedName GenerateControlOperator(QsScope contents, QsComments comments) + { + var newName = new QsQualifiedName( + _super._CurrentCallable.FullName.Namespace, + NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _super._CurrentCallable.FullName.Name.Value)); + + var parameters = QsTuple> + .NewQsTuple(ImmutableArray>>.Empty); // ToDo: add generic params to operator + + var paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Empty)); // ToDo: add generic params to operator + + var signature = new ResolvedSignature( + ImmutableArray.Empty, + paramTypes, + ResolvedType.New(ResolvedTypeKind.UnitType), + CallableInformation.NoInformation); + + var spec = new QsSpecialization( + QsSpecializationKind.QsBody, + newName, + ImmutableArray.Empty, + _super._CurrentCallable.SourceFile, + new QsLocation(Tuple.Create(0, 0), Tuple.Create(QsPositionInfo.Zero, QsPositionInfo.Zero)), //ToDo + QsNullable>.Null, + signature, + SpecializationImplementation.NewProvided(parameters, contents), + ImmutableArray.Empty, + comments); + + var controlCallable = new QsCallable( + QsCallableKind.Operation, + newName, + ImmutableArray.Empty, + _super._CurrentCallable.SourceFile, + new QsLocation(Tuple.Create(0, 0), Tuple.Create(QsPositionInfo.Zero, QsPositionInfo.Zero)), //ToDo + signature, + parameters, + ImmutableArray.Create(spec), //ToDo: account for ctrl and adjt + ImmutableArray.Empty, + comments); + + _super._ControlOperators.Add(controlCallable); + + return newName; + } + public override QsScope Transform(QsScope scope) { scope = base.Transform(scope); // process sub-scopes first @@ -172,6 +280,8 @@ public override QsScope Transform(QsScope scope) conditionExpression = idExpression; } + //statements.Add(CreateApplyIfStatement(statement, result, conditionExpression, conditionScope)); + foreach (var stmt in conditionScope.Statements) { statements.Add(CreateApplyIfStatement(result, conditionExpression, stmt)); @@ -179,6 +289,8 @@ public override QsScope Transform(QsScope scope) if (defaultScope != null) { + //statements.Add(CreateApplyIfStatement(statement, result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, defaultScope)); + foreach (var stmt in defaultScope.Statements) { statements.Add(CreateApplyIfStatement(result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, stmt)); From 5107fe31297c1de8dab589c28156312e759d1416 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Mon, 9 Dec 2019 17:08:06 -0800 Subject: [PATCH 05/53] Changed 'operator' to 'operation' --- .../ClassicallyControlledTransformation.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 1a3465d940..38ecf49fd9 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -17,7 +17,7 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTran public class ClassicallyControlledTransformation { - private List _ControlOperators; + private List _ControlOperations; private QsCallable _CurrentCallable = null; public static QsCompilation Apply(QsCompilation compilation) @@ -46,10 +46,10 @@ public override QsCallable onCallableImplementation(QsCallable c) public override QsNamespace Transform(QsNamespace ns) { - // Control operators list will be populated in the transform - _super._ControlOperators = new List(); + // Control operations list will be populated in the transform + _super._ControlOperations = new List(); return base.Transform(ns) - .WithElements(elems => elems.AddRange(_super._ControlOperators.Select(op => QsNamespaceElement.NewQsCallable(op)))); + .WithElements(elems => elems.AddRange(_super._ControlOperations.Select(op => QsNamespaceElement.NewQsCallable(op)))); } } @@ -152,11 +152,11 @@ private QsStatement CreateApplyIfStatement(QsResult result, TypedExpression cond private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) { - // Hoist the scope to its own operator - var targetName = GenerateControlOperator(contents, statement.Comments); + // Hoist the scope to its own operation + var targetName = GenerateControlOperation(contents, statement.Comments); var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( Tuple.Create( - ResolvedType.New(ResolvedTypeKind.UnitType), // ToDo: add generic params to operator + ResolvedType.New(ResolvedTypeKind.UnitType), // ToDo: add generic params to operation ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes CallableInformation.NoInformation )); @@ -164,7 +164,7 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul Identifier.NewGlobalCallable(new QsQualifiedName(targetName.Namespace, targetName.Name)), QsNullable>.Null, // ToDo: allow for type params to be passed in from super-scope targetOpType); - var targetArgs = CreateValueTupleExpression(); // ToDo: add generic params to operator + var targetArgs = CreateValueTupleExpression(); // ToDo: add generic params to operation // Build the surrounding apply-if call var (controlOp, controlOpType) = (result == QsResult.One) @@ -214,16 +214,16 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul return (stmt, idExpression); } - private QsQualifiedName GenerateControlOperator(QsScope contents, QsComments comments) + private QsQualifiedName GenerateControlOperation(QsScope contents, QsComments comments) { var newName = new QsQualifiedName( _super._CurrentCallable.FullName.Namespace, NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _super._CurrentCallable.FullName.Name.Value)); var parameters = QsTuple> - .NewQsTuple(ImmutableArray>>.Empty); // ToDo: add generic params to operator + .NewQsTuple(ImmutableArray>>.Empty); // ToDo: add generic params to operation - var paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Empty)); // ToDo: add generic params to operator + var paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Empty)); // ToDo: add generic params to operation var signature = new ResolvedSignature( ImmutableArray.Empty, @@ -255,7 +255,7 @@ private QsQualifiedName GenerateControlOperator(QsScope contents, QsComments com ImmutableArray.Empty, comments); - _super._ControlOperators.Add(controlCallable); + _super._ControlOperations.Add(controlCallable); return newName; } From 2c5f1cd1c181ab190bc5a89e46f82aba286256ee Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 10 Dec 2019 11:19:25 -0800 Subject: [PATCH 06/53] Nested if's will be processed. --- .../ConstantPropagation.fs | 4 +- .../ClassicallyControlledTransformation.cs | 45 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/QsCompiler/Optimizations/OptimizingTransformations/ConstantPropagation.fs b/src/QsCompiler/Optimizations/OptimizingTransformations/ConstantPropagation.fs index d3862246e3..4f80bf4967 100644 --- a/src/QsCompiler/Optimizations/OptimizingTransformations/ConstantPropagation.fs +++ b/src/QsCompiler/Optimizations/OptimizingTransformations/ConstantPropagation.fs @@ -16,8 +16,8 @@ open Microsoft.Quantum.QsCompiler.Transformations.Core /// 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. -let rec private shouldPropagate callables (expr : TypedExpression) = +/// then we should substitute x with [expr] wherever x occurs in future code. +let private shouldPropagate callables (expr : TypedExpression) = let folder ex sub = isLiteral callables ex || (match ex.Expression with diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 38ecf49fd9..7e1fe6ae77 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -164,7 +164,15 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul Identifier.NewGlobalCallable(new QsQualifiedName(targetName.Namespace, targetName.Name)), QsNullable>.Null, // ToDo: allow for type params to be passed in from super-scope targetOpType); - var targetArgs = CreateValueTupleExpression(); // ToDo: add generic params to operation + //var targetArgs = CreateValueTupleExpression(); + var targetArgs = new TypedExpression // ToDo: add generic params to operation + ( + ExpressionKind.UnitValue, + ImmutableArray, ResolvedType>>.Empty, + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); // Build the surrounding apply-if call var (controlOp, controlOpType) = (result == QsResult.One) @@ -217,19 +225,20 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul private QsQualifiedName GenerateControlOperation(QsScope contents, QsComments comments) { var newName = new QsQualifiedName( - _super._CurrentCallable.FullName.Namespace, - NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _super._CurrentCallable.FullName.Name.Value)); + _super._CurrentCallable.FullName.Namespace, + NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _super._CurrentCallable.FullName.Name.Value)); var parameters = QsTuple> .NewQsTuple(ImmutableArray>>.Empty); // ToDo: add generic params to operation - var paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Empty)); // ToDo: add generic params to operation + //var paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Empty)); + var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); // ToDo: add generic params to operation var signature = new ResolvedSignature( - ImmutableArray.Empty, - paramTypes, - ResolvedType.New(ResolvedTypeKind.UnitType), - CallableInformation.NoInformation); + ImmutableArray.Empty, + paramTypes, + ResolvedType.New(ResolvedTypeKind.UnitType), + CallableInformation.NoInformation); var spec = new QsSpecialization( QsSpecializationKind.QsBody, @@ -280,21 +289,21 @@ public override QsScope Transform(QsScope scope) conditionExpression = idExpression; } - //statements.Add(CreateApplyIfStatement(statement, result, conditionExpression, conditionScope)); + statements.Add(CreateApplyIfStatement(statement, result, conditionExpression, conditionScope)); - foreach (var stmt in conditionScope.Statements) - { - statements.Add(CreateApplyIfStatement(result, conditionExpression, stmt)); - } + //foreach (var stmt in conditionScope.Statements) + //{ + // statements.Add(CreateApplyIfStatement(result, conditionExpression, stmt)); + //} if (defaultScope != null) { - //statements.Add(CreateApplyIfStatement(statement, result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, defaultScope)); + statements.Add(CreateApplyIfStatement(statement, result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, defaultScope)); - foreach (var stmt in defaultScope.Statements) - { - statements.Add(CreateApplyIfStatement(result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, stmt)); - } + //foreach (var stmt in defaultScope.Statements) + //{ + // statements.Add(CreateApplyIfStatement(result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, stmt)); + //} } } else From 16d693184e6b0b91cb1c5115ee1bee24ab09749c Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 10 Dec 2019 14:14:49 -0800 Subject: [PATCH 07/53] Convert elif to nested if structure to allow for processing. --- .../ClassicallyControlledTransformation.cs | 72 +++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 7e1fe6ae77..1c89f02a20 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -60,9 +60,72 @@ private class ClassicallyControlledScope : ScopeTransformation.NewValue(new QsPositionedBlock( + new QsScope(ImmutableArray.Create(subIfStatment), secondCondBlock.Body.KnownSymbols), + secondCondBlock.Location, + secondCondBlock.Comments)); + + return new QsStatement + ( + QsStatementKind.NewQsConditionalStatement(new QsConditionalStatement(ImmutableArray.Create(cond.ConditionalBlocks[0]), newDefault)), + originalStatment.SymbolDeclarations, + originalStatment.Location, + originalStatment.Comments + ); + } + + private QsStatement MakeNestedIfs(QsStatement originalStatment) + { + if (originalStatment.Statement is QsStatementKind.QsConditionalStatement cond) + { + var nested = MakeNestedIfs(originalStatment, cond); + var nestedKind = ((QsStatementKind.QsConditionalStatement)nested.Statement).Item; + if (nestedKind.Default.IsValue) + { + var newDefault = QsNullable.NewValue(new QsPositionedBlock( + this.Transform(nestedKind.Default.Item.Body), + nestedKind.Default.Item.Location, + nestedKind.Default.Item.Comments)); + + nested = new QsStatement + ( + QsStatementKind.NewQsConditionalStatement(new QsConditionalStatement(nestedKind.ConditionalBlocks, newDefault)), + nested.SymbolDeclarations, + nested.Location, + nested.Comments + ); + } + + return nested; + } + + return originalStatment; + } + + private (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)) { @@ -274,9 +337,10 @@ public override QsScope Transform(QsScope scope) scope = base.Transform(scope); // process sub-scopes first var statements = ImmutableArray.CreateBuilder(); - foreach (var statement in scope.Statements) + foreach (var s in scope.Statements) { - var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(statement.Statement); + var statement = MakeNestedIfs(s); + var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(statement); if (isCondition && AreSimpleCallStatements(conditionScope.Statements) && (defaultScope == null || AreSimpleCallStatements(defaultScope.Statements))) { From 53f9f8b623ebf3e4965d4998d59e22c82aedc304 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 10 Dec 2019 17:51:43 -0800 Subject: [PATCH 08/53] Added logic to generated parameters/arguments for the generated operations and calls. --- .../ClassicallyControlledTransformation.cs | 93 ++++++++++--------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 1c89f02a20..33cb09b30c 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -14,6 +14,7 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTran { using ExpressionKind = QsExpressionKind; using ResolvedTypeKind = QsTypeKind; + using ParamTuple = QsTuple>; public class ClassicallyControlledTransformation { @@ -195,31 +196,13 @@ private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression ar QsNullable>.Null ); - private QsStatement CreateApplyIfStatement(QsResult result, TypedExpression conditionExpression, QsStatement s) - { - var (_, op, originalArgs) = IsSimpleCallStatement(s.Statement); - - var (controlOp, opType) = (result == QsResult.One) - ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) - : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); - var applyIfOp = Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)) as Identifier.GlobalCallable; - - var id = CreateIdentifierExpression(applyIfOp, QsNullable>.Null, opType); - var originalCall = CreateValueTupleExpression(op, originalArgs); - var args = CreateValueTupleExpression(conditionExpression, originalCall); - - var call = CreateApplyIfCall(id, args, controlOp, originalArgs.ResolvedType); - - return new QsStatement(QsStatementKind.NewQsExpressionStatement(call), s.SymbolDeclarations, s.Location, s.Comments); - } - private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) { // Hoist the scope to its own operation - var targetName = GenerateControlOperation(contents, statement.Comments); + var (targetName, targetParamType) = GenerateControlOperation(contents, statement.Comments); var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( Tuple.Create( - ResolvedType.New(ResolvedTypeKind.UnitType), // ToDo: add generic params to operation + targetParamType, ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes CallableInformation.NoInformation )); @@ -227,15 +210,27 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul Identifier.NewGlobalCallable(new QsQualifiedName(targetName.Namespace, targetName.Name)), QsNullable>.Null, // ToDo: allow for type params to be passed in from super-scope targetOpType); - //var targetArgs = CreateValueTupleExpression(); - var targetArgs = new TypedExpression // ToDo: add generic params to operation - ( - ExpressionKind.UnitValue, - ImmutableArray, ResolvedType>>.Empty, - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); + + TypedExpression targetArgs = null; + if (contents.KnownSymbols.Variables.Any()) + { + targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(x => CreateIdentifierExpression( + Identifier.NewLocalVariable(x.VariableName), + QsNullable>.Null, + x.Type + )).ToArray()); + } + else + { + targetArgs = new TypedExpression + ( + ExpressionKind.UnitValue, + ImmutableArray, ResolvedType>>.Empty, + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + } // Build the surrounding apply-if call var (controlOp, controlOpType) = (result == QsResult.One) @@ -264,7 +259,10 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul var name = NonNullable.New($"__classic_ctrl{_varCount}__"); // The typed expression with the identifier of the variable we just created: - var idExpression = CreateIdentifierExpression(Identifier.NewLocalVariable(name), QsNullable>.Null, value.ResolvedType); + var idExpression = CreateIdentifierExpression( + Identifier.NewLocalVariable(name), + QsNullable>.Null, + value.ResolvedType); // The actual binding statement: var binding = new QsBinding(QsBindingKind.ImmutableBinding, SymbolTuple.NewVariableName(name), value); @@ -285,17 +283,30 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul return (stmt, idExpression); } - private QsQualifiedName GenerateControlOperation(QsScope contents, QsComments comments) + private (QsQualifiedName, ResolvedType) GenerateControlOperation(QsScope contents, QsComments comments) { var newName = new QsQualifiedName( _super._CurrentCallable.FullName.Namespace, NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _super._CurrentCallable.FullName.Name.Value)); - var parameters = QsTuple> - .NewQsTuple(ImmutableArray>>.Empty); // ToDo: add generic params to operation + var knownVariables = contents.KnownSymbols.Variables; - //var paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Empty)); - var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); // ToDo: add generic params to operation + var parameters = ParamTuple.NewQsTuple(knownVariables + .Select(var => ParamTuple.NewQsTupleItem(new LocalVariableDeclaration( + QsLocalSymbol.NewValidName(var.VariableName), + var.Type, + var.InferredInformation, + var.Position, + var.Range))) + .ToImmutableArray()); + + var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); + if (knownVariables.Any()) + { + paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables + .Select(var => var.Type) + .ToImmutableArray())); + } var signature = new ResolvedSignature( ImmutableArray.Empty, @@ -329,7 +340,7 @@ private QsQualifiedName GenerateControlOperation(QsScope contents, QsComments co _super._ControlOperations.Add(controlCallable); - return newName; + return (newName, paramTypes); } public override QsScope Transform(QsScope scope) @@ -355,19 +366,9 @@ public override QsScope Transform(QsScope scope) statements.Add(CreateApplyIfStatement(statement, result, conditionExpression, conditionScope)); - //foreach (var stmt in conditionScope.Statements) - //{ - // statements.Add(CreateApplyIfStatement(result, conditionExpression, stmt)); - //} - if (defaultScope != null) { statements.Add(CreateApplyIfStatement(statement, result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, defaultScope)); - - //foreach (var stmt in defaultScope.Statements) - //{ - // statements.Add(CreateApplyIfStatement(result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, stmt)); - //} } } else From 43182e468bba106409306ee908c7c09e892848e9 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Wed, 11 Dec 2019 18:09:41 -0800 Subject: [PATCH 09/53] Added super-scope's type params to the signiture of the generated operations. Also added a transformation for reassigning all type param references in the scope to the new local type params of the genrerated operation. --- .../ClassicallyControlledTransformation.cs | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 33cb09b30c..f64ea1ecee 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -170,7 +170,7 @@ private TypedExpression CreateIdentifierExpression(Identifier id, new TypedExpression ( ExpressionKind.NewIdentifier(id, typeParams), - ImmutableArray, ResolvedType>>.Empty, + ImmutableArray, ResolvedType>>.Empty, // ToDo: allow for type params to be passed in from super-scope resolvedType, new InferredExpressionInformation(false, false), QsNullable>.Null @@ -214,10 +214,10 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul TypedExpression targetArgs = null; if (contents.KnownSymbols.Variables.Any()) { - targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(x => CreateIdentifierExpression( - Identifier.NewLocalVariable(x.VariableName), + targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => CreateIdentifierExpression( + Identifier.NewLocalVariable(var.VariableName), QsNullable>.Null, - x.Type + var.Type )).ToArray()); } else @@ -309,11 +309,14 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul } var signature = new ResolvedSignature( - ImmutableArray.Empty, + _super._CurrentCallable.Signature.TypeParameters, + //ImmutableArray.Empty, // ToDo: allow for type params to be passed in from super-scope paramTypes, ResolvedType.New(ResolvedTypeKind.UnitType), CallableInformation.NoInformation); + var filter = _super.GetRerouteTransformation(newName.Name); + var spec = new QsSpecialization( QsSpecializationKind.QsBody, newName, @@ -322,7 +325,7 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul new QsLocation(Tuple.Create(0, 0), Tuple.Create(QsPositionInfo.Zero, QsPositionInfo.Zero)), //ToDo QsNullable>.Null, signature, - SpecializationImplementation.NewProvided(parameters, contents), + SpecializationImplementation.NewProvided(parameters, filter.Transform(contents)), ImmutableArray.Empty, comments); @@ -380,5 +383,35 @@ public override QsScope Transform(QsScope scope) return new QsScope(statements.ToImmutableArray(), scope.KnownSymbols); } } + + private ScopeTransformation> + GetRerouteTransformation(NonNullable newName) => + new ScopeTransformation>( + new ExpressionTransformation( + expr => new ExpressionKindTransformation>(expr as ExpressionTransformation), + expr => new RerouteTypeParamOriginExpressionType(this, newName, expr))); + + private class RerouteTypeParamOriginExpressionType : ExpressionTypeTransformation> + { + private ClassicallyControlledTransformation _super; + private NonNullable _newName; + + public RerouteTypeParamOriginExpressionType(ClassicallyControlledTransformation super, NonNullable newName, ExpressionTransformation expr) : base(expr) { _super = super; _newName = newName; } + + public override ResolvedTypeKind onTypeParameter(QsTypeParameter tp) + { + if (_super._CurrentCallable.FullName.Equals(tp.Origin)) + { + tp = new QsTypeParameter + ( + new QsQualifiedName(tp.Origin.Namespace, _newName), + tp.TypeName, + tp.Range + ); + } + + return base.onTypeParameter(tp); + } + } } } From e0264dfbd9409f945d4e8e806c9368100a3fac20 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Thu, 12 Dec 2019 17:40:44 -0800 Subject: [PATCH 10/53] Forwarding type parameters into the generated operations. --- .../ClassicallyControlledTransformation.cs | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index f64ea1ecee..7fd996014c 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -204,12 +204,22 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul Tuple.Create( targetParamType, ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes - CallableInformation.NoInformation - )); - var targetOpId = CreateIdentifierExpression( - Identifier.NewGlobalCallable(new QsQualifiedName(targetName.Namespace, targetName.Name)), - QsNullable>.Null, // ToDo: allow for type params to be passed in from super-scope - targetOpType); + CallableInformation.NoInformation)); + + var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); + var targetOpId = new TypedExpression + ( + // ToDo: allow for type params to be passed in from super-scope + ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), + targetTypeParamTypes.IsNull + ? ImmutableArray, ResolvedType>>.Empty + : targetTypeParamTypes.Item + .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + .ToImmutableArray(), + targetOpType, + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); TypedExpression targetArgs = null; if (contents.KnownSymbols.Variables.Any()) @@ -217,8 +227,8 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => CreateIdentifierExpression( Identifier.NewLocalVariable(var.VariableName), QsNullable>.Null, - var.Type - )).ToArray()); + var.Type)) + .ToArray()); } else { @@ -283,6 +293,26 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul return (stmt, idExpression); } + private QsNullable> GetTypeParamTypesFromCallable(QsCallable callable) + { + if (callable.Signature.TypeParameters.Any(param => param.IsValidName)) + { + return 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 //ToDo: should be able to get this from specialization + )))) + .ToImmutableArray()); + } + else + { + return QsNullable>.Null; + } + } + private (QsQualifiedName, ResolvedType) GenerateControlOperation(QsScope contents, QsComments comments) { var newName = new QsQualifiedName( From 3bf4f6c2de58d1793256882ab52c8da1cdcf2e84 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Mon, 16 Dec 2019 12:26:43 -0800 Subject: [PATCH 11/53] Updated transformation with nullable locations --- .../ClassicallyControlledTransformation.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 7fd996014c..72ae2ac465 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -170,7 +170,7 @@ private TypedExpression CreateIdentifierExpression(Identifier id, new TypedExpression ( ExpressionKind.NewIdentifier(id, typeParams), - ImmutableArray, ResolvedType>>.Empty, // ToDo: allow for type params to be passed in from super-scope + ImmutableArray, ResolvedType>>.Empty, resolvedType, new InferredExpressionInformation(false, false), QsNullable>.Null @@ -209,7 +209,6 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); var targetOpId = new TypedExpression ( - // ToDo: allow for type params to be passed in from super-scope ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), targetTypeParamTypes.IsNull ? ImmutableArray, ResolvedType>>.Empty @@ -303,7 +302,7 @@ private QsNullable> GetTypeParamTypesFromCallable(Q ResolvedType.New(ResolvedTypeKind.NewTypeParameter(new QsTypeParameter( callable.FullName, ((QsLocalSymbol.ValidName)param).Item, - QsNullable>.Null //ToDo: should be able to get this from specialization + QsNullable>.Null )))) .ToImmutableArray()); } @@ -340,7 +339,6 @@ private QsNullable> GetTypeParamTypesFromCallable(Q var signature = new ResolvedSignature( _super._CurrentCallable.Signature.TypeParameters, - //ImmutableArray.Empty, // ToDo: allow for type params to be passed in from super-scope paramTypes, ResolvedType.New(ResolvedTypeKind.UnitType), CallableInformation.NoInformation); @@ -352,7 +350,7 @@ private QsNullable> GetTypeParamTypesFromCallable(Q newName, ImmutableArray.Empty, _super._CurrentCallable.SourceFile, - new QsLocation(Tuple.Create(0, 0), Tuple.Create(QsPositionInfo.Zero, QsPositionInfo.Zero)), //ToDo + QsNullable.Null, QsNullable>.Null, signature, SpecializationImplementation.NewProvided(parameters, filter.Transform(contents)), @@ -364,7 +362,7 @@ private QsNullable> GetTypeParamTypesFromCallable(Q newName, ImmutableArray.Empty, _super._CurrentCallable.SourceFile, - new QsLocation(Tuple.Create(0, 0), Tuple.Create(QsPositionInfo.Zero, QsPositionInfo.Zero)), //ToDo + QsNullable.Null, signature, parameters, ImmutableArray.Create(spec), //ToDo: account for ctrl and adjt From 6e4b13900ac38033cbeaabee29b390a466ed498c Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 17 Dec 2019 17:37:55 -0800 Subject: [PATCH 12/53] Expanded on the type param reroute transformation so that it operates on a whole callable, and added logic for some special cases where the transformation should not reroute. --- .../ClassicallyControlledTransformation.cs | 71 +++++++++++++++---- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 72ae2ac465..c4c676c381 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -343,8 +343,6 @@ private QsNullable> GetTypeParamTypesFromCallable(Q ResolvedType.New(ResolvedTypeKind.UnitType), CallableInformation.NoInformation); - var filter = _super.GetRerouteTransformation(newName.Name); - var spec = new QsSpecialization( QsSpecializationKind.QsBody, newName, @@ -353,7 +351,7 @@ private QsNullable> GetTypeParamTypesFromCallable(Q QsNullable.Null, QsNullable>.Null, signature, - SpecializationImplementation.NewProvided(parameters, filter.Transform(contents)), + SpecializationImplementation.NewProvided(parameters, contents), ImmutableArray.Empty, comments); @@ -369,7 +367,8 @@ private QsNullable> GetTypeParamTypesFromCallable(Q ImmutableArray.Empty, comments); - _super._ControlOperations.Add(controlCallable); + var filter = _super.GetRerouteTransformation(newName.Name); + _super._ControlOperations.Add(filter.onCallableImplementation(controlCallable)); return (newName, paramTypes); } @@ -412,23 +411,69 @@ public override QsScope Transform(QsScope scope) } } - private ScopeTransformation> - GetRerouteTransformation(NonNullable newName) => - new ScopeTransformation>( - new ExpressionTransformation( - expr => new ExpressionKindTransformation>(expr as ExpressionTransformation), - expr => new RerouteTypeParamOriginExpressionType(this, newName, expr))); + private SyntaxTreeTransformation> GetRerouteTransformation(NonNullable newName) => + new SyntaxTreeTransformation>( + new ScopeTransformation( + new RerouteTypeParamOriginExpression(this, newName))); + + private bool _IsRecursiveIdentifier = false; + + private class RerouteTypeParamOriginExpression : ExpressionTransformation + { + private ClassicallyControlledTransformation _super; + + public RerouteTypeParamOriginExpression(ClassicallyControlledTransformation super, NonNullable newName) : + base(expr => new RerouteTypeParamOriginExpressionKind(super, expr as RerouteTypeParamOriginExpression), + expr => new RerouteTypeParamOriginExpressionType(super, newName, expr as RerouteTypeParamOriginExpression)) { _super = super; } + + public override ImmutableDictionary>, ResolvedType> onTypeParamResolutions(ImmutableDictionary>, ResolvedType> typeParams) + { + return typeParams.ToImmutableDictionary(kvp => kvp.Key, kvp => this.Type.Transform(kvp.Value)); + } + + public override TypedExpression Transform(TypedExpression ex) + { + // prevent _IsRecursiveIdentifier from propagating beyond the typed expression it is in reference to + var isRecursiveIdentifier = _super._IsRecursiveIdentifier; + var rtrn = base.Transform(ex); + _super._IsRecursiveIdentifier = isRecursiveIdentifier; + return rtrn; + } + } + + private class RerouteTypeParamOriginExpressionKind : ExpressionKindTransformation + { + private ClassicallyControlledTransformation _super; + + public RerouteTypeParamOriginExpressionKind(ClassicallyControlledTransformation super, RerouteTypeParamOriginExpression expr) : base(expr) { _super = super; } + + public override ExpressionKind onIdentifier(Identifier sym, QsNullable> tArgs) + { + // Process the identifier (including its type arguments) + var rtrn = base.onIdentifier(sym, tArgs); + + // Then check if this is a recursive identifier + if (sym is Identifier.GlobalCallable callable && _super._CurrentCallable.FullName.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 RerouteTypeParamOriginExpressionType : ExpressionTypeTransformation> + private class RerouteTypeParamOriginExpressionType : ExpressionTypeTransformation { private ClassicallyControlledTransformation _super; private NonNullable _newName; - public RerouteTypeParamOriginExpressionType(ClassicallyControlledTransformation super, NonNullable newName, ExpressionTransformation expr) : base(expr) { _super = super; _newName = newName; } + public RerouteTypeParamOriginExpressionType(ClassicallyControlledTransformation super, NonNullable newName, RerouteTypeParamOriginExpression expr) : base(expr) { _super = super; _newName = newName; } public override ResolvedTypeKind onTypeParameter(QsTypeParameter tp) { - if (_super._CurrentCallable.FullName.Equals(tp.Origin)) + if (!_super._IsRecursiveIdentifier && _super._CurrentCallable.FullName.Equals(tp.Origin)) { tp = new QsTypeParameter ( From 45f306004f8ed2315e938e02be763b55a924af56 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 17 Dec 2019 17:41:08 -0800 Subject: [PATCH 13/53] Reorganized the reroute transform to be a self-contained transform subclass inside the ClassicallyControlledTranformation class. --- .../ClassicallyControlledTransformation.cs | 140 ++++++++++-------- 1 file changed, 76 insertions(+), 64 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index c4c676c381..930237d903 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -367,8 +367,8 @@ private QsNullable> GetTypeParamTypesFromCallable(Q ImmutableArray.Empty, comments); - var filter = _super.GetRerouteTransformation(newName.Name); - _super._ControlOperations.Add(filter.onCallableImplementation(controlCallable)); + var reroutedCallable = RerouteTypeParamOriginTransformation.Apply(controlCallable, _super._CurrentCallable.FullName, newName); + _super._ControlOperations.Add(reroutedCallable); return (newName, paramTypes); } @@ -411,80 +411,92 @@ public override QsScope Transform(QsScope scope) } } - private SyntaxTreeTransformation> GetRerouteTransformation(NonNullable newName) => - new SyntaxTreeTransformation>( - new ScopeTransformation( - new RerouteTypeParamOriginExpression(this, newName))); - - private bool _IsRecursiveIdentifier = false; - - private class RerouteTypeParamOriginExpression : ExpressionTransformation + private class RerouteTypeParamOriginTransformation { - private ClassicallyControlledTransformation _super; + private bool _IsRecursiveIdentifier = false; + private QsQualifiedName _OldName; + private QsQualifiedName _NewName; + + public static QsCallable Apply(QsCallable qsCallable, QsQualifiedName oldName, QsQualifiedName newName) + { + var filter = new SyntaxTreeTransformation>( + new ScopeTransformation( + new RerouteTypeParamOriginExpression( + new RerouteTypeParamOriginTransformation(oldName, newName)))); + + return filter.onCallableImplementation(qsCallable); + } - public RerouteTypeParamOriginExpression(ClassicallyControlledTransformation super, NonNullable newName) : - base(expr => new RerouteTypeParamOriginExpressionKind(super, expr as RerouteTypeParamOriginExpression), - expr => new RerouteTypeParamOriginExpressionType(super, newName, expr as RerouteTypeParamOriginExpression)) { _super = super; } + private RerouteTypeParamOriginTransformation(QsQualifiedName oldName, QsQualifiedName newName) { _OldName = oldName; _NewName = newName; } - public override ImmutableDictionary>, ResolvedType> onTypeParamResolutions(ImmutableDictionary>, ResolvedType> typeParams) + private class RerouteTypeParamOriginExpression : ExpressionTransformation { - return typeParams.ToImmutableDictionary(kvp => kvp.Key, kvp => this.Type.Transform(kvp.Value)); + private RerouteTypeParamOriginTransformation _super; + + public RerouteTypeParamOriginExpression(RerouteTypeParamOriginTransformation super) : + base(expr => new RerouteTypeParamOriginExpressionKind(super, expr as RerouteTypeParamOriginExpression), + expr => new RerouteTypeParamOriginExpressionType(super, expr as RerouteTypeParamOriginExpression)) + { _super = super; } + + public override ImmutableDictionary>, ResolvedType> onTypeParamResolutions(ImmutableDictionary>, ResolvedType> typeParams) + { + return typeParams.ToImmutableDictionary(kvp => kvp.Key, kvp => this.Type.Transform(kvp.Value)); + } + + public override TypedExpression Transform(TypedExpression ex) + { + // 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; + } } - public override TypedExpression Transform(TypedExpression ex) + private class RerouteTypeParamOriginExpressionKind : ExpressionKindTransformation { - // prevent _IsRecursiveIdentifier from propagating beyond the typed expression it is in reference to - var isRecursiveIdentifier = _super._IsRecursiveIdentifier; - var rtrn = base.Transform(ex); - _super._IsRecursiveIdentifier = isRecursiveIdentifier; - return rtrn; + private RerouteTypeParamOriginTransformation _super; + + public RerouteTypeParamOriginExpressionKind(RerouteTypeParamOriginTransformation super, RerouteTypeParamOriginExpression expr) : base(expr) { _super = super; } + + public override ExpressionKind onIdentifier(Identifier sym, QsNullable> tArgs) + { + // Process the identifier (including its type arguments) + var rtrn = base.onIdentifier(sym, tArgs); + + // Then check if this is a recursive identifier + 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 RerouteTypeParamOriginExpressionKind : ExpressionKindTransformation - { - private ClassicallyControlledTransformation _super; - - public RerouteTypeParamOriginExpressionKind(ClassicallyControlledTransformation super, RerouteTypeParamOriginExpression expr) : base(expr) { _super = super; } - - public override ExpressionKind onIdentifier(Identifier sym, QsNullable> tArgs) + + private class RerouteTypeParamOriginExpressionType : ExpressionTypeTransformation { - // Process the identifier (including its type arguments) - var rtrn = base.onIdentifier(sym, tArgs); + private RerouteTypeParamOriginTransformation _super; + + public RerouteTypeParamOriginExpressionType(RerouteTypeParamOriginTransformation super, RerouteTypeParamOriginExpression expr) : base(expr) { _super = super; } - // Then check if this is a recursive identifier - if (sym is Identifier.GlobalCallable callable && _super._CurrentCallable.FullName.Equals(callable.Item)) + public override ResolvedTypeKind onTypeParameter(QsTypeParameter tp) { - // 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; + if (!_super._IsRecursiveIdentifier && _super._OldName.Equals(tp.Origin)) + { + tp = new QsTypeParameter + ( + _super._NewName, + tp.TypeName, + tp.Range + ); + } + + return base.onTypeParameter(tp); } - return rtrn; - } - } - - private class RerouteTypeParamOriginExpressionType : ExpressionTypeTransformation - { - private ClassicallyControlledTransformation _super; - private NonNullable _newName; - - public RerouteTypeParamOriginExpressionType(ClassicallyControlledTransformation super, NonNullable newName, RerouteTypeParamOriginExpression expr) : base(expr) { _super = super; _newName = newName; } - - public override ResolvedTypeKind onTypeParameter(QsTypeParameter tp) - { - if (!_super._IsRecursiveIdentifier && _super._CurrentCallable.FullName.Equals(tp.Origin)) - { - tp = new QsTypeParameter - ( - new QsQualifiedName(tp.Origin.Namespace, _newName), - tp.TypeName, - tp.Range - ); - } - - return base.onTypeParameter(tp); - } + } } } } From 863df6d16a17c8a5e2db9d635b9db8c2f265d42a Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Thu, 19 Dec 2019 13:32:20 -0800 Subject: [PATCH 14/53] WIP --- .../ClassicallyControlledTransformation.cs | 436 +++++++++++++++--- 1 file changed, 370 insertions(+), 66 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 930237d903..885b0885e5 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -20,7 +20,9 @@ public class ClassicallyControlledTransformation { private List _ControlOperations; private QsCallable _CurrentCallable = null; - + private QsStatement _CurrentConditional; + private bool _IsConvertableContext = true; + public static QsCompilation Apply(QsCompilation compilation) { var filter = new ClassicallyControlledSyntax(new ClassicallyControlledTransformation()); @@ -30,14 +32,73 @@ public static QsCompilation Apply(QsCompilation compilation) private ClassicallyControlledTransformation() { } + private (QsQualifiedName, ResolvedType) GenerateOperation(QsScope contents) + { + var newName = new QsQualifiedName( + _CurrentCallable.FullName.Namespace, + NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _CurrentCallable.FullName.Name.Value)); + + var knownVariables = contents.KnownSymbols.Variables; + + var parameters = ParamTuple.NewQsTuple(knownVariables + .Select(var => ParamTuple.NewQsTupleItem(new LocalVariableDeclaration( + QsLocalSymbol.NewValidName(var.VariableName), + var.Type, + var.InferredInformation, + var.Position, + var.Range))) + .ToImmutableArray()); + + var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); + if (knownVariables.Any()) + { + paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables + .Select(var => var.Type) + .ToImmutableArray())); + } + + var signature = new ResolvedSignature( + _CurrentCallable.Signature.TypeParameters, + paramTypes, + ResolvedType.New(ResolvedTypeKind.UnitType), + CallableInformation.NoInformation); + + var spec = new QsSpecialization( + QsSpecializationKind.QsBody, + newName, + ImmutableArray.Empty, + _CurrentCallable.SourceFile, + QsNullable.Null, + QsNullable>.Null, + signature, + SpecializationImplementation.NewProvided(parameters, contents), + ImmutableArray.Empty, + QsComments.Empty); + + var controlCallable = new QsCallable( + QsCallableKind.Operation, + newName, + ImmutableArray.Empty, + _CurrentCallable.SourceFile, + QsNullable.Null, + signature, + parameters, + ImmutableArray.Create(spec), //ToDo: account for ctrl and adjt + ImmutableArray.Empty, + QsComments.Empty); + + var reroutedCallable = RerouteTypeParamOriginTransformation.Apply(controlCallable, _CurrentCallable.FullName, newName); + _ControlOperations.Add(reroutedCallable); + + return (newName, paramTypes); + } + private class ClassicallyControlledSyntax : SyntaxTreeTransformation { private ClassicallyControlledTransformation _super; public ClassicallyControlledSyntax(ClassicallyControlledTransformation super, ClassicallyControlledScope scope = null) : base(scope ?? new ClassicallyControlledScope(super)) - { - _super = super; - } + { _super = super; } public override QsCallable onCallableImplementation(QsCallable c) { @@ -54,12 +115,13 @@ public override QsNamespace Transform(QsNamespace ns) } } - private class ClassicallyControlledScope : ScopeTransformation + private class ClassicallyControlledScope : ScopeTransformation { private ClassicallyControlledTransformation _super; public ClassicallyControlledScope(ClassicallyControlledTransformation super, NoExpressionTransformations expr = null) - : base (expr ?? new NoExpressionTransformations()) { _super = super; } + : base (scope => new ClassicallyControlledStatementKind(super, scope as ClassicallyControlledScope), + expr ?? new NoExpressionTransformations()) { _super = super; } private QsStatement MakeNestedIfs(QsStatement originalStatment, QsStatementKind.QsConditionalStatement condStatmentKind) { @@ -199,7 +261,7 @@ private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression ar private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) { // Hoist the scope to its own operation - var (targetName, targetParamType) = GenerateControlOperation(contents, statement.Comments); + var (targetName, targetParamType) = _super.GenerateOperation(contents); var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( Tuple.Create( targetParamType, @@ -312,71 +374,25 @@ private QsNullable> GetTypeParamTypesFromCallable(Q } } - private (QsQualifiedName, ResolvedType) GenerateControlOperation(QsScope contents, QsComments comments) - { - var newName = new QsQualifiedName( - _super._CurrentCallable.FullName.Namespace, - NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _super._CurrentCallable.FullName.Name.Value)); - - var knownVariables = contents.KnownSymbols.Variables; - - var parameters = ParamTuple.NewQsTuple(knownVariables - .Select(var => ParamTuple.NewQsTupleItem(new LocalVariableDeclaration( - QsLocalSymbol.NewValidName(var.VariableName), - var.Type, - var.InferredInformation, - var.Position, - var.Range))) - .ToImmutableArray()); - - var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); - if (knownVariables.Any()) - { - paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables - .Select(var => var.Type) - .ToImmutableArray())); - } - - var signature = new ResolvedSignature( - _super._CurrentCallable.Signature.TypeParameters, - paramTypes, - ResolvedType.New(ResolvedTypeKind.UnitType), - CallableInformation.NoInformation); - - var spec = new QsSpecialization( - QsSpecializationKind.QsBody, - newName, - ImmutableArray.Empty, - _super._CurrentCallable.SourceFile, - QsNullable.Null, - QsNullable>.Null, - signature, - SpecializationImplementation.NewProvided(parameters, contents), - ImmutableArray.Empty, - comments); - - var controlCallable = new QsCallable( - QsCallableKind.Operation, - newName, - ImmutableArray.Empty, - _super._CurrentCallable.SourceFile, - QsNullable.Null, - signature, - parameters, - ImmutableArray.Create(spec), //ToDo: account for ctrl and adjt - ImmutableArray.Empty, - comments); - - var reroutedCallable = RerouteTypeParamOriginTransformation.Apply(controlCallable, _super._CurrentCallable.FullName, newName); - _super._ControlOperations.Add(reroutedCallable); - - return (newName, paramTypes); + public override QsStatement onStatement(QsStatement stm) + { + if (stm.Statement is QsStatementKind.QsConditionalStatement) + { + var superContextVal = _super._CurrentConditional; + _super._CurrentConditional = stm; + var rtrn = base.onStatement(stm); + _super._CurrentConditional = superContextVal; + return rtrn; + } + return base.onStatement(stm); } public override QsScope Transform(QsScope scope) { scope = base.Transform(scope); // process sub-scopes first + return scope; + var statements = ImmutableArray.CreateBuilder(); foreach (var s in scope.Statements) { @@ -411,6 +427,294 @@ public override QsScope Transform(QsScope scope) } } + private class ClassicallyControlledStatementKind : StatementKindTransformation + { + private ClassicallyControlledTransformation _super; + + public ClassicallyControlledStatementKind(ClassicallyControlledTransformation super, ClassicallyControlledScope scope) : base(scope) { _super = super; } + + private (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatementKind statementKind) + { + if (statementKind 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; + + if (expression.Item1.Expression is ExpressionKind.ResultLiteral exp1) + { + return (true, exp1.Item, expression.Item2, scope, defaultScope); + } + else if (expression.Item2.Expression is ExpressionKind.ResultLiteral exp2) + { + return (true, exp2.Item, expression.Item1, scope, defaultScope); + } + } + } + + return (false, null, null, null, null); + } + + private bool IsValidScope(QsScope scope) + { + return true; + } + + private QsNullable> GetTypeParamTypesFromCallable(QsCallable callable) + { + if (callable.Signature.TypeParameters.Any(param => param.IsValidName)) + { + return 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()); + } + else + { + return QsNullable>.Null; + } + } + + private TypedExpression CreateIdentifierExpression(Identifier id, + QsNullable> typeParams, ResolvedType resolvedType) => + new TypedExpression + ( + ExpressionKind.NewIdentifier(id, typeParams), + ImmutableArray, ResolvedType>>.Empty, + resolvedType, + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + private TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => + new TypedExpression + ( + ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), + ImmutableArray, ResolvedType>>.Empty, + ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, BuiltIn controlOp, ResolvedType opTypeParamResolution) => + new TypedExpression + ( + ExpressionKind.NewCallLikeExpression(id, args), + ImmutableArray.Create(Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), controlOp.TypeParameters.First(), opTypeParamResolution)), + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, true), + QsNullable>.Null + ); + + private QsStatement CreateApplyIfStatement(QsResult result, TypedExpression conditionExpression, QsScope contents) + { + // Hoist the scope to its own operation + var (targetName, targetParamType) = _super.GenerateOperation(contents); + var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( + Tuple.Create( + targetParamType, + ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes + CallableInformation.NoInformation)); + + var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); + var targetOpId = new TypedExpression + ( + ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), + targetTypeParamTypes.IsNull + ? ImmutableArray, ResolvedType>>.Empty + : targetTypeParamTypes.Item + .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + .ToImmutableArray(), + targetOpType, + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + TypedExpression targetArgs = null; + if (contents.KnownSymbols.Variables.Any()) + { + targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => CreateIdentifierExpression( + Identifier.NewLocalVariable(var.VariableName), + QsNullable>.Null, + var.Type)) + .ToArray()); + } + else + { + targetArgs = new TypedExpression + ( + ExpressionKind.UnitValue, + ImmutableArray, ResolvedType>>.Empty, + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + } + + // Build the surrounding apply-if call + var (controlOp, controlOpType) = (result == QsResult.One) + ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) + : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); + var controlOpId = CreateIdentifierExpression( + Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)), + QsNullable>.Null, + controlOpType); + var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); + + var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, targetArgs.ResolvedType); + + return new QsStatement( + QsStatementKind.NewQsExpressionStatement(controlCall), + LocalDeclarations.Empty, // ToDo + //statement.SymbolDeclarations, + QsNullable.Null, + QsComments.Empty); // ToDo + //statement.Comments); + } + + 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), + _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues + 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), + _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues + 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), + _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues + 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); + } + } + + public override QsStatementKind onConditionalStatement(QsConditionalStatement stm) + { + // ToDo: Need to create identifiers for comparison against Result Literal, to avoid executing the condition code multiple times + + (_, stm) = ProcessElif(stm); + bool wasOrProcessed, wasAndProcessed; + do + { + (wasOrProcessed, stm) = ProcessOR(stm); + (wasAndProcessed, stm) = ProcessAND(stm); + } while (wasOrProcessed || wasAndProcessed); + + var superContextVal = _super._IsConvertableContext; + _super._IsConvertableContext = true; + var condStatementKind = base.onConditionalStatement(stm); // Process sub-scopes first + if (_super._IsConvertableContext) // If sub-scope is convertible + { + var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(condStatementKind); + + if (isCondition) + { + if (IsValidScope(conditionScope)) + { + CreateApplyIfStatement(result, conditionExpression, conditionScope); + } + + if (defaultScope != null) + { + CreateApplyIfStatement(result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, defaultScope); + } + } + + + // ToDo: convert if + //stm = ((QsStatementKind.QsConditionalStatement)condStatementKind).Item; + //if(stm.ConditionalBlocks.Length == 1) + + // Return the flag to its super-context value + _super._IsConvertableContext = superContextVal; + } + // If the sub-context was non-convertible, then this if will not be + // converted, making the super-context non-convertible so leave the flag as false + return condStatementKind; + } + + public override QsStatementKind Transform(QsStatementKind kind) + { + if (!(kind is QsStatementKind.QsExpressionStatement expr && expr.Item.Expression is ExpressionKind.CallLikeExpression || + kind is QsStatementKind.QsConditionalStatement)) + _super._IsConvertableContext = false; + return base.Transform(kind); + } + } + private class RerouteTypeParamOriginTransformation { private bool _IsRecursiveIdentifier = false; From a814d8c0489f02d4e14ef55b5e894c098394b402 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Thu, 19 Dec 2019 17:30:40 -0800 Subject: [PATCH 15/53] WIP, all that's left is condition expression encapsulation and condition block hoisting. --- src/QsCompiler/Core/Dependencies.fs | 56 ++ .../ClassicallyControlledTransformation.cs | 916 +++++++++++------- 2 files changed, 639 insertions(+), 333 deletions(-) diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index 61287b790f..eb0cb06f17 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -136,6 +136,62 @@ type BuiltIn = { static member ApplyIfZeroResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfZero + // 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) + } + + static member private _MakeApplyIfElseResolvedType builtIn = + let typeParamT = + { + Origin = {Namespace = builtIn.Namespace; Name = builtIn.Name}; + TypeName = builtIn.TypeParameters.[0]; + Range = QsRangeInfo.Null + } |> TypeParameter |> ResolvedType.New + + let typeParamU = + { + Origin = {Namespace = builtIn.Namespace; Name = builtIn.Name}; + TypeName = builtIn.TypeParameters.[1]; + Range = QsRangeInfo.Null + } |> TypeParameter |> ResolvedType.New + + let args = + [ + Result |> ResolvedType.New; + [ + ( + ( + typeParamT, + UnitType |> ResolvedType.New + ), + CallableInformation.NoInformation + ) |> Operation |> ResolvedType.New; + typeParamT + ].ToImmutableArray() |> TupleType |> ResolvedType.New; + [ + ( + ( + typeParamU, + UnitType |> ResolvedType.New + ), + CallableInformation.NoInformation + ) |> Operation |> ResolvedType.New; + typeParamU + ].ToImmutableArray() |> TupleType |> ResolvedType.New + ].ToImmutableArray() |> TupleType |> ResolvedType.New + + ( + ( + args, + ResolvedType.New UnitType + ), + CallableInformation.NoInformation + ) |> Operation |> ResolvedType.New + + static member ApplyIfElseRResolvedType = BuiltIn._MakeApplyIfElseResolvedType BuiltIn.ApplyIfElseR // "weak dependencies" in other namespaces (e.g. things used for code actions) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 885b0885e5..e04fa36518 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.CompilerServices; using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; @@ -20,8 +21,8 @@ public class ClassicallyControlledTransformation { private List _ControlOperations; private QsCallable _CurrentCallable = null; - private QsStatement _CurrentConditional; - private bool _IsConvertableContext = true; + //private QsStatement _CurrentConditional; + //private bool _IsConvertableContext = true; public static QsCompilation Apply(QsCompilation compilation) { @@ -115,76 +116,76 @@ public override QsNamespace Transform(QsNamespace ns) } } - private class ClassicallyControlledScope : ScopeTransformation + private class ClassicallyControlledScope : ScopeTransformation { private ClassicallyControlledTransformation _super; public ClassicallyControlledScope(ClassicallyControlledTransformation super, NoExpressionTransformations expr = null) - : base (scope => new ClassicallyControlledStatementKind(super, scope as ClassicallyControlledScope), + : base (/*scope => new ClassicallyControlledStatementKind(super, scope as ClassicallyControlledScope),*/ expr ?? new NoExpressionTransformations()) { _super = super; } - private QsStatement MakeNestedIfs(QsStatement originalStatment, QsStatementKind.QsConditionalStatement condStatmentKind) - { - var cond = condStatmentKind.Item; - - if (cond.ConditionalBlocks.Length == 1) - { - return new QsStatement - ( - condStatmentKind, - originalStatment.SymbolDeclarations, - originalStatment.Location, - originalStatment.Comments - ); - } - - var subIfKind = (QsStatementKind.QsConditionalStatement)QsStatementKind.NewQsConditionalStatement( - new QsConditionalStatement(cond.ConditionalBlocks.RemoveAt(0), cond.Default)); - - var subIfStatment = MakeNestedIfs(originalStatment, subIfKind); - - var secondCondBlock = cond.ConditionalBlocks[1].Item2; - var newDefault = QsNullable.NewValue(new QsPositionedBlock( - new QsScope(ImmutableArray.Create(subIfStatment), secondCondBlock.Body.KnownSymbols), - secondCondBlock.Location, - secondCondBlock.Comments)); - - return new QsStatement - ( - QsStatementKind.NewQsConditionalStatement(new QsConditionalStatement(ImmutableArray.Create(cond.ConditionalBlocks[0]), newDefault)), - originalStatment.SymbolDeclarations, - originalStatment.Location, - originalStatment.Comments - ); - } - - private QsStatement MakeNestedIfs(QsStatement originalStatment) - { - if (originalStatment.Statement is QsStatementKind.QsConditionalStatement cond) - { - var nested = MakeNestedIfs(originalStatment, cond); - var nestedKind = ((QsStatementKind.QsConditionalStatement)nested.Statement).Item; - if (nestedKind.Default.IsValue) - { - var newDefault = QsNullable.NewValue(new QsPositionedBlock( - this.Transform(nestedKind.Default.Item.Body), - nestedKind.Default.Item.Location, - nestedKind.Default.Item.Comments)); - - nested = new QsStatement - ( - QsStatementKind.NewQsConditionalStatement(new QsConditionalStatement(nestedKind.ConditionalBlocks, newDefault)), - nested.SymbolDeclarations, - nested.Location, - nested.Comments - ); - } - - return nested; - } - - return originalStatment; - } + //private QsStatement MakeNestedIfs(QsStatement originalStatment, QsStatementKind.QsConditionalStatement condStatmentKind) + //{ + // var cond = condStatmentKind.Item; + // + // if (cond.ConditionalBlocks.Length == 1) + // { + // return new QsStatement + // ( + // condStatmentKind, + // originalStatment.SymbolDeclarations, + // originalStatment.Location, + // originalStatment.Comments + // ); + // } + // + // var subIfKind = (QsStatementKind.QsConditionalStatement)QsStatementKind.NewQsConditionalStatement( + // new QsConditionalStatement(cond.ConditionalBlocks.RemoveAt(0), cond.Default)); + // + // var subIfStatment = MakeNestedIfs(originalStatment, subIfKind); + // + // var secondCondBlock = cond.ConditionalBlocks[1].Item2; + // var newDefault = QsNullable.NewValue(new QsPositionedBlock( + // new QsScope(ImmutableArray.Create(subIfStatment), secondCondBlock.Body.KnownSymbols), + // secondCondBlock.Location, + // secondCondBlock.Comments)); + // + // return new QsStatement + // ( + // QsStatementKind.NewQsConditionalStatement(new QsConditionalStatement(ImmutableArray.Create(cond.ConditionalBlocks[0]), newDefault)), + // originalStatment.SymbolDeclarations, + // originalStatment.Location, + // originalStatment.Comments + // ); + //} + // + //private QsStatement MakeNestedIfs(QsStatement originalStatment) + //{ + // if (originalStatment.Statement is QsStatementKind.QsConditionalStatement cond) + // { + // var nested = MakeNestedIfs(originalStatment, cond); + // var nestedKind = ((QsStatementKind.QsConditionalStatement)nested.Statement).Item; + // if (nestedKind.Default.IsValue) + // { + // var newDefault = QsNullable.NewValue(new QsPositionedBlock( + // this.Transform(nestedKind.Default.Item.Body), + // nestedKind.Default.Item.Location, + // nestedKind.Default.Item.Comments)); + // + // nested = new QsStatement + // ( + // QsStatementKind.NewQsConditionalStatement(new QsConditionalStatement(nestedKind.ConditionalBlocks, newDefault)), + // nested.SymbolDeclarations, + // nested.Location, + // nested.Comments + // ); + // } + // + // return nested; + // } + // + // return originalStatment; + //} private (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatement statement) { @@ -209,24 +210,42 @@ private QsStatement MakeNestedIfs(QsStatement originalStatment) return (false, null, null, null, null); } - private bool AreSimpleCallStatements(IEnumerable stmts) => - stmts.Select(s => IsSimpleCallStatement(s.Statement).Item1).All(b => b); - - private (bool, TypedExpression, TypedExpression) IsSimpleCallStatement(QsStatementKind statement) - { - if (statement is QsStatementKind.QsExpressionStatement expr) - { - var returnType = expr.Item.ResolvedType; - - if (returnType.Resolution.IsUnitType && expr.Item.Expression is ExpressionKind.CallLikeExpression call) - { - return (true, call.Item1, call.Item2); - } - } - - return (false, null, null); + private (bool, TypedExpression, TypedExpression) IsValidScope(QsScope scope) + { + if (scope != null && scope.Statements.Length == 1) + { + if (scope.Statements[0].Statement is QsStatementKind.QsExpressionStatement expr) + { + var returnType = expr.Item.ResolvedType; + + if (returnType.Resolution.IsUnitType && expr.Item.Expression is ExpressionKind.CallLikeExpression call) + { + return (true, call.Item1, call.Item2); + } + } + } + + return (false, null, null); } + //private bool AreSimpleCallStatements(IEnumerable stmts) => + // stmts.Select(s => IsSimpleCallStatement(s.Statement).Item1).All(b => b); + // + //private (bool, TypedExpression, TypedExpression) IsSimpleCallStatement(QsStatementKind statement) + //{ + // if (statement is QsStatementKind.QsExpressionStatement expr) + // { + // var returnType = expr.Item.ResolvedType; + // + // if (returnType.Resolution.IsUnitType && expr.Item.Expression is ExpressionKind.CallLikeExpression call) + // { + // return (true, call.Item1, call.Item2); + // } + // } + // + // return (false, null, null); + //} + private TypedExpression CreateIdentifierExpression(Identifier id, QsNullable> typeParams, ResolvedType resolvedType) => new TypedExpression @@ -248,17 +267,99 @@ private TypedExpression CreateValueTupleExpression(params TypedExpression[] expr QsNullable>.Null ); - private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, BuiltIn controlOp, ResolvedType opTypeParamResolution) => + private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, BuiltIn controlOp, IEnumerable opTypeParamResolutions) => new TypedExpression ( ExpressionKind.NewCallLikeExpression(id, args), - ImmutableArray.Create(Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), controlOp.TypeParameters.First(), opTypeParamResolution)), + opTypeParamResolutions + .Zip(controlOp.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), param, type)) + .ToImmutableArray(), + //ImmutableArray.Create(Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), controlOp.TypeParameters.First(), opTypeParamResolutions)), 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 QsStatement CreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) + 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; + ResolvedType controlOpType; + TypedExpression controlArgs; + ImmutableArray targetArgs; + if (isCondValid && isDefaultValid) + { + controlOpInfo = BuiltIn.ApplyIfElseR; + controlOpType = BuiltIn.ApplyIfElseRResolvedType; + + controlArgs = CreateValueTupleExpression( + conditionExpression, + CreateValueTupleExpression(condId, condArgs), + CreateValueTupleExpression(defaultId, defaultArgs)); + + targetArgs = ImmutableArray.Create(condArgs.ResolvedType, defaultArgs.ResolvedType); + } + else if (isCondValid) + { + (controlOpInfo, controlOpType) = (result == QsResult.One) + ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) + : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); + + controlArgs = CreateValueTupleExpression( + conditionExpression, + CreateValueTupleExpression(condId, condArgs)); + + targetArgs = ImmutableArray.Create(condArgs.ResolvedType); + } + else if (isDefaultValid) + { + (controlOpInfo, controlOpType) = (result == QsResult.One) + ? (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType) + : (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType); + + controlArgs = CreateValueTupleExpression( + conditionExpression, + CreateValueTupleExpression(defaultId, defaultArgs)); + + targetArgs = ImmutableArray.Create(defaultArgs.ResolvedType); + } + else + { + return null; + } + + // Build the surrounding apply-if call + var controlOpId = CreateIdentifierExpression( + Identifier.NewGlobalCallable(new QsQualifiedName(controlOpInfo.Namespace, controlOpInfo.Name)), + QsNullable>.Null, + controlOpType); + //var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); + + return CreateApplyIfCall(controlOpId, controlArgs, controlOpInfo, targetArgs); + } + + private QsStatement oldCreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) { // Hoist the scope to its own operation var (targetName, targetParamType) = _super.GenerateOperation(contents); @@ -313,7 +414,7 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul controlOpType); var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); - var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, targetArgs.ResolvedType); + var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, ImmutableArray.Create(targetArgs.ResolvedType)); return new QsStatement( QsStatementKind.NewQsExpressionStatement(controlCall), @@ -372,211 +473,20 @@ private QsNullable> GetTypeParamTypesFromCallable(Q { return QsNullable>.Null; } - } - - public override QsStatement onStatement(QsStatement stm) - { - if (stm.Statement is QsStatementKind.QsConditionalStatement) - { - var superContextVal = _super._CurrentConditional; - _super._CurrentConditional = stm; - var rtrn = base.onStatement(stm); - _super._CurrentConditional = superContextVal; - return rtrn; - } - return base.onStatement(stm); - } - - public override QsScope Transform(QsScope scope) - { - scope = base.Transform(scope); // process sub-scopes first - - return scope; - - var statements = ImmutableArray.CreateBuilder(); - foreach (var s in scope.Statements) - { - var statement = MakeNestedIfs(s); - var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(statement); - - if (isCondition && AreSimpleCallStatements(conditionScope.Statements) && (defaultScope == null || AreSimpleCallStatements(defaultScope.Statements))) - { - // The condition must be an identifier, otherwise we'll call it multiple times. - // If not, create a new variable and use that: - if (!(conditionExpression.Expression is ExpressionKind.Identifier)) - { - var (letStmt, idExpression) = CreateNewConditionVariable(conditionExpression, statement); - statements.Add(letStmt); - conditionExpression = idExpression; - } - - statements.Add(CreateApplyIfStatement(statement, result, conditionExpression, conditionScope)); - - if (defaultScope != null) - { - statements.Add(CreateApplyIfStatement(statement, result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, defaultScope)); - } - } - else - { - statements.Add(this.onStatement(statement)); - } - } - - return new QsScope(statements.ToImmutableArray(), scope.KnownSymbols); - } - } - - private class ClassicallyControlledStatementKind : StatementKindTransformation - { - private ClassicallyControlledTransformation _super; - - public ClassicallyControlledStatementKind(ClassicallyControlledTransformation super, ClassicallyControlledScope scope) : base(scope) { _super = super; } - - private (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatementKind statementKind) - { - if (statementKind 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; - - if (expression.Item1.Expression is ExpressionKind.ResultLiteral exp1) - { - return (true, exp1.Item, expression.Item2, scope, defaultScope); - } - else if (expression.Item2.Expression is ExpressionKind.ResultLiteral exp2) - { - return (true, exp2.Item, expression.Item1, scope, defaultScope); - } - } - } - - return (false, null, null, null, null); } - private bool IsValidScope(QsScope scope) - { - return true; - } - - private QsNullable> GetTypeParamTypesFromCallable(QsCallable callable) - { - if (callable.Signature.TypeParameters.Any(param => param.IsValidName)) - { - return 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()); - } - else - { - return QsNullable>.Null; - } - } - - private TypedExpression CreateIdentifierExpression(Identifier id, - QsNullable> typeParams, ResolvedType resolvedType) => - new TypedExpression - ( - ExpressionKind.NewIdentifier(id, typeParams), - ImmutableArray, ResolvedType>>.Empty, - resolvedType, - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - - private TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => - new TypedExpression - ( - ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), - ImmutableArray, ResolvedType>>.Empty, - ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - - private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, BuiltIn controlOp, ResolvedType opTypeParamResolution) => - new TypedExpression - ( - ExpressionKind.NewCallLikeExpression(id, args), - ImmutableArray.Create(Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), controlOp.TypeParameters.First(), opTypeParamResolution)), - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, true), - QsNullable>.Null - ); - - private QsStatement CreateApplyIfStatement(QsResult result, TypedExpression conditionExpression, QsScope contents) - { - // Hoist the scope to its own operation - var (targetName, targetParamType) = _super.GenerateOperation(contents); - var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( - Tuple.Create( - targetParamType, - ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes - CallableInformation.NoInformation)); - - var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); - var targetOpId = new TypedExpression - ( - ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), - targetTypeParamTypes.IsNull - ? ImmutableArray, ResolvedType>>.Empty - : targetTypeParamTypes.Item - .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) - .ToImmutableArray(), - targetOpType, - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - - TypedExpression targetArgs = null; - if (contents.KnownSymbols.Variables.Any()) - { - targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => CreateIdentifierExpression( - Identifier.NewLocalVariable(var.VariableName), - QsNullable>.Null, - var.Type)) - .ToArray()); - } - else - { - targetArgs = new TypedExpression - ( - ExpressionKind.UnitValue, - ImmutableArray, ResolvedType>>.Empty, - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - } - - // Build the surrounding apply-if call - var (controlOp, controlOpType) = (result == QsResult.One) - ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) - : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); - var controlOpId = CreateIdentifierExpression( - Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)), - QsNullable>.Null, - controlOpType); - var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); - - var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, targetArgs.ResolvedType); - - return new QsStatement( - QsStatementKind.NewQsExpressionStatement(controlCall), - LocalDeclarations.Empty, // ToDo - //statement.SymbolDeclarations, - QsNullable.Null, - QsComments.Empty); // ToDo - //statement.Comments); - } + //public override QsStatement onStatement(QsStatement stm) + //{ + // if (stm.Statement is QsStatementKind.QsConditionalStatement) + // { + // var superContextVal = _super._CurrentConditional; + // _super._CurrentConditional = stm; + // var rtrn = base.onStatement(stm); + // _super._CurrentConditional = superContextVal; + // return rtrn; + // } + // return base.onStatement(stm); + //} private (bool, QsConditionalStatement) ProcessElif(QsConditionalStatement cond) { @@ -588,7 +498,8 @@ private QsStatement CreateApplyIfStatement(QsResult result, TypedExpression cond var subIfStatment = new QsStatement ( QsStatementKind.NewQsConditionalStatement(subCond), - _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues + LocalDeclarations.Empty, + //_super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues secondCondBlock.Location, secondCondBlock.Comments ); @@ -614,7 +525,8 @@ private QsStatement CreateApplyIfStatement(QsResult result, TypedExpression cond var subIfStatment = new QsStatement ( QsStatementKind.NewQsConditionalStatement(subCond), - _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues + LocalDeclarations.Empty, + //_super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues block.Location, QsComments.Empty ); @@ -644,7 +556,8 @@ private QsStatement CreateApplyIfStatement(QsResult result, TypedExpression cond var subIfStatment = new QsStatement ( QsStatementKind.NewQsConditionalStatement(subCond), - _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues + LocalDeclarations.Empty, + //_super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues block.Location, QsComments.Empty ); @@ -661,59 +574,396 @@ private QsStatement CreateApplyIfStatement(QsResult result, TypedExpression cond } } - public override QsStatementKind onConditionalStatement(QsConditionalStatement stm) + private QsStatement ReshapeConditional(QsStatement statement) { - // ToDo: Need to create identifiers for comparison against Result Literal, to avoid executing the condition code multiple times - - (_, stm) = ProcessElif(stm); - bool wasOrProcessed, wasAndProcessed; - do - { - (wasOrProcessed, stm) = ProcessOR(stm); - (wasAndProcessed, stm) = ProcessAND(stm); - } while (wasOrProcessed || wasAndProcessed); - - var superContextVal = _super._IsConvertableContext; - _super._IsConvertableContext = true; - var condStatementKind = base.onConditionalStatement(stm); // Process sub-scopes first - if (_super._IsConvertableContext) // If sub-scope is convertible + if (statement.Statement is QsStatementKind.QsConditionalStatement cond) { - var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(condStatementKind); + var stm = cond.Item; + (_, stm) = ProcessElif(stm); + bool wasOrProcessed, wasAndProcessed; + do + { + (wasOrProcessed, stm) = ProcessOR(stm); + (wasAndProcessed, stm) = ProcessAND(stm); + } while (wasOrProcessed || wasAndProcessed); - if (isCondition) + 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) { - if (IsValidScope(conditionScope)) - { - CreateApplyIfStatement(result, conditionExpression, conditionScope); + var stm = ReshapeConditional(statement); + stm = this.onStatement(stm); + + var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(stm); + + // ToDo: Maybe this Identifier logic should be done in first transformation instead. + + // The condition must be an identifier, otherwise we'll call it multiple times. + // If not, create a new variable and use that: + if (!(conditionExpression.Expression is ExpressionKind.Identifier)) + { + var (letStmt, idExpression) = CreateNewConditionVariable(conditionExpression, statement); + statements.Add(letStmt); + conditionExpression = idExpression; } - if (defaultScope != null) + if (isCondition) { - CreateApplyIfStatement(result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, defaultScope); + statements.Add(CreateApplyIfStatement(statement, result, conditionExpression, conditionScope, defaultScope)); } } + else + { + statements.Add(this.onStatement(statement)); + } + } + + return new QsScope(statements.ToImmutableArray(), parentSymbols); + } + + //public QsScope oldTransform(QsScope scope) + //{ + // scope = base.Transform(scope); // process sub-scopes first + // + // return scope; + // + // var statements = ImmutableArray.CreateBuilder(); + // foreach (var s in scope.Statements) + // { + // var statement = MakeNestedIfs(s); + // var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(statement); + // + // if (isCondition && AreSimpleCallStatements(conditionScope.Statements) && (defaultScope == null || AreSimpleCallStatements(defaultScope.Statements))) + // { + // // The condition must be an identifier, otherwise we'll call it multiple times. + // // If not, create a new variable and use that: + // if (!(conditionExpression.Expression is ExpressionKind.Identifier)) + // { + // var (letStmt, idExpression) = CreateNewConditionVariable(conditionExpression, statement); + // statements.Add(letStmt); + // conditionExpression = idExpression; + // } + // + // statements.Add(CreateApplyIfStatement(statement, result, conditionExpression, conditionScope)); + // + // if (defaultScope != null) + // { + // statements.Add(CreateApplyIfStatement(statement, result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, defaultScope)); + // } + // } + // else + // { + // statements.Add(this.onStatement(statement)); + // } + // } + // + // return new QsScope(statements.ToImmutableArray(), scope.KnownSymbols); + //} + } + + //private class ClassicallyControlledStatementKind : StatementKindTransformation + //{ + // private ClassicallyControlledTransformation _super; + // public ClassicallyControlledStatementKind(ClassicallyControlledTransformation super, ClassicallyControlledScope scope) : base(scope) { _super = super; } - // ToDo: convert if - //stm = ((QsStatementKind.QsConditionalStatement)condStatementKind).Item; - //if(stm.ConditionalBlocks.Length == 1) + // private (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatementKind statementKind) + // { + // if (statementKind 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; + + // if (expression.Item1.Expression is ExpressionKind.ResultLiteral exp1) + // { + // return (true, exp1.Item, expression.Item2, scope, defaultScope); + // } + // else if (expression.Item2.Expression is ExpressionKind.ResultLiteral exp2) + // { + // return (true, exp2.Item, expression.Item1, scope, defaultScope); + // } + // } + // } + + // return (false, null, null, null, null); + // } - // Return the flag to its super-context value - _super._IsConvertableContext = superContextVal; - } - // If the sub-context was non-convertible, then this if will not be - // converted, making the super-context non-convertible so leave the flag as false - return condStatementKind; - } + // private bool IsValidScope(QsScope scope) + // { + // return true; + // } + + // private QsNullable> GetTypeParamTypesFromCallable(QsCallable callable) + // { + // if (callable.Signature.TypeParameters.Any(param => param.IsValidName)) + // { + // return 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()); + // } + // else + // { + // return QsNullable>.Null; + // } + // } + + // private TypedExpression CreateIdentifierExpression(Identifier id, + // QsNullable> typeParams, ResolvedType resolvedType) => + // new TypedExpression + // ( + // ExpressionKind.NewIdentifier(id, typeParams), + // ImmutableArray, ResolvedType>>.Empty, + // resolvedType, + // new InferredExpressionInformation(false, false), + // QsNullable>.Null + // ); + + // private TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => + // new TypedExpression + // ( + // ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), + // ImmutableArray, ResolvedType>>.Empty, + // ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), + // new InferredExpressionInformation(false, false), + // QsNullable>.Null + // ); + + // private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, BuiltIn controlOp, ResolvedType opTypeParamResolution) => + // new TypedExpression + // ( + // ExpressionKind.NewCallLikeExpression(id, args), + // ImmutableArray.Create(Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), controlOp.TypeParameters.First(), opTypeParamResolution)), + // ResolvedType.New(ResolvedTypeKind.UnitType), + // new InferredExpressionInformation(false, true), + // QsNullable>.Null + // ); + + // private QsStatement CreateApplyIfStatement(QsResult result, TypedExpression conditionExpression, QsScope contents) + // { + // // Hoist the scope to its own operation + // var (targetName, targetParamType) = _super.GenerateOperation(contents); + // var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( + // Tuple.Create( + // targetParamType, + // ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes + // CallableInformation.NoInformation)); + + // var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); + // var targetOpId = new TypedExpression + // ( + // ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), + // targetTypeParamTypes.IsNull + // ? ImmutableArray, ResolvedType>>.Empty + // : targetTypeParamTypes.Item + // .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + // .ToImmutableArray(), + // targetOpType, + // new InferredExpressionInformation(false, false), + // QsNullable>.Null + // ); + + // TypedExpression targetArgs = null; + // if (contents.KnownSymbols.Variables.Any()) + // { + // targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => CreateIdentifierExpression( + // Identifier.NewLocalVariable(var.VariableName), + // QsNullable>.Null, + // var.Type)) + // .ToArray()); + // } + // else + // { + // targetArgs = new TypedExpression + // ( + // ExpressionKind.UnitValue, + // ImmutableArray, ResolvedType>>.Empty, + // ResolvedType.New(ResolvedTypeKind.UnitType), + // new InferredExpressionInformation(false, false), + // QsNullable>.Null + // ); + // } + + // // Build the surrounding apply-if call + // var (controlOp, controlOpType) = (result == QsResult.One) + // ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) + // : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); + // var controlOpId = CreateIdentifierExpression( + // Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)), + // QsNullable>.Null, + // controlOpType); + // var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); - public override QsStatementKind Transform(QsStatementKind kind) - { - if (!(kind is QsStatementKind.QsExpressionStatement expr && expr.Item.Expression is ExpressionKind.CallLikeExpression || - kind is QsStatementKind.QsConditionalStatement)) - _super._IsConvertableContext = false; - return base.Transform(kind); - } - } + // var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, targetArgs.ResolvedType); + + // return new QsStatement( + // QsStatementKind.NewQsExpressionStatement(controlCall), + // LocalDeclarations.Empty, // ToDo + // //statement.SymbolDeclarations, + // QsNullable.Null, + // QsComments.Empty); // ToDo + // //statement.Comments); + // } + + // 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), + // _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues + // 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), + // _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues + // 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), + // _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues + // 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); + // } + // } + + // public override QsStatementKind onConditionalStatement(QsConditionalStatement stm) + // { + // // ToDo: Need to create identifiers for comparison against Result Literal, to avoid executing the condition code multiple times + + // (_, stm) = ProcessElif(stm); + // bool wasOrProcessed, wasAndProcessed; + // do + // { + // (wasOrProcessed, stm) = ProcessOR(stm); + // (wasAndProcessed, stm) = ProcessAND(stm); + // } while (wasOrProcessed || wasAndProcessed); + + // var superContextVal = _super._IsConvertableContext; + // _super._IsConvertableContext = true; + // var condStatementKind = base.onConditionalStatement(stm); // Process sub-scopes first + // if (_super._IsConvertableContext) // If sub-scope is convertible + // { + // var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(condStatementKind); + + // if (isCondition) + // { + // if (IsValidScope(conditionScope)) + // { + // CreateApplyIfStatement(result, conditionExpression, conditionScope); + // } + + // if (defaultScope != null) + // { + // CreateApplyIfStatement(result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, defaultScope); + // } + // } + + + // // ToDo: convert if + // //stm = ((QsStatementKind.QsConditionalStatement)condStatementKind).Item; + // //if(stm.ConditionalBlocks.Length == 1) + + // // Return the flag to its super-context value + // _super._IsConvertableContext = superContextVal; + // } + // // If the sub-context was non-convertible, then this if will not be + // // converted, making the super-context non-convertible so leave the flag as false + // return condStatementKind; + // } + + // //public override QsStatementKind Transform(QsStatementKind kind) + // //{ + // // if (!(kind is QsStatementKind.QsExpressionStatement expr && expr.Item.Expression is ExpressionKind.CallLikeExpression || + // // kind is QsStatementKind.QsConditionalStatement)) + // // _super._IsConvertableContext = false; + // // return base.Transform(kind); + // //} + //} private class RerouteTypeParamOriginTransformation { From ed4a5da9c9d4d5e936cee1a88b5360df2a115b22 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Thu, 19 Dec 2019 20:30:31 -0800 Subject: [PATCH 16/53] WIP, not going to do expression encapsulation anymore. Still need to deal with mutables, and change logic to all-or-nothing with if-else conversions. --- .../ClassicallyControlledTransformation.cs | 1076 +++++++++-------- 1 file changed, 569 insertions(+), 507 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index e04fa36518..25c321e0eb 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -17,10 +17,34 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTran using ResolvedTypeKind = QsTypeKind; using ParamTuple = QsTuple>; + internal static class Helper + { + public static TypedExpression CreateIdentifierExpression(Identifier id, + QsNullable> typeParams, ResolvedType resolvedType) => + new TypedExpression + ( + ExpressionKind.NewIdentifier(id, typeParams), + ImmutableArray, ResolvedType>>.Empty, + resolvedType, + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + public static TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => + new TypedExpression + ( + ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), + ImmutableArray, ResolvedType>>.Empty, + ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + } + public class ClassicallyControlledTransformation { - private List _ControlOperations; - private QsCallable _CurrentCallable = null; + //private List _ControlOperations; + //private QsCallable _CurrentCallable = null; //private QsStatement _CurrentConditional; //private bool _IsConvertableContext = true; @@ -33,66 +57,66 @@ public static QsCompilation Apply(QsCompilation compilation) private ClassicallyControlledTransformation() { } - private (QsQualifiedName, ResolvedType) GenerateOperation(QsScope contents) - { - var newName = new QsQualifiedName( - _CurrentCallable.FullName.Namespace, - NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _CurrentCallable.FullName.Name.Value)); - - var knownVariables = contents.KnownSymbols.Variables; - - var parameters = ParamTuple.NewQsTuple(knownVariables - .Select(var => ParamTuple.NewQsTupleItem(new LocalVariableDeclaration( - QsLocalSymbol.NewValidName(var.VariableName), - var.Type, - var.InferredInformation, - var.Position, - var.Range))) - .ToImmutableArray()); - - var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); - if (knownVariables.Any()) - { - paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables - .Select(var => var.Type) - .ToImmutableArray())); - } + //private (QsQualifiedName, ResolvedType) GenerateOperation(QsScope contents) + //{ + // var newName = new QsQualifiedName( + // _CurrentCallable.FullName.Namespace, + // NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _CurrentCallable.FullName.Name.Value)); + + // var knownVariables = contents.KnownSymbols.Variables; + + // var parameters = ParamTuple.NewQsTuple(knownVariables + // .Select(var => ParamTuple.NewQsTupleItem(new LocalVariableDeclaration( + // QsLocalSymbol.NewValidName(var.VariableName), + // var.Type, + // var.InferredInformation, + // var.Position, + // var.Range))) + // .ToImmutableArray()); + + // var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); + // if (knownVariables.Any()) + // { + // paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables + // .Select(var => var.Type) + // .ToImmutableArray())); + // } - var signature = new ResolvedSignature( - _CurrentCallable.Signature.TypeParameters, - paramTypes, - ResolvedType.New(ResolvedTypeKind.UnitType), - CallableInformation.NoInformation); - - var spec = new QsSpecialization( - QsSpecializationKind.QsBody, - newName, - ImmutableArray.Empty, - _CurrentCallable.SourceFile, - QsNullable.Null, - QsNullable>.Null, - signature, - SpecializationImplementation.NewProvided(parameters, contents), - ImmutableArray.Empty, - QsComments.Empty); - - var controlCallable = new QsCallable( - QsCallableKind.Operation, - newName, - ImmutableArray.Empty, - _CurrentCallable.SourceFile, - QsNullable.Null, - signature, - parameters, - ImmutableArray.Create(spec), //ToDo: account for ctrl and adjt - ImmutableArray.Empty, - QsComments.Empty); - - var reroutedCallable = RerouteTypeParamOriginTransformation.Apply(controlCallable, _CurrentCallable.FullName, newName); - _ControlOperations.Add(reroutedCallable); - - return (newName, paramTypes); - } + // var signature = new ResolvedSignature( + // _CurrentCallable.Signature.TypeParameters, + // paramTypes, + // ResolvedType.New(ResolvedTypeKind.UnitType), + // CallableInformation.NoInformation); + + // var spec = new QsSpecialization( + // QsSpecializationKind.QsBody, + // newName, + // ImmutableArray.Empty, + // _CurrentCallable.SourceFile, + // QsNullable.Null, + // QsNullable>.Null, + // signature, + // SpecializationImplementation.NewProvided(parameters, contents), + // ImmutableArray.Empty, + // QsComments.Empty); + + // var controlCallable = new QsCallable( + // QsCallableKind.Operation, + // newName, + // ImmutableArray.Empty, + // _CurrentCallable.SourceFile, + // QsNullable.Null, + // signature, + // parameters, + // ImmutableArray.Create(spec), //ToDo: account for ctrl and adjt + // ImmutableArray.Empty, + // QsComments.Empty); + + // var reroutedCallable = RerouteTypeParamOriginTransformation.Apply(controlCallable, _CurrentCallable.FullName, newName); + // _ControlOperations.Add(reroutedCallable); + + // return (newName, paramTypes); + //} private class ClassicallyControlledSyntax : SyntaxTreeTransformation { @@ -101,19 +125,19 @@ private class ClassicallyControlledSyntax : SyntaxTreeTransformation(); - return base.Transform(ns) - .WithElements(elems => elems.AddRange(_super._ControlOperations.Select(op => QsNamespaceElement.NewQsCallable(op)))); - } + //public override QsCallable onCallableImplementation(QsCallable c) + //{ + // _super._CurrentCallable = c; + // return base.onCallableImplementation(c); + //} + // + //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 ClassicallyControlledScope : ScopeTransformation @@ -246,27 +270,6 @@ public ClassicallyControlledScope(ClassicallyControlledTransformation super, NoE // return (false, null, null); //} - private TypedExpression CreateIdentifierExpression(Identifier id, - QsNullable> typeParams, ResolvedType resolvedType) => - new TypedExpression - ( - ExpressionKind.NewIdentifier(id, typeParams), - ImmutableArray, ResolvedType>>.Empty, - resolvedType, - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - - private TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => - new TypedExpression - ( - ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), - ImmutableArray, ResolvedType>>.Empty, - ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, BuiltIn controlOp, IEnumerable opTypeParamResolutions) => new TypedExpression ( @@ -313,10 +316,10 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co controlOpInfo = BuiltIn.ApplyIfElseR; controlOpType = BuiltIn.ApplyIfElseRResolvedType; - controlArgs = CreateValueTupleExpression( + controlArgs = Helper.CreateValueTupleExpression( conditionExpression, - CreateValueTupleExpression(condId, condArgs), - CreateValueTupleExpression(defaultId, defaultArgs)); + Helper.CreateValueTupleExpression(condId, condArgs), + Helper.CreateValueTupleExpression(defaultId, defaultArgs)); targetArgs = ImmutableArray.Create(condArgs.ResolvedType, defaultArgs.ResolvedType); } @@ -326,9 +329,9 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); - controlArgs = CreateValueTupleExpression( + controlArgs = Helper.CreateValueTupleExpression( conditionExpression, - CreateValueTupleExpression(condId, condArgs)); + Helper.CreateValueTupleExpression(condId, condArgs)); targetArgs = ImmutableArray.Create(condArgs.ResolvedType); } @@ -338,9 +341,9 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co ? (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType) : (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType); - controlArgs = CreateValueTupleExpression( + controlArgs = Helper.CreateValueTupleExpression( conditionExpression, - CreateValueTupleExpression(defaultId, defaultArgs)); + Helper.CreateValueTupleExpression(defaultId, defaultArgs)); targetArgs = ImmutableArray.Create(defaultArgs.ResolvedType); } @@ -350,7 +353,7 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co } // Build the surrounding apply-if call - var controlOpId = CreateIdentifierExpression( + var controlOpId = Helper.CreateIdentifierExpression( Identifier.NewGlobalCallable(new QsQualifiedName(controlOpInfo.Namespace, controlOpInfo.Name)), QsNullable>.Null, controlOpType); @@ -359,121 +362,101 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co return CreateApplyIfCall(controlOpId, controlArgs, controlOpInfo, targetArgs); } - private QsStatement oldCreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) - { - // Hoist the scope to its own operation - var (targetName, targetParamType) = _super.GenerateOperation(contents); - var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( - Tuple.Create( - targetParamType, - ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes - CallableInformation.NoInformation)); - - var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); - var targetOpId = new TypedExpression - ( - ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), - targetTypeParamTypes.IsNull - ? ImmutableArray, ResolvedType>>.Empty - : targetTypeParamTypes.Item - .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) - .ToImmutableArray(), - targetOpType, - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); + //private QsStatement oldCreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) + //{ + // // Hoist the scope to its own operation + // var (targetName, targetParamType) = _super.GenerateOperation(contents); + // var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( + // Tuple.Create( + // targetParamType, + // ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes + // CallableInformation.NoInformation)); + + // var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); + // var targetOpId = new TypedExpression + // ( + // ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), + // targetTypeParamTypes.IsNull + // ? ImmutableArray, ResolvedType>>.Empty + // : targetTypeParamTypes.Item + // .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + // .ToImmutableArray(), + // targetOpType, + // new InferredExpressionInformation(false, false), + // QsNullable>.Null + // ); - TypedExpression targetArgs = null; - if (contents.KnownSymbols.Variables.Any()) - { - targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => CreateIdentifierExpression( - Identifier.NewLocalVariable(var.VariableName), - QsNullable>.Null, - var.Type)) - .ToArray()); - } - else - { - targetArgs = new TypedExpression - ( - ExpressionKind.UnitValue, - ImmutableArray, ResolvedType>>.Empty, - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - } + // TypedExpression targetArgs = null; + // if (contents.KnownSymbols.Variables.Any()) + // { + // targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => CreateIdentifierExpression( + // Identifier.NewLocalVariable(var.VariableName), + // QsNullable>.Null, + // var.Type)) + // .ToArray()); + // } + // else + // { + // targetArgs = new TypedExpression + // ( + // ExpressionKind.UnitValue, + // ImmutableArray, ResolvedType>>.Empty, + // ResolvedType.New(ResolvedTypeKind.UnitType), + // new InferredExpressionInformation(false, false), + // QsNullable>.Null + // ); + // } - // Build the surrounding apply-if call - var (controlOp, controlOpType) = (result == QsResult.One) - ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) - : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); - var controlOpId = CreateIdentifierExpression( - Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)), - QsNullable>.Null, - controlOpType); - var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); + // // Build the surrounding apply-if call + // var (controlOp, controlOpType) = (result == QsResult.One) + // ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) + // : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); + // var controlOpId = CreateIdentifierExpression( + // Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)), + // QsNullable>.Null, + // controlOpType); + // var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); - var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, ImmutableArray.Create(targetArgs.ResolvedType)); - - return new QsStatement( - QsStatementKind.NewQsExpressionStatement(controlCall), - statement.SymbolDeclarations, - QsNullable.Null, - statement.Comments); - } - - private static int _varCount = 0; - - private (QsStatement, TypedExpression) CreateNewConditionVariable(TypedExpression value, QsStatement condStatement) - { - _varCount++; - var name = NonNullable.New($"__classic_ctrl{_varCount}__"); - - // The typed expression with the identifier of the variable we just created: - var idExpression = CreateIdentifierExpression( - Identifier.NewLocalVariable(name), - QsNullable>.Null, - value.ResolvedType); + // var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, ImmutableArray.Create(targetArgs.ResolvedType)); - // The actual binding statement: - var binding = new QsBinding(QsBindingKind.ImmutableBinding, SymbolTuple.NewVariableName(name), value); - var symbDecl = new LocalDeclarations(condStatement.SymbolDeclarations.Variables.Add(new LocalVariableDeclaration> - ( - name, - value.ResolvedType, - new InferredExpressionInformation(false, false), - condStatement.Location.IsValue - ? QsNullable>.NewValue(condStatement.Location.Item.Offset) - : QsNullable>.Null, - condStatement.Location.IsValue - ? condStatement.Location.Item.Range - : Tuple.Create(QsPositionInfo.Zero, QsPositionInfo.Zero) - ))); - var stmt = new QsStatement(QsStatementKind.NewQsVariableDeclaration(binding), symbDecl, condStatement.Location, condStatement.Comments); - - return (stmt, idExpression); - } + // return new QsStatement( + // QsStatementKind.NewQsExpressionStatement(controlCall), + // statement.SymbolDeclarations, + // QsNullable.Null, + // statement.Comments); + //} - private QsNullable> GetTypeParamTypesFromCallable(QsCallable callable) - { - if (callable.Signature.TypeParameters.Any(param => param.IsValidName)) - { - return 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()); - } - else - { - return QsNullable>.Null; - } - } + //private static int _varCount = 0; + // + //private (QsStatement, TypedExpression) CreateNewConditionVariable(TypedExpression value, QsStatement condStatement) + //{ + // _varCount++; + // var name = NonNullable.New($"__classic_ctrl{_varCount}__"); + // + // // The typed expression with the identifier of the variable we just created: + // var idExpression = CreateIdentifierExpression( + // Identifier.NewLocalVariable(name), + // QsNullable>.Null, + // value.ResolvedType); + // + // // The actual binding statement: + // var binding = new QsBinding(QsBindingKind.ImmutableBinding, SymbolTuple.NewVariableName(name), value); + // var symbDecl = new LocalDeclarations(condStatement.SymbolDeclarations.Variables.Add(new LocalVariableDeclaration> + // ( + // name, + // value.ResolvedType, + // new InferredExpressionInformation(false, false), + // condStatement.Location.IsValue + // ? QsNullable>.NewValue(condStatement.Location.Item.Offset) + // : QsNullable>.Null, + // condStatement.Location.IsValue + // ? condStatement.Location.Item.Range + // : Tuple.Create(QsPositionInfo.Zero, QsPositionInfo.Zero) + // ))); + // var stmt = new QsStatement(QsStatementKind.NewQsVariableDeclaration(binding), symbDecl, condStatement.Location, condStatement.Comments); + // + // return (stmt, idExpression); + //} //public override QsStatement onStatement(QsStatement stm) //{ @@ -616,12 +599,12 @@ public override QsScope Transform(QsScope scope) // The condition must be an identifier, otherwise we'll call it multiple times. // If not, create a new variable and use that: - if (!(conditionExpression.Expression is ExpressionKind.Identifier)) - { - var (letStmt, idExpression) = CreateNewConditionVariable(conditionExpression, statement); - statements.Add(letStmt); - conditionExpression = idExpression; - } + //if (!(conditionExpression.Expression is ExpressionKind.Identifier)) + //{ + // var (letStmt, idExpression) = CreateNewConditionVariable(conditionExpression, statement); + // statements.Add(letStmt); + // conditionExpression = idExpression; + //} if (isCondition) { @@ -677,294 +660,6 @@ public override QsScope Transform(QsScope scope) //} } - //private class ClassicallyControlledStatementKind : StatementKindTransformation - //{ - // private ClassicallyControlledTransformation _super; - - // public ClassicallyControlledStatementKind(ClassicallyControlledTransformation super, ClassicallyControlledScope scope) : base(scope) { _super = super; } - - // private (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatementKind statementKind) - // { - // if (statementKind 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; - - // if (expression.Item1.Expression is ExpressionKind.ResultLiteral exp1) - // { - // return (true, exp1.Item, expression.Item2, scope, defaultScope); - // } - // else if (expression.Item2.Expression is ExpressionKind.ResultLiteral exp2) - // { - // return (true, exp2.Item, expression.Item1, scope, defaultScope); - // } - // } - // } - - // return (false, null, null, null, null); - // } - - // private bool IsValidScope(QsScope scope) - // { - // return true; - // } - - // private QsNullable> GetTypeParamTypesFromCallable(QsCallable callable) - // { - // if (callable.Signature.TypeParameters.Any(param => param.IsValidName)) - // { - // return 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()); - // } - // else - // { - // return QsNullable>.Null; - // } - // } - - // private TypedExpression CreateIdentifierExpression(Identifier id, - // QsNullable> typeParams, ResolvedType resolvedType) => - // new TypedExpression - // ( - // ExpressionKind.NewIdentifier(id, typeParams), - // ImmutableArray, ResolvedType>>.Empty, - // resolvedType, - // new InferredExpressionInformation(false, false), - // QsNullable>.Null - // ); - - // private TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => - // new TypedExpression - // ( - // ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), - // ImmutableArray, ResolvedType>>.Empty, - // ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), - // new InferredExpressionInformation(false, false), - // QsNullable>.Null - // ); - - // private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, BuiltIn controlOp, ResolvedType opTypeParamResolution) => - // new TypedExpression - // ( - // ExpressionKind.NewCallLikeExpression(id, args), - // ImmutableArray.Create(Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), controlOp.TypeParameters.First(), opTypeParamResolution)), - // ResolvedType.New(ResolvedTypeKind.UnitType), - // new InferredExpressionInformation(false, true), - // QsNullable>.Null - // ); - - // private QsStatement CreateApplyIfStatement(QsResult result, TypedExpression conditionExpression, QsScope contents) - // { - // // Hoist the scope to its own operation - // var (targetName, targetParamType) = _super.GenerateOperation(contents); - // var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( - // Tuple.Create( - // targetParamType, - // ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes - // CallableInformation.NoInformation)); - - // var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); - // var targetOpId = new TypedExpression - // ( - // ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), - // targetTypeParamTypes.IsNull - // ? ImmutableArray, ResolvedType>>.Empty - // : targetTypeParamTypes.Item - // .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) - // .ToImmutableArray(), - // targetOpType, - // new InferredExpressionInformation(false, false), - // QsNullable>.Null - // ); - - // TypedExpression targetArgs = null; - // if (contents.KnownSymbols.Variables.Any()) - // { - // targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => CreateIdentifierExpression( - // Identifier.NewLocalVariable(var.VariableName), - // QsNullable>.Null, - // var.Type)) - // .ToArray()); - // } - // else - // { - // targetArgs = new TypedExpression - // ( - // ExpressionKind.UnitValue, - // ImmutableArray, ResolvedType>>.Empty, - // ResolvedType.New(ResolvedTypeKind.UnitType), - // new InferredExpressionInformation(false, false), - // QsNullable>.Null - // ); - // } - - // // Build the surrounding apply-if call - // var (controlOp, controlOpType) = (result == QsResult.One) - // ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) - // : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); - // var controlOpId = CreateIdentifierExpression( - // Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)), - // QsNullable>.Null, - // controlOpType); - // var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); - - // var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, targetArgs.ResolvedType); - - // return new QsStatement( - // QsStatementKind.NewQsExpressionStatement(controlCall), - // LocalDeclarations.Empty, // ToDo - // //statement.SymbolDeclarations, - // QsNullable.Null, - // QsComments.Empty); // ToDo - // //statement.Comments); - // } - - // 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), - // _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues - // 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), - // _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues - // 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), - // _super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues - // 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); - // } - // } - - // public override QsStatementKind onConditionalStatement(QsConditionalStatement stm) - // { - // // ToDo: Need to create identifiers for comparison against Result Literal, to avoid executing the condition code multiple times - - // (_, stm) = ProcessElif(stm); - // bool wasOrProcessed, wasAndProcessed; - // do - // { - // (wasOrProcessed, stm) = ProcessOR(stm); - // (wasAndProcessed, stm) = ProcessAND(stm); - // } while (wasOrProcessed || wasAndProcessed); - - // var superContextVal = _super._IsConvertableContext; - // _super._IsConvertableContext = true; - // var condStatementKind = base.onConditionalStatement(stm); // Process sub-scopes first - // if (_super._IsConvertableContext) // If sub-scope is convertible - // { - // var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(condStatementKind); - - // if (isCondition) - // { - // if (IsValidScope(conditionScope)) - // { - // CreateApplyIfStatement(result, conditionExpression, conditionScope); - // } - - // if (defaultScope != null) - // { - // CreateApplyIfStatement(result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, defaultScope); - // } - // } - - - // // ToDo: convert if - // //stm = ((QsStatementKind.QsConditionalStatement)condStatementKind).Item; - // //if(stm.ConditionalBlocks.Length == 1) - - // // Return the flag to its super-context value - // _super._IsConvertableContext = superContextVal; - // } - // // If the sub-context was non-convertible, then this if will not be - // // converted, making the super-context non-convertible so leave the flag as false - // return condStatementKind; - // } - - // //public override QsStatementKind Transform(QsStatementKind kind) - // //{ - // // if (!(kind is QsStatementKind.QsExpressionStatement expr && expr.Item.Expression is ExpressionKind.CallLikeExpression || - // // kind is QsStatementKind.QsConditionalStatement)) - // // _super._IsConvertableContext = false; - // // return base.Transform(kind); - // //} - //} - private class RerouteTypeParamOriginTransformation { private bool _IsRecursiveIdentifier = false; @@ -1052,5 +747,372 @@ public override ResolvedTypeKind onTypeParameter(QsTypeParameter tp) } } } + + public class HoistTransformation + { + private bool _IsValidScope = true; + private List _ControlOperations; + private QsCallable _CurrentCallable = null; + //private QsStatement _CurrentConditional = null; + + 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 (QsQualifiedName, ResolvedType) GenerateOperation(QsScope contents) + { + var newName = new QsQualifiedName( + _CurrentCallable.FullName.Namespace, + NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _CurrentCallable.FullName.Name.Value)); + + var knownVariables = contents.KnownSymbols.Variables; + + var parameters = ParamTuple.NewQsTuple(knownVariables + .Select(var => ParamTuple.NewQsTupleItem(new LocalVariableDeclaration( + QsLocalSymbol.NewValidName(var.VariableName), + var.Type, + var.InferredInformation, + var.Position, + var.Range))) + .ToImmutableArray()); + + var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); + if (knownVariables.Any()) + { + paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables + .Select(var => var.Type) + .ToImmutableArray())); + } + + var signature = new ResolvedSignature( + _CurrentCallable.Signature.TypeParameters, + paramTypes, + ResolvedType.New(ResolvedTypeKind.UnitType), + CallableInformation.NoInformation); + + var spec = new QsSpecialization( + QsSpecializationKind.QsBody, + newName, + ImmutableArray.Empty, + _CurrentCallable.SourceFile, + QsNullable.Null, + QsNullable>.Null, + signature, + SpecializationImplementation.NewProvided(parameters, contents), + ImmutableArray.Empty, + QsComments.Empty); + + var controlCallable = new QsCallable( + QsCallableKind.Operation, + newName, + ImmutableArray.Empty, + _CurrentCallable.SourceFile, + QsNullable.Null, + signature, + parameters, + ImmutableArray.Create(spec), //ToDo: account for ctrl and adjt + ImmutableArray.Empty, + QsComments.Empty); + + var reroutedCallable = RerouteTypeParamOriginTransformation.Apply(controlCallable, _CurrentCallable.FullName, newName); + _ControlOperations.Add(reroutedCallable); + + return (newName, paramTypes); + } + + private HoistTransformation() { } + + private class HoistSyntax : SyntaxTreeTransformation + { + private HoistTransformation _super; + + public HoistSyntax(HoistTransformation super, HoistScope scope = null) : base(scope ?? new HoistScope(super)) { _super = super; } + + public override QsCallable onCallableImplementation(QsCallable c) + { + _super._CurrentCallable = c; + return base.onCallableImplementation(c); + } + + 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 HoistScope : ScopeTransformation + { + private HoistTransformation _super; + + public HoistScope(HoistTransformation super, HoistExpression expr = null) : + base(scope => new HoistStatementKind(super, scope as HoistScope), + expr ?? new HoistExpression(super)) + { _super = super; } + + //public override QsStatement onStatement(QsStatement stm) + //{ + // if (stm.Statement is QsStatementKind.QsConditionalStatement) + // { + // var context = _super._CurrentConditional; + // _super._CurrentConditional = stm; + // var rtrn = base.onStatement(stm); + // _super._CurrentConditional = context; + // return rtrn; + // } + // else + // { + // return base.onStatement(stm); + // } + // + //} + } + + private class HoistStatementKind : StatementKindTransformation + { + private HoistTransformation _super; + + public HoistStatementKind(HoistTransformation super, HoistScope scope) : base(scope) { _super = super; } + + private QsNullable> GetTypeParamTypesFromCallable(QsCallable callable) + { + if (callable.Signature.TypeParameters.Any(param => param.IsValidName)) + { + return 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()); + } + else + { + return QsNullable>.Null; + } + } + + public override QsStatementKind onReturnStatement(TypedExpression ex) + { + _super._IsValidScope = false; + return base.onReturnStatement(ex); + } + + public override QsStatementKind onValueUpdate(QsValueUpdate stm) + { + // ToDo + + // If lhs contains an identifier found in the scope's known variables, return false + //stm.Lhs; + + _super._IsValidScope = false; + + return base.onValueUpdate(stm); + } + + public override QsStatementKind onConditionalStatement(QsConditionalStatement stm) + { + // ToDo: Revisit this method when the F# Option type has been removed from the onPositionBlock function. + + var temp = _super._IsValidScope; + + var newConditionBlocks = stm.ConditionalBlocks + .Select(condBlock => + { + _super._IsValidScope = true; + var (expr, block) = this.onPositionedBlock(condBlock.Item1, condBlock.Item2); + if (_super._IsValidScope) // if sub-scope is valid, hoist content + { + // Hoist the scope to its own operation + var call = HoistIfContents(block.Body); + block = new QsPositionedBlock( + new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), + block.Location, + block.Comments); + } + return Tuple.Create(expr.Value, block); // ToDo: .Value may be unnecessary in the future + }).ToImmutableArray(); + + var newDefault = QsNullable.Null; + if (stm.Default.IsValue) + { + _super._IsValidScope = true; + var (_, block) = this.onPositionedBlock(null, stm.Default.Item); // ToDo: null is probably bad here + if (_super._IsValidScope) // if sub-scope is valid, hoist content + { + // Hoist the scope to its own operation + var call = HoistIfContents(block.Body); + block = new QsPositionedBlock( + new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), + block.Location, + block.Comments); + } + newDefault = QsNullable.NewValue(block); + } + + _super._IsValidScope = temp; + + return QsStatementKind.NewQsConditionalStatement( + new QsConditionalStatement(newConditionBlocks, newDefault)); + } + + private QsStatement HoistIfContents(QsScope contents) + { + var (targetName, targetParamType) = _super.GenerateOperation(contents); + var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( + Tuple.Create( + targetParamType, + ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes + CallableInformation.NoInformation)); + + var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); + var targetOpId = new TypedExpression + ( + ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), + targetTypeParamTypes.IsNull + ? ImmutableArray, ResolvedType>>.Empty + : targetTypeParamTypes.Item + .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + .ToImmutableArray(), + targetOpType, + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + TypedExpression targetArgs = null; + if (contents.KnownSymbols.Variables.Any()) + { + targetArgs = Helper.CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => Helper.CreateIdentifierExpression( + Identifier.NewLocalVariable(var.VariableName), + QsNullable>.Null, + var.Type)) + .ToArray()); + } + else + { + targetArgs = new TypedExpression + ( + ExpressionKind.UnitValue, + ImmutableArray, ResolvedType>>.Empty, + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + } + + var call = new TypedExpression + ( + ExpressionKind.NewCallLikeExpression(targetOpId, targetArgs), + ImmutableArray,ResolvedType>>.Empty, // ToDo: Fill out type param resolutions caused by application of arguments + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, true), + QsNullable>.Null + ); + + return new QsStatement( + QsStatementKind.NewQsExpressionStatement(call), + LocalDeclarations.Empty, + //statement.SymbolDeclarations, + QsNullable.Null, + QsComments.Empty); + //statement.Comments); + } + + //private QsStatement oldCreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) + //{ + // // Hoist the scope to its own operation + // var (targetName, targetParamType) = _super.GenerateOperation(contents); + // var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( + // Tuple.Create( + // targetParamType, + // ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes + // CallableInformation.NoInformation)); + + // var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); + // var targetOpId = new TypedExpression + // ( + // ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), + // targetTypeParamTypes.IsNull + // ? ImmutableArray, ResolvedType>>.Empty + // : targetTypeParamTypes.Item + // .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + // .ToImmutableArray(), + // targetOpType, + // new InferredExpressionInformation(false, false), + // QsNullable>.Null + // ); + + // TypedExpression targetArgs = null; + // if (contents.KnownSymbols.Variables.Any()) + // { + // targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => CreateIdentifierExpression( + // Identifier.NewLocalVariable(var.VariableName), + // QsNullable>.Null, + // var.Type)) + // .ToArray()); + // } + // else + // { + // targetArgs = new TypedExpression + // ( + // ExpressionKind.UnitValue, + // ImmutableArray, ResolvedType>>.Empty, + // ResolvedType.New(ResolvedTypeKind.UnitType), + // new InferredExpressionInformation(false, false), + // QsNullable>.Null + // ); + // } + + // // Build the surrounding apply-if call + // var (controlOp, controlOpType) = (result == QsResult.One) + // ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) + // : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); + // var controlOpId = CreateIdentifierExpression( + // Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)), + // QsNullable>.Null, + // controlOpType); + // var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); + + // var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, ImmutableArray.Create(targetArgs.ResolvedType)); + + // return new QsStatement( + // QsStatementKind.NewQsExpressionStatement(controlCall), + // statement.SymbolDeclarations, + // QsNullable.Null, + // statement.Comments); + //} + } + + private class HoistExpression : ExpressionTransformation + { + private HoistTransformation _super; + + public HoistExpression(HoistTransformation super) : + base(expr => new HoistExpressionKind(super, expr as HoistExpression), + expr => new HoistExpressionType(super, expr as HoistExpression)) + { _super = super; } + } + + private class HoistExpressionKind : ExpressionKindTransformation + { + private HoistTransformation _super; + + public HoistExpressionKind(HoistTransformation super, HoistExpression expr) : base(expr) { _super = super; } + } + + private class HoistExpressionType : ExpressionTypeTransformation + { + private HoistTransformation _super; + + public HoistExpressionType(HoistTransformation super, HoistExpression expr) : base(expr) { _super = super; } + } + } } } From 33b00e7bc4f5100c8cc53ab3c02f1726f88b8897 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 20 Dec 2019 11:31:02 -0800 Subject: [PATCH 17/53] WIP, mostly finished with implementation. Need to do some cleaning up. --- .../ClassicallyControlledTransformation.cs | 252 +++++++++++------- 1 file changed, 162 insertions(+), 90 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 25c321e0eb..95c1102bc9 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -6,6 +6,8 @@ using System.Collections.Immutable; using System.Linq; using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.FSharp.Core; using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; @@ -15,7 +17,7 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTran { using ExpressionKind = QsExpressionKind; using ResolvedTypeKind = QsTypeKind; - using ParamTuple = QsTuple>; + //using ParamTuple = QsTuple>; internal static class Helper { @@ -323,7 +325,7 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co targetArgs = ImmutableArray.Create(condArgs.ResolvedType, defaultArgs.ResolvedType); } - else if (isCondValid) + else if (isCondValid && defaultScope == null) { (controlOpInfo, controlOpType) = (result == QsResult.One) ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) @@ -335,18 +337,18 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co targetArgs = ImmutableArray.Create(condArgs.ResolvedType); } - else if (isDefaultValid) - { - (controlOpInfo, controlOpType) = (result == QsResult.One) - ? (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType) - : (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType); - - controlArgs = Helper.CreateValueTupleExpression( - conditionExpression, - Helper.CreateValueTupleExpression(defaultId, defaultArgs)); - - targetArgs = ImmutableArray.Create(defaultArgs.ResolvedType); - } + //else if (isDefaultValid) + //{ + // (controlOpInfo, controlOpType) = (result == QsResult.One) + // ? (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType) + // : (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType); + // + // controlArgs = Helper.CreateValueTupleExpression( + // conditionExpression, + // Helper.CreateValueTupleExpression(defaultId, defaultArgs)); + // + // targetArgs = ImmutableArray.Create(defaultArgs.ResolvedType); + //} else { return null; @@ -663,20 +665,26 @@ public override QsScope Transform(QsScope scope) private class RerouteTypeParamOriginTransformation { private bool _IsRecursiveIdentifier = false; + private ImmutableArray>> _Parameters; private QsQualifiedName _OldName; private QsQualifiedName _NewName; - public static QsCallable Apply(QsCallable qsCallable, QsQualifiedName oldName, QsQualifiedName newName) + public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) { var filter = new SyntaxTreeTransformation>( new ScopeTransformation( new RerouteTypeParamOriginExpression( - new RerouteTypeParamOriginTransformation(oldName, newName)))); + new RerouteTypeParamOriginTransformation(parameters, oldName, newName)))); return filter.onCallableImplementation(qsCallable); } - private RerouteTypeParamOriginTransformation(QsQualifiedName oldName, QsQualifiedName newName) { _OldName = oldName; _NewName = newName; } + private RerouteTypeParamOriginTransformation(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + { + _Parameters = parameters; + _OldName = oldName; + _NewName = newName; + } private class RerouteTypeParamOriginExpression : ExpressionTransformation { @@ -696,6 +704,22 @@ public override TypedExpression Transform(TypedExpression ex) { // prevent _IsRecursiveIdentifier from propagating beyond the typed expression it is referring to var isRecursiveIdentifier = _super._IsRecursiveIdentifier; + + // 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); + } + var rtrn = base.Transform(ex); _super._IsRecursiveIdentifier = isRecursiveIdentifier; return rtrn; @@ -753,6 +777,9 @@ public class HoistTransformation private bool _IsValidScope = true; private List _ControlOperations; private QsCallable _CurrentCallable = null; + private ImmutableArray>> _CurrentHoistParams = + ImmutableArray>>.Empty; + private bool _ContainsHoistParamRef = false; // ToDo: May need to explicitly reset this value after every statement. //private QsStatement _CurrentConditional = null; public static QsCompilation Apply(QsCompilation compilation) @@ -768,10 +795,12 @@ public static QsCompilation Apply(QsCompilation compilation) _CurrentCallable.FullName.Namespace, NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _CurrentCallable.FullName.Name.Value)); - var knownVariables = contents.KnownSymbols.Variables; + var knownVariables = contents.KnownSymbols.IsEmpty + ? ImmutableArray>>.Empty + : contents.KnownSymbols.Variables; - var parameters = ParamTuple.NewQsTuple(knownVariables - .Select(var => ParamTuple.NewQsTupleItem(new LocalVariableDeclaration( + var parameters = QsTuple>.NewQsTuple(knownVariables + .Select(var => QsTuple>.NewQsTupleItem(new LocalVariableDeclaration( QsLocalSymbol.NewValidName(var.VariableName), var.Type, var.InferredInformation, @@ -817,7 +846,7 @@ public static QsCompilation Apply(QsCompilation compilation) ImmutableArray.Empty, QsComments.Empty); - var reroutedCallable = RerouteTypeParamOriginTransformation.Apply(controlCallable, _CurrentCallable.FullName, newName); + var reroutedCallable = RerouteTypeParamOriginTransformation.Apply(controlCallable, knownVariables, _CurrentCallable.FullName, newName); _ControlOperations.Add(reroutedCallable); return (newName, paramTypes); @@ -879,6 +908,72 @@ private class HoistStatementKind : StatementKindTransformation public HoistStatementKind(HoistTransformation super, HoistScope scope) : base(scope) { _super = super; } + private QsStatement HoistIfContents(QsScope contents) + { + var (targetName, targetParamType) = _super.GenerateOperation(contents); + var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( + Tuple.Create( + targetParamType, + ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes + CallableInformation.NoInformation)); + + var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); + var targetOpId = new TypedExpression + ( + ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), + targetTypeParamTypes.IsNull + ? ImmutableArray, ResolvedType>>.Empty + : targetTypeParamTypes.Item + .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + .ToImmutableArray(), + targetOpType, + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + var knownSymbols = contents.KnownSymbols.IsEmpty + ? ImmutableArray>>.Empty + : contents.KnownSymbols.Variables; + + TypedExpression targetArgs = null; + if (knownSymbols.Any()) + { + targetArgs = Helper.CreateValueTupleExpression(knownSymbols.Select(var => Helper.CreateIdentifierExpression( + Identifier.NewLocalVariable(var.VariableName), + QsNullable>.Null, + var.Type)) + .ToArray()); + } + else + { + targetArgs = new TypedExpression + ( + ExpressionKind.UnitValue, + ImmutableArray, ResolvedType>>.Empty, + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + } + + var call = new TypedExpression + ( + ExpressionKind.NewCallLikeExpression(targetOpId, targetArgs), + ImmutableArray, ResolvedType>>.Empty, // ToDo: Fill out type param resolutions caused by application of arguments + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, true), + QsNullable>.Null + ); + + return new QsStatement( + QsStatementKind.NewQsExpressionStatement(call), + LocalDeclarations.Empty, + //statement.SymbolDeclarations, + QsNullable.Null, + QsComments.Empty); + //statement.Comments); + } + private QsNullable> GetTypeParamTypesFromCallable(QsCallable callable) { if (callable.Signature.TypeParameters.Any(param => param.IsValidName)) @@ -907,26 +1002,34 @@ public override QsStatementKind onReturnStatement(TypedExpression ex) public override QsStatementKind onValueUpdate(QsValueUpdate stm) { - // ToDo - // If lhs contains an identifier found in the scope's known variables, return false - //stm.Lhs; + + var lhs = this.ExpressionTransformation(stm.Lhs); - _super._IsValidScope = false; + if (_super._ContainsHoistParamRef) + { + _super._IsValidScope = false; + } - return base.onValueUpdate(stm); + var rhs = this.ExpressionTransformation(stm.Rhs); + return QsStatementKind.NewQsValueUpdate(new QsValueUpdate(lhs, rhs)); } public override QsStatementKind onConditionalStatement(QsConditionalStatement stm) { // ToDo: Revisit this method when the F# Option type has been removed from the onPositionBlock function. - var temp = _super._IsValidScope; + var contextValidScope = _super._IsValidScope; + var contextHoistParams = _super._CurrentHoistParams; var newConditionBlocks = stm.ConditionalBlocks .Select(condBlock => { _super._IsValidScope = true; + _super._CurrentHoistParams = condBlock.Item2.Body.KnownSymbols.IsEmpty + ? ImmutableArray>>.Empty + : condBlock.Item2.Body.KnownSymbols.Variables; + var (expr, block) = this.onPositionedBlock(condBlock.Item1, condBlock.Item2); if (_super._IsValidScope) // if sub-scope is valid, hoist content { @@ -944,6 +1047,10 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st if (stm.Default.IsValue) { _super._IsValidScope = true; + _super._CurrentHoistParams = stm.Default.Item.Body.KnownSymbols.IsEmpty + ? ImmutableArray>>.Empty + : stm.Default.Item.Body.KnownSymbols.Variables; + var (_, block) = this.onPositionedBlock(null, stm.Default.Item); // ToDo: null is probably bad here if (_super._IsValidScope) // if sub-scope is valid, hoist content { @@ -957,74 +1064,13 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st newDefault = QsNullable.NewValue(block); } - _super._IsValidScope = temp; + _super._CurrentHoistParams = contextHoistParams; + _super._IsValidScope = contextValidScope; return QsStatementKind.NewQsConditionalStatement( new QsConditionalStatement(newConditionBlocks, newDefault)); } - private QsStatement HoistIfContents(QsScope contents) - { - var (targetName, targetParamType) = _super.GenerateOperation(contents); - var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( - Tuple.Create( - targetParamType, - ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes - CallableInformation.NoInformation)); - - var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); - var targetOpId = new TypedExpression - ( - ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), - targetTypeParamTypes.IsNull - ? ImmutableArray, ResolvedType>>.Empty - : targetTypeParamTypes.Item - .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) - .ToImmutableArray(), - targetOpType, - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - - TypedExpression targetArgs = null; - if (contents.KnownSymbols.Variables.Any()) - { - targetArgs = Helper.CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => Helper.CreateIdentifierExpression( - Identifier.NewLocalVariable(var.VariableName), - QsNullable>.Null, - var.Type)) - .ToArray()); - } - else - { - targetArgs = new TypedExpression - ( - ExpressionKind.UnitValue, - ImmutableArray, ResolvedType>>.Empty, - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - } - - var call = new TypedExpression - ( - ExpressionKind.NewCallLikeExpression(targetOpId, targetArgs), - ImmutableArray,ResolvedType>>.Empty, // ToDo: Fill out type param resolutions caused by application of arguments - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, true), - QsNullable>.Null - ); - - return new QsStatement( - QsStatementKind.NewQsExpressionStatement(call), - LocalDeclarations.Empty, - //statement.SymbolDeclarations, - QsNullable.Null, - QsComments.Empty); - //statement.Comments); - } - //private QsStatement oldCreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) //{ // // Hoist the scope to its own operation @@ -1098,6 +1144,22 @@ public HoistExpression(HoistTransformation super) : base(expr => new HoistExpressionKind(super, expr as HoistExpression), expr => new HoistExpressionType(super, expr as HoistExpression)) { _super = super; } + + public override TypedExpression Transform(TypedExpression ex) + { + var contextContainsHoistParamRef = _super._ContainsHoistParamRef; + _super._ContainsHoistParamRef = false; + var rtrn = base.Transform(ex); + + // If the sub context contains a reference, then the super context contains a reference, + // otherwise return the super context to its original value + if (!_super._ContainsHoistParamRef) + { + _super._ContainsHoistParamRef = contextContainsHoistParamRef; + } + + return rtrn; + } } private class HoistExpressionKind : ExpressionKindTransformation @@ -1105,6 +1167,16 @@ private class HoistExpressionKind : ExpressionKindTransformation> tArgs) + { + if (sym is Identifier.LocalVariable local && + _super._CurrentHoistParams.Any(param => param.VariableName.Equals(local.Item))) + { + _super._ContainsHoistParamRef = true; + } + return base.onIdentifier(sym, tArgs); + } } private class HoistExpressionType : ExpressionTypeTransformation From e2c8d6e4a4fa21b28e9d632f2e595150e046af7f Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 20 Dec 2019 16:07:44 -0800 Subject: [PATCH 18/53] Cleaned up code. Everything seems to be working. --- .../ClassicallyControlledTransformation.cs | 568 +++--------------- 1 file changed, 71 insertions(+), 497 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 95c1102bc9..85a6e2b0ad 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -17,7 +17,6 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTran { using ExpressionKind = QsExpressionKind; using ResolvedTypeKind = QsTypeKind; - //using ParamTuple = QsTuple>; internal static class Helper { @@ -43,175 +42,27 @@ public static TypedExpression CreateValueTupleExpression(params TypedExpression[ ); } + // 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. public class ClassicallyControlledTransformation - { - //private List _ControlOperations; - //private QsCallable _CurrentCallable = null; - //private QsStatement _CurrentConditional; - //private bool _IsConvertableContext = true; - + { public static QsCompilation Apply(QsCompilation compilation) { - var filter = new ClassicallyControlledSyntax(new ClassicallyControlledTransformation()); + var filter = new SyntaxTreeTransformation(new ClassicallyControlledScope()); + + compilation = HoistTransformation.Apply(compilation); return new QsCompilation(compilation.Namespaces.Select(ns => filter.Transform(ns)).ToImmutableArray(), compilation.EntryPoints); } - private ClassicallyControlledTransformation() { } + private ClassicallyControlledTransformation() { } - //private (QsQualifiedName, ResolvedType) GenerateOperation(QsScope contents) - //{ - // var newName = new QsQualifiedName( - // _CurrentCallable.FullName.Namespace, - // NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _CurrentCallable.FullName.Name.Value)); - - // var knownVariables = contents.KnownSymbols.Variables; - - // var parameters = ParamTuple.NewQsTuple(knownVariables - // .Select(var => ParamTuple.NewQsTupleItem(new LocalVariableDeclaration( - // QsLocalSymbol.NewValidName(var.VariableName), - // var.Type, - // var.InferredInformation, - // var.Position, - // var.Range))) - // .ToImmutableArray()); - - // var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); - // if (knownVariables.Any()) - // { - // paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables - // .Select(var => var.Type) - // .ToImmutableArray())); - // } - - // var signature = new ResolvedSignature( - // _CurrentCallable.Signature.TypeParameters, - // paramTypes, - // ResolvedType.New(ResolvedTypeKind.UnitType), - // CallableInformation.NoInformation); - - // var spec = new QsSpecialization( - // QsSpecializationKind.QsBody, - // newName, - // ImmutableArray.Empty, - // _CurrentCallable.SourceFile, - // QsNullable.Null, - // QsNullable>.Null, - // signature, - // SpecializationImplementation.NewProvided(parameters, contents), - // ImmutableArray.Empty, - // QsComments.Empty); - - // var controlCallable = new QsCallable( - // QsCallableKind.Operation, - // newName, - // ImmutableArray.Empty, - // _CurrentCallable.SourceFile, - // QsNullable.Null, - // signature, - // parameters, - // ImmutableArray.Create(spec), //ToDo: account for ctrl and adjt - // ImmutableArray.Empty, - // QsComments.Empty); - - // var reroutedCallable = RerouteTypeParamOriginTransformation.Apply(controlCallable, _CurrentCallable.FullName, newName); - // _ControlOperations.Add(reroutedCallable); - - // return (newName, paramTypes); - //} - - private class ClassicallyControlledSyntax : SyntaxTreeTransformation + private class ClassicallyControlledScope : ScopeTransformation { - private ClassicallyControlledTransformation _super; - - public ClassicallyControlledSyntax(ClassicallyControlledTransformation super, ClassicallyControlledScope scope = null) : base(scope ?? new ClassicallyControlledScope(super)) - { _super = super; } - - //public override QsCallable onCallableImplementation(QsCallable c) - //{ - // _super._CurrentCallable = c; - // return base.onCallableImplementation(c); - //} - // - //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 ClassicallyControlledScope : ScopeTransformation - { - private ClassicallyControlledTransformation _super; - - public ClassicallyControlledScope(ClassicallyControlledTransformation super, NoExpressionTransformations expr = null) - : base (/*scope => new ClassicallyControlledStatementKind(super, scope as ClassicallyControlledScope),*/ - expr ?? new NoExpressionTransformations()) { _super = super; } - - //private QsStatement MakeNestedIfs(QsStatement originalStatment, QsStatementKind.QsConditionalStatement condStatmentKind) - //{ - // var cond = condStatmentKind.Item; - // - // if (cond.ConditionalBlocks.Length == 1) - // { - // return new QsStatement - // ( - // condStatmentKind, - // originalStatment.SymbolDeclarations, - // originalStatment.Location, - // originalStatment.Comments - // ); - // } - // - // var subIfKind = (QsStatementKind.QsConditionalStatement)QsStatementKind.NewQsConditionalStatement( - // new QsConditionalStatement(cond.ConditionalBlocks.RemoveAt(0), cond.Default)); - // - // var subIfStatment = MakeNestedIfs(originalStatment, subIfKind); - // - // var secondCondBlock = cond.ConditionalBlocks[1].Item2; - // var newDefault = QsNullable.NewValue(new QsPositionedBlock( - // new QsScope(ImmutableArray.Create(subIfStatment), secondCondBlock.Body.KnownSymbols), - // secondCondBlock.Location, - // secondCondBlock.Comments)); - // - // return new QsStatement - // ( - // QsStatementKind.NewQsConditionalStatement(new QsConditionalStatement(ImmutableArray.Create(cond.ConditionalBlocks[0]), newDefault)), - // originalStatment.SymbolDeclarations, - // originalStatment.Location, - // originalStatment.Comments - // ); - //} - // - //private QsStatement MakeNestedIfs(QsStatement originalStatment) - //{ - // if (originalStatment.Statement is QsStatementKind.QsConditionalStatement cond) - // { - // var nested = MakeNestedIfs(originalStatment, cond); - // var nestedKind = ((QsStatementKind.QsConditionalStatement)nested.Statement).Item; - // if (nestedKind.Default.IsValue) - // { - // var newDefault = QsNullable.NewValue(new QsPositionedBlock( - // this.Transform(nestedKind.Default.Item.Body), - // nestedKind.Default.Item.Location, - // nestedKind.Default.Item.Comments)); - // - // nested = new QsStatement - // ( - // QsStatementKind.NewQsConditionalStatement(new QsConditionalStatement(nestedKind.ConditionalBlocks, newDefault)), - // nested.SymbolDeclarations, - // nested.Location, - // nested.Comments - // ); - // } - // - // return nested; - // } - // - // return originalStatment; - //} + public ClassicallyControlledScope(NoExpressionTransformations expr = null) : base (expr ?? new NoExpressionTransformations()) {} private (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatement statement) { @@ -254,24 +105,6 @@ public ClassicallyControlledScope(ClassicallyControlledTransformation super, NoE return (false, null, null); } - //private bool AreSimpleCallStatements(IEnumerable stmts) => - // stmts.Select(s => IsSimpleCallStatement(s.Statement).Item1).All(b => b); - // - //private (bool, TypedExpression, TypedExpression) IsSimpleCallStatement(QsStatementKind statement) - //{ - // if (statement is QsStatementKind.QsExpressionStatement expr) - // { - // var returnType = expr.Item.ResolvedType; - // - // if (returnType.Resolution.IsUnitType && expr.Item.Expression is ExpressionKind.CallLikeExpression call) - // { - // return (true, call.Item1, call.Item2); - // } - // } - // - // return (false, null, null); - //} - private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, BuiltIn controlOp, IEnumerable opTypeParamResolutions) => new TypedExpression ( @@ -279,7 +112,6 @@ private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression ar opTypeParamResolutions .Zip(controlOp.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), param, type)) .ToImmutableArray(), - //ImmutableArray.Create(Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), controlOp.TypeParameters.First(), opTypeParamResolutions)), ResolvedType.New(ResolvedTypeKind.UnitType), new InferredExpressionInformation(false, true), QsNullable>.Null @@ -337,18 +169,6 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co targetArgs = ImmutableArray.Create(condArgs.ResolvedType); } - //else if (isDefaultValid) - //{ - // (controlOpInfo, controlOpType) = (result == QsResult.One) - // ? (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType) - // : (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType); - // - // controlArgs = Helper.CreateValueTupleExpression( - // conditionExpression, - // Helper.CreateValueTupleExpression(defaultId, defaultArgs)); - // - // targetArgs = ImmutableArray.Create(defaultArgs.ResolvedType); - //} else { return null; @@ -358,120 +178,10 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co var controlOpId = Helper.CreateIdentifierExpression( Identifier.NewGlobalCallable(new QsQualifiedName(controlOpInfo.Namespace, controlOpInfo.Name)), QsNullable>.Null, - controlOpType); - //var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); + controlOpType); return CreateApplyIfCall(controlOpId, controlArgs, controlOpInfo, targetArgs); } - - //private QsStatement oldCreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) - //{ - // // Hoist the scope to its own operation - // var (targetName, targetParamType) = _super.GenerateOperation(contents); - // var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( - // Tuple.Create( - // targetParamType, - // ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes - // CallableInformation.NoInformation)); - - // var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); - // var targetOpId = new TypedExpression - // ( - // ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), - // targetTypeParamTypes.IsNull - // ? ImmutableArray, ResolvedType>>.Empty - // : targetTypeParamTypes.Item - // .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) - // .ToImmutableArray(), - // targetOpType, - // new InferredExpressionInformation(false, false), - // QsNullable>.Null - // ); - - // TypedExpression targetArgs = null; - // if (contents.KnownSymbols.Variables.Any()) - // { - // targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => CreateIdentifierExpression( - // Identifier.NewLocalVariable(var.VariableName), - // QsNullable>.Null, - // var.Type)) - // .ToArray()); - // } - // else - // { - // targetArgs = new TypedExpression - // ( - // ExpressionKind.UnitValue, - // ImmutableArray, ResolvedType>>.Empty, - // ResolvedType.New(ResolvedTypeKind.UnitType), - // new InferredExpressionInformation(false, false), - // QsNullable>.Null - // ); - // } - - // // Build the surrounding apply-if call - // var (controlOp, controlOpType) = (result == QsResult.One) - // ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) - // : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); - // var controlOpId = CreateIdentifierExpression( - // Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)), - // QsNullable>.Null, - // controlOpType); - // var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); - - // var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, ImmutableArray.Create(targetArgs.ResolvedType)); - - // return new QsStatement( - // QsStatementKind.NewQsExpressionStatement(controlCall), - // statement.SymbolDeclarations, - // QsNullable.Null, - // statement.Comments); - //} - - //private static int _varCount = 0; - // - //private (QsStatement, TypedExpression) CreateNewConditionVariable(TypedExpression value, QsStatement condStatement) - //{ - // _varCount++; - // var name = NonNullable.New($"__classic_ctrl{_varCount}__"); - // - // // The typed expression with the identifier of the variable we just created: - // var idExpression = CreateIdentifierExpression( - // Identifier.NewLocalVariable(name), - // QsNullable>.Null, - // value.ResolvedType); - // - // // The actual binding statement: - // var binding = new QsBinding(QsBindingKind.ImmutableBinding, SymbolTuple.NewVariableName(name), value); - // var symbDecl = new LocalDeclarations(condStatement.SymbolDeclarations.Variables.Add(new LocalVariableDeclaration> - // ( - // name, - // value.ResolvedType, - // new InferredExpressionInformation(false, false), - // condStatement.Location.IsValue - // ? QsNullable>.NewValue(condStatement.Location.Item.Offset) - // : QsNullable>.Null, - // condStatement.Location.IsValue - // ? condStatement.Location.Item.Range - // : Tuple.Create(QsPositionInfo.Zero, QsPositionInfo.Zero) - // ))); - // var stmt = new QsStatement(QsStatementKind.NewQsVariableDeclaration(binding), symbDecl, condStatement.Location, condStatement.Comments); - // - // return (stmt, idExpression); - //} - - //public override QsStatement onStatement(QsStatement stm) - //{ - // if (stm.Statement is QsStatementKind.QsConditionalStatement) - // { - // var superContextVal = _super._CurrentConditional; - // _super._CurrentConditional = stm; - // var rtrn = base.onStatement(stm); - // _super._CurrentConditional = superContextVal; - // return rtrn; - // } - // return base.onStatement(stm); - //} private (bool, QsConditionalStatement) ProcessElif(QsConditionalStatement cond) { @@ -484,7 +194,6 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co ( QsStatementKind.NewQsConditionalStatement(subCond), LocalDeclarations.Empty, - //_super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues secondCondBlock.Location, secondCondBlock.Comments ); @@ -511,7 +220,6 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co ( QsStatementKind.NewQsConditionalStatement(subCond), LocalDeclarations.Empty, - //_super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues block.Location, QsComments.Empty ); @@ -542,7 +250,6 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co ( QsStatementKind.NewQsConditionalStatement(subCond), LocalDeclarations.Empty, - //_super._CurrentConditional.SymbolDeclarations, // ToDo: Duplicating this might cause issues block.Location, QsComments.Empty ); @@ -597,20 +304,13 @@ public override QsScope Transform(QsScope scope) var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(stm); - // ToDo: Maybe this Identifier logic should be done in first transformation instead. - - // The condition must be an identifier, otherwise we'll call it multiple times. - // If not, create a new variable and use that: - //if (!(conditionExpression.Expression is ExpressionKind.Identifier)) - //{ - // var (letStmt, idExpression) = CreateNewConditionVariable(conditionExpression, statement); - // statements.Add(letStmt); - // conditionExpression = idExpression; - //} - if (isCondition) { - statements.Add(CreateApplyIfStatement(statement, result, conditionExpression, conditionScope, defaultScope)); + statements.Add(CreateApplyIfStatement(stm, result, conditionExpression, conditionScope, defaultScope)); + } + else + { + statements.Add(this.onStatement(stm)); } } else @@ -621,48 +321,12 @@ public override QsScope Transform(QsScope scope) return new QsScope(statements.ToImmutableArray(), parentSymbols); } - - //public QsScope oldTransform(QsScope scope) - //{ - // scope = base.Transform(scope); // process sub-scopes first - // - // return scope; - // - // var statements = ImmutableArray.CreateBuilder(); - // foreach (var s in scope.Statements) - // { - // var statement = MakeNestedIfs(s); - // var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(statement); - // - // if (isCondition && AreSimpleCallStatements(conditionScope.Statements) && (defaultScope == null || AreSimpleCallStatements(defaultScope.Statements))) - // { - // // The condition must be an identifier, otherwise we'll call it multiple times. - // // If not, create a new variable and use that: - // if (!(conditionExpression.Expression is ExpressionKind.Identifier)) - // { - // var (letStmt, idExpression) = CreateNewConditionVariable(conditionExpression, statement); - // statements.Add(letStmt); - // conditionExpression = idExpression; - // } - // - // statements.Add(CreateApplyIfStatement(statement, result, conditionExpression, conditionScope)); - // - // if (defaultScope != null) - // { - // statements.Add(CreateApplyIfStatement(statement, result.IsOne ? QsResult.Zero : QsResult.One, conditionExpression, defaultScope)); - // } - // } - // else - // { - // statements.Add(this.onStatement(statement)); - // } - // } - // - // return new QsScope(statements.ToImmutableArray(), scope.KnownSymbols); - //} } - private class RerouteTypeParamOriginTransformation + // 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; @@ -671,40 +335,38 @@ private class RerouteTypeParamOriginTransformation public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) { - var filter = new SyntaxTreeTransformation>( - new ScopeTransformation( - new RerouteTypeParamOriginExpression( - new RerouteTypeParamOriginTransformation(parameters, oldName, newName)))); + var filter = new SyntaxTreeTransformation>( + new ScopeTransformation( + new UpdateGeneratedOpExpression( + new UpdateGeneratedOpTransformation(parameters, oldName, newName)))); return filter.onCallableImplementation(qsCallable); } - private RerouteTypeParamOriginTransformation(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + private UpdateGeneratedOpTransformation(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) { _Parameters = parameters; _OldName = oldName; _NewName = newName; } - private class RerouteTypeParamOriginExpression : ExpressionTransformation + private class UpdateGeneratedOpExpression : ExpressionTransformation { - private RerouteTypeParamOriginTransformation _super; + private UpdateGeneratedOpTransformation _super; - public RerouteTypeParamOriginExpression(RerouteTypeParamOriginTransformation super) : - base(expr => new RerouteTypeParamOriginExpressionKind(super, expr as RerouteTypeParamOriginExpression), - expr => new RerouteTypeParamOriginExpressionType(super, expr as RerouteTypeParamOriginExpression)) + 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) { - // prevent _IsRecursiveIdentifier from propagating beyond the typed expression it is referring to - var isRecursiveIdentifier = _super._IsRecursiveIdentifier; - // Checks if expression is mutable identifier that is in parameter list if (ex.InferredInformation.IsMutable && ex.Expression is ExpressionKind.Identifier id && @@ -720,21 +382,22 @@ id.Item1 is Identifier.LocalVariable variable && 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 RerouteTypeParamOriginExpressionKind : ExpressionKindTransformation + private class UpdateGeneratedOpExpressionKind : ExpressionKindTransformation { - private RerouteTypeParamOriginTransformation _super; + private UpdateGeneratedOpTransformation _super; - public RerouteTypeParamOriginExpressionKind(RerouteTypeParamOriginTransformation super, RerouteTypeParamOriginExpression expr) : base(expr) { _super = super; } + public UpdateGeneratedOpExpressionKind(UpdateGeneratedOpTransformation super, UpdateGeneratedOpExpression expr) : base(expr) { _super = super; } public override ExpressionKind onIdentifier(Identifier sym, QsNullable> tArgs) { - // Process the identifier (including its type arguments) var rtrn = base.onIdentifier(sym, tArgs); // Then check if this is a recursive identifier @@ -749,11 +412,11 @@ public override ExpressionKind onIdentifier(Identifier sym, QsNullable + private class UpdateGeneratedOpExpressionType : ExpressionTypeTransformation { - private RerouteTypeParamOriginTransformation _super; + private UpdateGeneratedOpTransformation _super; - public RerouteTypeParamOriginExpressionType(RerouteTypeParamOriginTransformation super, RerouteTypeParamOriginExpression expr) : base(expr) { _super = super; } + public UpdateGeneratedOpExpressionType(UpdateGeneratedOpTransformation super, UpdateGeneratedOpExpression expr) : base(expr) { _super = super; } public override ResolvedTypeKind onTypeParameter(QsTypeParameter tp) { @@ -772,7 +435,16 @@ public override ResolvedTypeKind onTypeParameter(QsTypeParameter tp) } } - public class HoistTransformation + // 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; @@ -780,7 +452,6 @@ public class HoistTransformation private ImmutableArray>> _CurrentHoistParams = ImmutableArray>>.Empty; private bool _ContainsHoistParamRef = false; // ToDo: May need to explicitly reset this value after every statement. - //private QsStatement _CurrentConditional = null; public static QsCompilation Apply(QsCompilation compilation) { @@ -846,7 +517,7 @@ public static QsCompilation Apply(QsCompilation compilation) ImmutableArray.Empty, QsComments.Empty); - var reroutedCallable = RerouteTypeParamOriginTransformation.Apply(controlCallable, knownVariables, _CurrentCallable.FullName, newName); + var reroutedCallable = UpdateGeneratedOpTransformation.Apply(controlCallable, knownVariables, _CurrentCallable.FullName, newName); _ControlOperations.Add(reroutedCallable); return (newName, paramTypes); @@ -854,11 +525,15 @@ public static QsCompilation Apply(QsCompilation compilation) private HoistTransformation() { } - private class HoistSyntax : SyntaxTreeTransformation + private class HoistSyntax : SyntaxTreeTransformation> { private HoistTransformation _super; - public HoistSyntax(HoistTransformation super, HoistScope scope = null) : base(scope ?? new HoistScope(super)) { _super = 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) { @@ -875,38 +550,11 @@ public override QsNamespace Transform(QsNamespace ns) } } - private class HoistScope : ScopeTransformation - { - private HoistTransformation _super; - - public HoistScope(HoistTransformation super, HoistExpression expr = null) : - base(scope => new HoistStatementKind(super, scope as HoistScope), - expr ?? new HoistExpression(super)) - { _super = super; } - - //public override QsStatement onStatement(QsStatement stm) - //{ - // if (stm.Statement is QsStatementKind.QsConditionalStatement) - // { - // var context = _super._CurrentConditional; - // _super._CurrentConditional = stm; - // var rtrn = base.onStatement(stm); - // _super._CurrentConditional = context; - // return rtrn; - // } - // else - // { - // return base.onStatement(stm); - // } - // - //} - } - - private class HoistStatementKind : StatementKindTransformation + private class HoistStatementKind : StatementKindTransformation> { private HoistTransformation _super; - public HoistStatementKind(HoistTransformation super, HoistScope scope) : base(scope) { _super = super; } + public HoistStatementKind(HoistTransformation super, ScopeTransformation scope) : base(scope) { _super = super; } private QsStatement HoistIfContents(QsScope contents) { @@ -917,13 +565,13 @@ private QsStatement HoistIfContents(QsScope contents) ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes CallableInformation.NoInformation)); - var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); + var targetTypeArgTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); var targetOpId = new TypedExpression ( - ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), - targetTypeParamTypes.IsNull + ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeArgTypes), + targetTypeArgTypes.IsNull ? ImmutableArray, ResolvedType>>.Empty - : targetTypeParamTypes.Item + : targetTypeArgTypes.Item .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) .ToImmutableArray(), targetOpType, @@ -959,7 +607,8 @@ private QsStatement HoistIfContents(QsScope contents) var call = new TypedExpression ( ExpressionKind.NewCallLikeExpression(targetOpId, targetArgs), - ImmutableArray, ResolvedType>>.Empty, // ToDo: Fill out type param resolutions caused by application of arguments + // All type params are resolved on the Identifier + ImmutableArray, ResolvedType>>.Empty, ResolvedType.New(ResolvedTypeKind.UnitType), new InferredExpressionInformation(false, true), QsNullable>.Null @@ -968,10 +617,8 @@ private QsStatement HoistIfContents(QsScope contents) return new QsStatement( QsStatementKind.NewQsExpressionStatement(call), LocalDeclarations.Empty, - //statement.SymbolDeclarations, QsNullable.Null, QsComments.Empty); - //statement.Comments); } private QsNullable> GetTypeParamTypesFromCallable(QsCallable callable) @@ -1002,8 +649,7 @@ public override QsStatementKind onReturnStatement(TypedExpression ex) public override QsStatementKind onValueUpdate(QsValueUpdate stm) { - // If lhs contains an identifier found in the scope's known variables, return false - + // If lhs contains an identifier found in the scope's known variables, the scope is not valid var lhs = this.ExpressionTransformation(stm.Lhs); if (_super._ContainsHoistParamRef) @@ -1031,7 +677,7 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st : condBlock.Item2.Body.KnownSymbols.Variables; var (expr, block) = this.onPositionedBlock(condBlock.Item1, condBlock.Item2); - if (_super._IsValidScope) // if sub-scope is valid, hoist content + if (_super._IsValidScope && block.Body.Statements.Length > 1) // if sub-scope is valid, hoist content { // Hoist the scope to its own operation var call = HoistIfContents(block.Body); @@ -1052,7 +698,7 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st : stm.Default.Item.Body.KnownSymbols.Variables; var (_, block) = this.onPositionedBlock(null, stm.Default.Item); // ToDo: null is probably bad here - if (_super._IsValidScope) // if sub-scope is valid, hoist content + if (_super._IsValidScope && block.Body.Statements.Length > 1) // if sub-scope is valid, hoist content { // Hoist the scope to its own operation var call = HoistIfContents(block.Body); @@ -1070,79 +716,14 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st return QsStatementKind.NewQsConditionalStatement( new QsConditionalStatement(newConditionBlocks, newDefault)); } - - //private QsStatement oldCreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope contents) - //{ - // // Hoist the scope to its own operation - // var (targetName, targetParamType) = _super.GenerateOperation(contents); - // var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( - // Tuple.Create( - // targetParamType, - // ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes - // CallableInformation.NoInformation)); - - // var targetTypeParamTypes = GetTypeParamTypesFromCallable(_super._CurrentCallable); - // var targetOpId = new TypedExpression - // ( - // ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeParamTypes), - // targetTypeParamTypes.IsNull - // ? ImmutableArray, ResolvedType>>.Empty - // : targetTypeParamTypes.Item - // .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) - // .ToImmutableArray(), - // targetOpType, - // new InferredExpressionInformation(false, false), - // QsNullable>.Null - // ); - - // TypedExpression targetArgs = null; - // if (contents.KnownSymbols.Variables.Any()) - // { - // targetArgs = CreateValueTupleExpression(contents.KnownSymbols.Variables.Select(var => CreateIdentifierExpression( - // Identifier.NewLocalVariable(var.VariableName), - // QsNullable>.Null, - // var.Type)) - // .ToArray()); - // } - // else - // { - // targetArgs = new TypedExpression - // ( - // ExpressionKind.UnitValue, - // ImmutableArray, ResolvedType>>.Empty, - // ResolvedType.New(ResolvedTypeKind.UnitType), - // new InferredExpressionInformation(false, false), - // QsNullable>.Null - // ); - // } - - // // Build the surrounding apply-if call - // var (controlOp, controlOpType) = (result == QsResult.One) - // ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) - // : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); - // var controlOpId = CreateIdentifierExpression( - // Identifier.NewGlobalCallable(new QsQualifiedName(controlOp.Namespace, controlOp.Name)), - // QsNullable>.Null, - // controlOpType); - // var controlArgs = CreateValueTupleExpression(conditionExpression, CreateValueTupleExpression(targetOpId, targetArgs)); - - // var controlCall = CreateApplyIfCall(controlOpId, controlArgs, controlOp, ImmutableArray.Create(targetArgs.ResolvedType)); - - // return new QsStatement( - // QsStatementKind.NewQsExpressionStatement(controlCall), - // statement.SymbolDeclarations, - // QsNullable.Null, - // statement.Comments); - //} } - private class HoistExpression : ExpressionTransformation + private class HoistExpression : ExpressionTransformation { private HoistTransformation _super; public HoistExpression(HoistTransformation super) : - base(expr => new HoistExpressionKind(super, expr as HoistExpression), - expr => new HoistExpressionType(super, expr as HoistExpression)) + base(expr => new HoistExpressionKind(super, expr as HoistExpression)) { _super = super; } public override TypedExpression Transform(TypedExpression ex) @@ -1178,13 +759,6 @@ public override ExpressionKind onIdentifier(Identifier sym, QsNullable - { - private HoistTransformation _super; - - public HoistExpressionType(HoistTransformation super, HoistExpression expr) : base(expr) { _super = super; } - } } } } From 31ef6f987ffdec1dd1bf2a2405471c39d294418f Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 3 Jan 2020 15:42:26 -0800 Subject: [PATCH 19/53] Partially fixed a bug with generic references being separated from their argument application. --- .../ClassicallyControlledTransformation.cs | 70 ++++++++++++++----- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 85a6e2b0ad..4c0a40d922 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -21,11 +21,17 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTran internal static class Helper { public static TypedExpression CreateIdentifierExpression(Identifier id, - QsNullable> typeParams, ResolvedType resolvedType) => + ImmutableArray, ResolvedType>> typeArgsMapping, ResolvedType resolvedType) => new TypedExpression ( - ExpressionKind.NewIdentifier(id, typeParams), - ImmutableArray, ResolvedType>>.Empty, + 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 @@ -58,11 +64,11 @@ public static QsCompilation Apply(QsCompilation compilation) return new QsCompilation(compilation.Namespaces.Select(ns => filter.Transform(ns)).ToImmutableArray(), compilation.EntryPoints); } - private ClassicallyControlledTransformation() { } - + private ClassicallyControlledTransformation() { } + private class ClassicallyControlledScope : ScopeTransformation { - public ClassicallyControlledScope(NoExpressionTransformations expr = null) : base (expr ?? new NoExpressionTransformations()) {} + public ClassicallyControlledScope(NoExpressionTransformations expr = null) : base(expr ?? new NoExpressionTransformations()) { } private (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatement statement) { @@ -97,7 +103,34 @@ private class ClassicallyControlledScope : ScopeTransformation>.NewValue(combinedTypeArguments + .Select(arg => arg.Item3) + .ToImmutableArray())), + combinedTypeArguments, + ResolvedType.New(ResolvedTypeKind.UnitType), // ToDo: This is really wrong + // Need to replace all type-param types in resolved type with their resolution + // I hope this does not involve a whole new transformation :( + //call.Item1.ResolvedType, + call.Item1.InferredInformation, + call.Item1.Range); + } + + return (true, newExpr1, call.Item2); } } } @@ -105,13 +138,14 @@ private class ClassicallyControlledScope : ScopeTransformation opTypeParamResolutions) => + private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args/*, BuiltIn controlOp, IEnumerable opTypeParamResolutions*/) => new TypedExpression ( ExpressionKind.NewCallLikeExpression(id, args), - opTypeParamResolutions - .Zip(controlOp.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), param, type)) - .ToImmutableArray(), + ImmutableArray, ResolvedType>>.Empty, + //opTypeParamResolutions + // .Zip(controlOp.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), param, type)) + // .ToImmutableArray(), ResolvedType.New(ResolvedTypeKind.UnitType), new InferredExpressionInformation(false, true), QsNullable>.Null @@ -177,10 +211,14 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co // Build the surrounding apply-if call var controlOpId = Helper.CreateIdentifierExpression( Identifier.NewGlobalCallable(new QsQualifiedName(controlOpInfo.Namespace, controlOpInfo.Name)), - QsNullable>.Null, + targetArgs + .Zip(controlOpInfo.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(controlOpInfo.Namespace, controlOpInfo.Name), param, type)) + .ToImmutableArray(), + //QsNullable>.NewValue(targetArgs), + //QsNullable>.Null, controlOpType); - return CreateApplyIfCall(controlOpId, controlArgs, controlOpInfo, targetArgs); + return CreateApplyIfCall(controlOpId, controlArgs/*, controlOpInfo, targetArgs*/); } private (bool, QsConditionalStatement) ProcessElif(QsConditionalStatement cond) @@ -294,7 +332,7 @@ 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) @@ -318,7 +356,7 @@ public override QsScope Transform(QsScope scope) statements.Add(this.onStatement(statement)); } } - + return new QsScope(statements.ToImmutableArray(), parentSymbols); } } @@ -588,7 +626,7 @@ private QsStatement HoistIfContents(QsScope contents) { targetArgs = Helper.CreateValueTupleExpression(knownSymbols.Select(var => Helper.CreateIdentifierExpression( Identifier.NewLocalVariable(var.VariableName), - QsNullable>.Null, + ImmutableArray, ResolvedType>>.Empty, var.Type)) .ToArray()); } From ce0d72f6e5bd8afc0da2c39c4a04627eab96b941 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 3 Jan 2020 16:18:33 -0800 Subject: [PATCH 20/53] Fixed minor bug with the logic that handles mutables. --- .../ClassicallyControlledTransformation.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 4c0a40d922..cc2410f5f4 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -489,7 +489,7 @@ private class HoistTransformation private QsCallable _CurrentCallable = null; private ImmutableArray>> _CurrentHoistParams = ImmutableArray>>.Empty; - private bool _ContainsHoistParamRef = false; // ToDo: May need to explicitly reset this value after every statement. + private bool _ContainsHoistParamRef = false; public static QsCompilation Apply(QsCompilation compilation) { @@ -754,6 +754,12 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st return QsStatementKind.NewQsConditionalStatement( new QsConditionalStatement(newConditionBlocks, newDefault)); } + + public override QsStatementKind Transform(QsStatementKind kind) + { + _super._ContainsHoistParamRef = false; // Every statement kind starts off false + return base.Transform(kind); + } } private class HoistExpression : ExpressionTransformation From 274a0cde021e2cb463fdc43533544029d4ad13fb Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Mon, 6 Jan 2020 16:39:04 -0800 Subject: [PATCH 21/53] Got most of the implementation for functor support in generated control operations. Still needs some work in resolving some ToDo's. --- src/QsCompiler/Compiler/FunctorGeneration.cs | 16 +- .../ClassicallyControlledTransformation.cs | 156 ++++++++++++++++-- 2 files changed, 152 insertions(+), 20 deletions(-) diff --git a/src/QsCompiler/Compiler/FunctorGeneration.cs b/src/QsCompiler/Compiler/FunctorGeneration.cs index 71ba0b8c1b..598c1d640b 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/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index cc2410f5f4..c0ec00b166 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Dynamic; using System.Linq; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -109,7 +110,14 @@ private class ClassicallyControlledScope : ScopeTransformation (x.Item1, x.Item2), x => x.Item3); + foreach (var arg in idTypeArguments) + { + mapping[(arg.Item1, arg.Item2)] = arg.Item3; + } + var combinedTypeArguments = mapping.Select(kvp => Tuple.Create(kvp.Key.Item1, kvp.Key.Item2, kvp.Value)).ToImmutableArray(); var newExpr1 = expr.Item; // ToDo: shouldn't rely on expr1 being identifier @@ -491,6 +499,11 @@ private class HoistTransformation ImmutableArray>>.Empty; private bool _ContainsHoistParamRef = false; + private bool _InBody = false; + private bool _InAdjoint = false; + private bool _InControlled = false; + //private bool _InControlledAdjoint = false; + public static QsCompilation Apply(QsCompilation compilation) { var filter = new HoistSyntax(new HoistTransformation()); @@ -498,6 +511,93 @@ public static QsCompilation Apply(QsCompilation compilation) return new QsCompilation(compilation.Namespaces.Select(ns => filter.Transform(ns)).ToImmutableArray(), compilation.EntryPoints); } + private QsSpecialization MakeSpecialization(QsSpecializationKind kind, QsQualifiedName parentName, ResolvedSignature signature, SpecializationImplementation impl) => + new QsSpecialization( + kind, + parentName, + ImmutableArray.Empty, + _CurrentCallable.SourceFile, + QsNullable.Null, + QsNullable>.Null, + signature, + impl, + ImmutableArray.Empty, + QsComments.Empty); + + private IEnumerable GetFunctorSpecializations(QsQualifiedName parentName, ResolvedSignature parentSignature) + { + var adj = _CurrentCallable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsAdjoint); + var ctl = _CurrentCallable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlled); + var ctlAdj = _CurrentCallable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlledAdjoint); + + var specializations = new List(); + + // ToDo: this Boolean logic could be cleaned up + bool addAdjoint = false; + bool addControlled = false; + //bool addControlledAdjoint = false; + if (_InBody) + { + if (adj != null && adj.Implementation.IsGenerated) + { + addAdjoint = true; + } + + if (ctl != null && ctl.Implementation.IsGenerated) + { + addControlled = true; + } + + // ToDo: I don't think you have to add the ControlledAdjoint if you add the Controlled and Adjoint specializations + //if (ctlAdj != null && ctlAdj.Implementation.IsGenerated) + //{ + // addControlledAdjoint = true; + //} + } + else if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated gen) + { + if (_InAdjoint && gen.Item.IsDistribute) + { + addControlled = true; + } + else if (_InControlled && gen.Item.IsInvert) + { + addAdjoint = true; + } + } + + if (addAdjoint) + { + specializations.Add(MakeSpecialization( + QsSpecializationKind.QsAdjoint, + parentName, + parentSignature, + SpecializationImplementation.NewGenerated(QsGeneratorDirective.InvalidGenerator))); // ToDo: find appropriate directive + } + + if (addControlled) + { + specializations.Add(MakeSpecialization( + QsSpecializationKind.QsControlled, + parentName, + new ResolvedSignature( + parentSignature.TypeParameters, + ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Create( + ResolvedType.New(ResolvedTypeKind.NewArrayType(ResolvedType.New(ResolvedTypeKind.Qubit))), + parentSignature.ArgumentType))), + parentSignature.ReturnType, + parentSignature.Information), + SpecializationImplementation.NewGenerated(QsGeneratorDirective.InvalidGenerator))); // ToDo: find appropriate directive + } + + //if (addControlledAdjoint) + //{ + // // add 'generated' implementation for ControlledAdjoint + //} + + return specializations; + } + private (QsQualifiedName, ResolvedType) GenerateOperation(QsScope contents) { var newName = new QsQualifiedName( @@ -518,7 +618,11 @@ public static QsCompilation Apply(QsCompilation compilation) .ToImmutableArray()); var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); - if (knownVariables.Any()) + if (knownVariables.Length == 1) + { + paramTypes = knownVariables.First().Type; + } + else if (knownVariables.Length > 1) { paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables .Select(var => var.Type) @@ -529,19 +633,13 @@ public static QsCompilation Apply(QsCompilation compilation) _CurrentCallable.Signature.TypeParameters, paramTypes, ResolvedType.New(ResolvedTypeKind.UnitType), - CallableInformation.NoInformation); + CallableInformation.NoInformation); // ToDo: this information should have the supported functors in it - var spec = new QsSpecialization( + var body = MakeSpecialization( QsSpecializationKind.QsBody, newName, - ImmutableArray.Empty, - _CurrentCallable.SourceFile, - QsNullable.Null, - QsNullable>.Null, signature, - SpecializationImplementation.NewProvided(parameters, contents), - ImmutableArray.Empty, - QsComments.Empty); + SpecializationImplementation.NewProvided(parameters, contents)); var controlCallable = new QsCallable( QsCallableKind.Operation, @@ -551,7 +649,9 @@ public static QsCompilation Apply(QsCompilation compilation) QsNullable.Null, signature, parameters, - ImmutableArray.Create(spec), //ToDo: account for ctrl and adjt + new List() { body } + .Concat(GetFunctorSpecializations(newName, signature)) + .ToImmutableArray(), ImmutableArray.Empty, QsComments.Empty); @@ -579,6 +679,38 @@ public override QsCallable onCallableImplementation(QsCallable 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 QsSpecialization onControlledAdjointSpecialization(QsSpecialization spec) + //{ + // _super._InControlledAdjoint = true; + // var rtrn = base.onControlledAdjointSpecialization(spec); + // _super._InControlledAdjoint = false; + // return rtrn; + //} + public override QsNamespace Transform(QsNamespace ns) { // Control operations list will be populated in the transform From 603cce366673e670b2d2743213c5ef41b6b6bb23 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 7 Jan 2020 12:00:16 -0800 Subject: [PATCH 22/53] Getting close, still need to use the appropriate ApplyIf for the call. --- .../ClassicallyControlledTransformation.cs | 161 ++++++++++-------- 1 file changed, 92 insertions(+), 69 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index c0ec00b166..75f95876c7 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -494,11 +494,39 @@ private class HoistTransformation { private bool _IsValidScope = true; private List _ControlOperations; - private QsCallable _CurrentCallable = null; private ImmutableArray>> _CurrentHoistParams = ImmutableArray>>.Empty; private bool _ContainsHoistParamRef = false; + private class CallableInfo + { + public QsCallable Callable; + public QsSpecialization Adjoint; + public QsSpecialization Controlled; + public QsSpecialization ControlledAdjoint; + public QsNullable> TypeParamTypes; + + public CallableInfo(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 CallableInfo _CurrentCallable = null; private bool _InBody = false; private bool _InAdjoint = false; private bool _InControlled = false; @@ -511,26 +539,24 @@ public static QsCompilation Apply(QsCompilation compilation) return new QsCompilation(compilation.Namespaces.Select(ns => filter.Transform(ns)).ToImmutableArray(), compilation.EntryPoints); } - private QsSpecialization MakeSpecialization(QsSpecializationKind kind, QsQualifiedName parentName, ResolvedSignature signature, SpecializationImplementation impl) => - new QsSpecialization( - kind, - parentName, - ImmutableArray.Empty, - _CurrentCallable.SourceFile, - QsNullable.Null, - QsNullable>.Null, - signature, - impl, - ImmutableArray.Empty, - QsComments.Empty); - - private IEnumerable GetFunctorSpecializations(QsQualifiedName parentName, ResolvedSignature parentSignature) + private (ResolvedSignature, IEnumerable) MakeSpecializations(QsQualifiedName callableName, ResolvedType argsType, SpecializationImplementation bodyImplementation) { - var adj = _CurrentCallable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsAdjoint); - var ctl = _CurrentCallable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlled); - var ctlAdj = _CurrentCallable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlledAdjoint); + 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 specializations = new List(); + var adj = _CurrentCallable.Adjoint; + var ctl = _CurrentCallable.Controlled; + var ctlAdj = _CurrentCallable.ControlledAdjoint; // ToDo: this Boolean logic could be cleaned up bool addAdjoint = false; @@ -566,27 +592,36 @@ private IEnumerable GetFunctorSpecializations(QsQualifiedName } } + 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 specializations = new List() { MakeSpec(QsSpecializationKind.QsBody, newSig, bodyImplementation) }; + if (addAdjoint) { - specializations.Add(MakeSpecialization( + specializations.Add(MakeSpec( QsSpecializationKind.QsAdjoint, - parentName, - parentSignature, + newSig, SpecializationImplementation.NewGenerated(QsGeneratorDirective.InvalidGenerator))); // ToDo: find appropriate directive } if (addControlled) { - specializations.Add(MakeSpecialization( + specializations.Add(MakeSpec( QsSpecializationKind.QsControlled, - parentName, new ResolvedSignature( - parentSignature.TypeParameters, + newSig.TypeParameters, ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Create( ResolvedType.New(ResolvedTypeKind.NewArrayType(ResolvedType.New(ResolvedTypeKind.Qubit))), - parentSignature.ArgumentType))), - parentSignature.ReturnType, - parentSignature.Information), + newSig.ArgumentType))), + newSig.ReturnType, + newSig.Information), SpecializationImplementation.NewGenerated(QsGeneratorDirective.InvalidGenerator))); // ToDo: find appropriate directive } @@ -595,14 +630,14 @@ private IEnumerable GetFunctorSpecializations(QsQualifiedName // // add 'generated' implementation for ControlledAdjoint //} - return specializations; + return (newSig, specializations); } private (QsQualifiedName, ResolvedType) GenerateOperation(QsScope contents) { var newName = new QsQualifiedName( - _CurrentCallable.FullName.Namespace, - NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _CurrentCallable.FullName.Name.Value)); + _CurrentCallable.Callable.FullName.Namespace, + NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + _CurrentCallable.Callable.FullName.Name.Value)); var knownVariables = contents.KnownSymbols.IsEmpty ? ImmutableArray>>.Empty @@ -629,34 +664,22 @@ private IEnumerable GetFunctorSpecializations(QsQualifiedName .ToImmutableArray())); } - var signature = new ResolvedSignature( - _CurrentCallable.Signature.TypeParameters, - paramTypes, - ResolvedType.New(ResolvedTypeKind.UnitType), - CallableInformation.NoInformation); // ToDo: this information should have the supported functors in it - - var body = MakeSpecialization( - QsSpecializationKind.QsBody, - newName, - signature, - SpecializationImplementation.NewProvided(parameters, contents)); + var (signature, specializations) = MakeSpecializations(newName, paramTypes, SpecializationImplementation.NewProvided(parameters, contents)); var controlCallable = new QsCallable( QsCallableKind.Operation, newName, ImmutableArray.Empty, - _CurrentCallable.SourceFile, + _CurrentCallable.Callable.SourceFile, QsNullable.Null, signature, parameters, - new List() { body } - .Concat(GetFunctorSpecializations(newName, signature)) - .ToImmutableArray(), + specializations.ToImmutableArray(), ImmutableArray.Empty, QsComments.Empty); - var reroutedCallable = UpdateGeneratedOpTransformation.Apply(controlCallable, knownVariables, _CurrentCallable.FullName, newName); - _ControlOperations.Add(reroutedCallable); + var updatedCallable = UpdateGeneratedOpTransformation.Apply(controlCallable, knownVariables, _CurrentCallable.Callable.FullName, newName); + _ControlOperations.Add(updatedCallable); return (newName, paramTypes); } @@ -675,7 +698,7 @@ public HoistSyntax(HoistTransformation super, ScopeTransformation> GetTypeParamTypesFromCallable(QsCallable callable) - { - if (callable.Signature.TypeParameters.Any(param => param.IsValidName)) - { - return 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()); - } - else - { - return QsNullable>.Null; - } - } + //private QsNullable> GetTypeParamTypesFromCallable(QsCallable callable) + //{ + // if (callable.Signature.TypeParameters.Any(param => param.IsValidName)) + // { + // return 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()); + // } + // else + // { + // return QsNullable>.Null; + // } + //} public override QsStatementKind onReturnStatement(TypedExpression ex) { From 6dcbd6abaeb399cfcf04998b7607499c5da0663d Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 7 Jan 2020 17:44:34 -0800 Subject: [PATCH 23/53] The generated operations now correctly have functor support, and the functor-appropriate ApplyIf operation is used to call them. --- .../Compiler/ExternalRewriteSteps.cs | 442 +++++++++--------- src/QsCompiler/Core/Dependencies.fs | 117 ++++- .../ClassicallyControlledTransformation.cs | 217 ++++++--- 3 files changed, 460 insertions(+), 316 deletions(-) diff --git a/src/QsCompiler/Compiler/ExternalRewriteSteps.cs b/src/QsCompiler/Compiler/ExternalRewriteSteps.cs index 6fb29eac52..a8cfafd7a1 100644 --- a/src/QsCompiler/Compiler/ExternalRewriteSteps.cs +++ b/src/QsCompiler/Compiler/ExternalRewriteSteps.cs @@ -1,221 +1,221 @@ -// 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 Microsoft.Quantum.QsCompiler.CompilationBuilder; -using Microsoft.Quantum.QsCompiler.Diagnostics; -using Microsoft.Quantum.QsCompiler.ReservedKeywords; -using Microsoft.Quantum.QsCompiler.SyntaxTree; -using Microsoft.VisualStudio.LanguageServer.Protocol; - - -namespace Microsoft.Quantum.QsCompiler -{ - internal static class RewriteSteps - { - /// - /// Concrete implementation of a rewrite steps with an additional property specifying the dll it was loaded from. - /// - internal class LoadedStep : IRewriteStep - { - internal readonly Uri Origin; - private readonly IRewriteStep _SelfAsStep; - private readonly object _SelfAsObject; - - private readonly MethodInfo[] _InterfaceMethods; - private MethodInfo InterfaceMethod(string name) => - // This choice of filtering the interface methods may seem a bit particular. - // However, unless you know what you are doing, please don't change it. - // If you are sure you know what you are doing, please make sure the loading via reflection works for rewrite steps - // implemented in both F# or C#, and whether they are compiled against the current compiler version or an older one. - this._InterfaceMethods?.FirstOrDefault(method => method.Name.Split("-").Last() == name); - - private T GetViaReflection(string name) => - (T)InterfaceMethod($"get_{name}")?.Invoke(_SelfAsObject, null); - - private void SetViaReflection(string name, T arg) => - InterfaceMethod($"set_{name}")?.Invoke(_SelfAsObject, new object[] { arg }); - - private T InvokeViaReflection(string name, params object[] args) => - (T)InterfaceMethod(name)?.Invoke(_SelfAsObject, args); - - - /// - /// Attempts to construct a rewrite step via reflection. - /// Note that the loading via reflection has the consequence that methods may fail on execution. - /// This is e.g. the case if they invoke methods from package references if the corresponding dll - /// has not been copied to output folder of the dll from which the rewrite step is loaded. - /// Throws the corresponding exception if that construction fails. - /// - internal LoadedStep(object implementation, Type interfaceType, Uri origin) - { - this.Origin = origin ?? throw new ArgumentNullException(nameof(origin)); - this._SelfAsObject = implementation ?? throw new ArgumentNullException(nameof(implementation)); - - // Initializing the _InterfaceMethods even if the implementation implements IRewriteStep - // would result in certain properties being loaded via reflection instead of simply being accessed via _SelfAsStep. - if (this._SelfAsObject is IRewriteStep step) this._SelfAsStep = step; - else this._InterfaceMethods = implementation.GetType().GetInterfaceMap(interfaceType).TargetMethods; - - // The Name and Priority need to be fixed throughout the loading, - // so whatever their value is when loaded that's what these values well be as far at the compiler is concerned. - this.Name = _SelfAsStep?.Name ?? this.GetViaReflection(nameof(IRewriteStep.Name)); - this.Priority = _SelfAsStep?.Priority ?? this.GetViaReflection(nameof(IRewriteStep.Priority)); - } - - public string Name { get; } - public int Priority { get; } - public IDictionary AssemblyConstants - { - get => _SelfAsStep?.AssemblyConstants - ?? this.GetViaReflection>(nameof(IRewriteStep.AssemblyConstants)); - } - - public bool ImplementsTransformation - { - get => _SelfAsStep?.ImplementsTransformation - ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsTransformation)); - } - - public bool ImplementsPreconditionVerification - { - get => _SelfAsStep?.ImplementsPreconditionVerification - ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsPreconditionVerification)); - } - - public bool ImplementsPostconditionVerification - { - get => _SelfAsStep?.ImplementsPostconditionVerification - ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsPostconditionVerification)); - } - - public bool Transformation(QsCompilation compilation, out QsCompilation transformed) - { - if (_SelfAsStep != null) return _SelfAsStep.Transformation(compilation, out transformed); - var args = new object[] { compilation, null }; - var success = this.InvokeViaReflection(nameof(IRewriteStep.Transformation), args); - transformed = success ? (QsCompilation)args[1] : compilation; - return success; - } - - public bool PreconditionVerification(QsCompilation compilation) => - _SelfAsStep?.PreconditionVerification(compilation) - ?? this.InvokeViaReflection(nameof(IRewriteStep.PreconditionVerification), compilation); - - public bool PostconditionVerification(QsCompilation compilation) => - _SelfAsStep?.PostconditionVerification(compilation) - ?? this.InvokeViaReflection(nameof(IRewriteStep.PostconditionVerification), compilation); - } - - - /// - /// Loads all dlls listed as containing rewrite steps to include in the compilation process in the given configuration. - /// Generates suitable diagnostics if a listed file can't be found or loaded. - /// Finds all types implementing the IRewriteStep interface and loads the corresponding rewrite steps - /// according to the specified priority, where a steps with a higher priority will be listed first in the returned array. - /// If the function onDiagnostic is specified and not null, calls it on all generated diagnostics, - /// and calls onException on all caught exceptions if it is specified and not null. - /// Returns an empty array if the rewrite steps in the given configurations are set to null. - /// - internal static ImmutableArray Load(CompilationLoader.Configuration config, - Action onDiagnostic = null, Action onException = null) - { - if (config.RewriteSteps == null) return ImmutableArray.Empty; - Uri WithFullPath(string file) - { - try - { - return String.IsNullOrWhiteSpace(file) ? null : new Uri(Path.GetFullPath(file)); - } - catch (Exception ex) - { - onDiagnostic?.Invoke(Errors.LoadError(ErrorCode.InvalidFilePath, new[] { file }, file)); - onException?.Invoke(ex); - return null; - } - } - - var specifiedPluginDlls = config.RewriteSteps.Select(step => (WithFullPath(step.Item1), step.Item2)).Where(step => step.Item1 != null).ToList(); - var (foundDlls, notFoundDlls) = specifiedPluginDlls.Partition(step => File.Exists(step.Item1.LocalPath)); - foreach (var file in notFoundDlls.Select(step => step.Item1).Distinct()) - { - onDiagnostic?.Invoke(Errors.LoadError(ErrorCode.UnknownCompilerPlugin, new[] { file.LocalPath }, file.LocalPath)); - } - - var rewriteSteps = ImmutableArray.CreateBuilder(); - foreach (var (target, outputFolder) in foundDlls) - { - var relevantTypes = new List(); - Diagnostic LoadError(ErrorCode code, params string[] args) => Errors.LoadError(code, args, ProjectManager.MessageSource(target)); - Diagnostic LoadWarning(WarningCode code, params string[] args) => Warnings.LoadWarning(code, args, ProjectManager.MessageSource(target)); - try - { - var interfaceMatch = Assembly.LoadFrom(target.LocalPath).GetTypes().Where( - t => typeof(IRewriteStep).IsAssignableFrom(t) || // inherited interface is defined in this exact dll - t.GetInterfaces().Any(t => t.FullName == typeof(IRewriteStep).FullName)); // inherited interface may be defined in older compiler version - relevantTypes.AddRange(interfaceMatch); - } - catch (BadImageFormatException ex) - { - onDiagnostic?.Invoke(LoadError(ErrorCode.FileIsNotAnAssembly, target.LocalPath)); - onException?.Invoke(ex); - } - catch (Exception ex) - { - onDiagnostic?.Invoke(LoadError(ErrorCode.CouldNotLoadCompilerPlugin, target.LocalPath)); - onException?.Invoke(ex); - } - var loadedSteps = new List(); - foreach (var type in relevantTypes) - { - try - { - var instance = Activator.CreateInstance(type); - if (instance is IRewriteStep step) - { - loadedSteps.Add(new LoadedStep(step, typeof(IRewriteStep), target)); - continue; - } - - try // we also try to load rewrite steps that have been compiled against a different compiler version - { - var interfaceType = type.GetInterfaces().First(t => t.FullName == typeof(IRewriteStep).FullName); - var loadedStep = new LoadedStep(instance, interfaceType, target); - onDiagnostic?.Invoke(LoadWarning(WarningCode.RewriteStepLoadedViaReflection, loadedStep.Name, target.LocalPath)); - loadedSteps.Add(loadedStep); - } - catch // we don't log the exception, since it is perfectly possible that we should have ignored this type in the first place - { - onDiagnostic?.Invoke(LoadWarning(WarningCode.FailedToLoadRewriteStepViaReflection, target.LocalPath)); - } - } - catch (Exception ex) - { - onDiagnostic?.Invoke(LoadError(ErrorCode.CouldNotInstantiateRewriteStep, type.ToString(), target.LocalPath)); - onException?.Invoke(ex); - } - } - foreach (var loaded in loadedSteps) - { - var assemblyConstants = loaded.AssemblyConstants; - if (assemblyConstants == null) continue; - foreach (var kvPair in config.AssemblyConstants ?? Enumerable.Empty>()) - { assemblyConstants[kvPair.Key] = kvPair.Value; } - - var defaultOutput = assemblyConstants.TryGetValue(AssemblyConstants.OutputPath, out var path) ? path : null; - assemblyConstants[AssemblyConstants.OutputPath] = outputFolder ?? defaultOutput ?? config.BuildOutputFolder; - assemblyConstants[AssemblyConstants.AssemblyName] = config.ProjectNameWithoutExtension; - } - - loadedSteps.Sort((fst, snd) => snd.Priority - fst.Priority); - rewriteSteps.AddRange(loadedSteps); - } - return rewriteSteps.ToImmutableArray(); - } - } -} +// 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 Microsoft.Quantum.QsCompiler.CompilationBuilder; +using Microsoft.Quantum.QsCompiler.Diagnostics; +using Microsoft.Quantum.QsCompiler.ReservedKeywords; +using Microsoft.Quantum.QsCompiler.SyntaxTree; +using Microsoft.VisualStudio.LanguageServer.Protocol; + + +namespace Microsoft.Quantum.QsCompiler +{ + internal static class RewriteSteps + { + /// + /// Concrete implementation of a rewrite steps with an additional property specifying the dll it was loaded from. + /// + internal class LoadedStep : IRewriteStep + { + internal readonly Uri Origin; + private readonly IRewriteStep _SelfAsStep; + private readonly object _SelfAsObject; + + private readonly MethodInfo[] _InterfaceMethods; + private MethodInfo InterfaceMethod(string name) => + // This choice of filtering the interface methods may seem a bit particular. + // However, unless you know what you are doing, please don't change it. + // If you are sure you know what you are doing, please make sure the loading via reflection works for rewrite steps + // implemented in both F# or C#, and whether they are compiled against the current compiler version or an older one. + this._InterfaceMethods?.FirstOrDefault(method => method.Name.Split("-").Last() == name); + + private T GetViaReflection(string name) => + (T)InterfaceMethod($"get_{name}")?.Invoke(_SelfAsObject, null); + + private void SetViaReflection(string name, T arg) => + InterfaceMethod($"set_{name}")?.Invoke(_SelfAsObject, new object[] { arg }); + + private T InvokeViaReflection(string name, params object[] args) => + (T)InterfaceMethod(name)?.Invoke(_SelfAsObject, args); + + + /// + /// Attempts to construct a rewrite step via reflection. + /// Note that the loading via reflection has the consequence that methods may fail on execution. + /// This is e.g. the case if they invoke methods from package references if the corresponding dll + /// has not been copied to output folder of the dll from which the rewrite step is loaded. + /// Throws the corresponding exception if that construction fails. + /// + internal LoadedStep(object implementation, Type interfaceType, Uri origin) + { + this.Origin = origin ?? throw new ArgumentNullException(nameof(origin)); + this._SelfAsObject = implementation ?? throw new ArgumentNullException(nameof(implementation)); + + // Initializing the _InterfaceMethods even if the implementation implements IRewriteStep + // would result in certain properties being loaded via reflection instead of simply being accessed via _SelfAsStep. + if (this._SelfAsObject is IRewriteStep step) this._SelfAsStep = step; + else this._InterfaceMethods = implementation.GetType().GetInterfaceMap(interfaceType).TargetMethods; + + // The Name and Priority need to be fixed throughout the loading, + // so whatever their value is when loaded that's what these values well be as far at the compiler is concerned. + this.Name = _SelfAsStep?.Name ?? this.GetViaReflection(nameof(IRewriteStep.Name)); + this.Priority = _SelfAsStep?.Priority ?? this.GetViaReflection(nameof(IRewriteStep.Priority)); + } + + public string Name { get; } + public int Priority { get; } + public IDictionary AssemblyConstants + { + get => _SelfAsStep?.AssemblyConstants + ?? this.GetViaReflection>(nameof(IRewriteStep.AssemblyConstants)); + } + + public bool ImplementsTransformation + { + get => _SelfAsStep?.ImplementsTransformation + ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsTransformation)); + } + + public bool ImplementsPreconditionVerification + { + get => _SelfAsStep?.ImplementsPreconditionVerification + ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsPreconditionVerification)); + } + + public bool ImplementsPostconditionVerification + { + get => _SelfAsStep?.ImplementsPostconditionVerification + ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsPostconditionVerification)); + } + + public bool Transformation(QsCompilation compilation, out QsCompilation transformed) + { + if (_SelfAsStep != null) return _SelfAsStep.Transformation(compilation, out transformed); + var args = new object[] { compilation, null }; + var success = this.InvokeViaReflection(nameof(IRewriteStep.Transformation), args); + transformed = success ? (QsCompilation)args[1] : compilation; + return success; + } + + public bool PreconditionVerification(QsCompilation compilation) => + _SelfAsStep?.PreconditionVerification(compilation) + ?? this.InvokeViaReflection(nameof(IRewriteStep.PreconditionVerification), compilation); + + public bool PostconditionVerification(QsCompilation compilation) => + _SelfAsStep?.PostconditionVerification(compilation) + ?? this.InvokeViaReflection(nameof(IRewriteStep.PostconditionVerification), compilation); + } + + + /// + /// Loads all dlls listed as containing rewrite steps to include in the compilation process in the given configuration. + /// Generates suitable diagnostics if a listed file can't be found or loaded. + /// Finds all types implementing the IRewriteStep interface and loads the corresponding rewrite steps + /// according to the specified priority, where a steps with a higher priority will be listed first in the returned array. + /// If the function onDiagnostic is specified and not null, calls it on all generated diagnostics, + /// and calls onException on all caught exceptions if it is specified and not null. + /// Returns an empty array if the rewrite steps in the given configurations are set to null. + /// + internal static ImmutableArray Load(CompilationLoader.Configuration config, + Action onDiagnostic = null, Action onException = null) + { + if (config.RewriteSteps == null) return ImmutableArray.Empty; + Uri WithFullPath(string file) + { + try + { + return String.IsNullOrWhiteSpace(file) ? null : new Uri(Path.GetFullPath(file)); + } + catch (Exception ex) + { + onDiagnostic?.Invoke(Errors.LoadError(ErrorCode.InvalidFilePath, new[] { file }, file)); + onException?.Invoke(ex); + return null; + } + } + + var specifiedPluginDlls = config.RewriteSteps.Select(step => (WithFullPath(step.Item1), step.Item2)).Where(step => step.Item1 != null).ToList(); + var (foundDlls, notFoundDlls) = specifiedPluginDlls.Partition(step => File.Exists(step.Item1.LocalPath)); + foreach (var file in notFoundDlls.Select(step => step.Item1).Distinct()) + { + onDiagnostic?.Invoke(Errors.LoadError(ErrorCode.UnknownCompilerPlugin, new[] { file.LocalPath }, file.LocalPath)); + } + + var rewriteSteps = ImmutableArray.CreateBuilder(); + foreach (var (target, outputFolder) in foundDlls) + { + var relevantTypes = new List(); + Diagnostic LoadError(ErrorCode code, params string[] args) => Errors.LoadError(code, args, ProjectManager.MessageSource(target)); + Diagnostic LoadWarning(WarningCode code, params string[] args) => Warnings.LoadWarning(code, args, ProjectManager.MessageSource(target)); + try + { + var interfaceMatch = Assembly.LoadFrom(target.LocalPath).GetTypes().Where( + t => typeof(IRewriteStep).IsAssignableFrom(t) || // inherited interface is defined in this exact dll + t.GetInterfaces().Any(t => t.FullName == typeof(IRewriteStep).FullName)); // inherited interface may be defined in older compiler version + relevantTypes.AddRange(interfaceMatch); + } + catch (BadImageFormatException ex) + { + onDiagnostic?.Invoke(LoadError(ErrorCode.FileIsNotAnAssembly, target.LocalPath)); + onException?.Invoke(ex); + } + catch (Exception ex) + { + onDiagnostic?.Invoke(LoadError(ErrorCode.CouldNotLoadCompilerPlugin, target.LocalPath)); + onException?.Invoke(ex); + } + var loadedSteps = new List(); + foreach (var type in relevantTypes) + { + try + { + var instance = Activator.CreateInstance(type); + if (instance is IRewriteStep step) + { + loadedSteps.Add(new LoadedStep(step, typeof(IRewriteStep), target)); + continue; + } + + try // we also try to load rewrite steps that have been compiled against a different compiler version + { + var interfaceType = type.GetInterfaces().First(t => t.FullName == typeof(IRewriteStep).FullName); + var loadedStep = new LoadedStep(instance, interfaceType, target); + onDiagnostic?.Invoke(LoadWarning(WarningCode.RewriteStepLoadedViaReflection, loadedStep.Name, target.LocalPath)); + loadedSteps.Add(loadedStep); + } + catch // we don't log the exception, since it is perfectly possible that we should have ignored this type in the first place + { + onDiagnostic?.Invoke(LoadWarning(WarningCode.FailedToLoadRewriteStepViaReflection, target.LocalPath)); + } + } + catch (Exception ex) + { + onDiagnostic?.Invoke(LoadError(ErrorCode.CouldNotInstantiateRewriteStep, type.ToString(), target.LocalPath)); + onException?.Invoke(ex); + } + } + foreach (var loaded in loadedSteps) + { + var assemblyConstants = loaded.AssemblyConstants; + if (assemblyConstants == null) continue; + foreach (var kvPair in config.AssemblyConstants ?? Enumerable.Empty>()) + { assemblyConstants[kvPair.Key] = kvPair.Value; } + + var defaultOutput = assemblyConstants.TryGetValue(AssemblyConstants.OutputPath, out var path) ? path : null; + assemblyConstants[AssemblyConstants.OutputPath] = outputFolder ?? defaultOutput ?? config.BuildOutputFolder; + assemblyConstants[AssemblyConstants.AssemblyName] = config.ProjectNameWithoutExtension; + } + + loadedSteps.Sort((fst, snd) => snd.Priority - fst.Priority); + rewriteSteps.AddRange(loadedSteps); + } + return rewriteSteps.ToImmutableArray(); + } + } +} diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index eb0cb06f17..00f48542c0 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -87,7 +87,7 @@ type BuiltIn = { TypeParameters = ImmutableArray.Empty } - static member private _MakeResolvedType builtIn = + static member private _MakeResolvedType builtIn props = let typeParamT = { Origin = {Namespace = builtIn.Namespace; Name = builtIn.Name}; @@ -95,6 +95,12 @@ type BuiltIn = { Range = QsRangeInfo.Null } |> TypeParameter |> ResolvedType.New + let characteristics = + { + Characteristics = ResolvedCharacteristics.FromProperties props; + InferredInformation = InferredCallableInformation.NoInformation + } + let args = [ Result |> ResolvedType.New; @@ -104,7 +110,7 @@ type BuiltIn = { typeParamT, UnitType |> ResolvedType.New ), - CallableInformation.NoInformation + characteristics ) |> Operation |> ResolvedType.New; typeParamT ].ToImmutableArray() |> TupleType |> ResolvedType.New @@ -115,35 +121,74 @@ type BuiltIn = { args, ResolvedType.New UnitType ), - CallableInformation.NoInformation + characteristics ) |> Operation |> ResolvedType.New // This is expected to have type <'T>((Result, (('T => Unit), 'T)) => Unit) - static member ApplyIfOne = { - Name = "ApplyIfOne" |> NonNullable.New + static member ApplyIfZero = { + Name = "ApplyIfZero" |> NonNullable.New + Namespace = BuiltIn.ClassicallyControlledNamespace + TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + } + static member ApplyIfZeroResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfZero [] + + // 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) } + static member ApplyIfZeroAResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfZeroA [OpProperty.Adjointable] - static member ApplyIfOneResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfOne + // 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) + } + static member ApplyIfZeroCResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfZeroC [OpProperty.Controllable] + + // 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) + } + static member ApplyIfZeroCAResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfZeroCA [OpProperty.Adjointable; OpProperty.Controllable] // This is expected to have type <'T>((Result, (('T => Unit), 'T)) => Unit) - static member ApplyIfZero = { - Name = "ApplyIfZero" |> NonNullable.New + static member ApplyIfOne = { + Name = "ApplyIfOne" |> NonNullable.New Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) } + static member ApplyIfOneResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfOne [] - static member ApplyIfZeroResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfZero + // 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) + } + static member ApplyIfOneAResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfOneA [OpProperty.Adjointable] - // This is expected to have type <'T, 'U>((Result, (('T => Unit), 'T), (('U => Unit), 'U)) => Unit) - static member ApplyIfElseR = { - Name = "ApplyIfElseR" |> 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, "U" |> NonNullable.New) + TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) } + static member ApplyIfOneCResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfOneC [OpProperty.Controllable] - static member private _MakeApplyIfElseResolvedType builtIn = + // 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) + } + static member ApplyIfOneCAResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfOneCA [OpProperty.Adjointable; OpProperty.Controllable] + + static member private _MakeApplyIfElseResolvedType builtIn props = let typeParamT = { Origin = {Namespace = builtIn.Namespace; Name = builtIn.Name}; @@ -158,6 +203,12 @@ type BuiltIn = { Range = QsRangeInfo.Null } |> TypeParameter |> ResolvedType.New + let characteristics = + { + Characteristics = ResolvedCharacteristics.FromProperties props; + InferredInformation = InferredCallableInformation.NoInformation + } + let args = [ Result |> ResolvedType.New; @@ -167,7 +218,7 @@ type BuiltIn = { typeParamT, UnitType |> ResolvedType.New ), - CallableInformation.NoInformation + characteristics ) |> Operation |> ResolvedType.New; typeParamT ].ToImmutableArray() |> TupleType |> ResolvedType.New; @@ -177,7 +228,7 @@ type BuiltIn = { typeParamU, UnitType |> ResolvedType.New ), - CallableInformation.NoInformation + characteristics ) |> Operation |> ResolvedType.New; typeParamU ].ToImmutableArray() |> TupleType |> ResolvedType.New @@ -188,10 +239,40 @@ type BuiltIn = { args, ResolvedType.New UnitType ), - CallableInformation.NoInformation + characteristics ) |> Operation |> ResolvedType.New - static member ApplyIfElseRResolvedType = BuiltIn._MakeApplyIfElseResolvedType BuiltIn.ApplyIfElseR + // 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) + } + static member ApplyIfElseRResolvedType = BuiltIn._MakeApplyIfElseResolvedType BuiltIn.ApplyIfElseR [] + + // 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) + } + static member ApplyIfElseRAResolvedType = BuiltIn._MakeApplyIfElseResolvedType BuiltIn.ApplyIfElseRA [OpProperty.Adjointable] + + // 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) + } + static member ApplyIfElseRCResolvedType = BuiltIn._MakeApplyIfElseResolvedType BuiltIn.ApplyIfElseRC [OpProperty.Controllable] + + // 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 ApplyIfElseCA = { + Name = "ApplyIfElseCA" |> NonNullable.New + Namespace = BuiltIn.ClassicallyControlledNamespace + TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) + } + static member ApplyIfElseCAResolvedType = BuiltIn._MakeApplyIfElseResolvedType BuiltIn.ApplyIfElseCA [OpProperty.Adjointable; OpProperty.Controllable] // "weak dependencies" in other namespaces (e.g. things used for code actions) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 75f95876c7..d7c2b4ebc3 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.FSharp.Core; using Microsoft.Quantum.QsCompiler.DataTypes; +using Microsoft.Quantum.QsCompiler.Documentation; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; @@ -96,51 +97,46 @@ private class ClassicallyControlledScope : ScopeTransformation (x.Item1, x.Item2), x => x.Item3); - foreach (var arg in idTypeArguments) - { - mapping[(arg.Item1, arg.Item2)] = arg.Item3; - } - var combinedTypeArguments = mapping.Select(kvp => Tuple.Create(kvp.Key.Item1, kvp.Key.Item2, kvp.Value)).ToImmutableArray(); - - var newExpr1 = expr.Item; - // ToDo: shouldn't rely on expr1 being identifier - if (combinedTypeArguments.Any() && call.Item1.Expression is ExpressionKind.Identifier id) - { - newExpr1 = new TypedExpression( - ExpressionKind.NewIdentifier( - id.Item1, - QsNullable>.NewValue(combinedTypeArguments - .Select(arg => arg.Item3) - .ToImmutableArray())), - combinedTypeArguments, - ResolvedType.New(ResolvedTypeKind.UnitType), // ToDo: This is really wrong - // Need to replace all type-param types in resolved type with their resolution - // I hope this does not involve a whole new transformation :( - //call.Item1.ResolvedType, - call.Item1.InferredInformation, - call.Item1.Range); - } + // Merge the two lists into one list with distinct argument mappings, + // giving preference to the id's type arguments. + var mapping = callTypeArguments.ToDictionary(x => (x.Item1, x.Item2), x => x.Item3); + foreach (var arg in idTypeArguments) + { + mapping[(arg.Item1, arg.Item2)] = arg.Item3; + } + var combinedTypeArguments = mapping.Select(kvp => Tuple.Create(kvp.Key.Item1, kvp.Key.Item2, kvp.Value)).ToImmutableArray(); - return (true, newExpr1, call.Item2); - } + var newExpr1 = call.Item1; + // ToDo: shouldn't rely on expr1 being identifier + if (combinedTypeArguments.Any() && newExpr1.Expression is ExpressionKind.Identifier id) + { + newExpr1 = new TypedExpression( + ExpressionKind.NewIdentifier( + id.Item1, + QsNullable>.NewValue(combinedTypeArguments + .Select(arg => arg.Item3) + .ToImmutableArray())), + combinedTypeArguments, + ResolvedType.New(ResolvedTypeKind.UnitType), // ToDo: This is really wrong + // Need to replace all type-param types in resolved type with their resolution + // I hope this does not involve a whole new transformation :( + //call.Item1.ResolvedType, + call.Item1.InferredInformation, + call.Item1.Range); } + + return (true, newExpr1, call.Item2); } return (false, null, null); @@ -187,33 +183,83 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co ResolvedType controlOpType; TypedExpression controlArgs; ImmutableArray targetArgs; - if (isCondValid && isDefaultValid) + if (isCondValid) { - controlOpInfo = BuiltIn.ApplyIfElseR; - controlOpType = BuiltIn.ApplyIfElseRResolvedType; + var props = GetCharacteristicsFromGlobalId(condId); + (bool adj, bool ctl) = (props.Contains(OpProperty.Adjointable), props.Contains(OpProperty.Controllable)); - controlArgs = Helper.CreateValueTupleExpression( - conditionExpression, - Helper.CreateValueTupleExpression(condId, condArgs), - Helper.CreateValueTupleExpression(defaultId, defaultArgs)); + if (isDefaultValid) + { + if (adj && ctl) + { + controlOpInfo = BuiltIn.ApplyIfElseCA; + controlOpType = BuiltIn.ApplyIfElseCAResolvedType; + } + else if (adj) + { + controlOpInfo = BuiltIn.ApplyIfElseRA; + controlOpType = BuiltIn.ApplyIfElseRAResolvedType; + } + else if (ctl) + { + controlOpInfo = BuiltIn.ApplyIfElseRC; + controlOpType = BuiltIn.ApplyIfElseRCResolvedType; + } + else + { + controlOpInfo = BuiltIn.ApplyIfElseR; + controlOpType = BuiltIn.ApplyIfElseRResolvedType; + } - targetArgs = ImmutableArray.Create(condArgs.ResolvedType, defaultArgs.ResolvedType); - } - else if (isCondValid && defaultScope == null) - { - (controlOpInfo, controlOpType) = (result == QsResult.One) - ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) - : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); + controlArgs = Helper.CreateValueTupleExpression( + conditionExpression, + Helper.CreateValueTupleExpression(condId, condArgs), + Helper.CreateValueTupleExpression(defaultId, defaultArgs)); - controlArgs = Helper.CreateValueTupleExpression( - conditionExpression, - Helper.CreateValueTupleExpression(condId, condArgs)); + targetArgs = ImmutableArray.Create(condArgs.ResolvedType, defaultArgs.ResolvedType); + } + else if (defaultScope == null) + { + if (adj && ctl) + { + (controlOpInfo, controlOpType) = (result == QsResult.One) + ? (BuiltIn.ApplyIfOneCA, BuiltIn.ApplyIfOneCAResolvedType) + : (BuiltIn.ApplyIfZeroCA, BuiltIn.ApplyIfZeroCAResolvedType); + } + else if (adj) + { + (controlOpInfo, controlOpType) = (result == QsResult.One) + ? (BuiltIn.ApplyIfOneA, BuiltIn.ApplyIfOneAResolvedType) + : (BuiltIn.ApplyIfZeroA, BuiltIn.ApplyIfZeroAResolvedType); + } + else if (ctl) + { + (controlOpInfo, controlOpType) = (result == QsResult.One) + ? (BuiltIn.ApplyIfOneC, BuiltIn.ApplyIfOneCResolvedType) + : (BuiltIn.ApplyIfZeroC, BuiltIn.ApplyIfZeroCResolvedType); + } + else + { + (controlOpInfo, controlOpType) = (result == QsResult.One) + ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) + : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); + } + + controlArgs = Helper.CreateValueTupleExpression( + conditionExpression, + Helper.CreateValueTupleExpression(condId, condArgs)); + + targetArgs = ImmutableArray.Create(condArgs.ResolvedType); + } + else + { + return null; // ToDo: Diagnostic message - default body exists, but is not valid + } - targetArgs = ImmutableArray.Create(condArgs.ResolvedType); } else { - return null; + return null; // ToDo: Diagnostic message - cond body not valid } // Build the surrounding apply-if call @@ -229,6 +275,18 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co return CreateApplyIfCall(controlOpId, controlArgs/*, controlOpInfo, targetArgs*/); } + private ImmutableHashSet GetCharacteristicsFromGlobalId(TypedExpression globalId) + { + if (globalId.ResolvedType.Resolution is ResolvedTypeKind.Operation op) + { + return op.Item2.Characteristics.GetProperties(); + } + else + { + return ImmutableHashSet.Empty; + } + } + private (bool, QsConditionalStatement) ProcessElif(QsConditionalStatement cond) { if (cond.ConditionalBlocks.Length < 2) return (false, cond); @@ -498,7 +556,7 @@ private class HoistTransformation ImmutableArray>>.Empty; private bool _ContainsHoistParamRef = false; - private class CallableInfo + private class CallableDetails { public QsCallable Callable; public QsSpecialization Adjoint; @@ -506,7 +564,7 @@ private class CallableInfo public QsSpecialization ControlledAdjoint; public QsNullable> TypeParamTypes; - public CallableInfo(QsCallable callable) + public CallableDetails(QsCallable callable) { Callable = callable; Adjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsAdjoint); @@ -526,7 +584,7 @@ public CallableInfo(QsCallable callable) } } - private CallableInfo _CurrentCallable = null; + private CallableDetails _CurrentCallable = null; private bool _InBody = false; private bool _InAdjoint = false; private bool _InControlled = false; @@ -601,6 +659,14 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature 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) @@ -608,32 +674,29 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature specializations.Add(MakeSpec( QsSpecializationKind.QsAdjoint, newSig, - SpecializationImplementation.NewGenerated(QsGeneratorDirective.InvalidGenerator))); // ToDo: find appropriate directive + SpecializationImplementation.NewGenerated(QsGeneratorDirective.Invert))); // ToDo: find appropriate directive } if (addControlled) { specializations.Add(MakeSpec( QsSpecializationKind.QsControlled, - new ResolvedSignature( - newSig.TypeParameters, - ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Create( - ResolvedType.New(ResolvedTypeKind.NewArrayType(ResolvedType.New(ResolvedTypeKind.Qubit))), - newSig.ArgumentType))), - newSig.ReturnType, - newSig.Information), - SpecializationImplementation.NewGenerated(QsGeneratorDirective.InvalidGenerator))); // ToDo: find appropriate directive + controlledSig, + SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); // ToDo: find appropriate directive } //if (addControlledAdjoint) //{ - // // add 'generated' implementation for ControlledAdjoint + // specializations.Add(MakeSpec( + // QsSpecializationKind.QsControlledAdjoint, + // controlledSig, + // SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); // ToDo: find appropriate directive //} return (newSig, specializations); } - private (QsQualifiedName, ResolvedType) GenerateOperation(QsScope contents) + private (QsQualifiedName, ResolvedType, CallableInformation) GenerateOperation(QsScope contents) { var newName = new QsQualifiedName( _CurrentCallable.Callable.FullName.Namespace, @@ -681,7 +744,7 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature var updatedCallable = UpdateGeneratedOpTransformation.Apply(controlCallable, knownVariables, _CurrentCallable.Callable.FullName, newName); _ControlOperations.Add(updatedCallable); - return (newName, paramTypes); + return (newName, paramTypes, signature.Information); } private HoistTransformation() { } @@ -698,7 +761,7 @@ public HoistSyntax(HoistTransformation super, ScopeTransformation Date: Wed, 8 Jan 2020 12:36:06 -0800 Subject: [PATCH 24/53] Cleanup --- .../ClassicallyControlledTransformation.cs | 1643 ++++++++--------- 1 file changed, 787 insertions(+), 856 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index d7c2b4ebc3..b5e0933762 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -4,13 +4,8 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Dynamic; using System.Linq; -using System.Runtime.CompilerServices; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.FSharp.Core; using Microsoft.Quantum.QsCompiler.DataTypes; -using Microsoft.Quantum.QsCompiler.Documentation; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; @@ -20,43 +15,13 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTran using ExpressionKind = QsExpressionKind; using ResolvedTypeKind = QsTypeKind; - internal static class Helper - { - public static TypedExpression CreateIdentifierExpression(Identifier id, - ImmutableArray, ResolvedType>> 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 - ); - - public static TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => - new TypedExpression - ( - ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), - ImmutableArray, ResolvedType>>.Empty, - ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - } - // 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. public class ClassicallyControlledTransformation - { + { public static QsCompilation Apply(QsCompilation compilation) { var filter = new SyntaxTreeTransformation(new ClassicallyControlledScope()); @@ -66,8 +31,35 @@ public static QsCompilation Apply(QsCompilation compilation) return new QsCompilation(compilation.Namespaces.Select(ns => filter.Transform(ns)).ToImmutableArray(), compilation.EntryPoints); } - private ClassicallyControlledTransformation() { } - + private static TypedExpression CreateIdentifierExpression(Identifier id, + ImmutableArray, ResolvedType>> 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()), + ImmutableArray, ResolvedType>>.Empty, + ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + private ClassicallyControlledTransformation() { } + private class ClassicallyControlledScope : ScopeTransformation { public ClassicallyControlledScope(NoExpressionTransformations expr = null) : base(expr ?? new NoExpressionTransformations()) { } @@ -95,198 +87,185 @@ private class ClassicallyControlledScope : ScopeTransformation (x.Item1, x.Item2), x => x.Item3); - foreach (var arg in idTypeArguments) - { - mapping[(arg.Item1, arg.Item2)] = arg.Item3; - } - var combinedTypeArguments = mapping.Select(kvp => Tuple.Create(kvp.Key.Item1, kvp.Key.Item2, kvp.Value)).ToImmutableArray(); - - var newExpr1 = call.Item1; - // ToDo: shouldn't rely on expr1 being identifier - if (combinedTypeArguments.Any() && newExpr1.Expression is ExpressionKind.Identifier id) - { - newExpr1 = new TypedExpression( - ExpressionKind.NewIdentifier( - id.Item1, - QsNullable>.NewValue(combinedTypeArguments - .Select(arg => arg.Item3) - .ToImmutableArray())), - combinedTypeArguments, - ResolvedType.New(ResolvedTypeKind.UnitType), // ToDo: This is really wrong - // Need to replace all type-param types in resolved type with their resolution - // I hope this does not involve a whole new transformation :( - //call.Item1.ResolvedType, - call.Item1.InferredInformation, - call.Item1.Range); - } - - return (true, newExpr1, call.Item2); - } - - return (false, null, null); + 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) + { + // 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; + + // Merge the two lists into one list with distinct argument mappings, + // giving preference to the id's type arguments. + var mapping = callTypeArguments.ToDictionary(x => (x.Item1, x.Item2), x => x.Item3); + foreach (var arg in idTypeArguments) + { + mapping[(arg.Item1, arg.Item2)] = arg.Item3; + } + var combinedTypeArguments = mapping.Select(kvp => Tuple.Create(kvp.Key.Item1, kvp.Key.Item2, kvp.Value)).ToImmutableArray(); + + var newExpr1 = call.Item1; + // ToDo: shouldn't rely on expr1 being identifier + if (combinedTypeArguments.Any() && newExpr1.Expression is ExpressionKind.Identifier id) + { + newExpr1 = new TypedExpression( + ExpressionKind.NewIdentifier( + id.Item1, + QsNullable>.NewValue(combinedTypeArguments + .Select(arg => arg.Item3) + .ToImmutableArray())), + combinedTypeArguments, + ResolvedType.New(ResolvedTypeKind.UnitType), // ToDo: This is really wrong + // Need to replace all type-param types in resolved type with their resolution + // I hope this does not involve a whole new transformation :( + //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/*, BuiltIn controlOp, IEnumerable opTypeParamResolutions*/) => + private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args) => new TypedExpression ( ExpressionKind.NewCallLikeExpression(id, args), - ImmutableArray, ResolvedType>>.Empty, - //opTypeParamResolutions - // .Zip(controlOp.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(controlOp.Namespace, controlOp.Name), param, type)) - // .ToImmutableArray(), + ImmutableArray, ResolvedType>>.Empty, ResolvedType.New(ResolvedTypeKind.UnitType), new InferredExpressionInformation(false, true), QsNullable>.Null - ); - - private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope conditionScope, QsScope defaultScope) - { + ); + + private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope conditionScope, QsScope defaultScope) + { var controlCall = GetApplyIfExpression(result, conditionExpression, conditionScope, defaultScope); - if (controlCall != null) - { + if (controlCall != null) + { return new QsStatement( QsStatementKind.NewQsExpressionStatement(controlCall), statement.SymbolDeclarations, QsNullable.Null, - statement.Comments); + statement.Comments); + } + else + { + // ToDo: add diagnostic message here + return statement; // If the blocks can't be converted, return the original } - else - { - // ToDo: add diagnostic message here - return statement; // If the blocks can't be converted, return the original - } } - private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression conditionExpression, QsScope conditionScope, QsScope defaultScope) + 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; - ResolvedType controlOpType; - TypedExpression controlArgs; - ImmutableArray targetArgs; - if (isCondValid) - { - var props = GetCharacteristicsFromGlobalId(condId); - (bool adj, bool ctl) = (props.Contains(OpProperty.Adjointable), props.Contains(OpProperty.Controllable)); - - if (isDefaultValid) - { - if (adj && ctl) - { - controlOpInfo = BuiltIn.ApplyIfElseCA; - controlOpType = BuiltIn.ApplyIfElseCAResolvedType; - } - else if (adj) - { - controlOpInfo = BuiltIn.ApplyIfElseRA; - controlOpType = BuiltIn.ApplyIfElseRAResolvedType; - } - else if (ctl) - { - controlOpInfo = BuiltIn.ApplyIfElseRC; - controlOpType = BuiltIn.ApplyIfElseRCResolvedType; - } - else - { - controlOpInfo = BuiltIn.ApplyIfElseR; - controlOpType = BuiltIn.ApplyIfElseRResolvedType; - } - - controlArgs = Helper.CreateValueTupleExpression( - conditionExpression, - Helper.CreateValueTupleExpression(condId, condArgs), - Helper.CreateValueTupleExpression(defaultId, defaultArgs)); - - targetArgs = ImmutableArray.Create(condArgs.ResolvedType, defaultArgs.ResolvedType); - } - else if (defaultScope == null) - { - if (adj && ctl) - { + var (isCondValid, condId, condArgs) = IsValidScope(conditionScope); + var (isDefaultValid, defaultId, defaultArgs) = IsValidScope(defaultScope); + + BuiltIn controlOpInfo; + ResolvedType controlOpType; + TypedExpression controlArgs; + ImmutableArray targetArgs; + if (isCondValid) + { + // Get characteristic properties from global id + var props = condId.ResolvedType.Resolution is ResolvedTypeKind.Operation op + ? op.Item2.Characteristics.GetProperties() + : ImmutableHashSet.Empty; + + (bool adj, bool ctl) = (props.Contains(OpProperty.Adjointable), props.Contains(OpProperty.Controllable)); + + if (isDefaultValid) + { + if (adj && ctl) + { + controlOpInfo = BuiltIn.ApplyIfElseCA; + controlOpType = BuiltIn.ApplyIfElseCAResolvedType; + } + else if (adj) + { + controlOpInfo = BuiltIn.ApplyIfElseRA; + controlOpType = BuiltIn.ApplyIfElseRAResolvedType; + } + else if (ctl) + { + controlOpInfo = BuiltIn.ApplyIfElseRC; + controlOpType = BuiltIn.ApplyIfElseRCResolvedType; + } + else + { + controlOpInfo = BuiltIn.ApplyIfElseR; + controlOpType = BuiltIn.ApplyIfElseRResolvedType; + } + + controlArgs = CreateValueTupleExpression( + conditionExpression, + CreateValueTupleExpression(condId, condArgs), + CreateValueTupleExpression(defaultId, defaultArgs)); + + targetArgs = ImmutableArray.Create(condArgs.ResolvedType, defaultArgs.ResolvedType); + } + else if (defaultScope == null) + { + if (adj && ctl) + { (controlOpInfo, controlOpType) = (result == QsResult.One) ? (BuiltIn.ApplyIfOneCA, BuiltIn.ApplyIfOneCAResolvedType) - : (BuiltIn.ApplyIfZeroCA, BuiltIn.ApplyIfZeroCAResolvedType); - } - else if (adj) - { - (controlOpInfo, controlOpType) = (result == QsResult.One) - ? (BuiltIn.ApplyIfOneA, BuiltIn.ApplyIfOneAResolvedType) - : (BuiltIn.ApplyIfZeroA, BuiltIn.ApplyIfZeroAResolvedType); - } - else if (ctl) - { + : (BuiltIn.ApplyIfZeroCA, BuiltIn.ApplyIfZeroCAResolvedType); + } + else if (adj) + { + (controlOpInfo, controlOpType) = (result == QsResult.One) + ? (BuiltIn.ApplyIfOneA, BuiltIn.ApplyIfOneAResolvedType) + : (BuiltIn.ApplyIfZeroA, BuiltIn.ApplyIfZeroAResolvedType); + } + else if (ctl) + { (controlOpInfo, controlOpType) = (result == QsResult.One) ? (BuiltIn.ApplyIfOneC, BuiltIn.ApplyIfOneCResolvedType) - : (BuiltIn.ApplyIfZeroC, BuiltIn.ApplyIfZeroCResolvedType); - } - else - { + : (BuiltIn.ApplyIfZeroC, BuiltIn.ApplyIfZeroCResolvedType); + } + else + { (controlOpInfo, controlOpType) = (result == QsResult.One) ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) - : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); - } - - controlArgs = Helper.CreateValueTupleExpression( - conditionExpression, - Helper.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 - } - + : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); + } + + 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 = Helper.CreateIdentifierExpression( + 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(), - //QsNullable>.NewValue(targetArgs), - //QsNullable>.Null, - controlOpType); - - return CreateApplyIfCall(controlOpId, controlArgs/*, controlOpInfo, targetArgs*/); - } - - private ImmutableHashSet GetCharacteristicsFromGlobalId(TypedExpression globalId) - { - if (globalId.ResolvedType.Resolution is ResolvedTypeKind.Operation op) - { - return op.Item2.Characteristics.GetProperties(); - } - else - { - return ImmutableHashSet.Empty; - } - } - + .ToImmutableArray(), + controlOpType); + + return CreateApplyIfCall(controlOpId, controlArgs); + } + private (bool, QsConditionalStatement) ProcessElif(QsConditionalStatement cond) { if (cond.ConditionalBlocks.Length < 2) return (false, cond); @@ -308,8 +287,8 @@ private ImmutableHashSet GetCharacteristicsFromGlobalId(TypedExpress 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 @@ -317,29 +296,29 @@ private ImmutableHashSet GetCharacteristicsFromGlobalId(TypedExpress 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); + 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)); + QsComments.Empty)); + + return (true, new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(orCond.Item1, block)), newDefault)); } - else - { - return (false, cond); + else + { + return (false, cond); } - } - + } + private (bool, QsConditionalStatement) ProcessAND(QsConditionalStatement cond) { // This method expects elif blocks to have been abstracted out @@ -347,196 +326,193 @@ private ImmutableHashSet GetCharacteristicsFromGlobalId(TypedExpress 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); + 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)); + QsComments.Empty); + + return (true, new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(andCond.Item1, newBlock)), cond.Default)); } - else - { - return (false, cond); + 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 + ); } - } - - 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(this.onStatement(stm)); - } - } - else - { - statements.Add(this.onStatement(statement)); - } - } - - return new QsScope(statements.ToImmutableArray(), parentSymbols); + 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(this.onStatement(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; - + 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 - 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) - { - if (!_super._IsRecursiveIdentifier && _super._OldName.Equals(tp.Origin)) - { - tp = new QsTypeParameter - ( - _super._NewName, - tp.TypeName, - tp.Range - ); - } - - return base.onTypeParameter(tp); - } - } + 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. @@ -548,479 +524,434 @@ public override ResolvedTypeKind onTypeParameter(QsTypeParameter tp) // 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 _InControlledAdjoint = 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; - - // ToDo: this Boolean logic could be cleaned up - bool addAdjoint = false; - bool addControlled = false; - //bool addControlledAdjoint = false; - if (_InBody) - { - if (adj != null && adj.Implementation.IsGenerated) - { - addAdjoint = true; - } - - if (ctl != null && ctl.Implementation.IsGenerated) - { - addControlled = true; - } - - // ToDo: I don't think you have to add the ControlledAdjoint if you add the Controlled and Adjoint specializations - //if (ctlAdj != null && ctlAdj.Implementation.IsGenerated) - //{ - // addControlledAdjoint = true; - //} - } - else if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated gen) - { - if (_InAdjoint && gen.Item.IsDistribute) - { - addControlled = true; - } - else if (_InControlled && gen.Item.IsInvert) - { - addAdjoint = true; - } - } - - 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))); // ToDo: find appropriate directive - } - - if (addControlled) - { - specializations.Add(MakeSpec( - QsSpecializationKind.QsControlled, - controlledSig, - SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); // ToDo: find appropriate directive - } - - //if (addControlledAdjoint) - //{ - // specializations.Add(MakeSpec( - // QsSpecializationKind.QsControlledAdjoint, - // controlledSig, - // SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); // ToDo: find appropriate directive - //} - - return (newSig, specializations); - } - - private (QsQualifiedName, ResolvedType, CallableInformation) 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); - _ControlOperations.Add(updatedCallable); - - return (newName, paramTypes, signature.Information); - } - - 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 QsSpecialization onControlledAdjointSpecialization(QsSpecialization spec) - //{ - // _super._InControlledAdjoint = true; - // var rtrn = base.onControlledAdjointSpecialization(spec); - // _super._InControlledAdjoint = false; - // return rtrn; - //} - - 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 QsStatement HoistIfContents(QsScope contents) - { - var (targetName, targetParamType, callInfo) = _super.GenerateOperation(contents); - var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( - Tuple.Create( - targetParamType, - ResolvedType.New(ResolvedTypeKind.UnitType)), // ToDo: something has to be done to allow for mutables in sub-scopes - callInfo)); - - var targetTypeArgTypes = _super._CurrentCallable.TypeParamTypes; - var targetOpId = new TypedExpression - ( - ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeArgTypes), - targetTypeArgTypes.IsNull - ? ImmutableArray, ResolvedType>>.Empty - : targetTypeArgTypes.Item - .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) - .ToImmutableArray(), - targetOpType, - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - - var knownSymbols = contents.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : contents.KnownSymbols.Variables; - - TypedExpression targetArgs = null; - if (knownSymbols.Any()) - { - targetArgs = Helper.CreateValueTupleExpression(knownSymbols.Select(var => Helper.CreateIdentifierExpression( + 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; + + 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; + + // ToDo: I don't think you have to add the ControlledAdjoint if you add the Controlled and Adjoint specializations + bool addAdjoint = false; + bool addControlled = false; + //bool addControlledAdjoint = false; + if (_InBody) + { + addAdjoint = adj != null && adj.Implementation.IsGenerated; + addControlled = ctl != null && ctl.Implementation.IsGenerated; + //addControlledAdjoint = ctlAdj != null && ctlAdj.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 (addControlledAdjoint) + //{ + // specializations.Add(MakeSpec( + // QsSpecializationKind.QsControlledAdjoint, + // controlledSig, + // SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); + //} + + return (newSig, specializations); + } + + private (QsQualifiedName, ResolvedType, CallableInformation) 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); + _ControlOperations.Add(updatedCallable); + + return (newName, paramTypes, signature.Information); + } + + 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 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 QsStatement HoistIfContents(QsScope contents) + { + var (targetName, targetParamType, callInfo) = _super.GenerateOperation(contents); + var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( + Tuple.Create( + targetParamType, + ResolvedType.New(ResolvedTypeKind.UnitType)), + callInfo)); + + var targetTypeArgTypes = _super._CurrentCallable.TypeParamTypes; + var targetOpId = new TypedExpression + ( + ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeArgTypes), + targetTypeArgTypes.IsNull + ? ImmutableArray, ResolvedType>>.Empty + : targetTypeArgTypes.Item + .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + .ToImmutableArray(), + targetOpType, + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + // ToDo: double-check this is necessary + var knownSymbols = contents.KnownSymbols.IsEmpty + ? ImmutableArray>>.Empty + : contents.KnownSymbols.Variables; + + TypedExpression targetArgs = null; + if (knownSymbols.Any()) + { + targetArgs = CreateValueTupleExpression(knownSymbols.Select(var => CreateIdentifierExpression( Identifier.NewLocalVariable(var.VariableName), - ImmutableArray, ResolvedType>>.Empty, - var.Type)) - .ToArray()); - } - else - { - targetArgs = new TypedExpression - ( - ExpressionKind.UnitValue, - ImmutableArray, ResolvedType>>.Empty, - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - } - - var call = new TypedExpression - ( - ExpressionKind.NewCallLikeExpression(targetOpId, targetArgs), - // All type params are resolved on the Identifier - ImmutableArray, ResolvedType>>.Empty, + // ToDo: may need to be more careful here with the type argument mapping on the identifiers + ImmutableArray, ResolvedType>>.Empty, + var.Type)) + .ToArray()); + } + else + { + targetArgs = new TypedExpression + ( + ExpressionKind.UnitValue, + ImmutableArray, ResolvedType>>.Empty, + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + } + + var call = new TypedExpression + ( + ExpressionKind.NewCallLikeExpression(targetOpId, targetArgs), + // All type parameters are resolved on the Identifier + ImmutableArray, ResolvedType>>.Empty, ResolvedType.New(ResolvedTypeKind.UnitType), new InferredExpressionInformation(false, true), - QsNullable>.Null - ); - - return new QsStatement( - QsStatementKind.NewQsExpressionStatement(call), - LocalDeclarations.Empty, - QsNullable.Null, - QsComments.Empty); - } - - //private QsNullable> GetTypeParamTypesFromCallable(QsCallable callable) - //{ - // if (callable.Signature.TypeParameters.Any(param => param.IsValidName)) - // { - // return 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()); - // } - // else - // { - // return QsNullable>.Null; - // } - //} - - 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, 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) - { - // ToDo: Revisit this method when the F# Option type has been removed from the onPositionBlock function. - - var contextValidScope = _super._IsValidScope; - var contextHoistParams = _super._CurrentHoistParams; - - var newConditionBlocks = stm.ConditionalBlocks - .Select(condBlock => - { - _super._IsValidScope = true; - _super._CurrentHoistParams = condBlock.Item2.Body.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : condBlock.Item2.Body.KnownSymbols.Variables; - - var (expr, block) = this.onPositionedBlock(condBlock.Item1, condBlock.Item2); - if (_super._IsValidScope && block.Body.Statements.Length > 1) // if sub-scope is valid, hoist content - { - // Hoist the scope to its own operation - var call = HoistIfContents(block.Body); - block = new QsPositionedBlock( - new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), - block.Location, - block.Comments); - } - return Tuple.Create(expr.Value, block); // ToDo: .Value may be unnecessary in the future - }).ToImmutableArray(); - - var newDefault = QsNullable.Null; - if (stm.Default.IsValue) - { - _super._IsValidScope = true; - _super._CurrentHoistParams = stm.Default.Item.Body.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : stm.Default.Item.Body.KnownSymbols.Variables; - - var (_, block) = this.onPositionedBlock(null, stm.Default.Item); // ToDo: null is probably bad here - if (_super._IsValidScope && block.Body.Statements.Length > 1) // if sub-scope is valid, hoist content - { - // Hoist the scope to its own operation - var call = HoistIfContents(block.Body); - block = new QsPositionedBlock( - new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), - block.Location, - block.Comments); - } - newDefault = QsNullable.NewValue(block); - } - - _super._CurrentHoistParams = contextHoistParams; - _super._IsValidScope = contextValidScope; - - return QsStatementKind.NewQsConditionalStatement( - new QsConditionalStatement(newConditionBlocks, newDefault)); - } - - public override QsStatementKind Transform(QsStatementKind kind) - { - _super._ContainsHoistParamRef = false; // Every statement kind starts off false - return base.Transform(kind); - } - } - - private class HoistExpression : ExpressionTransformation - { - private HoistTransformation _super; - - public HoistExpression(HoistTransformation super) : - base(expr => new HoistExpressionKind(super, expr as HoistExpression)) - { _super = super; } - - public override TypedExpression Transform(TypedExpression ex) - { - var contextContainsHoistParamRef = _super._ContainsHoistParamRef; - _super._ContainsHoistParamRef = false; - var rtrn = base.Transform(ex); - - // If the sub context contains a reference, then the super context contains a reference, - // otherwise return the super context to its original value - if (!_super._ContainsHoistParamRef) - { - _super._ContainsHoistParamRef = contextContainsHoistParamRef; - } - - return rtrn; - } - } - - private class HoistExpressionKind : ExpressionKindTransformation - { - private HoistTransformation _super; - - public HoistExpressionKind(HoistTransformation super, HoistExpression expr) : base(expr) { _super = super; } - - public override ExpressionKind onIdentifier(Identifier sym, QsNullable> tArgs) - { - if (sym is Identifier.LocalVariable local && - _super._CurrentHoistParams.Any(param => param.VariableName.Equals(local.Item))) - { - _super._ContainsHoistParamRef = true; - } - return base.onIdentifier(sym, tArgs); - } - } + QsNullable>.Null + ); + + return new QsStatement( + QsStatementKind.NewQsExpressionStatement(call), + LocalDeclarations.Empty, + QsNullable.Null, + QsComments.Empty); + } + + 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) + { + // ToDo: Revisit this method when the F# Option type has been removed from the onPositionBlock function. + + var contextValidScope = _super._IsValidScope; + var contextHoistParams = _super._CurrentHoistParams; + + var newConditionBlocks = stm.ConditionalBlocks + .Select(condBlock => + { + _super._IsValidScope = true; + _super._CurrentHoistParams = condBlock.Item2.Body.KnownSymbols.IsEmpty + ? ImmutableArray>>.Empty + : condBlock.Item2.Body.KnownSymbols.Variables; + + var (expr, block) = this.onPositionedBlock(condBlock.Item1, condBlock.Item2); + if (_super._IsValidScope && block.Body.Statements.Length > 1) // if sub-scope is valid, hoist content + { + // Hoist the scope to its own operation + var call = HoistIfContents(block.Body); + block = new QsPositionedBlock( + new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), + block.Location, + block.Comments); + } + return Tuple.Create(expr.Value, block); // ToDo: .Value may be unnecessary in the future + }).ToImmutableArray(); + + var newDefault = QsNullable.Null; + if (stm.Default.IsValue) + { + _super._IsValidScope = true; + _super._CurrentHoistParams = stm.Default.Item.Body.KnownSymbols.IsEmpty + ? ImmutableArray>>.Empty + : stm.Default.Item.Body.KnownSymbols.Variables; + + var (_, block) = this.onPositionedBlock(null, stm.Default.Item); // ToDo: null is probably bad here + if (_super._IsValidScope && block.Body.Statements.Length > 1) // if sub-scope is valid, hoist content + { + // Hoist the scope to its own operation + var call = HoistIfContents(block.Body); + block = new QsPositionedBlock( + new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), + block.Location, + block.Comments); + } + newDefault = QsNullable.NewValue(block); + } + + _super._CurrentHoistParams = contextHoistParams; + _super._IsValidScope = contextValidScope; + + return QsStatementKind.NewQsConditionalStatement( + new QsConditionalStatement(newConditionBlocks, newDefault)); + } + + public override QsStatementKind Transform(QsStatementKind kind) + { + _super._ContainsHoistParamRef = false; // Every statement kind starts off false + return base.Transform(kind); + } + } + + private class HoistExpression : ExpressionTransformation + { + private HoistTransformation _super; + + public HoistExpression(HoistTransformation super) : + base(expr => new HoistExpressionKind(super, expr as HoistExpression)) + { _super = super; } + + public override TypedExpression Transform(TypedExpression ex) + { + var contextContainsHoistParamRef = _super._ContainsHoistParamRef; + _super._ContainsHoistParamRef = false; + var rtrn = base.Transform(ex); + + // If the sub context contains a reference, then the super context contains a reference, + // otherwise return the super context to its original value + if (!_super._ContainsHoistParamRef) + { + _super._ContainsHoistParamRef = contextContainsHoistParamRef; + } + + return rtrn; + } + } + + private class HoistExpressionKind : ExpressionKindTransformation + { + private HoistTransformation _super; + + public HoistExpressionKind(HoistTransformation super, HoistExpression expr) : base(expr) { _super = super; } + + public override ExpressionKind onIdentifier(Identifier sym, QsNullable> tArgs) + { + if (sym is Identifier.LocalVariable local && + _super._CurrentHoistParams.Any(param => param.VariableName.Equals(local.Item))) + { + _super._ContainsHoistParamRef = true; + } + return base.onIdentifier(sym, tArgs); + } + } } } } From 739ceb16a3d4fbfacef1c7c019ae806b1c6f5609 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Wed, 8 Jan 2020 13:53:45 -0800 Subject: [PATCH 25/53] Fixed some line endings --- .../Compiler/ExternalRewriteSteps.cs | 442 +++++++++--------- src/QsCompiler/Compiler/FunctorGeneration.cs | 4 +- .../ClassicallyControlledTransformation.cs | 6 +- 3 files changed, 226 insertions(+), 226 deletions(-) diff --git a/src/QsCompiler/Compiler/ExternalRewriteSteps.cs b/src/QsCompiler/Compiler/ExternalRewriteSteps.cs index a8cfafd7a1..ddfbc4d953 100644 --- a/src/QsCompiler/Compiler/ExternalRewriteSteps.cs +++ b/src/QsCompiler/Compiler/ExternalRewriteSteps.cs @@ -1,221 +1,221 @@ -// 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 Microsoft.Quantum.QsCompiler.CompilationBuilder; -using Microsoft.Quantum.QsCompiler.Diagnostics; -using Microsoft.Quantum.QsCompiler.ReservedKeywords; -using Microsoft.Quantum.QsCompiler.SyntaxTree; -using Microsoft.VisualStudio.LanguageServer.Protocol; - - -namespace Microsoft.Quantum.QsCompiler -{ - internal static class RewriteSteps - { - /// - /// Concrete implementation of a rewrite steps with an additional property specifying the dll it was loaded from. - /// - internal class LoadedStep : IRewriteStep - { - internal readonly Uri Origin; - private readonly IRewriteStep _SelfAsStep; - private readonly object _SelfAsObject; - - private readonly MethodInfo[] _InterfaceMethods; - private MethodInfo InterfaceMethod(string name) => - // This choice of filtering the interface methods may seem a bit particular. - // However, unless you know what you are doing, please don't change it. - // If you are sure you know what you are doing, please make sure the loading via reflection works for rewrite steps - // implemented in both F# or C#, and whether they are compiled against the current compiler version or an older one. - this._InterfaceMethods?.FirstOrDefault(method => method.Name.Split("-").Last() == name); - - private T GetViaReflection(string name) => - (T)InterfaceMethod($"get_{name}")?.Invoke(_SelfAsObject, null); - - private void SetViaReflection(string name, T arg) => - InterfaceMethod($"set_{name}")?.Invoke(_SelfAsObject, new object[] { arg }); - - private T InvokeViaReflection(string name, params object[] args) => - (T)InterfaceMethod(name)?.Invoke(_SelfAsObject, args); - - - /// - /// Attempts to construct a rewrite step via reflection. - /// Note that the loading via reflection has the consequence that methods may fail on execution. - /// This is e.g. the case if they invoke methods from package references if the corresponding dll - /// has not been copied to output folder of the dll from which the rewrite step is loaded. - /// Throws the corresponding exception if that construction fails. - /// - internal LoadedStep(object implementation, Type interfaceType, Uri origin) - { - this.Origin = origin ?? throw new ArgumentNullException(nameof(origin)); - this._SelfAsObject = implementation ?? throw new ArgumentNullException(nameof(implementation)); - - // Initializing the _InterfaceMethods even if the implementation implements IRewriteStep - // would result in certain properties being loaded via reflection instead of simply being accessed via _SelfAsStep. - if (this._SelfAsObject is IRewriteStep step) this._SelfAsStep = step; - else this._InterfaceMethods = implementation.GetType().GetInterfaceMap(interfaceType).TargetMethods; - - // The Name and Priority need to be fixed throughout the loading, - // so whatever their value is when loaded that's what these values well be as far at the compiler is concerned. - this.Name = _SelfAsStep?.Name ?? this.GetViaReflection(nameof(IRewriteStep.Name)); - this.Priority = _SelfAsStep?.Priority ?? this.GetViaReflection(nameof(IRewriteStep.Priority)); - } - - public string Name { get; } - public int Priority { get; } - public IDictionary AssemblyConstants - { - get => _SelfAsStep?.AssemblyConstants - ?? this.GetViaReflection>(nameof(IRewriteStep.AssemblyConstants)); - } - - public bool ImplementsTransformation - { - get => _SelfAsStep?.ImplementsTransformation - ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsTransformation)); - } - - public bool ImplementsPreconditionVerification - { - get => _SelfAsStep?.ImplementsPreconditionVerification - ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsPreconditionVerification)); - } - - public bool ImplementsPostconditionVerification - { - get => _SelfAsStep?.ImplementsPostconditionVerification - ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsPostconditionVerification)); - } - - public bool Transformation(QsCompilation compilation, out QsCompilation transformed) - { - if (_SelfAsStep != null) return _SelfAsStep.Transformation(compilation, out transformed); - var args = new object[] { compilation, null }; - var success = this.InvokeViaReflection(nameof(IRewriteStep.Transformation), args); - transformed = success ? (QsCompilation)args[1] : compilation; - return success; - } - - public bool PreconditionVerification(QsCompilation compilation) => - _SelfAsStep?.PreconditionVerification(compilation) - ?? this.InvokeViaReflection(nameof(IRewriteStep.PreconditionVerification), compilation); - - public bool PostconditionVerification(QsCompilation compilation) => - _SelfAsStep?.PostconditionVerification(compilation) - ?? this.InvokeViaReflection(nameof(IRewriteStep.PostconditionVerification), compilation); - } - - - /// - /// Loads all dlls listed as containing rewrite steps to include in the compilation process in the given configuration. - /// Generates suitable diagnostics if a listed file can't be found or loaded. - /// Finds all types implementing the IRewriteStep interface and loads the corresponding rewrite steps - /// according to the specified priority, where a steps with a higher priority will be listed first in the returned array. - /// If the function onDiagnostic is specified and not null, calls it on all generated diagnostics, - /// and calls onException on all caught exceptions if it is specified and not null. - /// Returns an empty array if the rewrite steps in the given configurations are set to null. - /// - internal static ImmutableArray Load(CompilationLoader.Configuration config, - Action onDiagnostic = null, Action onException = null) - { - if (config.RewriteSteps == null) return ImmutableArray.Empty; - Uri WithFullPath(string file) - { - try - { - return String.IsNullOrWhiteSpace(file) ? null : new Uri(Path.GetFullPath(file)); - } - catch (Exception ex) - { - onDiagnostic?.Invoke(Errors.LoadError(ErrorCode.InvalidFilePath, new[] { file }, file)); - onException?.Invoke(ex); - return null; - } - } - - var specifiedPluginDlls = config.RewriteSteps.Select(step => (WithFullPath(step.Item1), step.Item2)).Where(step => step.Item1 != null).ToList(); - var (foundDlls, notFoundDlls) = specifiedPluginDlls.Partition(step => File.Exists(step.Item1.LocalPath)); - foreach (var file in notFoundDlls.Select(step => step.Item1).Distinct()) - { - onDiagnostic?.Invoke(Errors.LoadError(ErrorCode.UnknownCompilerPlugin, new[] { file.LocalPath }, file.LocalPath)); - } - - var rewriteSteps = ImmutableArray.CreateBuilder(); - foreach (var (target, outputFolder) in foundDlls) - { - var relevantTypes = new List(); - Diagnostic LoadError(ErrorCode code, params string[] args) => Errors.LoadError(code, args, ProjectManager.MessageSource(target)); - Diagnostic LoadWarning(WarningCode code, params string[] args) => Warnings.LoadWarning(code, args, ProjectManager.MessageSource(target)); - try - { - var interfaceMatch = Assembly.LoadFrom(target.LocalPath).GetTypes().Where( - t => typeof(IRewriteStep).IsAssignableFrom(t) || // inherited interface is defined in this exact dll - t.GetInterfaces().Any(t => t.FullName == typeof(IRewriteStep).FullName)); // inherited interface may be defined in older compiler version - relevantTypes.AddRange(interfaceMatch); - } - catch (BadImageFormatException ex) - { - onDiagnostic?.Invoke(LoadError(ErrorCode.FileIsNotAnAssembly, target.LocalPath)); - onException?.Invoke(ex); - } - catch (Exception ex) - { - onDiagnostic?.Invoke(LoadError(ErrorCode.CouldNotLoadCompilerPlugin, target.LocalPath)); - onException?.Invoke(ex); - } - var loadedSteps = new List(); - foreach (var type in relevantTypes) - { - try - { - var instance = Activator.CreateInstance(type); - if (instance is IRewriteStep step) - { - loadedSteps.Add(new LoadedStep(step, typeof(IRewriteStep), target)); - continue; - } - - try // we also try to load rewrite steps that have been compiled against a different compiler version - { - var interfaceType = type.GetInterfaces().First(t => t.FullName == typeof(IRewriteStep).FullName); - var loadedStep = new LoadedStep(instance, interfaceType, target); - onDiagnostic?.Invoke(LoadWarning(WarningCode.RewriteStepLoadedViaReflection, loadedStep.Name, target.LocalPath)); - loadedSteps.Add(loadedStep); - } - catch // we don't log the exception, since it is perfectly possible that we should have ignored this type in the first place - { - onDiagnostic?.Invoke(LoadWarning(WarningCode.FailedToLoadRewriteStepViaReflection, target.LocalPath)); - } - } - catch (Exception ex) - { - onDiagnostic?.Invoke(LoadError(ErrorCode.CouldNotInstantiateRewriteStep, type.ToString(), target.LocalPath)); - onException?.Invoke(ex); - } - } - foreach (var loaded in loadedSteps) - { - var assemblyConstants = loaded.AssemblyConstants; - if (assemblyConstants == null) continue; - foreach (var kvPair in config.AssemblyConstants ?? Enumerable.Empty>()) - { assemblyConstants[kvPair.Key] = kvPair.Value; } - - var defaultOutput = assemblyConstants.TryGetValue(AssemblyConstants.OutputPath, out var path) ? path : null; - assemblyConstants[AssemblyConstants.OutputPath] = outputFolder ?? defaultOutput ?? config.BuildOutputFolder; - assemblyConstants[AssemblyConstants.AssemblyName] = config.ProjectNameWithoutExtension; - } - - loadedSteps.Sort((fst, snd) => snd.Priority - fst.Priority); - rewriteSteps.AddRange(loadedSteps); - } - return rewriteSteps.ToImmutableArray(); - } - } -} +// 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 Microsoft.Quantum.QsCompiler.CompilationBuilder; +using Microsoft.Quantum.QsCompiler.Diagnostics; +using Microsoft.Quantum.QsCompiler.ReservedKeywords; +using Microsoft.Quantum.QsCompiler.SyntaxTree; +using Microsoft.VisualStudio.LanguageServer.Protocol; + + +namespace Microsoft.Quantum.QsCompiler +{ + internal static class RewriteSteps + { + /// + /// Concrete implementation of a rewrite steps with an additional property specifying the dll it was loaded from. + /// + internal class LoadedStep : IRewriteStep + { + internal readonly Uri Origin; + private readonly IRewriteStep _SelfAsStep; + private readonly object _SelfAsObject; + + private readonly MethodInfo[] _InterfaceMethods; + private MethodInfo InterfaceMethod(string name) => + // This choice of filtering the interface methods may seem a bit particular. + // However, unless you know what you are doing, please don't change it. + // If you are sure you know what you are doing, please make sure the loading via reflection works for rewrite steps + // implemented in both F# or C#, and whether they are compiled against the current compiler version or an older one. + this._InterfaceMethods?.FirstOrDefault(method => method.Name.Split("-").Last() == name); + + private T GetViaReflection(string name) => + (T)InterfaceMethod($"get_{name}")?.Invoke(_SelfAsObject, null); + + private void SetViaReflection(string name, T arg) => + InterfaceMethod($"set_{name}")?.Invoke(_SelfAsObject, new object[] { arg }); + + private T InvokeViaReflection(string name, params object[] args) => + (T)InterfaceMethod(name)?.Invoke(_SelfAsObject, args); + + + /// + /// Attempts to construct a rewrite step via reflection. + /// Note that the loading via reflection has the consequence that methods may fail on execution. + /// This is e.g. the case if they invoke methods from package references if the corresponding dll + /// has not been copied to output folder of the dll from which the rewrite step is loaded. + /// Throws the corresponding exception if that construction fails. + /// + internal LoadedStep(object implementation, Type interfaceType, Uri origin) + { + this.Origin = origin ?? throw new ArgumentNullException(nameof(origin)); + this._SelfAsObject = implementation ?? throw new ArgumentNullException(nameof(implementation)); + + // Initializing the _InterfaceMethods even if the implementation implements IRewriteStep + // would result in certain properties being loaded via reflection instead of simply being accessed via _SelfAsStep. + if (this._SelfAsObject is IRewriteStep step) this._SelfAsStep = step; + else this._InterfaceMethods = implementation.GetType().GetInterfaceMap(interfaceType).TargetMethods; + + // The Name and Priority need to be fixed throughout the loading, + // so whatever their value is when loaded that's what these values well be as far at the compiler is concerned. + this.Name = _SelfAsStep?.Name ?? this.GetViaReflection(nameof(IRewriteStep.Name)); + this.Priority = _SelfAsStep?.Priority ?? this.GetViaReflection(nameof(IRewriteStep.Priority)); + } + + public string Name { get; } + public int Priority { get; } + public IDictionary AssemblyConstants + { + get => _SelfAsStep?.AssemblyConstants + ?? this.GetViaReflection>(nameof(IRewriteStep.AssemblyConstants)); + } + + public bool ImplementsTransformation + { + get => _SelfAsStep?.ImplementsTransformation + ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsTransformation)); + } + + public bool ImplementsPreconditionVerification + { + get => _SelfAsStep?.ImplementsPreconditionVerification + ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsPreconditionVerification)); + } + + public bool ImplementsPostconditionVerification + { + get => _SelfAsStep?.ImplementsPostconditionVerification + ?? this.GetViaReflection(nameof(IRewriteStep.ImplementsPostconditionVerification)); + } + + public bool Transformation(QsCompilation compilation, out QsCompilation transformed) + { + if (_SelfAsStep != null) return _SelfAsStep.Transformation(compilation, out transformed); + var args = new object[] { compilation, null }; + var success = this.InvokeViaReflection(nameof(IRewriteStep.Transformation), args); + transformed = success ? (QsCompilation)args[1] : compilation; + return success; + } + + public bool PreconditionVerification(QsCompilation compilation) => + _SelfAsStep?.PreconditionVerification(compilation) + ?? this.InvokeViaReflection(nameof(IRewriteStep.PreconditionVerification), compilation); + + public bool PostconditionVerification(QsCompilation compilation) => + _SelfAsStep?.PostconditionVerification(compilation) + ?? this.InvokeViaReflection(nameof(IRewriteStep.PostconditionVerification), compilation); + } + + + /// + /// Loads all dlls listed as containing rewrite steps to include in the compilation process in the given configuration. + /// Generates suitable diagnostics if a listed file can't be found or loaded. + /// Finds all types implementing the IRewriteStep interface and loads the corresponding rewrite steps + /// according to the specified priority, where a steps with a higher priority will be listed first in the returned array. + /// If the function onDiagnostic is specified and not null, calls it on all generated diagnostics, + /// and calls onException on all caught exceptions if it is specified and not null. + /// Returns an empty array if the rewrite steps in the given configurations are set to null. + /// + internal static ImmutableArray Load(CompilationLoader.Configuration config, + Action onDiagnostic = null, Action onException = null) + { + if (config.RewriteSteps == null) return ImmutableArray.Empty; + Uri WithFullPath(string file) + { + try + { + return String.IsNullOrWhiteSpace(file) ? null : new Uri(Path.GetFullPath(file)); + } + catch (Exception ex) + { + onDiagnostic?.Invoke(Errors.LoadError(ErrorCode.InvalidFilePath, new[] { file }, file)); + onException?.Invoke(ex); + return null; + } + } + + var specifiedPluginDlls = config.RewriteSteps.Select(step => (WithFullPath(step.Item1), step.Item2)).Where(step => step.Item1 != null).ToList(); + var (foundDlls, notFoundDlls) = specifiedPluginDlls.Partition(step => File.Exists(step.Item1.LocalPath)); + foreach (var file in notFoundDlls.Select(step => step.Item1).Distinct()) + { + onDiagnostic?.Invoke(Errors.LoadError(ErrorCode.UnknownCompilerPlugin, new[] { file.LocalPath }, file.LocalPath)); + } + + var rewriteSteps = ImmutableArray.CreateBuilder(); + foreach (var (target, outputFolder) in foundDlls) + { + var relevantTypes = new List(); + Diagnostic LoadError(ErrorCode code, params string[] args) => Errors.LoadError(code, args, ProjectManager.MessageSource(target)); + Diagnostic LoadWarning(WarningCode code, params string[] args) => Warnings.LoadWarning(code, args, ProjectManager.MessageSource(target)); + try + { + var interfaceMatch = Assembly.LoadFrom(target.LocalPath).GetTypes().Where( + t => typeof(IRewriteStep).IsAssignableFrom(t) || // inherited interface is defined in this exact dll + t.GetInterfaces().Any(t => t.FullName == typeof(IRewriteStep).FullName)); // inherited interface may be defined in older compiler version + relevantTypes.AddRange(interfaceMatch); + } + catch (BadImageFormatException ex) + { + onDiagnostic?.Invoke(LoadError(ErrorCode.FileIsNotAnAssembly, target.LocalPath)); + onException?.Invoke(ex); + } + catch (Exception ex) + { + onDiagnostic?.Invoke(LoadError(ErrorCode.CouldNotLoadCompilerPlugin, target.LocalPath)); + onException?.Invoke(ex); + } + var loadedSteps = new List(); + foreach (var type in relevantTypes) + { + try + { + var instance = Activator.CreateInstance(type); + if (instance is IRewriteStep step) + { + loadedSteps.Add(new LoadedStep(step, typeof(IRewriteStep), target)); + continue; + } + + try // we also try to load rewrite steps that have been compiled against a different compiler version + { + var interfaceType = type.GetInterfaces().First(t => t.FullName == typeof(IRewriteStep).FullName); + var loadedStep = new LoadedStep(instance, interfaceType, target); + onDiagnostic?.Invoke(LoadWarning(WarningCode.RewriteStepLoadedViaReflection, loadedStep.Name, target.LocalPath)); + loadedSteps.Add(loadedStep); + } + catch // we don't log the exception, since it is perfectly possible that we should have ignored this type in the first place + { + onDiagnostic?.Invoke(LoadWarning(WarningCode.FailedToLoadRewriteStepViaReflection, target.LocalPath)); + } + } + catch (Exception ex) + { + onDiagnostic?.Invoke(LoadError(ErrorCode.CouldNotInstantiateRewriteStep, type.ToString(), target.LocalPath)); + onException?.Invoke(ex); + } + } + foreach (var loaded in loadedSteps) + { + var assemblyConstants = loaded.AssemblyConstants; + if (assemblyConstants == null) continue; + foreach (var kvPair in config.AssemblyConstants ?? Enumerable.Empty>()) + { assemblyConstants[kvPair.Key] = kvPair.Value; } + + var defaultOutput = assemblyConstants.TryGetValue(AssemblyConstants.OutputPath, out var path) ? path : null; + assemblyConstants[AssemblyConstants.OutputPath] = outputFolder ?? defaultOutput ?? config.BuildOutputFolder; + assemblyConstants[AssemblyConstants.AssemblyName] = config.ProjectNameWithoutExtension; + } + + loadedSteps.Sort((fst, snd) => snd.Priority - fst.Priority); + rewriteSteps.AddRange(loadedSteps); + } + return rewriteSteps.ToImmutableArray(); + } + } +} diff --git a/src/QsCompiler/Compiler/FunctorGeneration.cs b/src/QsCompiler/Compiler/FunctorGeneration.cs index 598c1d640b..123da37375 100644 --- a/src/QsCompiler/Compiler/FunctorGeneration.cs +++ b/src/QsCompiler/Compiler/FunctorGeneration.cs @@ -153,9 +153,9 @@ 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"); - if (gen.Item.IsInvert) + if (gen.Item.IsInvert) { - ctlAdj = ctlAdj.WithImplementation(SpecializationImplementation.NewProvided(ctlArg, ctlImpl.GenerateAdjoint())); + ctlAdj = ctlAdj.WithImplementation(SpecializationImplementation.NewProvided(ctlArg, ctlImpl.GenerateAdjoint())); } } } diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index b5e0933762..037c3bb221 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -416,7 +416,7 @@ private class UpdateGeneratedOpTransformation private QsQualifiedName _OldName; private QsQualifiedName _NewName; - public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) { var filter = new SyntaxTreeTransformation>( new ScopeTransformation( @@ -793,8 +793,8 @@ private QsStatement HoistIfContents(QsScope contents) if (knownSymbols.Any()) { targetArgs = CreateValueTupleExpression(knownSymbols.Select(var => CreateIdentifierExpression( - Identifier.NewLocalVariable(var.VariableName), - // ToDo: may need to be more careful here with the type argument mapping on the identifiers + Identifier.NewLocalVariable(var.VariableName), + // ToDo: may need to be more careful here with the type argument mapping on the identifiers ImmutableArray, ResolvedType>>.Empty, var.Type)) .ToArray()); From 8dc3ba98c4e3fad218d5c2ba007ca50503962d8c Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Thu, 9 Jan 2020 17:02:24 -0800 Subject: [PATCH 26/53] Basic test --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 523 +++++++++--------- .../LinkingTests/ClassicalControl.qs | 48 ++ .../Tests.Compiler/TestUtils/Signatures.fs | 383 +++++++------ .../Tests.Compiler/Tests.Compiler.fsproj | 3 + 4 files changed, 527 insertions(+), 430 deletions(-) create mode 100644 src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 39206b7e61..6273d401d5 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -1,247 +1,276 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.QsCompiler.Testing - -open System -open System.Collections.Generic -open System.IO -open Microsoft.Quantum.QsCompiler -open Microsoft.Quantum.QsCompiler.CompilationBuilder -open Microsoft.Quantum.QsCompiler.DataTypes -open Microsoft.Quantum.QsCompiler.Diagnostics -open Microsoft.Quantum.QsCompiler.SyntaxExtensions -open Microsoft.Quantum.QsCompiler.SyntaxTree -open Microsoft.Quantum.QsCompiler.Transformations.IntrinsicResolutionTransformation -open Microsoft.Quantum.QsCompiler.Transformations.Monomorphization -open Microsoft.Quantum.QsCompiler.Transformations.MonomorphizationValidation -open Xunit -open Xunit.Abstractions - - -type LinkingTests (output:ITestOutputHelper) = - inherit CompilerTests(CompilerTests.Compile (Path.Combine ("TestCases", "LinkingTests" )) ["Core.qs"; "InvalidEntryPoints.qs"], output) - - 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 - - static member private ReadAndChunkSourceFile fileName = - let sourceInput = Path.Combine ("TestCases", "LinkingTests", fileName) |> File.ReadAllText - sourceInput.Split ([|"==="|], StringSplitOptions.RemoveEmptyEntries) - - member private this.Expect name (diag : IEnumerable) = - let ns = "Microsoft.Quantum.Testing.EntryPoints" |> NonNullable<_>.New - let name = name |> NonNullable<_>.New - this.Verify (QsQualifiedName.New (ns, name), diag) - - member private this.CompileAndVerify input (diag : DiagnosticItem seq) = - - let fileId = getTempFile() - let file = getManager fileId input - let inFile (c : QsCallable) = c.SourceFile = file.FileName - - compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore - let built = compilationManager.Build() - compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore - let tests = new CompilerTests(built, output) - - for callable in built.Callables.Values |> Seq.filter inFile do - tests.Verify (callable.FullName, diag) - - member private this.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 - - member private this.CompileMonomorphization input = - - let compilationDataStructures = this.BuildContent input - - let monomorphicCompilation = MonomorphizationTransformation.Apply compilationDataStructures.BuiltCompilation - - Assert.NotNull monomorphicCompilation - MonomorphizationValidationTransformation.Apply monomorphicCompilation - - 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 - let chunckNumber = 2 * (testNumber - 1) - let result = this.CompileIntrinsicResolution srcChunks.[chunckNumber] srcChunks.[chunckNumber+1] - Signatures.SignatureCheck [Signatures.IntrinsicResolutionNs] Signatures.IntrinsicResolutionSignatures.[testNumber-1] result - - (*Find the overridden operation in the appropriate namespace*) - let targetCallName = QsQualifiedName.New(NonNullable<_>.New Signatures.IntrinsicResolutionNs, NonNullable<_>.New "Override") - let targetCallable = - result.Namespaces - |> Seq.find (fun ns -> ns.Name.Value = Signatures.IntrinsicResolutionNs) - |> (fun x -> [x]) |> SyntaxExtensions.Callables - |> Seq.find (fun call -> call.FullName = targetCallName) - - (*Check that the operation is not intrinsic*) - targetCallable.Specializations.Length > 0 |> Assert.True - targetCallable.Specializations - |> Seq.map (fun spec -> - match spec.Implementation with - | Provided _ -> true - | _ -> false - |> Assert.True) - |> ignore - - [] - member this.``Monomorphization`` () = - - let filePath = Path.Combine ("TestCases", "LinkingTests", "Generics.qs") |> Path.GetFullPath - let fileId = (new Uri(filePath)) - getManager fileId (File.ReadAllText filePath) - |> compilationManager.AddOrUpdateSourceFileAsync |> ignore - - for testCase in LinkingTests.ReadAndChunkSourceFile "Monomorphization.qs" |> Seq.zip Signatures.MonomorphizationSignatures do - this.CompileMonomorphization (snd testCase) |> - Signatures.SignatureCheck [Signatures.GenericsNs; Signatures.MonomorphizationNs] (fst testCase) - - compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore - - [] - [] - member this.``Intrinsic Resolution Basic Implementation`` () = - this.RunIntrinsicResolutionTest 1 - - - [] - [] - member this.``Intrinsic Resolution Returns UDT`` () = - this.RunIntrinsicResolutionTest 2 - - - [] - [] - member this.``Intrinsic Resolution Type Mismatch Error`` () = - Assert.Throws (fun _ -> this.RunIntrinsicResolutionTest 3) |> ignore - - - [] - [] - member this.``Intrinsic Resolution Param UDT`` () = - this.RunIntrinsicResolutionTest 4 - - - [] - [] - member this.``Intrinsic Resolution With Adj`` () = - this.RunIntrinsicResolutionTest 5 - - - [] - [] - member this.``Intrinsic Resolution Spec Mismatch Error`` () = - Assert.Throws (fun _ -> this.RunIntrinsicResolutionTest 6) |> ignore - - - [] - member this.``Fail on multiple entry points`` () = - - let entryPoints = LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" - Assert.True (entryPoints.Length > 1) - - let fileId = getTempFile() - let file = getManager fileId entryPoints.[0] - compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore - this.CompileAndVerify entryPoints.[1] [Error ErrorCode.MultipleEntryPoints] - compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore - - - [] - member this.``Entry point specialization verification`` () = - - for entryPoint in LinkingTests.ReadAndChunkSourceFile "EntryPointSpecializations.qs" do - this.CompileAndVerify entryPoint [Error ErrorCode.InvalidEntryPointSpecialization] - - for entryPoint in LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" do - this.CompileAndVerify entryPoint [] - - - [] - member this.``Entry point attribute placement verification`` () = - - this.Expect "InvalidEntryPointPlacement1" [Error ErrorCode.InvalidEntryPointPlacement] - this.Expect "InvalidEntryPointPlacement2" [Error ErrorCode.InvalidEntryPointPlacement] - this.Expect "InvalidEntryPointPlacement3" [Error ErrorCode.InvalidEntryPointPlacement] - - // the error messages here should become InvalidEntryPointPlacement if / when - // we support attaching attributes to specializations in general - this.Expect "InvalidEntryPointPlacement4" [Error ErrorCode.MisplacedDeclarationAttribute] - this.Expect "InvalidEntryPointPlacement5" [Error ErrorCode.MisplacedDeclarationAttribute] - this.Expect "InvalidEntryPointPlacement6" [Error ErrorCode.MisplacedDeclarationAttribute] - this.Expect "InvalidEntryPointPlacement7" [Error ErrorCode.MisplacedDeclarationAttribute] - - - [] - member this.``Entry point argument and return type verification`` () = - - this.Expect "InvalidEntryPoint1" [Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint2" [Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint3" [Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint4" [Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint5" [Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint6" [Error ErrorCode.QubitTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint7" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint8" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint9" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint10" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint11" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint12" [Error ErrorCode.CallableTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint13" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint14" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint15" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint16" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint17" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint18" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint19" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint20" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint21" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint22" [Error ErrorCode.CallableTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint23" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint24" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint25" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint26" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint27" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint28" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint29" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint30" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint31" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint32" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint33" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint34" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint35" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint36" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] - +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.QsCompiler.Testing + +open System +open System.Collections.Generic +open System.IO +open Microsoft.Quantum.QsCompiler +open Microsoft.Quantum.QsCompiler.CompilationBuilder +open Microsoft.Quantum.QsCompiler.DataTypes +open Microsoft.Quantum.QsCompiler.Diagnostics +open Microsoft.Quantum.QsCompiler.SyntaxExtensions +open Microsoft.Quantum.QsCompiler.SyntaxTree +open Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTransformation +open Microsoft.Quantum.QsCompiler.Transformations.IntrinsicResolutionTransformation +open Microsoft.Quantum.QsCompiler.Transformations.Monomorphization +open Microsoft.Quantum.QsCompiler.Transformations.MonomorphizationValidation +open Xunit +open Xunit.Abstractions + + +type LinkingTests (output:ITestOutputHelper) = + inherit CompilerTests(CompilerTests.Compile (Path.Combine ("TestCases", "LinkingTests" )) ["Core.qs"; "InvalidEntryPoints.qs"], output) + + 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 + + static member private ReadAndChunkSourceFile fileName = + let sourceInput = Path.Combine ("TestCases", "LinkingTests", fileName) |> File.ReadAllText + sourceInput.Split ([|"==="|], StringSplitOptions.RemoveEmptyEntries) + + member private this.Expect name (diag : IEnumerable) = + let ns = "Microsoft.Quantum.Testing.EntryPoints" |> NonNullable<_>.New + let name = name |> NonNullable<_>.New + this.Verify (QsQualifiedName.New (ns, name), diag) + + member private this.CompileAndVerify input (diag : DiagnosticItem seq) = + + let fileId = getTempFile() + let file = getManager fileId input + let inFile (c : QsCallable) = c.SourceFile = file.FileName + + compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore + let built = compilationManager.Build() + compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore + let tests = new CompilerTests(built, output) + + for callable in built.Callables.Values |> Seq.filter inFile do + tests.Verify (callable.FullName, diag) + + member private this.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 + + member private this.CompileMonomorphization input = + + let compilationDataStructures = this.BuildContent input + + let monomorphicCompilation = MonomorphizationTransformation.Apply compilationDataStructures.BuiltCompilation + + Assert.NotNull monomorphicCompilation + MonomorphizationValidationTransformation.Apply monomorphicCompilation + + 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 + let chunckNumber = 2 * (testNumber - 1) + let result = this.CompileIntrinsicResolution srcChunks.[chunckNumber] srcChunks.[chunckNumber+1] + Signatures.SignatureCheck [Signatures.IntrinsicResolutionNs] Signatures.IntrinsicResolutionSignatures.[testNumber-1] result + + (*Find the overridden operation in the appropriate namespace*) + let targetCallName = QsQualifiedName.New(NonNullable<_>.New Signatures.IntrinsicResolutionNs, NonNullable<_>.New "Override") + let targetCallable = + result.Namespaces + |> Seq.find (fun ns -> ns.Name.Value = Signatures.IntrinsicResolutionNs) + |> (fun x -> [x]) |> SyntaxExtensions.Callables + |> Seq.find (fun call -> call.FullName = targetCallName) + + (*Check that the operation is not intrinsic*) + targetCallable.Specializations.Length > 0 |> Assert.True + targetCallable.Specializations + |> Seq.map (fun spec -> + match spec.Implementation with + | Provided _ -> true + | _ -> false + |> Assert.True) + |> ignore + + member private this.CompileClassicalControl input = + let compilationDataStructures = this.BuildContent input + + let processedCompilation = ClassicallyControlledTransformation.Apply compilationDataStructures.BuiltCompilation + + Assert.NotNull processedCompilation + processedCompilation + + member private this.RunClassicalControlTest testNumber = + let srcChunks = LinkingTests.ReadAndChunkSourceFile "ClassicalControl.qs" + srcChunks.Length >= testNumber + 1 |> Assert.True + let shared = srcChunks.[0] + let result = this.CompileClassicalControl <| shared + srcChunks.[testNumber] + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + [] + member this.``Monomorphization`` () = + + let filePath = Path.Combine ("TestCases", "LinkingTests", "Generics.qs") |> Path.GetFullPath + let fileId = (new Uri(filePath)) + getManager fileId (File.ReadAllText filePath) + |> compilationManager.AddOrUpdateSourceFileAsync |> ignore + + for testCase in LinkingTests.ReadAndChunkSourceFile "Monomorphization.qs" |> Seq.zip Signatures.MonomorphizationSignatures do + this.CompileMonomorphization (snd testCase) |> + Signatures.SignatureCheck [Signatures.GenericsNs; Signatures.MonomorphizationNs] (fst testCase) + + compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore + + [] + [] + member this.``Intrinsic Resolution Basic Implementation`` () = + this.RunIntrinsicResolutionTest 1 + + + [] + [] + member this.``Intrinsic Resolution Returns UDT`` () = + this.RunIntrinsicResolutionTest 2 + + + [] + [] + member this.``Intrinsic Resolution Type Mismatch Error`` () = + Assert.Throws (fun _ -> this.RunIntrinsicResolutionTest 3) |> ignore + + + [] + [] + member this.``Intrinsic Resolution Param UDT`` () = + this.RunIntrinsicResolutionTest 4 + + + [] + [] + member this.``Intrinsic Resolution With Adj`` () = + this.RunIntrinsicResolutionTest 5 + + + [] + [] + member this.``Intrinsic Resolution Spec Mismatch Error`` () = + Assert.Throws (fun _ -> this.RunIntrinsicResolutionTest 6) |> ignore + + + [] + [] + member this.``Classical Control Basic Implementation`` () = + this.RunClassicalControlTest 1 + + + [] + [] + member this.``Classical Control Single Expression`` () = + // Single expressions should not be hoisted into their own operation + this.RunClassicalControlTest 2 + + + [] + member this.``Fail on multiple entry points`` () = + + let entryPoints = LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" + Assert.True (entryPoints.Length > 1) + + let fileId = getTempFile() + let file = getManager fileId entryPoints.[0] + compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore + this.CompileAndVerify entryPoints.[1] [Error ErrorCode.MultipleEntryPoints] + compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore + + + [] + member this.``Entry point specialization verification`` () = + + for entryPoint in LinkingTests.ReadAndChunkSourceFile "EntryPointSpecializations.qs" do + this.CompileAndVerify entryPoint [Error ErrorCode.InvalidEntryPointSpecialization] + + for entryPoint in LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" do + this.CompileAndVerify entryPoint [] + + + [] + member this.``Entry point attribute placement verification`` () = + + this.Expect "InvalidEntryPointPlacement1" [Error ErrorCode.InvalidEntryPointPlacement] + this.Expect "InvalidEntryPointPlacement2" [Error ErrorCode.InvalidEntryPointPlacement] + this.Expect "InvalidEntryPointPlacement3" [Error ErrorCode.InvalidEntryPointPlacement] + + // the error messages here should become InvalidEntryPointPlacement if / when + // we support attaching attributes to specializations in general + this.Expect "InvalidEntryPointPlacement4" [Error ErrorCode.MisplacedDeclarationAttribute] + this.Expect "InvalidEntryPointPlacement5" [Error ErrorCode.MisplacedDeclarationAttribute] + this.Expect "InvalidEntryPointPlacement6" [Error ErrorCode.MisplacedDeclarationAttribute] + this.Expect "InvalidEntryPointPlacement7" [Error ErrorCode.MisplacedDeclarationAttribute] + + + [] + member this.``Entry point argument and return type verification`` () = + + this.Expect "InvalidEntryPoint1" [Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint2" [Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint3" [Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint4" [Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint5" [Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint6" [Error ErrorCode.QubitTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint7" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint8" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint9" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint10" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint11" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint12" [Error ErrorCode.CallableTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint13" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint14" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint15" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint16" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint17" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint18" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint19" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint20" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint21" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint22" [Error ErrorCode.CallableTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint23" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint24" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint25" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint26" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint27" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint28" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint29" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint30" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint31" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint32" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint33" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint34" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint35" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint36" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] + 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..f38ee3d931 --- /dev/null +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace SubOps { + operation SubOp1() : Unit { } + operation SubOp2() : Unit { } + operation SubOp3() : Unit { } +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTest1() : Unit { + Foo(); + } + + operation Foo() : Unit { + let r = One; + if (r == One) { + SubOp1(); + SubOp2(); + SubOp3(); + } + } + +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTest2() : Unit { + Foo(); + } + + operation Foo() : Unit { + let r = One; + if (r == One) { + SubOp1(); + } + } + +} \ 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..caddb885b6 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -1,184 +1,201 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -module Microsoft.Quantum.QsCompiler.Testing.Signatures - -open System.Collections.Generic -open System.Collections.Immutable -open Microsoft.Quantum.QsCompiler -open Microsoft.Quantum.QsCompiler.DataTypes -open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput -open Microsoft.Quantum.QsCompiler.SyntaxTokens -open Microsoft.Quantum.QsCompiler.SyntaxTree -open Xunit - -let private _BaseTypes = - [| - "Unit", UnitType; - "Int", Int; - "Double", Double; - "String", String; - "Qubit", Qubit; - "Qubit[]", ResolvedType.New Qubit |> ArrayType; - |] - -let private _MakeTypeMap udts = - Array.concat - [ - _BaseTypes - udts - ] - |> Seq.map (fun (k, v) -> k, ResolvedType.New v) |> dict - -let private _DefaultTypes = _MakeTypeMap [||] - -let private _MakeSig input (typeMap : IDictionary ) = - let ns, name, args, rtrn = input - let fullName = { Namespace = NonNullable.New ns; Name = NonNullable.New name } - let argType = - if Array.isEmpty args then - typeMap.["Unit"] - else - args |> Seq.map (fun typ -> typeMap.[typ]) |> ImmutableArray.ToImmutableArray |> QsTypeKind.TupleType |> ResolvedType.New - let returnType = typeMap.[rtrn] - (fullName, argType, returnType) - -let private _MakeSignatures sigs = - sigs - |> Seq.map (fun (types, case) -> Seq.map (fun _sig -> _MakeSig _sig types) case) - |> Seq.toArray - -/// 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 = - - let getNs targetNs = - match Seq.tryFind (fun (ns : QsNamespace) -> ns.Name.Value = targetNs) compilation.Namespaces with - | Some ns -> ns - | None -> sprintf "Expected but did not find namespace: %s" targetNs |> failwith - - let callableSigs = - checkedNamespaces - |> Seq.map (fun checkedNs -> getNs checkedNs) - |> SyntaxExtensions.Callables - |> Seq.map (fun call -> (call.FullName, StripPositionInfo.Apply call.Signature.ArgumentType, StripPositionInfo.Apply call.Signature.ReturnType)) - - let doesCallMatchSig call signature = - let (call_fullName : QsQualifiedName), call_argType, call_rtrnType = call - let (sig_fullName : QsQualifiedName), sig_argType, sig_rtrnType = signature - - call_fullName.Namespace.Value = sig_fullName.Namespace.Value && - call_fullName.Name.Value.EndsWith sig_fullName.Name.Value && - call_argType = sig_argType && - call_rtrnType = sig_rtrnType - - let makeArgsString (args : ResolvedType) = - match args.Resolution with - | QsTypeKind.UnitType -> "()" - | _ -> args |> (ExpressionToQs () |> ExpressionTypeToQs).Apply - - (*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)) - - (*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)) - -/// 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" - -/// 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"; - |]); - (_DefaultTypes, [| (*Test Case 2*) - MonomorphizationNs, "Test2", [||], "Unit"; - GenericsNs, "Test2Main", [||], "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"; - |]) - |] - |> _MakeSignatures - -let private _IntrinsicResolutionTypes = _MakeTypeMap [| - "TestType", {Namespace = NonNullable<_>.New "Microsoft.Quantum.Testing.IntrinsicResolution"; Name = NonNullable<_>.New "TestType"; Range = Null} |> UserDefinedType - |] - -/// Expected callable signatures to be found when running Intrinsic Resolution tests -let public IntrinsicResolutionSignatures = - [| - (_DefaultTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest1", [||], "Unit"; - IntrinsicResolutionNs, "LocalIntrinsic", [||], "Unit"; - IntrinsicResolutionNs, "Override", [||], "Unit"; - IntrinsicResolutionNs, "EnvironmentIntrinsic", [||], "Unit"; - |]); - (_IntrinsicResolutionTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest2", [||], "Unit"; - IntrinsicResolutionNs, "Override", [||], "TestType"; - IntrinsicResolutionNs, "TestType", [||], "TestType"; - |]); - (_IntrinsicResolutionTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest3", [||], "Unit"; - IntrinsicResolutionNs, "Override", [||], "TestType"; - IntrinsicResolutionNs, "TestType", [||], "TestType"; - |]); - (_IntrinsicResolutionTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest4", [||], "Unit"; - IntrinsicResolutionNs, "Override", [|"TestType"|], "Unit"; - IntrinsicResolutionNs, "TestType", [||], "TestType"; - |]); - (_DefaultTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest5", [||], "Unit"; - IntrinsicResolutionNs, "Override", [||], "Unit"; - |]); - (_DefaultTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest6", [||], "Unit"; - IntrinsicResolutionNs, "Override", [||], "Unit"; - |]); - |] +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module Microsoft.Quantum.QsCompiler.Testing.Signatures + +open System.Collections.Generic +open System.Collections.Immutable +open Microsoft.Quantum.QsCompiler +open Microsoft.Quantum.QsCompiler.DataTypes +open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput +open Microsoft.Quantum.QsCompiler.SyntaxTokens +open Microsoft.Quantum.QsCompiler.SyntaxTree +open Xunit + +let private _BaseTypes = + [| + "Unit", UnitType; + "Int", Int; + "Double", Double; + "String", String; + "Result", Result; + "Qubit", Qubit; + "Qubit[]", ResolvedType.New Qubit |> ArrayType; + |] + +let private _MakeTypeMap udts = + Array.concat + [ + _BaseTypes + udts + ] + |> Seq.map (fun (k, v) -> k, ResolvedType.New v) |> dict + +let private _DefaultTypes = _MakeTypeMap [||] + +let private _MakeSig input (typeMap : IDictionary ) = + let ns, name, args, rtrn = input + let fullName = { Namespace = NonNullable.New ns; Name = NonNullable.New name } + let argType = + if Array.isEmpty args then + typeMap.["Unit"] + else + args |> Seq.map (fun typ -> typeMap.[typ]) |> ImmutableArray.ToImmutableArray |> QsTypeKind.TupleType |> ResolvedType.New + let returnType = typeMap.[rtrn] + (fullName, argType, returnType) + +let private _MakeSignatures sigs = + sigs + |> Seq.map (fun (types, case) -> Seq.map (fun _sig -> _MakeSig _sig types) case) + |> Seq.toArray + +/// 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 = + + let getNs targetNs = + match Seq.tryFind (fun (ns : QsNamespace) -> ns.Name.Value = targetNs) compilation.Namespaces with + | Some ns -> ns + | None -> sprintf "Expected but did not find namespace: %s" targetNs |> failwith + + let callableSigs = + checkedNamespaces + |> Seq.map (fun checkedNs -> getNs checkedNs) + |> SyntaxExtensions.Callables + |> Seq.map (fun call -> (call.FullName, StripPositionInfo.Apply call.Signature.ArgumentType, StripPositionInfo.Apply call.Signature.ReturnType)) + + let doesCallMatchSig call signature = + let (call_fullName : QsQualifiedName), call_argType, call_rtrnType = call + let (sig_fullName : QsQualifiedName), sig_argType, sig_rtrnType = signature + + call_fullName.Namespace.Value = sig_fullName.Namespace.Value && + call_fullName.Name.Value.EndsWith sig_fullName.Name.Value && + call_argType = sig_argType && + call_rtrnType = sig_rtrnType + + let makeArgsString (args : ResolvedType) = + match args.Resolution with + | QsTypeKind.UnitType -> "()" + | _ -> args |> (ExpressionToQs () |> ExpressionTypeToQs).Apply + + (*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)) + + (*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)) + +/// 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"; + |]); + (_DefaultTypes, [| (*Test Case 2*) + MonomorphizationNs, "Test2", [||], "Unit"; + GenericsNs, "Test2Main", [||], "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"; + |]) + |] + |> _MakeSignatures + +let private _IntrinsicResolutionTypes = _MakeTypeMap [| + "TestType", {Namespace = NonNullable<_>.New "Microsoft.Quantum.Testing.IntrinsicResolution"; Name = NonNullable<_>.New "TestType"; Range = Null} |> UserDefinedType + |] + +/// Expected callable signatures to be found when running Intrinsic Resolution tests +let public IntrinsicResolutionSignatures = + [| + (_DefaultTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest1", [||], "Unit"; + IntrinsicResolutionNs, "LocalIntrinsic", [||], "Unit"; + IntrinsicResolutionNs, "Override", [||], "Unit"; + IntrinsicResolutionNs, "EnvironmentIntrinsic", [||], "Unit"; + |]); + (_IntrinsicResolutionTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest2", [||], "Unit"; + IntrinsicResolutionNs, "Override", [||], "TestType"; + IntrinsicResolutionNs, "TestType", [||], "TestType"; + |]); + (_IntrinsicResolutionTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest3", [||], "Unit"; + IntrinsicResolutionNs, "Override", [||], "TestType"; + IntrinsicResolutionNs, "TestType", [||], "TestType"; + |]); + (_IntrinsicResolutionTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest4", [||], "Unit"; + IntrinsicResolutionNs, "Override", [|"TestType"|], "Unit"; + IntrinsicResolutionNs, "TestType", [||], "TestType"; + |]); + (_DefaultTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest5", [||], "Unit"; + IntrinsicResolutionNs, "Override", [||], "Unit"; + |]); + (_DefaultTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest6", [||], "Unit"; + IntrinsicResolutionNs, "Override", [||], "Unit"; + |]); + |] + |> _MakeSignatures + +/// Expected callable signatures to be found when running Classical Control tests +let public ClassicalControlSignatures = + [| + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTest1", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; // The original operation + ClassicalControlNs, "Foo", [|"Result"|], "Unit"; // The generated operation + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTest2", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; // The original operation + |]); + |] |> _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..9868547ab0 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -41,6 +41,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest From 57d22b9af7804ebe549aebfb5bb6a9c691ea4a40 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 10 Jan 2020 10:57:44 -0800 Subject: [PATCH 27/53] Added test to check if-else and if-elif-else structures. --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 6 ++ .../LinkingTests/ClassicalControl.qs | 71 +++++++++++++++++++ .../Tests.Compiler/TestUtils/Signatures.fs | 51 +++++++------ 3 files changed, 107 insertions(+), 21 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 6273d401d5..08817ae368 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -190,6 +190,12 @@ type LinkingTests (output:ITestOutputHelper) = this.RunClassicalControlTest 2 + [] + [] + member this.``Classical Control If Elif`` () = + this.RunClassicalControlTest 3 + + [] member this.``Fail on multiple entry points`` () = diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs index f38ee3d931..c5db4ef640 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. + namespace SubOps { operation SubOp1() : Unit { } operation SubOp2() : Unit { } @@ -19,11 +20,20 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { operation Foo() : Unit { let r = One; + if (r == One) { SubOp1(); SubOp2(); SubOp3(); } + + let temp = 0; + + if (r == Zero) { + SubOp2(); + SubOp3(); + SubOp1(); + } } } @@ -45,4 +55,65 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { } } +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTest3() : Unit { + Foo(); + } + + operation Foo() : Unit { + let r = One; + + if (r == One) { + SubOp1(); + SubOp2(); + } else { + SubOp2(); + SubOp3(); + } + + let temp = 0; + + if (r == Zero) { + SubOp1(); + SubOp2(); + } else { + SubOp2(); + SubOp3(); + } + } + +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTest3() : Unit { + Foo(); + } + + operation Foo() : Unit { + let r = One; + + if (r == One) { + SubOp1(); + SubOp2(); + } elif (r == Zero) { + SubOp3(); + SubOp1(); + } else { + SubOp2(); + SubOp3(); + } + } + } \ 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 caddb885b6..0d44da7311 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -154,33 +154,33 @@ 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 @@ -189,13 +189,22 @@ let public IntrinsicResolutionSignatures = let public ClassicalControlSignatures = [| (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTest1", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; // The original operation - ClassicalControlNs, "Foo", [|"Result"|], "Unit"; // The generated operation + ClassicalControlNs, "ClassicalControlTest1", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; // The original operation + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; // The 1st generated operation + ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; // The 2nd generated operation |]); (_DefaultTypes, [| ClassicalControlNs, "ClassicalControlTest2", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; // The original operation + ClassicalControlNs, "Foo", [||], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTest3", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; |]); |] |> _MakeSignatures \ No newline at end of file From 5dc5d895735d3ae5d192bf691f4ca00a9c024942 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Sat, 11 Jan 2020 16:34:41 -0800 Subject: [PATCH 28/53] Set up some logic to tests what calls are being made in callables. --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 111 +++++++++++++++--- .../LinkingTests/ClassicalControl.qs | 36 ++++-- .../Tests.Compiler/TestUtils/Signatures.fs | 3 +- 3 files changed, 127 insertions(+), 23 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 08817ae368..5e084fddf4 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -18,6 +18,8 @@ open Microsoft.Quantum.QsCompiler.Transformations.Monomorphization open Microsoft.Quantum.QsCompiler.Transformations.MonomorphizationValidation open Xunit open Xunit.Abstractions +open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput +open System.Text.RegularExpressions type LinkingTests (output:ITestOutputHelper) = @@ -112,20 +114,23 @@ type LinkingTests (output:ITestOutputHelper) = |> Assert.True) |> ignore - member private this.CompileClassicalControl input = - let compilationDataStructures = this.BuildContent input - + member private this.CompileClassicalControlTest testNumber = + let srcChunks = LinkingTests.ReadAndChunkSourceFile "ClassicalControl.qs" + srcChunks.Length >= testNumber + 1 |> Assert.True + let shared = srcChunks.[0] + let compilationDataStructures = this.BuildContent <| shared + srcChunks.[testNumber] let processedCompilation = ClassicallyControlledTransformation.Apply compilationDataStructures.BuiltCompilation - Assert.NotNull processedCompilation processedCompilation - member private this.RunClassicalControlTest testNumber = - let srcChunks = LinkingTests.ReadAndChunkSourceFile "ClassicalControl.qs" - srcChunks.Length >= testNumber + 1 |> Assert.True - let shared = srcChunks.[0] - let result = this.CompileClassicalControl <| shared + srcChunks.[testNumber] - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + //member private this.RunClassicalControlTest testNumber = + // let srcChunks = LinkingTests.ReadAndChunkSourceFile "ClassicalControl.qs" + // srcChunks.Length >= testNumber + 1 |> Assert.True + // let shared = srcChunks.[0] + // let result = this.CompileClassicalControl <| shared + srcChunks.[testNumber] + // Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + [] member this.``Monomorphization`` () = @@ -177,23 +182,101 @@ type LinkingTests (output:ITestOutputHelper) = Assert.Throws (fun _ -> this.RunIntrinsicResolutionTest 6) |> ignore + static member private GetLinesFromCallable callable = + let writer = new SyntaxTreeToQs() + + callable.Specializations + |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsBody) + |> fun x -> match x.Implementation with | Provided (_, body) -> Some body | _ -> None + |> Option.get + |> writer.Scope.Transform + |> ignore + + (writer.Scope :?> ScopeToQs).Output.Split("\r\n") + + + static member private CheckIfCallable ``namespace`` name input = + let call = "(" + (Regex.Escape ``namespace``) + @"\.)?" + (Regex.Escape name) + let typeArgs = @"<\s*(.*[^\s])\s*>" + let args = @"\(\s*(.*[^\s])?\s*\)" + let regex = "^" + call + @"\s*(" + typeArgs + @")?\s*" + args + @";[\r\n]*$" + + let regexMatch = Regex.Match(input, regex) + if regexMatch.Success then + (true, regexMatch.Groups.[3].Value, regexMatch.Groups.[4].Value) + else + (false, "", "") + [] [] - member this.``Classical Control Basic Implementation`` () = - this.RunClassicalControlTest 1 + member this.``Classical Control Basic Hoist`` () = + let testNumber = 1 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + let lines = result.Namespaces + |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) + |> GlobalCallableResolutions + |> Seq.find (fun x -> x.Key.Name.Value.EndsWith("_Foo")) + |> fun x -> x.Value + |> LinkingTests.GetLinesFromCallable + + lines.[0] + |> LinkingTests.CheckIfCallable "SubOps" "SubOp1" + |> (fun (x, _, _) -> Assert.True(x)) + + lines.[1] + |> LinkingTests.CheckIfCallable "SubOps" "SubOp2" + |> (fun (x, _, _) -> Assert.True(x)) + + lines.[2] + |> LinkingTests.CheckIfCallable "SubOps" "SubOp3" + |> (fun (x, _, _) -> Assert.True(x)) + + //[] + //[] + //member this.``Classical Control Basic Implementation`` () = + // let testNumber = 1 + // let result = this.CompileClassicalControlTest testNumber + // Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + // + // let Foo = result.Namespaces + // |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) + // |> GlobalCallableResolutions + // |> Seq.find (fun x -> x.Key.Name.Value = "Foo") + // |> fun x -> x.Value.Specializations + // |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsBody) + // |> fun x -> match x.Implementation with | Provided (_, body) -> Some body | _ -> None + // |> Option.get + // + // let writer = new SyntaxTreeToQs() + // + // writer.Scope.Transform(Foo) |> ignore + // let lines = (writer.Scope :?> ScopeToQs).Output.Split('\n'); + // + // lines.[1] + // |> LinkingTests.CheckIfCallable {Namespace = BuiltIn.ApplyIfOne.Namespace; Name = BuiltIn.ApplyIfOne.Name} + // |> (fun (x, _, _) -> Assert.True(x)) + // + // lines.[3] + // |> LinkingTests.CheckIfCallable {Namespace = BuiltIn.ApplyIfZero.Namespace; Name = BuiltIn.ApplyIfZero.Name} + // |> (fun (x, _, _) -> Assert.True(x)) [] [] member this.``Classical Control Single Expression`` () = // Single expressions should not be hoisted into their own operation - this.RunClassicalControlTest 2 + let testNumber = 2 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result [] [] member this.``Classical Control If Elif`` () = - this.RunClassicalControlTest 3 + let testNumber = 3 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result [] diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs index c5db4ef640..6bbae3854b 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -26,12 +26,23 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { SubOp2(); SubOp3(); } + } - let temp = 0; +} - if (r == Zero) { - SubOp2(); - SubOp3(); +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTest2() : Unit { + Foo(); + } + + operation Foo() : Unit { + let r = One; + if (r == One) { SubOp1(); } } @@ -44,14 +55,25 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest2() : Unit { + operation ClassicalControlTest3() : Unit { Foo(); } operation Foo() : Unit { let r = One; + if (r == One) { SubOp1(); + SubOp2(); + SubOp3(); + } + + let temp = 0; + + if (r == Zero) { + SubOp2(); + SubOp3(); + SubOp1(); } } @@ -63,7 +85,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest3() : Unit { + operation ClassicalControlTest4() : Unit { Foo(); } @@ -97,7 +119,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest3() : Unit { + operation ClassicalControlTest5() : Unit { Foo(); } diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs index 0d44da7311..e60d93b3b6 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -191,8 +191,7 @@ let public ClassicalControlSignatures = (_DefaultTypes, [| ClassicalControlNs, "ClassicalControlTest1", [||], "Unit"; ClassicalControlNs, "Foo", [||], "Unit"; // The original operation - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; // The 1st generated operation - ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; // The 2nd generated operation + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; // The generated operation |]); (_DefaultTypes, [| ClassicalControlNs, "ClassicalControlTest2", [||], "Unit"; From 0c831caa410f5a84e7bfe8e47577aba5b9d12fe1 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Mon, 13 Jan 2020 18:58:20 -0800 Subject: [PATCH 29/53] just more progress --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 168 +++++++++++++----- .../LinkingTests/ClassicalControl.qs | 38 ++-- .../Tests.Compiler/TestUtils/Signatures.fs | 18 ++ 3 files changed, 163 insertions(+), 61 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 5e084fddf4..2786b95647 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -193,13 +193,12 @@ type LinkingTests (output:ITestOutputHelper) = |> ignore (writer.Scope :?> ScopeToQs).Output.Split("\r\n") - - static member private CheckIfCallable ``namespace`` name input = + static member private CheckIfLineIsCall ``namespace`` name input = let call = "(" + (Regex.Escape ``namespace``) + @"\.)?" + (Regex.Escape name) let typeArgs = @"<\s*(.*[^\s])\s*>" let args = @"\(\s*(.*[^\s])?\s*\)" - let regex = "^" + call + @"\s*(" + typeArgs + @")?\s*" + args + @";[\r\n]*$" + let regex = "^" + call + @"\s*(" + typeArgs + @")?\s*" + args + @";$" let regexMatch = Regex.Match(input, regex) if regexMatch.Success then @@ -207,6 +206,13 @@ type LinkingTests (output:ITestOutputHelper) = else (false, "", "") + static member private CheckIfCallHasContent call (content : seq) = + let lines = LinkingTests.GetLinesFromCallable call + Seq.forall (fun (i, ns, name) -> LinkingTests.CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content + + static member private AssertCallHasContent call content = + Assert.True(LinkingTests.CheckIfCallHasContent call content, sprintf "Callable %A did not have expected content" call.FullName) + [] [] member this.``Classical Control Basic Hoist`` () = @@ -214,53 +220,19 @@ type LinkingTests (output:ITestOutputHelper) = let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - let lines = result.Namespaces - |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) - |> GlobalCallableResolutions - |> Seq.find (fun x -> x.Key.Name.Value.EndsWith("_Foo")) - |> fun x -> x.Value - |> LinkingTests.GetLinesFromCallable + let _Foo = result.Namespaces + |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) + |> GlobalCallableResolutions + |> Seq.find (fun x -> x.Key.Name.Value.EndsWith "_Foo") - lines.[0] - |> LinkingTests.CheckIfCallable "SubOps" "SubOp1" - |> (fun (x, _, _) -> Assert.True(x)) + let content = + [ + (0, "SubOps", "SubOp1"); + (1, "SubOps", "SubOp2"); + (2, "SubOps", "SubOp3"); + ] - lines.[1] - |> LinkingTests.CheckIfCallable "SubOps" "SubOp2" - |> (fun (x, _, _) -> Assert.True(x)) - - lines.[2] - |> LinkingTests.CheckIfCallable "SubOps" "SubOp3" - |> (fun (x, _, _) -> Assert.True(x)) - - //[] - //[] - //member this.``Classical Control Basic Implementation`` () = - // let testNumber = 1 - // let result = this.CompileClassicalControlTest testNumber - // Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - // - // let Foo = result.Namespaces - // |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) - // |> GlobalCallableResolutions - // |> Seq.find (fun x -> x.Key.Name.Value = "Foo") - // |> fun x -> x.Value.Specializations - // |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsBody) - // |> fun x -> match x.Implementation with | Provided (_, body) -> Some body | _ -> None - // |> Option.get - // - // let writer = new SyntaxTreeToQs() - // - // writer.Scope.Transform(Foo) |> ignore - // let lines = (writer.Scope :?> ScopeToQs).Output.Split('\n'); - // - // lines.[1] - // |> LinkingTests.CheckIfCallable {Namespace = BuiltIn.ApplyIfOne.Namespace; Name = BuiltIn.ApplyIfOne.Name} - // |> (fun (x, _, _) -> Assert.True(x)) - // - // lines.[3] - // |> LinkingTests.CheckIfCallable {Namespace = BuiltIn.ApplyIfZero.Namespace; Name = BuiltIn.ApplyIfZero.Name} - // |> (fun (x, _, _) -> Assert.True(x)) + LinkingTests.AssertCallHasContent _Foo.Value content [] [] @@ -270,11 +242,109 @@ type LinkingTests (output:ITestOutputHelper) = let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + [] + [] + member this.``Classical Control Use ApplyIfZero And ApplyIfOne`` () = + let testNumber = 3 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + let Foo = result.Namespaces + |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) + |> GlobalCallableResolutions + |> Seq.find (fun x -> x.Key.Name.Value = "Foo") + + let getNameFromBuiltin (builtIn : BuiltIn) = builtIn.Namespace.Value, builtIn.Name.Value + + let content = + [ + (1, getNameFromBuiltin BuiltIn.ApplyIfZero); + (3, getNameFromBuiltin BuiltIn.ApplyIfOne); + ] + |> Seq.map (fun (i,(ns,name)) -> (i,ns,name)) + + LinkingTests.AssertCallHasContent Foo.Value content + + + member private this.ApplyIfElseTest testNumber = + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + let generated = result.Namespaces + |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) + |> GlobalCallableResolutions + |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_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 getGeneratedCallables gen1 gen2 = + if LinkingTests.CheckIfCallHasContent gen1 ifContent then + LinkingTests.AssertCallHasContent gen2 elseContent + (gen1, gen2) + else + LinkingTests.AssertCallHasContent gen2 ifContent + LinkingTests.AssertCallHasContent gen1 elseContent + (gen2, gen1) + + let ifOp, elseOp = getGeneratedCallables (Seq.item 0 generated).Value (Seq.item 1 generated).Value + + let lines = result.Namespaces + |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) + |> GlobalCallableResolutions + |> Seq.find (fun x -> x.Key.Name.Value = "Foo") + |> fun x -> x.Value + |> LinkingTests.GetLinesFromCallable + + Assert.True(2 = Seq.length lines, sprintf "Callable %s.%s did not have the expected number of statements" Signatures.ClassicalControlNs "Foo") + + let (success, _, args) = LinkingTests.CheckIfLineIsCall BuiltIn.ApplyIfElseR.Namespace.Value BuiltIn.ApplyIfElseR.Name.Value lines.[1] + Assert.True(success, sprintf "Callable %s.%s did not have expected content" Signatures.ClassicalControlNs "Foo") + + let isApplyIfElseArgsMatch resultVar (opName1 : QsQualifiedName) (opName2 : QsQualifiedName) = + + let op1 = Regex.Escape <| opName1.ToString() + let op2 = Regex.Escape <| opName2.ToString() + + let ApplyIfElseRegex = sprintf + <| @"^%s, \(%s, \(.*\)\), \(%s, \(.*\)\)$" + <| Regex.Escape resultVar + <| op1 + <| op2 + + Regex.Match(args, ApplyIfElseRegex).Success + + isApplyIfElseArgsMatch, ifOp.FullName, elseOp.FullName + + + [] + [] + member this.``Classical Control Apply If Zero Else One`` () = + let (isMatch, ifOp, elseOp) = this.ApplyIfElseTest 4 + Assert.True(isMatch "r" ifOp elseOp, "ApplyIfElse did not have the correct arguments") + + [] + [] + member this.``Classical Control Apply If One Else Zero`` () = + let (isMatch, ifOp, elseOp) = this.ApplyIfElseTest 5 + // The operation arguments should be swapped from the previous test + Assert.True(isMatch "r" elseOp ifOp, "ApplyIfElse did not have the correct arguments") + [] [] member this.``Classical Control If Elif`` () = - let testNumber = 3 + let testNumber = 6 let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs index 6bbae3854b..a18d6ee3e2 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -19,9 +19,9 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { } operation Foo() : Unit { - let r = One; + let r = Zero; - if (r == One) { + if (r == Zero) { SubOp1(); SubOp2(); SubOp3(); @@ -41,8 +41,8 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { } operation Foo() : Unit { - let r = One; - if (r == One) { + let r = Zero; + if (r == Zero) { SubOp1(); } } @@ -60,9 +60,9 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { } operation Foo() : Unit { - let r = One; + let r = Zero; - if (r == One) { + if (r == Zero) { SubOp1(); SubOp2(); SubOp3(); @@ -70,7 +70,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { let temp = 0; - if (r == Zero) { + if (r == One) { SubOp2(); SubOp3(); SubOp1(); @@ -90,19 +90,33 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { } operation Foo() : Unit { - let r = One; + let r = Zero; - if (r == One) { + if (r == Zero) { SubOp1(); SubOp2(); } else { SubOp2(); SubOp3(); } + } - let temp = 0; +} - if (r == Zero) { +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTest5() : Unit { + Foo(); + } + + operation Foo() : Unit { + let r = Zero; + + if (r == One) { SubOp1(); SubOp2(); } else { @@ -119,7 +133,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest5() : Unit { + operation ClassicalControlTest6() : Unit { Foo(); } diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs index e60d93b3b6..52217429b9 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -201,6 +201,24 @@ let public ClassicalControlSignatures = ClassicalControlNs, "ClassicalControlTest3", [||], "Unit"; ClassicalControlNs, "Foo", [||], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTest4", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTest5", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTest6", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; From 483db3f4286f167ab372fbe4a31cad6b4b347c95 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 14 Jan 2020 12:56:08 -0800 Subject: [PATCH 30/53] Trying to make test for Elif structure. Not quite there yet. --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 105 +++++++++++++----- .../Tests.Compiler/TestUtils/Signatures.fs | 21 ++-- 2 files changed, 93 insertions(+), 33 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 2786b95647..a9a8dbccaa 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -211,7 +211,7 @@ type LinkingTests (output:ITestOutputHelper) = Seq.forall (fun (i, ns, name) -> LinkingTests.CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content static member private AssertCallHasContent call content = - Assert.True(LinkingTests.CheckIfCallHasContent call content, sprintf "Callable %A did not have expected content" call.FullName) + Assert.True(LinkingTests.CheckIfCallHasContent call content, sprintf "Callable %O did not have expected content" call.FullName) [] [] @@ -220,19 +220,17 @@ type LinkingTests (output:ITestOutputHelper) = let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - let _Foo = result.Namespaces - |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) - |> GlobalCallableResolutions - |> Seq.find (fun x -> x.Key.Name.Value.EndsWith "_Foo") - - let content = - [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); - (2, "SubOps", "SubOp3"); - ] + let generated = result.Namespaces + |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) + |> GlobalCallableResolutions + |> Seq.find (fun x -> x.Key.Name.Value.EndsWith "_Foo") - LinkingTests.AssertCallHasContent _Foo.Value content + [ + (0, "SubOps", "SubOp1"); + (1, "SubOps", "SubOp2"); + (2, "SubOps", "SubOp3"); + ] + |> LinkingTests.AssertCallHasContent generated.Value [] [] @@ -249,21 +247,19 @@ type LinkingTests (output:ITestOutputHelper) = let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - let Foo = result.Namespaces - |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) - |> GlobalCallableResolutions - |> Seq.find (fun x -> x.Key.Name.Value = "Foo") + let originalOp = result.Namespaces + |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) + |> GlobalCallableResolutions + |> Seq.find (fun x -> x.Key.Name.Value = "Foo") let getNameFromBuiltin (builtIn : BuiltIn) = builtIn.Namespace.Value, builtIn.Name.Value - let content = - [ - (1, getNameFromBuiltin BuiltIn.ApplyIfZero); - (3, getNameFromBuiltin BuiltIn.ApplyIfOne); - ] - |> Seq.map (fun (i,(ns,name)) -> (i,ns,name)) - - LinkingTests.AssertCallHasContent Foo.Value content + [ + (1, getNameFromBuiltin BuiltIn.ApplyIfZero); + (3, getNameFromBuiltin BuiltIn.ApplyIfOne); + ] + |> Seq.map (fun (i,(ns,name)) -> (i,ns,name)) + |> LinkingTests.AssertCallHasContent originalOp.Value member private this.ApplyIfElseTest testNumber = @@ -348,6 +344,65 @@ type LinkingTests (output:ITestOutputHelper) = let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + let generated = result.Namespaces + |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) + |> GlobalCallableResolutions + |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_Foo") + |> Seq.map (fun x -> x.Value) + + Assert.True(3 = Seq.length generated) // Should already be asserted by the signature check + + let ifContent = + [ + (0, "SubOps", "SubOp1"); + (1, "SubOps", "SubOp2"); + ] + let ifOp = Seq.tryFind (fun callable -> LinkingTests.CheckIfCallHasContent callable ifContent) generated + |> (fun callOpion -> Assert.True(callOpion <> None, "Could not find the generated operation for the if block"); callOpion.Value) + + let elifContent = + [ + (0, "SubOps", "SubOp3"); + (1, "SubOps", "SubOp1"); + ] + let elifOp = Seq.tryFind (fun callable -> LinkingTests.CheckIfCallHasContent callable elifContent) generated + |> (fun callOpion -> Assert.True(callOpion <> None, "Could not find the generated operation for the elif block"); callOpion.Value) + + let elseContent = + [ + (0, "SubOps", "SubOp2"); + (1, "SubOps", "SubOp3"); + ] + let elseOp = Seq.tryFind (fun callable -> LinkingTests.CheckIfCallHasContent callable elseContent) generated + |> (fun callOpion -> Assert.True(callOpion <> None, "Could not find the generated operation for the else block"); callOpion.Value) + + let lines = result.Namespaces + |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) + |> GlobalCallableResolutions + |> Seq.find (fun x -> x.Key.Name.Value = "Foo") + |> fun x -> x.Value + |> LinkingTests.GetLinesFromCallable + + Assert.True(2 = Seq.length lines, sprintf "Callable %s.%s did not have the expected number of statements" Signatures.ClassicalControlNs "Foo") + + let (success, _, args) = LinkingTests.CheckIfLineIsCall BuiltIn.ApplyIfElseR.Namespace.Value BuiltIn.ApplyIfElseR.Name.Value lines.[1] + Assert.True(success, sprintf "Callable %s.%s did not have expected content" Signatures.ClassicalControlNs "Foo") + + let isApplyIfElseArgsMatch resultVar (opName1 : QsQualifiedName) (opName2 : QsQualifiedName) = + + let op1 = Regex.Escape <| opName1.ToString() + let op2 = Regex.Escape <| opName2.ToString() + + let ApplyIfElseRegex = sprintf + <| @"^%s, \(%s, \(.*\)\), \(%s, \(.*\)\)$" + <| Regex.Escape resultVar + <| op1 + <| op2 + + Regex.Match(args, ApplyIfElseRegex).Success + + Assert.True(isApplyIfElseArgsMatch "r" ifOp.FullName { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name }, "ApplyIfElse did not have the correct arguments") + [] member this.``Fail on multiple entry points`` () = diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs index 52217429b9..42d4b8df9b 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -58,7 +58,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 @@ -78,19 +78,25 @@ 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" @@ -220,8 +226,7 @@ let public ClassicalControlSignatures = ClassicalControlNs, "Foo", [||], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; |]); |] |> _MakeSignatures \ No newline at end of file From 53a9db1925878e1d04a2875e0f78c18c0d6f7d8e Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 14 Jan 2020 15:00:59 -0800 Subject: [PATCH 31/53] Pulled out some common logic. --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index a9a8dbccaa..606b9badd9 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -196,7 +196,7 @@ type LinkingTests (output:ITestOutputHelper) = static member private CheckIfLineIsCall ``namespace`` name input = let call = "(" + (Regex.Escape ``namespace``) + @"\.)?" + (Regex.Escape name) - let typeArgs = @"<\s*(.*[^\s])\s*>" + let typeArgs = @"<\s*([^<]*[^<\s])\s*>" // Does not support nested type args let args = @"\(\s*(.*[^\s])?\s*\)" let regex = "^" + call + @"\s*(" + typeArgs + @")?\s*" + args + @";$" @@ -206,6 +206,27 @@ type LinkingTests (output:ITestOutputHelper) = else (false, "", "") + static member private isApplyIfElseArgsMatch input resultVar (opName1 : QsQualifiedName) (opName2 : QsQualifiedName) = + let typeArgs = @"(<\s*([^<]*[^<\s])\s*>)?" // Does not support nested type args + let args = @"\(\s*(.*[^\s])?\s*\)" + let opToRegex (op : QsQualifiedName) = sprintf @"\((%s\.)?%s\s*%s,\s*%s\)" + <| Regex.Escape op.Namespace.Value + <| Regex.Escape op.Name.Value + <| typeArgs + <| args + + let ApplyIfElseRegex = sprintf + <| @"^%s,\s*%s,\s*%s$" + <| Regex.Escape resultVar + <| opToRegex opName1 + <| opToRegex 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, "", "", "", "") + static member private CheckIfCallHasContent call (content : seq) = let lines = LinkingTests.GetLinesFromCallable call Seq.forall (fun (i, ns, name) -> LinkingTests.CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content @@ -308,34 +329,23 @@ type LinkingTests (output:ITestOutputHelper) = let (success, _, args) = LinkingTests.CheckIfLineIsCall BuiltIn.ApplyIfElseR.Namespace.Value BuiltIn.ApplyIfElseR.Name.Value lines.[1] Assert.True(success, sprintf "Callable %s.%s did not have expected content" Signatures.ClassicalControlNs "Foo") - let isApplyIfElseArgsMatch resultVar (opName1 : QsQualifiedName) (opName2 : QsQualifiedName) = - - let op1 = Regex.Escape <| opName1.ToString() - let op2 = Regex.Escape <| opName2.ToString() - - let ApplyIfElseRegex = sprintf - <| @"^%s, \(%s, \(.*\)\), \(%s, \(.*\)\)$" - <| Regex.Escape resultVar - <| op1 - <| op2 - - Regex.Match(args, ApplyIfElseRegex).Success - - isApplyIfElseArgsMatch, ifOp.FullName, elseOp.FullName + args, ifOp.FullName, elseOp.FullName [] [] member this.``Classical Control Apply If Zero Else One`` () = - let (isMatch, ifOp, elseOp) = this.ApplyIfElseTest 4 - Assert.True(isMatch "r" ifOp elseOp, "ApplyIfElse did not have the correct arguments") + let (args, ifOp, elseOp) = this.ApplyIfElseTest 4 + LinkingTests.isApplyIfElseArgsMatch args "r" ifOp elseOp + |> (fun (x,_,_,_,_) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) [] [] member this.``Classical Control Apply If One Else Zero`` () = - let (isMatch, ifOp, elseOp) = this.ApplyIfElseTest 5 + let (args, ifOp, elseOp) = this.ApplyIfElseTest 5 // The operation arguments should be swapped from the previous test - Assert.True(isMatch "r" elseOp ifOp, "ApplyIfElse did not have the correct arguments") + LinkingTests.isApplyIfElseArgsMatch args "r" elseOp ifOp + |> (fun (x,_,_,_,_) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) [] [] @@ -387,22 +397,12 @@ type LinkingTests (output:ITestOutputHelper) = let (success, _, args) = LinkingTests.CheckIfLineIsCall BuiltIn.ApplyIfElseR.Namespace.Value BuiltIn.ApplyIfElseR.Name.Value lines.[1] Assert.True(success, sprintf "Callable %s.%s did not have expected content" Signatures.ClassicalControlNs "Foo") - - let isApplyIfElseArgsMatch resultVar (opName1 : QsQualifiedName) (opName2 : QsQualifiedName) = - - let op1 = Regex.Escape <| opName1.ToString() - let op2 = Regex.Escape <| opName2.ToString() - - let ApplyIfElseRegex = sprintf - <| @"^%s, \(%s, \(.*\)\), \(%s, \(.*\)\)$" - <| Regex.Escape resultVar - <| op1 - <| op2 - - Regex.Match(args, ApplyIfElseRegex).Success - - Assert.True(isApplyIfElseArgsMatch "r" ifOp.FullName { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name }, "ApplyIfElse did not have the correct arguments") - + + let errorMsg = "ApplyIfElse did not have the correct arguments" + let (success, _, _, _, subArgs) = LinkingTests.isApplyIfElseArgsMatch args "r" ifOp.FullName { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } + Assert.True(success, errorMsg) + LinkingTests.isApplyIfElseArgsMatch subArgs "r" elifOp.FullName elseOp.FullName + |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) [] member this.``Fail on multiple entry points`` () = From 2b41f24783e2fd14ed66e2f912b1d74bed9a197e Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 14 Jan 2020 16:28:08 -0800 Subject: [PATCH 32/53] And and Or conditions tests --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 22 +++++++++ .../LinkingTests/ClassicalControl.qs | 48 +++++++++++++++++++ .../Tests.Compiler/TestUtils/Signatures.fs | 12 +++++ 3 files changed, 82 insertions(+) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 606b9badd9..bfa6de5d61 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -404,6 +404,28 @@ type LinkingTests (output:ITestOutputHelper) = LinkingTests.isApplyIfElseArgsMatch subArgs "r" elifOp.FullName elseOp.FullName |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) + [] + [] + member this.``Classical Control And Contidtion`` () = + let (args, ifOp, elseOp) = this.ApplyIfElseTest 7 + + let errorMsg = "ApplyIfElse did not have the correct arguments" + let (success, _, subArgs, _, _) = LinkingTests.isApplyIfElseArgsMatch args "r" { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } elseOp + Assert.True(success, errorMsg) + LinkingTests.isApplyIfElseArgsMatch subArgs "r" ifOp elseOp + |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) + + [] + [] + member this.``Classical Control Or Contidtion`` () = + let (args, ifOp, elseOp) = this.ApplyIfElseTest 8 + + let errorMsg = "ApplyIfElse did not have the correct arguments" + let (success, _, _, _, subArgs) = LinkingTests.isApplyIfElseArgsMatch args "r" ifOp { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } + Assert.True(success, errorMsg) + LinkingTests.isApplyIfElseArgsMatch subArgs "r" ifOp elseOp + |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) + [] member this.``Fail on multiple entry points`` () = diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs index a18d6ee3e2..8ce19156a3 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -152,4 +152,52 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { } } +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTest7() : Unit { + Foo(); + } + + operation Foo() : Unit { + let r = One; + + if (r == One and r == Zero) { + SubOp1(); + SubOp2(); + } else { + SubOp2(); + SubOp3(); + } + } + +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTest8() : Unit { + Foo(); + } + + operation Foo() : Unit { + let r = One; + + if (r == One or r == Zero) { + SubOp1(); + SubOp2(); + } else { + SubOp2(); + SubOp3(); + } + } + } \ 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 42d4b8df9b..aafb70d3e8 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -228,5 +228,17 @@ let public ClassicalControlSignatures = ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTest7", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTest8", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); |] |> _MakeSignatures \ No newline at end of file From f155c86d83f775491add74f0e75a438bec216521 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 14 Jan 2020 18:19:15 -0800 Subject: [PATCH 33/53] Finished off 'Hoisting' tests. Still need tests for mutable support, functor support, and generics support. --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 57 +++++- .../LinkingTests/ClassicalControl.qs | 177 ++++++++++++++++-- .../Tests.Compiler/TestUtils/Signatures.fs | 41 +++- 3 files changed, 241 insertions(+), 34 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index bfa6de5d61..8fbd56a422 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -255,19 +255,48 @@ type LinkingTests (output:ITestOutputHelper) = [] [] - member this.``Classical Control Single Expression`` () = - // Single expressions should not be hoisted into their own operation + member this.``Classical Control Hoist Loops`` () = let testNumber = 2 let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result [] [] - member this.``Classical Control Use ApplyIfZero And ApplyIfOne`` () = + member this.``Classical Control Don't Hoist Single Call`` () = + // Single calls should not be hoisted into their own operation let testNumber = 3 let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + [] + [] + member this.``Classical Control Hoist Single Non-Call`` () = + // Single expressions that are not calls should be hoisted into their own operation + let testNumber = 4 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + [] + [] + member this.``Classical Control Don't Hoist Return Statments`` () = + let testNumber = 5 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + [] + [] + member this.``Classical Control All-Or-None Hoisting`` () = + let testNumber = 6 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + [] + [] + member this.``Classical Control ApplyIfZero And ApplyIfOne`` () = + let testNumber = 7 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + let originalOp = result.Namespaces |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) |> GlobalCallableResolutions @@ -335,14 +364,14 @@ type LinkingTests (output:ITestOutputHelper) = [] [] member this.``Classical Control Apply If Zero Else One`` () = - let (args, ifOp, elseOp) = this.ApplyIfElseTest 4 + let (args, ifOp, elseOp) = this.ApplyIfElseTest 8 LinkingTests.isApplyIfElseArgsMatch args "r" ifOp elseOp |> (fun (x,_,_,_,_) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) [] [] member this.``Classical Control Apply If One Else Zero`` () = - let (args, ifOp, elseOp) = this.ApplyIfElseTest 5 + let (args, ifOp, elseOp) = this.ApplyIfElseTest 9 // The operation arguments should be swapped from the previous test LinkingTests.isApplyIfElseArgsMatch args "r" elseOp ifOp |> (fun (x,_,_,_,_) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) @@ -350,7 +379,7 @@ type LinkingTests (output:ITestOutputHelper) = [] [] member this.``Classical Control If Elif`` () = - let testNumber = 6 + let testNumber = 10 let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result @@ -406,8 +435,8 @@ type LinkingTests (output:ITestOutputHelper) = [] [] - member this.``Classical Control And Contidtion`` () = - let (args, ifOp, elseOp) = this.ApplyIfElseTest 7 + member this.``Classical Control And Condition`` () = + let (args, ifOp, elseOp) = this.ApplyIfElseTest 11 let errorMsg = "ApplyIfElse did not have the correct arguments" let (success, _, subArgs, _, _) = LinkingTests.isApplyIfElseArgsMatch args "r" { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } elseOp @@ -417,8 +446,8 @@ type LinkingTests (output:ITestOutputHelper) = [] [] - member this.``Classical Control Or Contidtion`` () = - let (args, ifOp, elseOp) = this.ApplyIfElseTest 8 + member this.``Classical Control Or Condition`` () = + let (args, ifOp, elseOp) = this.ApplyIfElseTest 12 let errorMsg = "ApplyIfElse did not have the correct arguments" let (success, _, _, _, subArgs) = LinkingTests.isApplyIfElseArgsMatch args "r" ifOp { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } @@ -426,6 +455,14 @@ type LinkingTests (output:ITestOutputHelper) = LinkingTests.isApplyIfElseArgsMatch subArgs "r" ifOp elseOp |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) + [] + [] + member this.``Classical Control Don't Hoist Functions`` () = + let testNumber = 13 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + [] member this.``Fail on multiple entry points`` () = diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs index 8ce19156a3..158594c26e 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -14,7 +14,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest1() : Unit { + operation ClassicalControlTestMain() : Unit { Foo(); } @@ -25,6 +25,38 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { SubOp1(); SubOp2(); SubOp3(); + let temp = 4; + using (q = Qubit()) { + let temp2 = q; + } + } + } + +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + + @EntryPoint() + operation ClassicalControlTestMain() : Unit { + Foo(); + } + + 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; + } } } @@ -36,7 +68,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest2() : Unit { + operation ClassicalControlTestMain() : Unit { Foo(); } @@ -55,7 +87,98 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest3() : Unit { + operation ClassicalControlTestMain() : Unit { + Foo(); + } + + operation Foo() : Unit { + let r = Zero; + + if (r == Zero) { + let temp = 2; + } + } + +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTestMain() : Unit { + Foo(); + } + + operation Foo() : Unit { + let r = Zero; + if (r == Zero) { + SubOp1(); + return (); + } + } + +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTestMain() : Unit { + IfInvalid(); + ElseInvalid(); + BothInvalid(); + } + + 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 (); + } + } + +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTestMain() : Unit { Foo(); } @@ -85,7 +208,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest4() : Unit { + operation ClassicalControlTestMain() : Unit { Foo(); } @@ -109,12 +232,12 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest5() : Unit { + operation ClassicalControlTestMain() : Unit { Foo(); } operation Foo() : Unit { - let r = Zero; + let r = One; if (r == One) { SubOp1(); @@ -133,17 +256,17 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest6() : Unit { + operation ClassicalControlTestMain() : Unit { Foo(); } operation Foo() : Unit { - let r = One; + let r = Zero; - if (r == One) { + if (r == Zero) { SubOp1(); SubOp2(); - } elif (r == Zero) { + } elif (r == One) { SubOp3(); SubOp1(); } else { @@ -160,14 +283,14 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest7() : Unit { + operation ClassicalControlTestMain() : Unit { Foo(); } operation Foo() : Unit { - let r = One; + let r = Zero; - if (r == One and r == Zero) { + if (r == Zero and r == One) { SubOp1(); SubOp2(); } else { @@ -184,14 +307,14 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @EntryPoint() - operation ClassicalControlTest8() : Unit { + operation ClassicalControlTestMain() : Unit { Foo(); } operation Foo() : Unit { - let r = One; + let r = Zero; - if (r == One or r == Zero) { + if (r == Zero or r == One) { SubOp1(); SubOp2(); } else { @@ -200,4 +323,26 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { } } +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTestMain() : Unit { + Foo(); + } + + function Foo() : Unit { + let r = Zero; + + if (r == Zero) { + SubOp1(); + SubOp2(); + SubOp3(); + } + } + } \ 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 aafb70d3e8..3d44258668 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -195,50 +195,75 @@ let public IntrinsicResolutionSignatures = let public ClassicalControlSignatures = [| (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTest1", [||], "Unit"; + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; ClassicalControlNs, "Foo", [||], "Unit"; // The original operation ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; // The generated operation |]); (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTest2", [||], "Unit"; + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; |]); (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTest3", [||], "Unit"; + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "IfInvalid", [||], "Unit"; + ClassicalControlNs, "ElseInvalid", [||], "Unit"; + ClassicalControlNs, "BothInvalid", [||], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; ClassicalControlNs, "Foo", [||], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; |]); (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTest4", [||], "Unit"; + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; ClassicalControlNs, "Foo", [||], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; |]); (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTest5", [||], "Unit"; + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; ClassicalControlNs, "Foo", [||], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; |]); (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTest6", [||], "Unit"; + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; ClassicalControlNs, "Foo", [||], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; |]); (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTest7", [||], "Unit"; + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; ClassicalControlNs, "Foo", [||], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; |]); (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTest8", [||], "Unit"; + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; ClassicalControlNs, "Foo", [||], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); |] |> _MakeSignatures \ No newline at end of file From 30afb1610ded356fdfbcdc6d870e693c91b57445 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Wed, 15 Jan 2020 16:11:37 -0800 Subject: [PATCH 34/53] Added tests to cover mutable variables and support for generics. --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 91 ++++++++++++++++--- .../LinkingTests/ClassicalControl.qs | 63 +++++++++++++ .../Tests.Compiler/TestUtils/Signatures.fs | 14 +++ 3 files changed, 154 insertions(+), 14 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 8fbd56a422..46659d0cf0 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -195,10 +195,10 @@ type LinkingTests (output:ITestOutputHelper) = (writer.Scope :?> ScopeToQs).Output.Split("\r\n") static member private CheckIfLineIsCall ``namespace`` name input = - let call = "(" + (Regex.Escape ``namespace``) + @"\.)?" + (Regex.Escape name) - let typeArgs = @"<\s*([^<]*[^<\s])\s*>" // Does not support nested type args + 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 = "^" + call + @"\s*(" + typeArgs + @")?\s*" + args + @";$" + let regex = sprintf @"^%s\s*%s\s*%s;$" call typeArgs args let regexMatch = Regex.Match(input, regex) if regexMatch.Success then @@ -206,20 +206,27 @@ type LinkingTests (output:ITestOutputHelper) = else (false, "", "") - static member private isApplyIfElseArgsMatch input resultVar (opName1 : QsQualifiedName) (opName2 : QsQualifiedName) = + static member private 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*\)" - let opToRegex (op : QsQualifiedName) = sprintf @"\((%s\.)?%s\s*%s,\s*%s\)" - <| Regex.Escape op.Namespace.Value - <| Regex.Escape op.Name.Value - <| typeArgs - <| args - - let ApplyIfElseRegex = sprintf - <| @"^%s,\s*%s,\s*%s$" + + sprintf @"\(%s\s*%s,\s*%s\)" <| call <| typeArgs <| args + + static member private isApplyIfArgMatch input resultVar (opName : QsQualifiedName) = + let regexMatch = Regex.Match(input, sprintf @"^%s,\s*%s$" <| Regex.Escape resultVar <| LinkingTests.MakeApplicationRegex opName) + + if regexMatch.Success then + (true, regexMatch.Groups.[3].Value, regexMatch.Groups.[4].Value) + else + (false, "", "") + + + static member private isApplyIfElseArgsMatch input resultVar (opName1 : QsQualifiedName) (opName2 : QsQualifiedName) = + let ApplyIfElseRegex = sprintf @"^%s,\s*%s,\s*%s$" <| Regex.Escape resultVar - <| opToRegex opName1 - <| opToRegex opName2 + <| LinkingTests.MakeApplicationRegex opName1 + <| LinkingTests.MakeApplicationRegex opName2 let regexMatch = Regex.Match(input, ApplyIfElseRegex) if regexMatch.Success then @@ -462,6 +469,62 @@ type LinkingTests (output:ITestOutputHelper) = let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + [] + [] + member this.``Classical Control Hoist Self-Contained Mutable`` () = + let testNumber = 14 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + [] + [] + member this.``Classical Control Don't Hoist General Mutable`` () = + let testNumber = 15 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + [] + [] + member this.``Classical Control Generics Support`` () = + let testNumber = 16 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + 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 = LinkingTests.GetLinesFromCallable original + let (success, _, args) = LinkingTests.CheckIfLineIsCall BuiltIn.ApplyIfZero.Namespace.Value BuiltIn.ApplyIfZero.Name.Value lines.[1] + Assert.True(success, sprintf "Callable %O did not have expected content" original.FullName) + + let (success, typeArgs, _) = LinkingTests.isApplyIfArgMatch args "r" generated.FullName + Assert.True(success, sprintf "ApplyIfZero did not have the correct arguments") + + AssertTypeArgsMatch originalTypeParams <| typeArgs.Replace("'", "").Replace(" ", "").Split(",") [] member this.``Fail on multiple entry points`` () = diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs index 158594c26e..a81343c8d0 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -345,4 +345,67 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { } } +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTestMain() : Unit { + Foo(); + } + + function Foo() : Unit { + let r = Zero; + + if (r == Zero) { + mutable temp = 3; + set temp = 4; + } + } + +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTestMain() : Unit { + Foo(); + } + + function Foo() : Unit { + let r = Zero; + + mutable temp = 3; + if (r == Zero) { + set temp = 4; + } + } + +} + +// ================================= + +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + @EntryPoint() + operation ClassicalControlTestMain() : Unit { + Foo(); + } + + operation Foo<'A, 'B, 'C>() : Unit { + let r = Zero; + + if (r == Zero) { + SubOp1(); + SubOp2(); + } + } + } \ 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 3d44258668..4b3a4a8a45 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -265,5 +265,19 @@ let public ClassicalControlSignatures = ClassicalControlNs, "Foo", [||], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); |] |> _MakeSignatures \ No newline at end of file From 2d67001a7e8945a261034d186c227f1743b3019a Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Thu, 16 Jan 2020 17:42:29 -0800 Subject: [PATCH 35/53] Test for Adjoint functor support. --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 222 +++++++++++++----- .../LinkingTests/ClassicalControl.qs | 59 ++++- .../Tests.Compiler/TestUtils/Signatures.fs | 9 + .../ClassicallyControlledTransformation.cs | 4 +- 4 files changed, 234 insertions(+), 60 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 46659d0cf0..05207880e2 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -20,6 +20,7 @@ open Xunit open Xunit.Abstractions open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput open System.Text.RegularExpressions +open Microsoft.Quantum.QsCompiler.SyntaxTokens type LinkingTests (output:ITestOutputHelper) = @@ -182,11 +183,10 @@ type LinkingTests (output:ITestOutputHelper) = Assert.Throws (fun _ -> this.RunIntrinsicResolutionTest 6) |> ignore - static member private GetLinesFromCallable callable = + static member private GetLinesFromSpecialization specialization = let writer = new SyntaxTreeToQs() - callable.Specializations - |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsBody) + specialization |> fun x -> match x.Implementation with | Provided (_, body) -> Some body | _ -> None |> Option.get |> writer.Scope.Transform @@ -234,12 +234,31 @@ type LinkingTests (output:ITestOutputHelper) = else (false, "", "", "", "") - static member private CheckIfCallHasContent call (content : seq) = - let lines = LinkingTests.GetLinesFromCallable call + static member private CheckIfSpecializationHasContent specialization (content : seq) = + let lines = LinkingTests.GetLinesFromSpecialization specialization Seq.forall (fun (i, ns, name) -> LinkingTests.CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content - static member private AssertCallHasContent call content = - Assert.True(LinkingTests.CheckIfCallHasContent call content, sprintf "Callable %O did not have expected content" call.FullName) + static member private AssertSpecializationHasContent specialization content = + Assert.True(LinkingTests.CheckIfSpecializationHasContent specialization content, sprintf "Callable %O(%A) did not have expected content" specialization.Parent specialization.Kind) + + static member private 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) + + static member private 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) + + static member private GetBodyFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsBody) + static member private GetAdjFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsAdjoint) + static member private GetCtlFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsControlled) + static member private GetCtlAdjFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsControlledAdjoint) [] [] @@ -248,17 +267,15 @@ type LinkingTests (output:ITestOutputHelper) = let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - let generated = result.Namespaces - |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) - |> GlobalCallableResolutions - |> Seq.find (fun x -> x.Key.Name.Value.EndsWith "_Foo") + let generated = LinkingTests.GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo" + |> (fun x -> Assert.True(1 = Seq.length x); Seq.item 0 x |> LinkingTests.GetBodyFromCallable) [ (0, "SubOps", "SubOp1"); (1, "SubOps", "SubOp2"); (2, "SubOps", "SubOp3"); ] - |> LinkingTests.AssertCallHasContent generated.Value + |> LinkingTests.AssertSpecializationHasContent generated [] [] @@ -304,10 +321,7 @@ type LinkingTests (output:ITestOutputHelper) = let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - let originalOp = result.Namespaces - |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) - |> GlobalCallableResolutions - |> Seq.find (fun x -> x.Key.Name.Value = "Foo") + let originalOp = LinkingTests.GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> LinkingTests.GetBodyFromCallable let getNameFromBuiltin (builtIn : BuiltIn) = builtIn.Namespace.Value, builtIn.Name.Value @@ -316,17 +330,14 @@ type LinkingTests (output:ITestOutputHelper) = (3, getNameFromBuiltin BuiltIn.ApplyIfOne); ] |> Seq.map (fun (i,(ns,name)) -> (i,ns,name)) - |> LinkingTests.AssertCallHasContent originalOp.Value + |> LinkingTests.AssertSpecializationHasContent originalOp member private this.ApplyIfElseTest testNumber = let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - let generated = result.Namespaces - |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) - |> GlobalCallableResolutions - |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_Foo") + let generated = LinkingTests.GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo" |> Seq.map LinkingTests.GetBodyFromCallable Assert.True(2 = Seq.length generated) // Should already be asserted by the signature check @@ -343,29 +354,25 @@ type LinkingTests (output:ITestOutputHelper) = ] let getGeneratedCallables gen1 gen2 = - if LinkingTests.CheckIfCallHasContent gen1 ifContent then - LinkingTests.AssertCallHasContent gen2 elseContent + if LinkingTests.CheckIfSpecializationHasContent gen1 ifContent then + LinkingTests.AssertSpecializationHasContent gen2 elseContent (gen1, gen2) else - LinkingTests.AssertCallHasContent gen2 ifContent - LinkingTests.AssertCallHasContent gen1 elseContent + LinkingTests.AssertSpecializationHasContent gen2 ifContent + LinkingTests.AssertSpecializationHasContent gen1 elseContent (gen2, gen1) - let ifOp, elseOp = getGeneratedCallables (Seq.item 0 generated).Value (Seq.item 1 generated).Value + let ifOp, elseOp = getGeneratedCallables (Seq.item 0 generated) (Seq.item 1 generated) - let lines = result.Namespaces - |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) - |> GlobalCallableResolutions - |> Seq.find (fun x -> x.Key.Name.Value = "Foo") - |> fun x -> x.Value - |> LinkingTests.GetLinesFromCallable + let original = LinkingTests.GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> LinkingTests.GetBodyFromCallable + let lines = original |> LinkingTests.GetLinesFromSpecialization - Assert.True(2 = Seq.length lines, sprintf "Callable %s.%s did not have the expected number of statements" Signatures.ClassicalControlNs "Foo") + 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) = LinkingTests.CheckIfLineIsCall BuiltIn.ApplyIfElseR.Namespace.Value BuiltIn.ApplyIfElseR.Name.Value lines.[1] - Assert.True(success, sprintf "Callable %s.%s did not have expected content" Signatures.ClassicalControlNs "Foo") + Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent original.Kind) - args, ifOp.FullName, elseOp.FullName + args, ifOp.Parent, elseOp.Parent [] @@ -390,11 +397,7 @@ type LinkingTests (output:ITestOutputHelper) = let result = this.CompileClassicalControlTest testNumber Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - let generated = result.Namespaces - |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) - |> GlobalCallableResolutions - |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_Foo") - |> Seq.map (fun x -> x.Value) + let generated = LinkingTests.GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo" |> Seq.map LinkingTests.GetBodyFromCallable Assert.True(3 = Seq.length generated) // Should already be asserted by the signature check @@ -403,7 +406,7 @@ type LinkingTests (output:ITestOutputHelper) = (0, "SubOps", "SubOp1"); (1, "SubOps", "SubOp2"); ] - let ifOp = Seq.tryFind (fun callable -> LinkingTests.CheckIfCallHasContent callable ifContent) generated + let ifOp = Seq.tryFind (fun spec -> LinkingTests.CheckIfSpecializationHasContent spec ifContent) generated |> (fun callOpion -> Assert.True(callOpion <> None, "Could not find the generated operation for the if block"); callOpion.Value) let elifContent = @@ -411,7 +414,7 @@ type LinkingTests (output:ITestOutputHelper) = (0, "SubOps", "SubOp3"); (1, "SubOps", "SubOp1"); ] - let elifOp = Seq.tryFind (fun callable -> LinkingTests.CheckIfCallHasContent callable elifContent) generated + let elifOp = Seq.tryFind (fun spec -> LinkingTests.CheckIfSpecializationHasContent spec elifContent) generated |> (fun callOpion -> Assert.True(callOpion <> None, "Could not find the generated operation for the elif block"); callOpion.Value) let elseContent = @@ -419,25 +422,21 @@ type LinkingTests (output:ITestOutputHelper) = (0, "SubOps", "SubOp2"); (1, "SubOps", "SubOp3"); ] - let elseOp = Seq.tryFind (fun callable -> LinkingTests.CheckIfCallHasContent callable elseContent) generated + let elseOp = Seq.tryFind (fun spec -> LinkingTests.CheckIfSpecializationHasContent spec elseContent) generated |> (fun callOpion -> Assert.True(callOpion <> None, "Could not find the generated operation for the else block"); callOpion.Value) - let lines = result.Namespaces - |> Seq.filter (fun x -> x.Name.Value = Signatures.ClassicalControlNs) - |> GlobalCallableResolutions - |> Seq.find (fun x -> x.Key.Name.Value = "Foo") - |> fun x -> x.Value - |> LinkingTests.GetLinesFromCallable + let original = LinkingTests.GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> LinkingTests.GetBodyFromCallable + let lines = original |> LinkingTests.GetLinesFromSpecialization - Assert.True(2 = Seq.length lines, sprintf "Callable %s.%s did not have the expected number of statements" Signatures.ClassicalControlNs "Foo") + 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) = LinkingTests.CheckIfLineIsCall BuiltIn.ApplyIfElseR.Namespace.Value BuiltIn.ApplyIfElseR.Name.Value lines.[1] - Assert.True(success, sprintf "Callable %s.%s did not have expected content" Signatures.ClassicalControlNs "Foo") + 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) = LinkingTests.isApplyIfElseArgsMatch args "r" ifOp.FullName { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } + let (success, _, _, _, subArgs) = LinkingTests.isApplyIfElseArgsMatch args "r" ifOp.Parent { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } Assert.True(success, errorMsg) - LinkingTests.isApplyIfElseArgsMatch subArgs "r" elifOp.FullName elseOp.FullName + LinkingTests.isApplyIfElseArgsMatch subArgs "r" elifOp.Parent elseOp.Parent |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) [] @@ -517,15 +516,128 @@ type LinkingTests (output:ITestOutputHelper) = AssertTypeArgsMatch originalTypeParams generatedTypeParams (*Assert that the original operation calls the generated operation with the appropriate type arguments*) - let lines = LinkingTests.GetLinesFromCallable original + let lines = LinkingTests.GetBodyFromCallable original |> LinkingTests.GetLinesFromSpecialization let (success, _, args) = LinkingTests.CheckIfLineIsCall BuiltIn.ApplyIfZero.Namespace.Value BuiltIn.ApplyIfZero.Name.Value lines.[1] - Assert.True(success, sprintf "Callable %O did not have expected content" original.FullName) + Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.FullName QsSpecializationKind.QsBody) let (success, typeArgs, _) = LinkingTests.isApplyIfArgMatch args "r" generated.FullName Assert.True(success, sprintf "ApplyIfZero did not have the correct arguments") AssertTypeArgsMatch originalTypeParams <| typeArgs.Replace("'", "").Replace(" ", "").Split(",") + static member private DoesCallSupportsFunctors 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 + | _ -> 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 + | _ -> false + else true + + charMatch.Value && adjMatch.Value && ctlMatch.Value + + static member private AssertCallSupportsFunctors expectedFunctors call = + Assert.True(LinkingTests.DoesCallSupportsFunctors expectedFunctors call, sprintf "Callable %O did not support the expected functors" call.FullName) + + [] + [] + member this.``Classical Control Adjoint Support`` () = + let testNumber = 17 + let result = this.CompileClassicalControlTest testNumber + Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result + + 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 + + let getNameFromBuiltin (builtIn : BuiltIn) = builtIn.Namespace.Value, builtIn.Name.Value + + [(1, getNameFromBuiltin BuiltIn.ApplyIfZero)] + |> Seq.map (fun (i,(ns,name)) -> (i,ns,name)) + |> LinkingTests.AssertSpecializationHasContent (LinkingTests.GetBodyFromCallable selfOp) + + [(1, getNameFromBuiltin BuiltIn.ApplyIfZeroA)] + |> Seq.map (fun (i,(ns,name)) -> (i,ns,name)) + |> LinkingTests.AssertSpecializationHasContent (LinkingTests.GetBodyFromCallable invertOp) + + [(1, getNameFromBuiltin BuiltIn.ApplyIfZero)] + |> Seq.map (fun (i,(ns,name)) -> (i,ns,name)) + |> LinkingTests.AssertSpecializationHasContent (LinkingTests.GetBodyFromCallable selfOp) + + 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 getGeneratedCallables gen1 gen2 = + let spec1 = LinkingTests.GetBodyFromCallable gen1 + let spec2 = LinkingTests.GetBodyFromCallable gen2 + if LinkingTests.CheckIfSpecializationHasContent spec1 bodyContent then + LinkingTests.AssertSpecializationHasContent spec2 adjointContent + (gen1, gen2) + else + LinkingTests.AssertSpecializationHasContent spec2 bodyContent + LinkingTests.AssertSpecializationHasContent spec1 adjointContent + (gen2, gen1) + + let bodyProvidedOp, adjointProvidedOp = getGeneratedCallables (Seq.item 0 _providedOps) (Seq.item 1 _providedOps) + + LinkingTests.AssertCallSupportsFunctors [] _selfOp + LinkingTests.AssertCallSupportsFunctors [QsFunctor.Adjoint] _invertOp + LinkingTests.AssertCallSupportsFunctors [] bodyProvidedOp + LinkingTests.AssertCallSupportsFunctors [] adjointProvidedOp + + [] member this.``Fail on multiple entry points`` () = diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs index a81343c8d0..58c9bb3434 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -3,9 +3,9 @@ namespace SubOps { - operation SubOp1() : Unit { } - operation SubOp2() : Unit { } - operation SubOp3() : Unit { } + operation SubOp1() : Unit is Adj + Ctl { } + operation SubOp2() : Unit is Adj + Ctl { } + operation SubOp3() : Unit is Adj + Ctl { } } // ================================= @@ -408,4 +408,57 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { } } +} + +// ================================= + +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; + } + } \ 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 4b3a4a8a45..fc3d3e3bb7 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -279,5 +279,14 @@ let public ClassicalControlSignatures = ClassicalControlNs, "Foo", [||], "Unit"; ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; |]); + (_DefaultTypes, [| + 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"; + |]); |] |> _MakeSignatures \ No newline at end of file diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 037c3bb221..6d063f3add 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -597,8 +597,8 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature //bool addControlledAdjoint = false; if (_InBody) { - addAdjoint = adj != null && adj.Implementation.IsGenerated; - addControlled = ctl != null && ctl.Implementation.IsGenerated; + 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; //addControlledAdjoint = ctlAdj != null && ctlAdj.Implementation.IsGenerated; } else if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated gen) From 4153762a05444f0a8274d0859e6425097da58a26 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 17 Jan 2020 20:56:28 -0800 Subject: [PATCH 36/53] Tested functor support for generated operations. Moved Classical Control tests to their own file. --- .../Tests.Compiler/ClasicalControlTests.fs | 1166 +++++++++++++++++ src/QsCompiler/Tests.Compiler/LinkingTests.fs | 487 +------ .../LinkingTests/ClassicalControl.qs | 396 ++++++ .../Tests.Compiler/TestUtils/Signatures.fs | 74 ++ .../Tests.Compiler/Tests.Compiler.fsproj | 1 + .../ClassicallyControlledTransformation.cs | 6 +- 6 files changed, 1647 insertions(+), 483 deletions(-) create mode 100644 src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs diff --git a/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs new file mode 100644 index 0000000000..8397b13ede --- /dev/null +++ b/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs @@ -0,0 +1,1166 @@ +// 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 + + 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("\r\n") + + 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;$" 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$" <| 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 CheckIfSpecializationHasContent specialization (content : seq) = + let lines = GetLinesFromSpecialization specialization + Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content + + let AssertSpecializationHasContent specialization content = + Assert.True(CheckIfSpecializationHasContent specialization content, sprintf "Callable %O(%A) did not have expected content" specialization.Parent specialization.Kind) + + let IdentifyGeneratedByContent generatedCalls contents = + let mutable calls = generatedCalls |> Seq.map (fun x -> x, x |> (GetBodyFromCallable >> GetLinesFromSpecialization)) + let hasContent call (content : seq) = + let (_, lines : string[]) = call + Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content + + Assert.True(Seq.length calls = Seq.length contents) // This should be true if this method is call correctly + + let mutable rtrn = Seq.empty + + let removeAt i lst = + Seq.append + <| Seq.take i lst + <| Seq.skip (i+1) lst + + for content in contents do + calls + |> Seq.tryFindIndex (fun callSig -> hasContent callSig content) + |> (fun x -> + Assert.True (x <> None, sprintf "Did not find expected generated content") + rtrn <- Seq.append rtrn [Seq.item x.Value calls] + calls <- removeAt x.Value calls + ) + 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 = IdentifyGeneratedByContent 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.``Classical Control 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"); + ] + |> AssertSpecializationHasContent generated + + [] + [] + member this.``Classical Control Hoist Loops`` () = + CompileClassicalControlTest 2 |> ignore + + [] + [] + member this.``Classical Control Don't Hoist Single Call`` () = + // Single calls should not be hoisted into their own operation + CompileClassicalControlTest 3 |> ignore + + [] + [] + member this.``Classical Control Hoist Single Non-Call`` () = + // Single expressions that are not calls should be hoisted into their own operation + CompileClassicalControlTest 4 |> ignore + + [] + [] + member this.``Classical Control Don't Hoist Return Statments`` () = + CompileClassicalControlTest 5 |> ignore + + [] + [] + member this.``Classical Control All-Or-None Hoisting`` () = + CompileClassicalControlTest 6 |> ignore + + [] + [] + member this.``Classical Control ApplyIfZero And ApplyIfOne`` () = + let result = CompileClassicalControlTest 7 + + let originalOp = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable + + [ + (1, BuiltIn.ApplyIfZero); + (3, BuiltIn.ApplyIfOne); + ] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent originalOp + + [] + [] + member this.``Classical Control 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.``Classical Control 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.``Classical Control 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 = IdentifyGeneratedByContent 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" elifOp.FullName elseOp.FullName + |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) + + [] + [] + member this.``Classical Control 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" ifOp elseOp + |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) + + [] + [] + member this.``Classical Control 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" ifOp elseOp + |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) + + [] + [] + member this.``Classical Control Don't Hoist Functions`` () = + CompileClassicalControlTest 13 |> ignore + + [] + [] + member this.``Classical Control Hoist Self-Contained Mutable`` () = + CompileClassicalControlTest 14 |> ignore + + [] + [] + member this.``Classical Control Don't Hoist General Mutable`` () = + CompileClassicalControlTest 15 |> ignore + + [] + [] + member this.``Classical Control 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.``Classical Control 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable selfOp) + + [(1, BuiltIn.ApplyIfZeroA)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable invertOp) + + [(1, BuiltIn.ApplyIfZero)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent _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.``Classical Control 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable distributeOp) + + [(1, BuiltIn.ApplyIfZero)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent _providedOps [bodyContent; controlledContent] + let bodyGen, ctlGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens) + + AssertCallSupportsFunctors [QsFunctor.Controlled] _distributeOp + AssertCallSupportsFunctors [] bodyGen + AssertCallSupportsFunctors [] ctlGen + + [] + [] + member this.``Classical Control 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetCtlFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetAdjFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetCtlFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetAdjFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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.``Classical Control 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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) + AssertSpecializationHasContent (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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOneC)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetCtlFromCallable original) + + [(1, BuiltIn.ApplyIfOneC)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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.``Classical Control 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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) + AssertSpecializationHasContent (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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOneA)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOneA)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetCtlFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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.``Classical Control 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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) + AssertSpecializationHasContent (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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 () diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 05207880e2..1e6708f6f5 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -12,15 +12,11 @@ open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTree -open Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTransformation open Microsoft.Quantum.QsCompiler.Transformations.IntrinsicResolutionTransformation open Microsoft.Quantum.QsCompiler.Transformations.Monomorphization open Microsoft.Quantum.QsCompiler.Transformations.MonomorphizationValidation open Xunit open Xunit.Abstractions -open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput -open System.Text.RegularExpressions -open Microsoft.Quantum.QsCompiler.SyntaxTokens type LinkingTests (output:ITestOutputHelper) = @@ -58,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 @@ -83,14 +79,14 @@ 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 let chunckNumber = 2 * (testNumber - 1) @@ -115,27 +111,9 @@ type LinkingTests (output:ITestOutputHelper) = |> Assert.True) |> ignore - member private this.CompileClassicalControlTest testNumber = - let srcChunks = LinkingTests.ReadAndChunkSourceFile "ClassicalControl.qs" - srcChunks.Length >= testNumber + 1 |> Assert.True - let shared = srcChunks.[0] - let compilationDataStructures = this.BuildContent <| shared + srcChunks.[testNumber] - let processedCompilation = ClassicallyControlledTransformation.Apply compilationDataStructures.BuiltCompilation - Assert.NotNull processedCompilation - processedCompilation - - //member private this.RunClassicalControlTest testNumber = - // let srcChunks = LinkingTests.ReadAndChunkSourceFile "ClassicalControl.qs" - // srcChunks.Length >= testNumber + 1 |> Assert.True - // let shared = srcChunks.[0] - // let result = this.CompileClassicalControl <| shared + srcChunks.[testNumber] - // Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - - [] member this.``Monomorphization`` () = - + let filePath = Path.Combine ("TestCases", "LinkingTests", "Generics.qs") |> Path.GetFullPath let fileId = (new Uri(filePath)) getManager fileId (File.ReadAllText filePath) @@ -158,7 +136,7 @@ type LinkingTests (output:ITestOutputHelper) = member this.``Intrinsic Resolution Returns UDT`` () = this.RunIntrinsicResolutionTest 2 - + [] [] member this.``Intrinsic Resolution Type Mismatch Error`` () = @@ -183,461 +161,6 @@ type LinkingTests (output:ITestOutputHelper) = Assert.Throws (fun _ -> this.RunIntrinsicResolutionTest 6) |> ignore - static member private 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("\r\n") - - static member private 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;$" call typeArgs args - - let regexMatch = Regex.Match(input, regex) - if regexMatch.Success then - (true, regexMatch.Groups.[3].Value, regexMatch.Groups.[4].Value) - else - (false, "", "") - - static member private 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 - - static member private isApplyIfArgMatch input resultVar (opName : QsQualifiedName) = - let regexMatch = Regex.Match(input, sprintf @"^%s,\s*%s$" <| Regex.Escape resultVar <| LinkingTests.MakeApplicationRegex opName) - - if regexMatch.Success then - (true, regexMatch.Groups.[3].Value, regexMatch.Groups.[4].Value) - else - (false, "", "") - - - static member private isApplyIfElseArgsMatch input resultVar (opName1 : QsQualifiedName) (opName2 : QsQualifiedName) = - let ApplyIfElseRegex = sprintf @"^%s,\s*%s,\s*%s$" - <| Regex.Escape resultVar - <| LinkingTests.MakeApplicationRegex opName1 - <| LinkingTests.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, "", "", "", "") - - static member private CheckIfSpecializationHasContent specialization (content : seq) = - let lines = LinkingTests.GetLinesFromSpecialization specialization - Seq.forall (fun (i, ns, name) -> LinkingTests.CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content - - static member private AssertSpecializationHasContent specialization content = - Assert.True(LinkingTests.CheckIfSpecializationHasContent specialization content, sprintf "Callable %O(%A) did not have expected content" specialization.Parent specialization.Kind) - - static member private 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) - - static member private 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) - - static member private GetBodyFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsBody) - static member private GetAdjFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsAdjoint) - static member private GetCtlFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsControlled) - static member private GetCtlAdjFromCallable call = call.Specializations |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsControlledAdjoint) - - [] - [] - member this.``Classical Control Basic Hoist`` () = - let testNumber = 1 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - let generated = LinkingTests.GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo" - |> (fun x -> Assert.True(1 = Seq.length x); Seq.item 0 x |> LinkingTests.GetBodyFromCallable) - - [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); - (2, "SubOps", "SubOp3"); - ] - |> LinkingTests.AssertSpecializationHasContent generated - - [] - [] - member this.``Classical Control Hoist Loops`` () = - let testNumber = 2 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - [] - [] - member this.``Classical Control Don't Hoist Single Call`` () = - // Single calls should not be hoisted into their own operation - let testNumber = 3 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - [] - [] - member this.``Classical Control Hoist Single Non-Call`` () = - // Single expressions that are not calls should be hoisted into their own operation - let testNumber = 4 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - [] - [] - member this.``Classical Control Don't Hoist Return Statments`` () = - let testNumber = 5 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - [] - [] - member this.``Classical Control All-Or-None Hoisting`` () = - let testNumber = 6 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - [] - [] - member this.``Classical Control ApplyIfZero And ApplyIfOne`` () = - let testNumber = 7 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - let originalOp = LinkingTests.GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> LinkingTests.GetBodyFromCallable - - let getNameFromBuiltin (builtIn : BuiltIn) = builtIn.Namespace.Value, builtIn.Name.Value - - [ - (1, getNameFromBuiltin BuiltIn.ApplyIfZero); - (3, getNameFromBuiltin BuiltIn.ApplyIfOne); - ] - |> Seq.map (fun (i,(ns,name)) -> (i,ns,name)) - |> LinkingTests.AssertSpecializationHasContent originalOp - - - member private this.ApplyIfElseTest testNumber = - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - let generated = LinkingTests.GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo" |> Seq.map LinkingTests.GetBodyFromCallable - - 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 getGeneratedCallables gen1 gen2 = - if LinkingTests.CheckIfSpecializationHasContent gen1 ifContent then - LinkingTests.AssertSpecializationHasContent gen2 elseContent - (gen1, gen2) - else - LinkingTests.AssertSpecializationHasContent gen2 ifContent - LinkingTests.AssertSpecializationHasContent gen1 elseContent - (gen2, gen1) - - let ifOp, elseOp = getGeneratedCallables (Seq.item 0 generated) (Seq.item 1 generated) - - let original = LinkingTests.GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> LinkingTests.GetBodyFromCallable - let lines = original |> LinkingTests.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) = LinkingTests.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.Parent, elseOp.Parent - - - [] - [] - member this.``Classical Control Apply If Zero Else One`` () = - let (args, ifOp, elseOp) = this.ApplyIfElseTest 8 - LinkingTests.isApplyIfElseArgsMatch args "r" ifOp elseOp - |> (fun (x,_,_,_,_) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) - - [] - [] - member this.``Classical Control Apply If One Else Zero`` () = - let (args, ifOp, elseOp) = this.ApplyIfElseTest 9 - // The operation arguments should be swapped from the previous test - LinkingTests.isApplyIfElseArgsMatch args "r" elseOp ifOp - |> (fun (x,_,_,_,_) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) - - [] - [] - member this.``Classical Control If Elif`` () = - let testNumber = 10 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - let generated = LinkingTests.GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo" |> Seq.map LinkingTests.GetBodyFromCallable - - Assert.True(3 = Seq.length generated) // Should already be asserted by the signature check - - let ifContent = - [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); - ] - let ifOp = Seq.tryFind (fun spec -> LinkingTests.CheckIfSpecializationHasContent spec ifContent) generated - |> (fun callOpion -> Assert.True(callOpion <> None, "Could not find the generated operation for the if block"); callOpion.Value) - - let elifContent = - [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); - ] - let elifOp = Seq.tryFind (fun spec -> LinkingTests.CheckIfSpecializationHasContent spec elifContent) generated - |> (fun callOpion -> Assert.True(callOpion <> None, "Could not find the generated operation for the elif block"); callOpion.Value) - - let elseContent = - [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); - ] - let elseOp = Seq.tryFind (fun spec -> LinkingTests.CheckIfSpecializationHasContent spec elseContent) generated - |> (fun callOpion -> Assert.True(callOpion <> None, "Could not find the generated operation for the else block"); callOpion.Value) - - let original = LinkingTests.GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> LinkingTests.GetBodyFromCallable - let lines = original |> LinkingTests.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) = LinkingTests.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) = LinkingTests.isApplyIfElseArgsMatch args "r" ifOp.Parent { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } - Assert.True(success, errorMsg) - LinkingTests.isApplyIfElseArgsMatch subArgs "r" elifOp.Parent elseOp.Parent - |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) - - [] - [] - member this.``Classical Control And Condition`` () = - let (args, ifOp, elseOp) = this.ApplyIfElseTest 11 - - let errorMsg = "ApplyIfElse did not have the correct arguments" - let (success, _, subArgs, _, _) = LinkingTests.isApplyIfElseArgsMatch args "r" { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } elseOp - Assert.True(success, errorMsg) - LinkingTests.isApplyIfElseArgsMatch subArgs "r" ifOp elseOp - |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) - - [] - [] - member this.``Classical Control Or Condition`` () = - let (args, ifOp, elseOp) = this.ApplyIfElseTest 12 - - let errorMsg = "ApplyIfElse did not have the correct arguments" - let (success, _, _, _, subArgs) = LinkingTests.isApplyIfElseArgsMatch args "r" ifOp { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } - Assert.True(success, errorMsg) - LinkingTests.isApplyIfElseArgsMatch subArgs "r" ifOp elseOp - |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) - - [] - [] - member this.``Classical Control Don't Hoist Functions`` () = - let testNumber = 13 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - [] - [] - member this.``Classical Control Hoist Self-Contained Mutable`` () = - let testNumber = 14 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - [] - [] - member this.``Classical Control Don't Hoist General Mutable`` () = - let testNumber = 15 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - [] - [] - member this.``Classical Control Generics Support`` () = - let testNumber = 16 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - 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 = LinkingTests.GetBodyFromCallable original |> LinkingTests.GetLinesFromSpecialization - let (success, _, args) = LinkingTests.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, _) = LinkingTests.isApplyIfArgMatch args "r" generated.FullName - Assert.True(success, sprintf "ApplyIfZero did not have the correct arguments") - - AssertTypeArgsMatch originalTypeParams <| typeArgs.Replace("'", "").Replace(" ", "").Split(",") - - static member private DoesCallSupportsFunctors 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 - | _ -> 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 - | _ -> false - else true - - charMatch.Value && adjMatch.Value && ctlMatch.Value - - static member private AssertCallSupportsFunctors expectedFunctors call = - Assert.True(LinkingTests.DoesCallSupportsFunctors expectedFunctors call, sprintf "Callable %O did not support the expected functors" call.FullName) - - [] - [] - member this.``Classical Control Adjoint Support`` () = - let testNumber = 17 - let result = this.CompileClassicalControlTest testNumber - Signatures.SignatureCheck [Signatures.ClassicalControlNs] Signatures.ClassicalControlSignatures.[testNumber-1] result - - 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 - - let getNameFromBuiltin (builtIn : BuiltIn) = builtIn.Namespace.Value, builtIn.Name.Value - - [(1, getNameFromBuiltin BuiltIn.ApplyIfZero)] - |> Seq.map (fun (i,(ns,name)) -> (i,ns,name)) - |> LinkingTests.AssertSpecializationHasContent (LinkingTests.GetBodyFromCallable selfOp) - - [(1, getNameFromBuiltin BuiltIn.ApplyIfZeroA)] - |> Seq.map (fun (i,(ns,name)) -> (i,ns,name)) - |> LinkingTests.AssertSpecializationHasContent (LinkingTests.GetBodyFromCallable invertOp) - - [(1, getNameFromBuiltin BuiltIn.ApplyIfZero)] - |> Seq.map (fun (i,(ns,name)) -> (i,ns,name)) - |> LinkingTests.AssertSpecializationHasContent (LinkingTests.GetBodyFromCallable selfOp) - - 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 getGeneratedCallables gen1 gen2 = - let spec1 = LinkingTests.GetBodyFromCallable gen1 - let spec2 = LinkingTests.GetBodyFromCallable gen2 - if LinkingTests.CheckIfSpecializationHasContent spec1 bodyContent then - LinkingTests.AssertSpecializationHasContent spec2 adjointContent - (gen1, gen2) - else - LinkingTests.AssertSpecializationHasContent spec2 bodyContent - LinkingTests.AssertSpecializationHasContent spec1 adjointContent - (gen2, gen1) - - let bodyProvidedOp, adjointProvidedOp = getGeneratedCallables (Seq.item 0 _providedOps) (Seq.item 1 _providedOps) - - LinkingTests.AssertCallSupportsFunctors [] _selfOp - LinkingTests.AssertCallSupportsFunctors [QsFunctor.Adjoint] _invertOp - LinkingTests.AssertCallSupportsFunctors [] bodyProvidedOp - LinkingTests.AssertCallSupportsFunctors [] adjointProvidedOp - - [] member this.``Fail on multiple entry points`` () = diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs index 58c9bb3434..6930ce1002 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -461,4 +461,400 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { adjoint invert; } +} + +// ================================= + +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; + } + +} + +// ================================= + +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(); + } + } + } + +} + +// ================================= + +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; + } + +} + +// ================================= + +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; + } + +} + +// ================================= + +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; + } + } \ 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 fc3d3e3bb7..25ee90e3bf 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -288,5 +288,79 @@ let public ClassicalControlSignatures = ClassicalControlNs, "_Self", [|"Result"|], "Unit"; ClassicalControlNs, "_Invert", [|"Result"|], "Unit"; |]); + (_DefaultTypes, [| + ClassicalControlNs, "Provided", [||], "Unit"; + ClassicalControlNs, "Distribute", [||], "Unit"; + ClassicalControlNs, "_Provided", [|"Result"|], "Unit"; + ClassicalControlNs, "_Provided", [|"Result";"Qubit[]";"Unit"|], "Unit"; + ClassicalControlNs, "_Distribute", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + 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, [| + 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, [| + 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, [| + ClassicalControlNs, "SelfBody", [||], "Unit"; + ClassicalControlNs, "SelfControlled", [||], "Unit"; + + ClassicalControlNs, "_SelfBody", [|"Result"|], "Unit"; + + ClassicalControlNs, "_SelfControlled", [|"Result"|], "Unit"; + ClassicalControlNs, "_SelfControlled", [|"Result";"Qubit[]";"Unit"|], "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 9868547ab0..1625070acf 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -134,6 +134,7 @@ + diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 6d063f3add..8243f686f2 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -599,7 +599,11 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature { 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; - //addControlledAdjoint = ctlAdj != null && ctlAdj.Implementation.IsGenerated; + 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) { From e8e84cb95bbd335aee04ea9ac1b3a3db34e51d93 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 17 Jan 2020 21:20:43 -0800 Subject: [PATCH 37/53] Cleaned up the names and categories. Skipped tests that are failing, but their failures are tracked in the system with tasks. --- .../Tests.Compiler/ClasicalControlTests.fs | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs index 8397b13ede..7e38d47f59 100644 --- a/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs @@ -223,8 +223,8 @@ type ClassicalControlTests () = Assert.True(DoesCallSupportFunctors expectedFunctors call, sprintf "Callable %O did not support the expected functors" call.FullName) [] - [] - member this.``Classical Control Basic Hoist`` () = + [] + member this.``Basic Hoist`` () = let result = CompileClassicalControlTest 1 let generated = GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo" @@ -238,35 +238,35 @@ type ClassicalControlTests () = |> AssertSpecializationHasContent generated [] - [] - member this.``Classical Control Hoist Loops`` () = + [] + member this.``Hoist Loops`` () = CompileClassicalControlTest 2 |> ignore [] - [] - member this.``Classical Control Don't Hoist Single Call`` () = + [] + member this.``Don't Hoist Single Call`` () = // Single calls should not be hoisted into their own operation CompileClassicalControlTest 3 |> ignore - [] - [] - member this.``Classical Control Hoist Single Non-Call`` () = + [] + [] + member this.``Hoist Single Non-Call`` () = // Single expressions that are not calls should be hoisted into their own operation CompileClassicalControlTest 4 |> ignore [] - [] - member this.``Classical Control Don't Hoist Return Statments`` () = + [] + member this.``Don't Hoist Return Statments`` () = CompileClassicalControlTest 5 |> ignore - [] - [] - member this.``Classical Control All-Or-None Hoisting`` () = + [] + [] + member this.``All-Or-None Hoisting`` () = CompileClassicalControlTest 6 |> ignore [] - [] - member this.``Classical Control ApplyIfZero And ApplyIfOne`` () = + [] + member this.``ApplyIfZero And ApplyIfOne`` () = let result = CompileClassicalControlTest 7 let originalOp = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable @@ -279,23 +279,23 @@ type ClassicalControlTests () = |> AssertSpecializationHasContent originalOp [] - [] - member this.``Classical Control Apply If Zero Else One`` () = + [] + 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.``Classical Control Apply If One Else Zero`` () = + [] + [] + 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.``Classical Control If Elif`` () = + [] + member this.``If Elif`` () = let result = CompileClassicalControlTest 10 let generated = GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo" @@ -336,8 +336,8 @@ type ClassicalControlTests () = |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) [] - [] - member this.``Classical Control And Condition`` () = + [] + member this.``And Condition`` () = let (args, ifOp, elseOp) = CompileClassicalControlTest 11 |> ApplyIfElseTest let errorMsg = "ApplyIfElse did not have the correct arguments" @@ -347,8 +347,8 @@ type ClassicalControlTests () = |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) [] - [] - member this.``Classical Control Or Condition`` () = + [] + member this.``Or Condition`` () = let (args, ifOp, elseOp) = CompileClassicalControlTest 12 |> ApplyIfElseTest let errorMsg = "ApplyIfElse did not have the correct arguments" @@ -357,24 +357,24 @@ type ClassicalControlTests () = IsApplyIfElseArgsMatch subArgs "r" ifOp elseOp |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) - [] - [] - member this.``Classical Control Don't Hoist Functions`` () = + [] + [] + member this.``Don't Hoist Functions`` () = CompileClassicalControlTest 13 |> ignore [] - [] - member this.``Classical Control Hoist Self-Contained Mutable`` () = + [] + member this.``Hoist Self-Contained Mutable`` () = CompileClassicalControlTest 14 |> ignore [] - [] - member this.``Classical Control Don't Hoist General Mutable`` () = + [] + member this.``Don't Hoist General Mutable`` () = CompileClassicalControlTest 15 |> ignore [] - [] - member this.``Classical Control Generics Support`` () = + [] + member this.``Generics Support`` () = let result = CompileClassicalControlTest 16 let callables = result.Namespaces @@ -414,8 +414,8 @@ type ClassicalControlTests () = AssertTypeArgsMatch originalTypeParams <| typeArgs.Replace("'", "").Replace(" ", "").Split(",") [] - [] - member this.``Classical Control Adjoint Support`` () = + [] + member this.``Adjoint Support`` () = let result = CompileClassicalControlTest 17 let callables = result.Namespaces @@ -476,8 +476,8 @@ type ClassicalControlTests () = AssertCallSupportsFunctors [] adjGen [] - [] - member this.``Classical Control Controlled Support`` () = + [] + member this.``Controlled Support`` () = let result = CompileClassicalControlTest 18 let callables = result.Namespaces @@ -527,8 +527,8 @@ type ClassicalControlTests () = AssertCallSupportsFunctors [] ctlGen [] - [] - member this.``Classical Control Controlled Adjoint Support - Provided`` () = + [] + member this.``Controlled Adjoint Support - Provided`` () = let result = CompileClassicalControlTest 19 let callables = result.Namespaces @@ -739,8 +739,8 @@ type ClassicalControlTests () = allCheck () [] - [] - member this.``Classical Control Controlled Adjoint Support - Distribute`` ()= + [] + member this.``Controlled Adjoint Support - Distribute`` ()= let result = CompileClassicalControlTest 20 let callables = result.Namespaces @@ -912,8 +912,8 @@ type ClassicalControlTests () = allCheck () [] - [] - member this.``Classical Control Controlled Adjoint Support - Invert`` ()= + [] + member this.``Controlled Adjoint Support - Invert`` ()= let result = CompileClassicalControlTest 21 let callables = result.Namespaces @@ -1085,8 +1085,8 @@ type ClassicalControlTests () = allCheck () [] - [] - member this.``Classical Control Controlled Adjoint Support - Self`` ()= + [] + member this.``Controlled Adjoint Support - Self`` ()= let result = CompileClassicalControlTest 22 let callables = result.Namespaces From c3fd9c557ff9dd9151fd43b8e56ae6fb17060453 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 21 Jan 2020 09:42:06 -0800 Subject: [PATCH 38/53] Updated Rewrite step class for transformation. --- .../RewriteSteps/ClassicallyControlled.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs b/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs index 2ac40e6da3..cd3a802cc5 100644 --- a/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs +++ b/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs @@ -11,22 +11,17 @@ namespace Microsoft.Quantum.QsCompiler.BuiltInRewriteSteps { internal class ClassicallyControlled : IRewriteStep { - public string Name { get; } - public int Priority { get; } + public string Name => "ClassicallyControlled"; + public int Priority => 10; // Not used for built-in transformations like this public IDictionary AssemblyConstants { get; } - public bool ImplementsTransformation { get; } - public bool ImplementsPreconditionVerification { get; } - public bool ImplementsPostconditionVerification { get; } + public bool ImplementsTransformation => true; + public bool ImplementsPreconditionVerification => false; + public bool ImplementsPostconditionVerification => false; public ClassicallyControlled() { - Name = "ClassicallyControlled"; - Priority = 10; // Not used for built-in transformations like this AssemblyConstants = new Dictionary(); - ImplementsTransformation = true; - ImplementsPreconditionVerification = false; - ImplementsPostconditionVerification = false; } public bool Transformation(QsCompilation compilation, out QsCompilation transformed) From b8df44a2ae261b176c81799c42a62f1c381f3978 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 23 Jan 2020 13:08:51 -0800 Subject: [PATCH 39/53] enabling ConvertClassicalControl if the execution target is a quantum processor backend --- src/QsCompiler/CommandLineTool/Commands/Build.cs | 3 ++- src/QsCompiler/CommandLineTool/Commands/Diagnose.cs | 3 ++- src/QsCompiler/Compiler/CompilationLoader.cs | 6 ++++-- src/QuantumSdk/Sdk/Sdk.targets | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index baf9382af5..fbbd12a3ed 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -127,7 +127,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 7189d66693..290cdb8157 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -373,13 +373,15 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference if (this.Config.ConvertClassicalControl) { var rewriteStep = new RewriteSteps.LoadedStep(new ClassicallyControlled(), typeof(IRewriteStep), thisDllUri); - this.CompilationStatus.ConvertClassicalControl = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out this.CompilationOutput); + this.CompilationStatus.ConvertClassicalControl = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out var transformed); + if (this.CompilationStatus.ConvertClassicalControl == Status.Succeeded) this.CompilationOutput = transformed; } if (!this.Config.SkipMonomorphization && this.CompilationOutput?.EntryPoints.Length != 0) { var rewriteStep = new RewriteSteps.LoadedStep(new Monomorphization(), typeof(IRewriteStep), thisDllUri); - this.CompilationStatus.Monomorphization = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out this.CompilationOutput); + this.CompilationStatus.Monomorphization = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out var transformed); + if (this.CompilationStatus.Monomorphization == Status.Succeeded) this.CompilationOutput = transformed; } if (this.Config.GenerateFunctorSupport) diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index 6b850f40df..f5e712e57e 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -88,8 +88,9 @@ <_QscCommandInputFlag Condition="@(QsharpCompile->Count()) > 0">--input "@(QsharpCompile,'" "')" <_QscCommandReferencesFlag Condition="@(ResolvedQsharpReferences->Count()) > 0">--references "@(ResolvedQsharpReferences,'" "')" <_QscCommandLoadFlag Condition="@(_PrioritizedResolvedQscReferences->Count()) > 0">--load "@(_PrioritizedResolvedQscReferences,'" "')" + <_QscCommandTrimFlag Condition="'$(ResolvedQsharpExecutionTarget)' == 'QuantumProcessorBackend'">--trim 2 <_QscPackageLoadFallbackFoldersFlag Condition="'$(OutputPath)' != '' And '$(MSBuildProjectDirectory)' != ''">--package-load-fallback-folders $(MSBuildProjectDirectory)/$(OutputPath) - <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscPackageLoadFallbackFoldersFlag) + <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandTrimFlag) $(_QscPackageLoadFallbackFoldersFlag) <_QscCommandArgsFile>$(QscBuildConfigOutputPath)qsc.rsp $(QscExe) build --format MsBuild $(_VerbosityFlag) --response-files $(_QscCommandArgsFile) From fd3f1de6289e140b3e89cce447d53a2126ea170c Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 24 Jan 2020 13:45:48 -0800 Subject: [PATCH 40/53] Fixed line endings --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 494 ++++++------ .../Tests.Compiler/TestUtils/Signatures.fs | 730 +++++++++--------- 2 files changed, 612 insertions(+), 612 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 1e6708f6f5..7956a74edf 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -1,247 +1,247 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.QsCompiler.Testing - -open System -open System.Collections.Generic -open System.IO -open Microsoft.Quantum.QsCompiler -open Microsoft.Quantum.QsCompiler.CompilationBuilder -open Microsoft.Quantum.QsCompiler.DataTypes -open Microsoft.Quantum.QsCompiler.Diagnostics -open Microsoft.Quantum.QsCompiler.SyntaxExtensions -open Microsoft.Quantum.QsCompiler.SyntaxTree -open Microsoft.Quantum.QsCompiler.Transformations.IntrinsicResolutionTransformation -open Microsoft.Quantum.QsCompiler.Transformations.Monomorphization -open Microsoft.Quantum.QsCompiler.Transformations.MonomorphizationValidation -open Xunit -open Xunit.Abstractions - - -type LinkingTests (output:ITestOutputHelper) = - inherit CompilerTests(CompilerTests.Compile (Path.Combine ("TestCases", "LinkingTests" )) ["Core.qs"; "InvalidEntryPoints.qs"], output) - - 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 - - static member private ReadAndChunkSourceFile fileName = - let sourceInput = Path.Combine ("TestCases", "LinkingTests", fileName) |> File.ReadAllText - sourceInput.Split ([|"==="|], StringSplitOptions.RemoveEmptyEntries) - - member private this.Expect name (diag : IEnumerable) = - let ns = "Microsoft.Quantum.Testing.EntryPoints" |> NonNullable<_>.New - let name = name |> NonNullable<_>.New - this.Verify (QsQualifiedName.New (ns, name), diag) - - member private this.CompileAndVerify input (diag : DiagnosticItem seq) = - - let fileId = getTempFile() - let file = getManager fileId input - let inFile (c : QsCallable) = c.SourceFile = file.FileName - - compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore - let built = compilationManager.Build() - compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore - let tests = new CompilerTests(built, output) - - for callable in built.Callables.Values |> Seq.filter inFile do - tests.Verify (callable.FullName, diag) - - member private this.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 - - member private this.CompileMonomorphization input = - - let compilationDataStructures = this.BuildContent input - - let monomorphicCompilation = MonomorphizationTransformation.Apply compilationDataStructures.BuiltCompilation - - Assert.NotNull monomorphicCompilation - MonomorphizationValidationTransformation.Apply monomorphicCompilation - - 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 - let chunckNumber = 2 * (testNumber - 1) - let result = this.CompileIntrinsicResolution srcChunks.[chunckNumber] srcChunks.[chunckNumber+1] - Signatures.SignatureCheck [Signatures.IntrinsicResolutionNs] Signatures.IntrinsicResolutionSignatures.[testNumber-1] result - - (*Find the overridden operation in the appropriate namespace*) - let targetCallName = QsQualifiedName.New(NonNullable<_>.New Signatures.IntrinsicResolutionNs, NonNullable<_>.New "Override") - let targetCallable = - result.Namespaces - |> Seq.find (fun ns -> ns.Name.Value = Signatures.IntrinsicResolutionNs) - |> (fun x -> [x]) |> SyntaxExtensions.Callables - |> Seq.find (fun call -> call.FullName = targetCallName) - - (*Check that the operation is not intrinsic*) - targetCallable.Specializations.Length > 0 |> Assert.True - targetCallable.Specializations - |> Seq.map (fun spec -> - match spec.Implementation with - | Provided _ -> true - | _ -> false - |> Assert.True) - |> ignore - - [] - member this.``Monomorphization`` () = - - let filePath = Path.Combine ("TestCases", "LinkingTests", "Generics.qs") |> Path.GetFullPath - let fileId = (new Uri(filePath)) - getManager fileId (File.ReadAllText filePath) - |> compilationManager.AddOrUpdateSourceFileAsync |> ignore - - for testCase in LinkingTests.ReadAndChunkSourceFile "Monomorphization.qs" |> Seq.zip Signatures.MonomorphizationSignatures do - this.CompileMonomorphization (snd testCase) |> - Signatures.SignatureCheck [Signatures.GenericsNs; Signatures.MonomorphizationNs] (fst testCase) - - compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore - - [] - [] - member this.``Intrinsic Resolution Basic Implementation`` () = - this.RunIntrinsicResolutionTest 1 - - - [] - [] - member this.``Intrinsic Resolution Returns UDT`` () = - this.RunIntrinsicResolutionTest 2 - - - [] - [] - member this.``Intrinsic Resolution Type Mismatch Error`` () = - Assert.Throws (fun _ -> this.RunIntrinsicResolutionTest 3) |> ignore - - - [] - [] - member this.``Intrinsic Resolution Param UDT`` () = - this.RunIntrinsicResolutionTest 4 - - - [] - [] - member this.``Intrinsic Resolution With Adj`` () = - this.RunIntrinsicResolutionTest 5 - - - [] - [] - member this.``Intrinsic Resolution Spec Mismatch Error`` () = - Assert.Throws (fun _ -> this.RunIntrinsicResolutionTest 6) |> ignore - - - [] - member this.``Fail on multiple entry points`` () = - - let entryPoints = LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" - Assert.True (entryPoints.Length > 1) - - let fileId = getTempFile() - let file = getManager fileId entryPoints.[0] - compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore - this.CompileAndVerify entryPoints.[1] [Error ErrorCode.MultipleEntryPoints] - compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore - - - [] - member this.``Entry point specialization verification`` () = - - for entryPoint in LinkingTests.ReadAndChunkSourceFile "EntryPointSpecializations.qs" do - this.CompileAndVerify entryPoint [Error ErrorCode.InvalidEntryPointSpecialization] - - for entryPoint in LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" do - this.CompileAndVerify entryPoint [] - - - [] - member this.``Entry point attribute placement verification`` () = - - this.Expect "InvalidEntryPointPlacement1" [Error ErrorCode.InvalidEntryPointPlacement] - this.Expect "InvalidEntryPointPlacement2" [Error ErrorCode.InvalidEntryPointPlacement] - this.Expect "InvalidEntryPointPlacement3" [Error ErrorCode.InvalidEntryPointPlacement] - - // the error messages here should become InvalidEntryPointPlacement if / when - // we support attaching attributes to specializations in general - this.Expect "InvalidEntryPointPlacement4" [Error ErrorCode.MisplacedDeclarationAttribute] - this.Expect "InvalidEntryPointPlacement5" [Error ErrorCode.MisplacedDeclarationAttribute] - this.Expect "InvalidEntryPointPlacement6" [Error ErrorCode.MisplacedDeclarationAttribute] - this.Expect "InvalidEntryPointPlacement7" [Error ErrorCode.MisplacedDeclarationAttribute] - - - [] - member this.``Entry point argument and return type verification`` () = - - this.Expect "InvalidEntryPoint1" [Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint2" [Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint3" [Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint4" [Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint5" [Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint6" [Error ErrorCode.QubitTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint7" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint8" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint9" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint10" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint11" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint12" [Error ErrorCode.CallableTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint13" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint14" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint15" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint16" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint17" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint18" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint19" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint20" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint21" [Error ErrorCode.CallableTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint22" [Error ErrorCode.CallableTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint23" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint24" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint25" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint26" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint27" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint28" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint29" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint30" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint31" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint32" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] - - this.Expect "InvalidEntryPoint33" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint34" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint35" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] - this.Expect "InvalidEntryPoint36" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] - +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.QsCompiler.Testing + +open System +open System.Collections.Generic +open System.IO +open Microsoft.Quantum.QsCompiler +open Microsoft.Quantum.QsCompiler.CompilationBuilder +open Microsoft.Quantum.QsCompiler.DataTypes +open Microsoft.Quantum.QsCompiler.Diagnostics +open Microsoft.Quantum.QsCompiler.SyntaxExtensions +open Microsoft.Quantum.QsCompiler.SyntaxTree +open Microsoft.Quantum.QsCompiler.Transformations.IntrinsicResolutionTransformation +open Microsoft.Quantum.QsCompiler.Transformations.Monomorphization +open Microsoft.Quantum.QsCompiler.Transformations.MonomorphizationValidation +open Xunit +open Xunit.Abstractions + + +type LinkingTests (output:ITestOutputHelper) = + inherit CompilerTests(CompilerTests.Compile (Path.Combine ("TestCases", "LinkingTests" )) ["Core.qs"; "InvalidEntryPoints.qs"], output) + + 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 + + static member private ReadAndChunkSourceFile fileName = + let sourceInput = Path.Combine ("TestCases", "LinkingTests", fileName) |> File.ReadAllText + sourceInput.Split ([|"==="|], StringSplitOptions.RemoveEmptyEntries) + + member private this.Expect name (diag : IEnumerable) = + let ns = "Microsoft.Quantum.Testing.EntryPoints" |> NonNullable<_>.New + let name = name |> NonNullable<_>.New + this.Verify (QsQualifiedName.New (ns, name), diag) + + member private this.CompileAndVerify input (diag : DiagnosticItem seq) = + + let fileId = getTempFile() + let file = getManager fileId input + let inFile (c : QsCallable) = c.SourceFile = file.FileName + + compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore + let built = compilationManager.Build() + compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore + let tests = new CompilerTests(built, output) + + for callable in built.Callables.Values |> Seq.filter inFile do + tests.Verify (callable.FullName, diag) + + member private this.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 + + member private this.CompileMonomorphization input = + + let compilationDataStructures = this.BuildContent input + + let monomorphicCompilation = MonomorphizationTransformation.Apply compilationDataStructures.BuiltCompilation + + Assert.NotNull monomorphicCompilation + MonomorphizationValidationTransformation.Apply monomorphicCompilation + + 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 + let chunckNumber = 2 * (testNumber - 1) + let result = this.CompileIntrinsicResolution srcChunks.[chunckNumber] srcChunks.[chunckNumber+1] + Signatures.SignatureCheck [Signatures.IntrinsicResolutionNs] Signatures.IntrinsicResolutionSignatures.[testNumber-1] result + + (*Find the overridden operation in the appropriate namespace*) + let targetCallName = QsQualifiedName.New(NonNullable<_>.New Signatures.IntrinsicResolutionNs, NonNullable<_>.New "Override") + let targetCallable = + result.Namespaces + |> Seq.find (fun ns -> ns.Name.Value = Signatures.IntrinsicResolutionNs) + |> (fun x -> [x]) |> SyntaxExtensions.Callables + |> Seq.find (fun call -> call.FullName = targetCallName) + + (*Check that the operation is not intrinsic*) + targetCallable.Specializations.Length > 0 |> Assert.True + targetCallable.Specializations + |> Seq.map (fun spec -> + match spec.Implementation with + | Provided _ -> true + | _ -> false + |> Assert.True) + |> ignore + + [] + member this.``Monomorphization`` () = + + let filePath = Path.Combine ("TestCases", "LinkingTests", "Generics.qs") |> Path.GetFullPath + let fileId = (new Uri(filePath)) + getManager fileId (File.ReadAllText filePath) + |> compilationManager.AddOrUpdateSourceFileAsync |> ignore + + for testCase in LinkingTests.ReadAndChunkSourceFile "Monomorphization.qs" |> Seq.zip Signatures.MonomorphizationSignatures do + this.CompileMonomorphization (snd testCase) |> + Signatures.SignatureCheck [Signatures.GenericsNs; Signatures.MonomorphizationNs] (fst testCase) + + compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore + + [] + [] + member this.``Intrinsic Resolution Basic Implementation`` () = + this.RunIntrinsicResolutionTest 1 + + + [] + [] + member this.``Intrinsic Resolution Returns UDT`` () = + this.RunIntrinsicResolutionTest 2 + + + [] + [] + member this.``Intrinsic Resolution Type Mismatch Error`` () = + Assert.Throws (fun _ -> this.RunIntrinsicResolutionTest 3) |> ignore + + + [] + [] + member this.``Intrinsic Resolution Param UDT`` () = + this.RunIntrinsicResolutionTest 4 + + + [] + [] + member this.``Intrinsic Resolution With Adj`` () = + this.RunIntrinsicResolutionTest 5 + + + [] + [] + member this.``Intrinsic Resolution Spec Mismatch Error`` () = + Assert.Throws (fun _ -> this.RunIntrinsicResolutionTest 6) |> ignore + + + [] + member this.``Fail on multiple entry points`` () = + + let entryPoints = LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" + Assert.True (entryPoints.Length > 1) + + let fileId = getTempFile() + let file = getManager fileId entryPoints.[0] + compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore + this.CompileAndVerify entryPoints.[1] [Error ErrorCode.MultipleEntryPoints] + compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore + + + [] + member this.``Entry point specialization verification`` () = + + for entryPoint in LinkingTests.ReadAndChunkSourceFile "EntryPointSpecializations.qs" do + this.CompileAndVerify entryPoint [Error ErrorCode.InvalidEntryPointSpecialization] + + for entryPoint in LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" do + this.CompileAndVerify entryPoint [] + + + [] + member this.``Entry point attribute placement verification`` () = + + this.Expect "InvalidEntryPointPlacement1" [Error ErrorCode.InvalidEntryPointPlacement] + this.Expect "InvalidEntryPointPlacement2" [Error ErrorCode.InvalidEntryPointPlacement] + this.Expect "InvalidEntryPointPlacement3" [Error ErrorCode.InvalidEntryPointPlacement] + + // the error messages here should become InvalidEntryPointPlacement if / when + // we support attaching attributes to specializations in general + this.Expect "InvalidEntryPointPlacement4" [Error ErrorCode.MisplacedDeclarationAttribute] + this.Expect "InvalidEntryPointPlacement5" [Error ErrorCode.MisplacedDeclarationAttribute] + this.Expect "InvalidEntryPointPlacement6" [Error ErrorCode.MisplacedDeclarationAttribute] + this.Expect "InvalidEntryPointPlacement7" [Error ErrorCode.MisplacedDeclarationAttribute] + + + [] + member this.``Entry point argument and return type verification`` () = + + this.Expect "InvalidEntryPoint1" [Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint2" [Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint3" [Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint4" [Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint5" [Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint6" [Error ErrorCode.QubitTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint7" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint8" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint9" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint10" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint11" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint12" [Error ErrorCode.CallableTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint13" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint14" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint15" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint16" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint17" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint18" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint19" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint20" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint21" [Error ErrorCode.CallableTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint22" [Error ErrorCode.CallableTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint23" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint24" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint25" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint26" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.QubitTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint27" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint28" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint29" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint30" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint31" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint32" [Error ErrorCode.UserDefinedTypeInEntryPointSignature] + + this.Expect "InvalidEntryPoint33" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint34" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint35" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] + this.Expect "InvalidEntryPoint36" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] + diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs index 25ee90e3bf..25d8e101da 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -1,366 +1,366 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -module Microsoft.Quantum.QsCompiler.Testing.Signatures - -open System.Collections.Generic -open System.Collections.Immutable -open Microsoft.Quantum.QsCompiler -open Microsoft.Quantum.QsCompiler.DataTypes -open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput -open Microsoft.Quantum.QsCompiler.SyntaxTokens -open Microsoft.Quantum.QsCompiler.SyntaxTree -open Xunit - -let private _BaseTypes = - [| - "Unit", UnitType; - "Int", Int; - "Double", Double; - "String", String; - "Result", Result; - "Qubit", Qubit; - "Qubit[]", ResolvedType.New Qubit |> ArrayType; - |] - -let private _MakeTypeMap udts = - Array.concat - [ - _BaseTypes - udts - ] - |> Seq.map (fun (k, v) -> k, ResolvedType.New v) |> dict - -let private _DefaultTypes = _MakeTypeMap [||] - -let private _MakeSig input (typeMap : IDictionary ) = - let ns, name, args, rtrn = input - let fullName = { Namespace = NonNullable.New ns; Name = NonNullable.New name } - let argType = - if Array.isEmpty args then - typeMap.["Unit"] - else - args |> Seq.map (fun typ -> typeMap.[typ]) |> ImmutableArray.ToImmutableArray |> QsTypeKind.TupleType |> ResolvedType.New - let returnType = typeMap.[rtrn] - (fullName, argType, returnType) - -let private _MakeSignatures sigs = - sigs - |> Seq.map (fun (types, case) -> Seq.map (fun _sig -> _MakeSig _sig types) case) - |> Seq.toArray - -/// 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 = - - let getNs targetNs = - match Seq.tryFind (fun (ns : QsNamespace) -> ns.Name.Value = targetNs) compilation.Namespaces with - | Some ns -> ns - | None -> sprintf "Expected but did not find namespace: %s" targetNs |> failwith - - let mutable callableSigs = - checkedNamespaces - |> Seq.map (fun checkedNs -> getNs checkedNs) - |> SyntaxExtensions.Callables - |> Seq.map (fun call -> (call.FullName, StripPositionInfo.Apply call.Signature.ArgumentType, StripPositionInfo.Apply call.Signature.ReturnType)) - - let doesCallMatchSig call signature = - let (call_fullName : QsQualifiedName), call_argType, call_rtrnType = call - let (sig_fullName : QsQualifiedName), sig_argType, sig_rtrnType = signature - - call_fullName.Namespace.Value = sig_fullName.Namespace.Value && - call_fullName.Name.Value.EndsWith sig_fullName.Name.Value && - call_argType = sig_argType && - call_rtrnType = sig_rtrnType - - let makeArgsString (args : ResolvedType) = - match args.Resolution with - | 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.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 - 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"; - |]); - (_DefaultTypes, [| (*Test Case 2*) - MonomorphizationNs, "Test2", [||], "Unit"; - GenericsNs, "Test2Main", [||], "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"; - |]) - |] - |> _MakeSignatures - -let private _IntrinsicResolutionTypes = _MakeTypeMap [| - "TestType", {Namespace = NonNullable<_>.New "Microsoft.Quantum.Testing.IntrinsicResolution"; Name = NonNullable<_>.New "TestType"; Range = Null} |> UserDefinedType - |] - -/// Expected callable signatures to be found when running Intrinsic Resolution tests -let public IntrinsicResolutionSignatures = - [| - (_DefaultTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest1", [||], "Unit"; - IntrinsicResolutionNs, "LocalIntrinsic", [||], "Unit"; - IntrinsicResolutionNs, "Override", [||], "Unit"; - IntrinsicResolutionNs, "EnvironmentIntrinsic", [||], "Unit"; - |]); - (_IntrinsicResolutionTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest2", [||], "Unit"; - IntrinsicResolutionNs, "Override", [||], "TestType"; - IntrinsicResolutionNs, "TestType", [||], "TestType"; - |]); - (_IntrinsicResolutionTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest3", [||], "Unit"; - IntrinsicResolutionNs, "Override", [||], "TestType"; - IntrinsicResolutionNs, "TestType", [||], "TestType"; - |]); - (_IntrinsicResolutionTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest4", [||], "Unit"; - IntrinsicResolutionNs, "Override", [|"TestType"|], "Unit"; - IntrinsicResolutionNs, "TestType", [||], "TestType"; - |]); - (_DefaultTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest5", [||], "Unit"; - IntrinsicResolutionNs, "Override", [||], "Unit"; - |]); - (_DefaultTypes, [| - IntrinsicResolutionNs, "IntrinsicResolutionTest6", [||], "Unit"; - IntrinsicResolutionNs, "Override", [||], "Unit"; - |]); - |] - |> _MakeSignatures - -/// Expected callable signatures to be found when running Classical Control tests -let public ClassicalControlSignatures = - [| - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; // The original operation - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; // The generated operation - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "IfInvalid", [||], "Unit"; - ClassicalControlNs, "ElseInvalid", [||], "Unit"; - ClassicalControlNs, "BothInvalid", [||], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - |]); - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); - (_DefaultTypes, [| - 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, [| - ClassicalControlNs, "Provided", [||], "Unit"; - ClassicalControlNs, "Distribute", [||], "Unit"; - ClassicalControlNs, "_Provided", [|"Result"|], "Unit"; - ClassicalControlNs, "_Provided", [|"Result";"Qubit[]";"Unit"|], "Unit"; - ClassicalControlNs, "_Distribute", [|"Result"|], "Unit"; - |]); - (_DefaultTypes, [| - 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, [| - 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, [| - 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, [| - ClassicalControlNs, "SelfBody", [||], "Unit"; - ClassicalControlNs, "SelfControlled", [||], "Unit"; - - ClassicalControlNs, "_SelfBody", [|"Result"|], "Unit"; - - ClassicalControlNs, "_SelfControlled", [|"Result"|], "Unit"; - ClassicalControlNs, "_SelfControlled", [|"Result";"Qubit[]";"Unit"|], "Unit"; - |]); - |] +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module Microsoft.Quantum.QsCompiler.Testing.Signatures + +open System.Collections.Generic +open System.Collections.Immutable +open Microsoft.Quantum.QsCompiler +open Microsoft.Quantum.QsCompiler.DataTypes +open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput +open Microsoft.Quantum.QsCompiler.SyntaxTokens +open Microsoft.Quantum.QsCompiler.SyntaxTree +open Xunit + +let private _BaseTypes = + [| + "Unit", UnitType; + "Int", Int; + "Double", Double; + "String", String; + "Result", Result; + "Qubit", Qubit; + "Qubit[]", ResolvedType.New Qubit |> ArrayType; + |] + +let private _MakeTypeMap udts = + Array.concat + [ + _BaseTypes + udts + ] + |> Seq.map (fun (k, v) -> k, ResolvedType.New v) |> dict + +let private _DefaultTypes = _MakeTypeMap [||] + +let private _MakeSig input (typeMap : IDictionary ) = + let ns, name, args, rtrn = input + let fullName = { Namespace = NonNullable.New ns; Name = NonNullable.New name } + let argType = + if Array.isEmpty args then + typeMap.["Unit"] + else + args |> Seq.map (fun typ -> typeMap.[typ]) |> ImmutableArray.ToImmutableArray |> QsTypeKind.TupleType |> ResolvedType.New + let returnType = typeMap.[rtrn] + (fullName, argType, returnType) + +let private _MakeSignatures sigs = + sigs + |> Seq.map (fun (types, case) -> Seq.map (fun _sig -> _MakeSig _sig types) case) + |> Seq.toArray + +/// 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 = + + let getNs targetNs = + match Seq.tryFind (fun (ns : QsNamespace) -> ns.Name.Value = targetNs) compilation.Namespaces with + | Some ns -> ns + | None -> sprintf "Expected but did not find namespace: %s" targetNs |> failwith + + let mutable callableSigs = + checkedNamespaces + |> Seq.map (fun checkedNs -> getNs checkedNs) + |> SyntaxExtensions.Callables + |> Seq.map (fun call -> (call.FullName, StripPositionInfo.Apply call.Signature.ArgumentType, StripPositionInfo.Apply call.Signature.ReturnType)) + + let doesCallMatchSig call signature = + let (call_fullName : QsQualifiedName), call_argType, call_rtrnType = call + let (sig_fullName : QsQualifiedName), sig_argType, sig_rtrnType = signature + + call_fullName.Namespace.Value = sig_fullName.Namespace.Value && + call_fullName.Name.Value.EndsWith sig_fullName.Name.Value && + call_argType = sig_argType && + call_rtrnType = sig_rtrnType + + let makeArgsString (args : ResolvedType) = + match args.Resolution with + | 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.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 + 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"; + |]); + (_DefaultTypes, [| (*Test Case 2*) + MonomorphizationNs, "Test2", [||], "Unit"; + GenericsNs, "Test2Main", [||], "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"; + |]) + |] + |> _MakeSignatures + +let private _IntrinsicResolutionTypes = _MakeTypeMap [| + "TestType", {Namespace = NonNullable<_>.New "Microsoft.Quantum.Testing.IntrinsicResolution"; Name = NonNullable<_>.New "TestType"; Range = Null} |> UserDefinedType + |] + +/// Expected callable signatures to be found when running Intrinsic Resolution tests +let public IntrinsicResolutionSignatures = + [| + (_DefaultTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest1", [||], "Unit"; + IntrinsicResolutionNs, "LocalIntrinsic", [||], "Unit"; + IntrinsicResolutionNs, "Override", [||], "Unit"; + IntrinsicResolutionNs, "EnvironmentIntrinsic", [||], "Unit"; + |]); + (_IntrinsicResolutionTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest2", [||], "Unit"; + IntrinsicResolutionNs, "Override", [||], "TestType"; + IntrinsicResolutionNs, "TestType", [||], "TestType"; + |]); + (_IntrinsicResolutionTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest3", [||], "Unit"; + IntrinsicResolutionNs, "Override", [||], "TestType"; + IntrinsicResolutionNs, "TestType", [||], "TestType"; + |]); + (_IntrinsicResolutionTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest4", [||], "Unit"; + IntrinsicResolutionNs, "Override", [|"TestType"|], "Unit"; + IntrinsicResolutionNs, "TestType", [||], "TestType"; + |]); + (_DefaultTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest5", [||], "Unit"; + IntrinsicResolutionNs, "Override", [||], "Unit"; + |]); + (_DefaultTypes, [| + IntrinsicResolutionNs, "IntrinsicResolutionTest6", [||], "Unit"; + IntrinsicResolutionNs, "Override", [||], "Unit"; + |]); + |] + |> _MakeSignatures + +/// Expected callable signatures to be found when running Classical Control tests +let public ClassicalControlSignatures = + [| + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; // The original operation + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; // The generated operation + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "IfInvalid", [||], "Unit"; + ClassicalControlNs, "ElseInvalid", [||], "Unit"; + ClassicalControlNs, "BothInvalid", [||], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + |]); + (_DefaultTypes, [| + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; + ClassicalControlNs, "Foo", [||], "Unit"; + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + 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, [| + ClassicalControlNs, "Provided", [||], "Unit"; + ClassicalControlNs, "Distribute", [||], "Unit"; + ClassicalControlNs, "_Provided", [|"Result"|], "Unit"; + ClassicalControlNs, "_Provided", [|"Result";"Qubit[]";"Unit"|], "Unit"; + ClassicalControlNs, "_Distribute", [|"Result"|], "Unit"; + |]); + (_DefaultTypes, [| + 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, [| + 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, [| + 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, [| + ClassicalControlNs, "SelfBody", [||], "Unit"; + ClassicalControlNs, "SelfControlled", [||], "Unit"; + + ClassicalControlNs, "_SelfBody", [|"Result"|], "Unit"; + + ClassicalControlNs, "_SelfControlled", [|"Result"|], "Unit"; + ClassicalControlNs, "_SelfControlled", [|"Result";"Qubit[]";"Unit"|], "Unit"; + |]); + |] |> _MakeSignatures \ No newline at end of file From 7ae62c605816543906d7eb9d14b1bddd244c72cd Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 24 Jan 2020 13:46:59 -0800 Subject: [PATCH 41/53] Fixed line endings --- .../Tests.Compiler/ClasicalControlTests.fs | 2332 ++++++++--------- 1 file changed, 1166 insertions(+), 1166 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs index 7e38d47f59..70ad9286b5 100644 --- a/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs @@ -1,1166 +1,1166 @@ -// 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 - - 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("\r\n") - - 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;$" 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$" <| 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 CheckIfSpecializationHasContent specialization (content : seq) = - let lines = GetLinesFromSpecialization specialization - Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content - - let AssertSpecializationHasContent specialization content = - Assert.True(CheckIfSpecializationHasContent specialization content, sprintf "Callable %O(%A) did not have expected content" specialization.Parent specialization.Kind) - - let IdentifyGeneratedByContent generatedCalls contents = - let mutable calls = generatedCalls |> Seq.map (fun x -> x, x |> (GetBodyFromCallable >> GetLinesFromSpecialization)) - let hasContent call (content : seq) = - let (_, lines : string[]) = call - Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content - - Assert.True(Seq.length calls = Seq.length contents) // This should be true if this method is call correctly - - let mutable rtrn = Seq.empty - - let removeAt i lst = - Seq.append - <| Seq.take i lst - <| Seq.skip (i+1) lst - - for content in contents do - calls - |> Seq.tryFindIndex (fun callSig -> hasContent callSig content) - |> (fun x -> - Assert.True (x <> None, sprintf "Did not find expected generated content") - rtrn <- Seq.append rtrn [Seq.item x.Value calls] - calls <- removeAt x.Value calls - ) - 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 = IdentifyGeneratedByContent 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"); - ] - |> AssertSpecializationHasContent 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 Statments`` () = - 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent 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 = IdentifyGeneratedByContent 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" elifOp.FullName elseOp.FullName - |> (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" ifOp elseOp - |> (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" ifOp elseOp - |> (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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable selfOp) - - [(1, BuiltIn.ApplyIfZeroA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable invertOp) - - [(1, BuiltIn.ApplyIfZero)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent _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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable distributeOp) - - [(1, BuiltIn.ApplyIfZero)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent _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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetAdjFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetAdjFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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) - AssertSpecializationHasContent (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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) - - [(1, BuiltIn.ApplyIfOneC)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlFromCallable original) - - [(1, BuiltIn.ApplyIfOneC)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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) - AssertSpecializationHasContent (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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) - - [(1, BuiltIn.ApplyIfOneA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) - - [(1, BuiltIn.ApplyIfOneA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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) - AssertSpecializationHasContent (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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) - - [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 () +// 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 + + 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("\r\n") + + 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;$" 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$" <| 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 CheckIfSpecializationHasContent specialization (content : seq) = + let lines = GetLinesFromSpecialization specialization + Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content + + let AssertSpecializationHasContent specialization content = + Assert.True(CheckIfSpecializationHasContent specialization content, sprintf "Callable %O(%A) did not have expected content" specialization.Parent specialization.Kind) + + let IdentifyGeneratedByContent generatedCalls contents = + let mutable calls = generatedCalls |> Seq.map (fun x -> x, x |> (GetBodyFromCallable >> GetLinesFromSpecialization)) + let hasContent call (content : seq) = + let (_, lines : string[]) = call + Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content + + Assert.True(Seq.length calls = Seq.length contents) // This should be true if this method is call correctly + + let mutable rtrn = Seq.empty + + let removeAt i lst = + Seq.append + <| Seq.take i lst + <| Seq.skip (i+1) lst + + for content in contents do + calls + |> Seq.tryFindIndex (fun callSig -> hasContent callSig content) + |> (fun x -> + Assert.True (x <> None, sprintf "Did not find expected generated content") + rtrn <- Seq.append rtrn [Seq.item x.Value calls] + calls <- removeAt x.Value calls + ) + 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 = IdentifyGeneratedByContent 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"); + ] + |> AssertSpecializationHasContent 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 Statments`` () = + 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent 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 = IdentifyGeneratedByContent 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" elifOp.FullName elseOp.FullName + |> (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" ifOp elseOp + |> (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" ifOp elseOp + |> (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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable selfOp) + + [(1, BuiltIn.ApplyIfZeroA)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable invertOp) + + [(1, BuiltIn.ApplyIfZero)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent _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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable distributeOp) + + [(1, BuiltIn.ApplyIfZero)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent _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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetCtlFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetAdjFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetCtlFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetAdjFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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) + AssertSpecializationHasContent (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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOneC)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetCtlFromCallable original) + + [(1, BuiltIn.ApplyIfOneC)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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) + AssertSpecializationHasContent (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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOneA)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOneA)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetCtlFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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) + AssertSpecializationHasContent (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 (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (GetBodyFromCallable original) + + [(1, BuiltIn.ApplyIfOne)] + |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) + |> AssertSpecializationHasContent (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 = IdentifyGeneratedByContent 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 () From 57a6420d967ec75640fd6c6db03e116480019029 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 31 Jan 2020 15:20:19 -0800 Subject: [PATCH 42/53] PR comments --- ...ntrolTests.fs => ClassicalControlTests.fs} | 241 +++++------ .../Tests.Compiler/TestUtils/Signatures.fs | 406 +++++++++--------- .../Tests.Compiler/Tests.Compiler.fsproj | 2 +- 3 files changed, 325 insertions(+), 324 deletions(-) rename src/QsCompiler/Tests.Compiler/{ClasicalControlTests.fs => ClassicalControlTests.fs} (80%) diff --git a/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs similarity index 80% rename from src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs rename to src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index 70ad9286b5..6ef6e666b5 100644 --- a/src/QsCompiler/Tests.Compiler/ClasicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -15,7 +15,6 @@ 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)) @@ -23,8 +22,8 @@ type ClassicalControlTests () = 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 + do let addOrUpdateSourceFile filePath = getManager (new Uri(filePath)) (File.ReadAllText filePath) |> compilationManager.AddOrUpdateSourceFileAsync |> ignore + Path.Combine ("TestCases", "LinkingTests", "Core.qs") |> Path.GetFullPath |> addOrUpdateSourceFile let ReadAndChunkSourceFile fileName = let sourceInput = Path.Combine ("TestCases", "LinkingTests", fileName) |> File.ReadAllText @@ -68,7 +67,7 @@ type ClassicalControlTests () = |> writer.Scope.Transform |> ignore - (writer.Scope :?> ScopeToQs).Output.Split("\r\n") + (writer.Scope :?> ScopeToQs).Output.Split(Environment.NewLine) let CheckIfLineIsCall ``namespace`` name input = let call = sprintf @"(%s\.)?%s" <| Regex.Escape ``namespace`` <| Regex.Escape name @@ -110,20 +109,22 @@ type ClassicalControlTests () = else (false, "", "", "", "") - let CheckIfSpecializationHasContent specialization (content : seq) = + let CheckIfSpecializationHasCalls specialization (calls : seq) = let lines = GetLinesFromSpecialization specialization - Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content + 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 AssertSpecializationHasContent specialization content = - Assert.True(CheckIfSpecializationHasContent specialization content, 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 IdentifyGeneratedByContent generatedCalls contents = - let mutable calls = generatedCalls |> Seq.map (fun x -> x, x |> (GetBodyFromCallable >> GetLinesFromSpecialization)) + let IdentifyGeneratedByCalls generatedCallables calls = + let mutable callables = generatedCallables |> Seq.map (fun x -> x, x |> (GetBodyFromCallable >> GetLinesFromSpecialization)) let hasContent call (content : seq) = let (_, lines : string[]) = call - Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x,_,_) -> x)) content + Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x, _, _) -> x)) content - Assert.True(Seq.length calls = Seq.length contents) // This should be true if this method is call correctly + Assert.True(Seq.length callables = Seq.length calls) // This should be true if this method is call correctly let mutable rtrn = Seq.empty @@ -132,13 +133,13 @@ type ClassicalControlTests () = <| Seq.take i lst <| Seq.skip (i+1) lst - for content in contents do - calls + for content in calls do + callables |> Seq.tryFindIndex (fun callSig -> hasContent callSig content) |> (fun x -> Assert.True (x <> None, sprintf "Did not find expected generated content") - rtrn <- Seq.append rtrn [Seq.item x.Value calls] - calls <- removeAt x.Value calls + rtrn <- Seq.append rtrn [Seq.item x.Value callables] + callables <- removeAt x.Value callables ) rtrn |> Seq.map (fun (x,y) -> x) @@ -172,7 +173,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp3"); ] - let orderedGens = IdentifyGeneratedByContent generated [ifContent; elseContent] + 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 @@ -189,12 +190,12 @@ type ClassicalControlTests () = let hasAdjoint = expectedFunctors |> Seq.contains QsFunctor.Adjoint let hasControlled = expectedFunctors |> Seq.contains QsFunctor.Controlled - (*Checks the Characteristics match*) + // 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*) + // Checks that the target specializations are present let adjMatch = lazy if hasAdjoint then call.Specializations |> Seq.tryFind (fun x -> x.Kind = QsSpecializationKind.QsAdjoint) @@ -235,7 +236,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp2"); (2, "SubOps", "SubOp3"); ] - |> AssertSpecializationHasContent generated + |> AssertSpecializationHasCalls generated [] [] @@ -275,15 +276,15 @@ type ClassicalControlTests () = (1, BuiltIn.ApplyIfZero); (3, BuiltIn.ApplyIfOne); ] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent originalOp + |> 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")) + |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) [] [] @@ -291,7 +292,7 @@ type ClassicalControlTests () = 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")) + |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) [] [] @@ -318,7 +319,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp3"); ] - let orderedGens = IdentifyGeneratedByContent generated [ifContent; elifContent; elseContent] + 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 @@ -333,7 +334,7 @@ type ClassicalControlTests () = let (success, _, _, _, subArgs) = IsApplyIfElseArgsMatch args "r" ifOp.FullName { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } Assert.True(success, errorMsg) IsApplyIfElseArgsMatch subArgs "r" elifOp.FullName elseOp.FullName - |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) + |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg)) [] [] @@ -344,7 +345,7 @@ type ClassicalControlTests () = let (success, _, subArgs, _, _) = IsApplyIfElseArgsMatch args "r" { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } elseOp Assert.True(success, errorMsg) IsApplyIfElseArgsMatch subArgs "r" ifOp elseOp - |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) + |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg)) [] [] @@ -355,7 +356,7 @@ type ClassicalControlTests () = let (success, _, _, _, subArgs) = IsApplyIfElseArgsMatch args "r" ifOp { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } Assert.True(success, errorMsg) IsApplyIfElseArgsMatch subArgs "r" ifOp elseOp - |> (fun (x,_,_,_,_) -> Assert.True(x, errorMsg)) + |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg)) [] [] @@ -398,12 +399,12 @@ type ClassicalControlTests () = 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*) + // 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*) + // 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) @@ -433,16 +434,16 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZero)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable selfOp) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable selfOp) [(1, BuiltIn.ApplyIfZeroA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable invertOp) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable invertOp) [(1, BuiltIn.ApplyIfZero)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable providedOp) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable providedOp) let _selfOp = callables |> Seq.find (fun x -> x.Key.Name.Value.EndsWith "_Self") @@ -467,7 +468,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp3"); ] - let orderedGens = IdentifyGeneratedByContent _providedOps [bodyContent; adjointContent] + let orderedGens = IdentifyGeneratedByCalls _providedOps [bodyContent; adjointContent] let bodyGen, adjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens) AssertCallSupportsFunctors [] _selfOp @@ -488,16 +489,16 @@ type ClassicalControlTests () = |> 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 + |> Seq.find (fun x -> x.Key.Name.Value = "Provided") + |> fun x -> x.Value [(1, BuiltIn.ApplyIfZeroC)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable distributeOp) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable distributeOp) [(1, BuiltIn.ApplyIfZero)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable providedOp) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable providedOp) let _distributeOp = callables |> Seq.find (fun x -> x.Key.Name.Value.EndsWith "_Distribute") @@ -519,7 +520,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp3"); ] - let orderedGens = IdentifyGeneratedByContent _providedOps [bodyContent; controlledContent] + let orderedGens = IdentifyGeneratedByCalls _providedOps [bodyContent; controlledContent] let bodyGen, ctlGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens) AssertCallSupportsFunctors [QsFunctor.Controlled] _distributeOp @@ -543,12 +544,12 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZeroCA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlAdjFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetCtlAdjFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_ProvidedBody") @@ -567,7 +568,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp3"); ] - let orderedGens = IdentifyGeneratedByContent generated [bodyContent; ctlAdjContent] + let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlAdjContent] let bodyGen, ctlAdjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens) AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original @@ -584,16 +585,16 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZeroA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetCtlFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlAdjFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetCtlAdjFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_ProvidedControlled") @@ -617,7 +618,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp3"); ] - let orderedGens = IdentifyGeneratedByContent generated [bodyContent; ctlContent; ctlAdjContent] + 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 @@ -635,16 +636,16 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZeroC)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetAdjFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetAdjFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlAdjFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetCtlAdjFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_ProvidedAdjoint") @@ -668,7 +669,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp3"); ] - let orderedGens = IdentifyGeneratedByContent generated [bodyContent; adjContent; ctlAdjContent] + 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 @@ -686,20 +687,20 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZero)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetCtlFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetAdjFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetAdjFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlAdjFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetCtlAdjFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_ProvidedAll") @@ -727,7 +728,7 @@ type ClassicalControlTests () = (2, "SubOps", "SubOp3"); ] - let orderedGens = IdentifyGeneratedByContent generated [bodyContent; ctlContent; adjContent; ctlAdjContent] + 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 @@ -755,8 +756,8 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZeroCA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_DistributeBody") @@ -771,7 +772,7 @@ type ClassicalControlTests () = ] let bodyGen = (Seq.item 0 generated) - AssertSpecializationHasContent (GetBodyFromCallable bodyGen) bodyContent + AssertSpecializationHasCalls (GetBodyFromCallable bodyGen) bodyContent AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] bodyGen @@ -786,12 +787,12 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZeroCA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetCtlFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_DistributeControlled") @@ -810,7 +811,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp1"); ] - let orderedGens = IdentifyGeneratedByContent generated [bodyContent; ctlContent] + let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent] let bodyGen, ctlGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens) AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original @@ -827,12 +828,12 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZeroC)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) [(1, BuiltIn.ApplyIfOneC)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetAdjFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetAdjFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_DistributeAdjoint") @@ -851,7 +852,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp1"); ] - let orderedGens = IdentifyGeneratedByContent generated [bodyContent; adjContent] + let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; adjContent] let bodyGen, adjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens) AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original @@ -868,16 +869,16 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZero)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetCtlFromCallable original) [(1, BuiltIn.ApplyIfOneC)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetAdjFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetAdjFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_DistributeAll") @@ -901,7 +902,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp3"); ] - let orderedGens = IdentifyGeneratedByContent generated [bodyContent; ctlContent; adjContent] + 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 @@ -928,8 +929,8 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZeroCA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_InvertBody") @@ -944,7 +945,7 @@ type ClassicalControlTests () = ] let bodyGen = (Seq.item 0 generated) - AssertSpecializationHasContent (GetBodyFromCallable bodyGen) bodyContent + AssertSpecializationHasCalls (GetBodyFromCallable bodyGen) bodyContent AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] bodyGen @@ -959,12 +960,12 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZeroA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) [(1, BuiltIn.ApplyIfOneA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetCtlFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_InvertControlled") @@ -983,7 +984,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp1"); ] - let orderedGens = IdentifyGeneratedByContent generated [bodyContent; ctlContent] + let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent] let bodyGen, ctlGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens) AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original @@ -1000,12 +1001,12 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZeroCA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetAdjFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetAdjFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_InvertAdjoint") @@ -1024,7 +1025,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp1"); ] - let orderedGens = IdentifyGeneratedByContent generated [bodyContent; adjContent] + let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; adjContent] let bodyGen, adjGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens) AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original @@ -1041,16 +1042,16 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZero)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) [(1, BuiltIn.ApplyIfOneA)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetCtlFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetAdjFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetAdjFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_InvertAll") @@ -1074,7 +1075,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp3"); ] - let orderedGens = IdentifyGeneratedByContent generated [bodyContent; ctlContent; adjContent] + 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 @@ -1101,8 +1102,8 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZeroC)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_SelfBody") @@ -1117,7 +1118,7 @@ type ClassicalControlTests () = ] let bodyGen = (Seq.item 0 generated) - AssertSpecializationHasContent (GetBodyFromCallable bodyGen) bodyContent + AssertSpecializationHasCalls (GetBodyFromCallable bodyGen) bodyContent AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original AssertCallSupportsFunctors [QsFunctor.Controlled] bodyGen @@ -1132,12 +1133,12 @@ type ClassicalControlTests () = |> fun x -> x.Value [(1, BuiltIn.ApplyIfZero)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetBodyFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetBodyFromCallable original) [(1, BuiltIn.ApplyIfOne)] - |> Seq.map (fun (i, builtin) -> (i, builtin.Namespace.Value, builtin.Name.Value)) - |> AssertSpecializationHasContent (GetCtlFromCallable original) + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls (GetCtlFromCallable original) let generated = callables |> Seq.filter (fun x -> x.Key.Name.Value.EndsWith "_SelfControlled") @@ -1156,7 +1157,7 @@ type ClassicalControlTests () = (1, "SubOps", "SubOp1"); ] - let orderedGens = IdentifyGeneratedByContent generated [bodyContent; ctlContent] + let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent] let bodyGen, ctlGen = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens) AssertCallSupportsFunctors [QsFunctor.Controlled;QsFunctor.Adjoint] original diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs index 25d8e101da..f8abaae641 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -14,13 +14,13 @@ open Xunit let private _BaseTypes = [| - "Unit", UnitType; - "Int", Int; - "Double", Double; - "String", String; - "Result", Result; - "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 = @@ -108,46 +108,46 @@ let public ClassicalControlNs = "Microsoft.Quantum.Testing.ClassicalControl" 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 @@ -160,34 +160,34 @@ 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 @@ -195,172 +195,172 @@ let public IntrinsicResolutionSignatures = let public ClassicalControlSignatures = [| (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; // The original operation - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; // The generated operation - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" // The original operation + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" // The generated operation + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "IfInvalid", [||], "Unit"; - ClassicalControlNs, "ElseInvalid", [||], "Unit"; - ClassicalControlNs, "BothInvalid", [||], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "IfInvalid", [||], "Unit" + ClassicalControlNs, "ElseInvalid", [||], "Unit" + ClassicalControlNs, "BothInvalid", [||], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit"; - ClassicalControlNs, "Foo", [||], "Unit"; - ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; - |]); + ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + |]) (_DefaultTypes, [| - 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"; - |]); + 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, [| - ClassicalControlNs, "Provided", [||], "Unit"; - ClassicalControlNs, "Distribute", [||], "Unit"; - ClassicalControlNs, "_Provided", [|"Result"|], "Unit"; - ClassicalControlNs, "_Provided", [|"Result";"Qubit[]";"Unit"|], "Unit"; - ClassicalControlNs, "_Distribute", [|"Result"|], "Unit"; - |]); + ClassicalControlNs, "Provided", [||], "Unit" + ClassicalControlNs, "Distribute", [||], "Unit" + ClassicalControlNs, "_Provided", [|"Result"|], "Unit" + ClassicalControlNs, "_Provided", [|"Result";"Qubit[]";"Unit"|], "Unit" + ClassicalControlNs, "_Distribute", [|"Result"|], "Unit" + |]) (_DefaultTypes, [| - 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"; - |]); + 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, [| - ClassicalControlNs, "DistributeBody", [||], "Unit"; - ClassicalControlNs, "DistributeAdjoint", [||], "Unit"; - ClassicalControlNs, "DistributeControlled", [||], "Unit"; - ClassicalControlNs, "DistributeAll", [||], "Unit"; + ClassicalControlNs, "DistributeBody", [||], "Unit" + ClassicalControlNs, "DistributeAdjoint", [||], "Unit" + ClassicalControlNs, "DistributeControlled", [||], "Unit" + ClassicalControlNs, "DistributeAll", [||], "Unit" - ClassicalControlNs, "_DistributeBody", [|"Result"|], "Unit"; + ClassicalControlNs, "_DistributeBody", [|"Result"|], "Unit" - ClassicalControlNs, "_DistributeAdjoint", [|"Result"|], "Unit"; - ClassicalControlNs, "_DistributeAdjoint", [|"Result"|], "Unit"; + ClassicalControlNs, "_DistributeAdjoint", [|"Result"|], "Unit" + ClassicalControlNs, "_DistributeAdjoint", [|"Result"|], "Unit" - ClassicalControlNs, "_DistributeControlled", [|"Result"|], "Unit"; - ClassicalControlNs, "_DistributeControlled", [|"Result";"Qubit[]";"Unit"|], "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"; - |]); + ClassicalControlNs, "_DistributeAll", [|"Result"|], "Unit" + ClassicalControlNs, "_DistributeAll", [|"Result"|], "Unit" + ClassicalControlNs, "_DistributeAll", [|"Result";"Qubit[]";"Unit"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "InvertBody", [||], "Unit"; - ClassicalControlNs, "InvertAdjoint", [||], "Unit"; - ClassicalControlNs, "InvertControlled", [||], "Unit"; - ClassicalControlNs, "InvertAll", [||], "Unit"; + ClassicalControlNs, "InvertBody", [||], "Unit" + ClassicalControlNs, "InvertAdjoint", [||], "Unit" + ClassicalControlNs, "InvertControlled", [||], "Unit" + ClassicalControlNs, "InvertAll", [||], "Unit" - ClassicalControlNs, "_InvertBody", [|"Result"|], "Unit"; + ClassicalControlNs, "_InvertBody", [|"Result"|], "Unit" - ClassicalControlNs, "_InvertAdjoint", [|"Result"|], "Unit"; - ClassicalControlNs, "_InvertAdjoint", [|"Result"|], "Unit"; + ClassicalControlNs, "_InvertAdjoint", [|"Result"|], "Unit" + ClassicalControlNs, "_InvertAdjoint", [|"Result"|], "Unit" - ClassicalControlNs, "_InvertControlled", [|"Result"|], "Unit"; - ClassicalControlNs, "_InvertControlled", [|"Result";"Qubit[]";"Unit"|], "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"; - |]); + ClassicalControlNs, "_InvertAll", [|"Result"|], "Unit" + ClassicalControlNs, "_InvertAll", [|"Result"|], "Unit" + ClassicalControlNs, "_InvertAll", [|"Result";"Qubit[]";"Unit"|], "Unit" + |]) (_DefaultTypes, [| - ClassicalControlNs, "SelfBody", [||], "Unit"; - ClassicalControlNs, "SelfControlled", [||], "Unit"; + ClassicalControlNs, "SelfBody", [||], "Unit" + ClassicalControlNs, "SelfControlled", [||], "Unit" - ClassicalControlNs, "_SelfBody", [|"Result"|], "Unit"; + ClassicalControlNs, "_SelfBody", [|"Result"|], "Unit" - ClassicalControlNs, "_SelfControlled", [|"Result"|], "Unit"; - ClassicalControlNs, "_SelfControlled", [|"Result";"Qubit[]";"Unit"|], "Unit"; - |]); + ClassicalControlNs, "_SelfControlled", [|"Result"|], "Unit" + ClassicalControlNs, "_SelfControlled", [|"Result";"Qubit[]";"Unit"|], "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 1625070acf..d96ff2484a 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -134,7 +134,7 @@ - + From 7900e2e003551df7f9e84a2eacd54880b5246068 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 31 Jan 2020 17:34:11 -0800 Subject: [PATCH 43/53] PR comments --- .../Tests.Compiler/ClassicalControlTests.fs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index 6ef6e666b5..ddd534b179 100644 --- a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -120,11 +120,11 @@ type ClassicalControlTests () = let IdentifyGeneratedByCalls generatedCallables calls = let mutable callables = generatedCallables |> Seq.map (fun x -> x, x |> (GetBodyFromCallable >> GetLinesFromSpecialization)) - let hasContent call (content : seq) = - let (_, lines : string[]) = call - Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x, _, _) -> x)) content + 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 call correctly + Assert.True(Seq.length callables = Seq.length calls) // This should be true if this method is called correctly let mutable rtrn = Seq.empty @@ -133,9 +133,9 @@ type ClassicalControlTests () = <| Seq.take i lst <| Seq.skip (i+1) lst - for content in calls do + for call in calls do callables - |> Seq.tryFindIndex (fun callSig -> hasContent callSig content) + |> 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] From e0bc6d9d463b70414bb8ad48183a49991ffc3627 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Sun, 2 Feb 2020 15:32:10 -0800 Subject: [PATCH 44/53] helper function for ensuring that IRewriteSteps are executed atomic --- src/QsCompiler/Compiler/CompilationLoader.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 290cdb8157..033ee7f8a3 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -365,23 +365,27 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference 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")); } - // executing the specified rewrite steps + 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.CompilationStatus.ConvertClassicalControl = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out var transformed); - if (this.CompilationStatus.ConvertClassicalControl == Status.Succeeded) this.CompilationOutput = transformed; + 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.CompilationStatus.Monomorphization = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out var transformed); - if (this.CompilationStatus.Monomorphization == Status.Succeeded) this.CompilationOutput = transformed; + this.CompilationOutput = ExecuteAsAtomicTransformation(rewriteStep, ref this.CompilationStatus.Monomorphization); } if (this.Config.GenerateFunctorSupport) @@ -435,9 +439,7 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference 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; + this.CompilationOutput = ExecuteAsAtomicTransformation(this.ExternalRewriteSteps[i], ref this.CompilationStatus.LoadedRewriteSteps[i]); } } From d9c5e97238c97d0308c9364f3727f158a4b1dce8 Mon Sep 17 00:00:00 2001 From: Scott Carda <55811729+ScottCarda-MS@users.noreply.github.com> Date: Thu, 6 Feb 2020 18:38:55 -0800 Subject: [PATCH 45/53] Fix Unit Test Failures for Classical Control (#289) Fixed Issues found with Unit Tests. --- QsCompiler.sln | 15 - src/QsCompiler/Core/Dependencies.fs | 109 +---- .../DataStructures/SyntaxExtensions.fs | 15 - src/QsCompiler/DataStructures/SyntaxTree.fs | 11 + .../Optimizations/Utils/Evaluation.fs | 6 +- src/QsCompiler/Optimizations/Utils/Utils.fs | 12 +- .../SyntaxProcessor/ExpressionVerification.fs | 4 +- .../Tests.Compiler/ClassicalControlTests.fs | 95 +++- .../Tests.Compiler/ExecutionTests.fs | 4 +- .../LinkingTests/ClassicalControl.qs | 201 +++++---- .../QuantumProcessorExtensions.qs | 20 + .../Tests.Compiler/TestUtils/Signatures.fs | 112 +++-- .../Tests.Compiler/Tests.Compiler.fsproj | 6 +- .../ClassicallyControlledTransformation.cs | 421 ++++++++++++------ src/QsCompiler/Transformations/CodeOutput.cs | 6 +- 15 files changed, 604 insertions(+), 433 deletions(-) create mode 100644 src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/QuantumProcessorExtensions.qs diff --git a/QsCompiler.sln b/QsCompiler.sln index 7a330f37f1..f7ee3f64dc 100644 --- a/QsCompiler.sln +++ b/QsCompiler.sln @@ -34,8 +34,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simulation", "Simulation", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Simulation", "src\QsCompiler\TestTargets\Simulation\Target\Simulation.csproj", "{D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "src\QsCompiler\TestTargets\Simulation\Example\Example.csproj", "{2E331781-F7ED-4EF1-8451-896636C6D93A}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{D2E36476-A65F-4310-9C4C-B721BCC47B00}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Library1", "src\QsCompiler\TestTargets\Libraries\Library1\Library1.csproj", "{DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}" @@ -222,18 +220,6 @@ Global {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x64.Build.0 = Release|Any CPU {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x86.ActiveCfg = Release|Any CPU {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x86.Build.0 = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x64.ActiveCfg = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x64.Build.0 = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x86.ActiveCfg = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x86.Build.0 = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|Any CPU.Build.0 = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x64.ActiveCfg = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x64.Build.0 = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x86.ActiveCfg = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x86.Build.0 = Release|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -268,7 +254,6 @@ Global {256A6275-FC7F-42E9-9931-BC6EA6D0F31A} = {B4A9484D-31FC-4A27-9E26-4C8DE3E02D77} {76BA96DA-DC1E-4315-A3ED-5F0700A79812} = {6077A717-50BF-4F87-B439-CA549AF6A4AE} {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1} = {76BA96DA-DC1E-4315-A3ED-5F0700A79812} - {2E331781-F7ED-4EF1-8451-896636C6D93A} = {76BA96DA-DC1E-4315-A3ED-5F0700A79812} {D2E36476-A65F-4310-9C4C-B721BCC47B00} = {6077A717-50BF-4F87-B439-CA549AF6A4AE} {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F} = {D2E36476-A65F-4310-9C4C-B721BCC47B00} EndGlobalSection diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index 00f48542c0..000e21a2ca 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -22,7 +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.ClassicallyControlled" + 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) @@ -87,42 +87,7 @@ type BuiltIn = { TypeParameters = ImmutableArray.Empty } - static member private _MakeResolvedType builtIn props = - let typeParamT = - { - Origin = {Namespace = builtIn.Namespace; Name = builtIn.Name}; - TypeName = builtIn.TypeParameters.[0]; - Range = QsRangeInfo.Null - } |> TypeParameter |> ResolvedType.New - - let characteristics = - { - Characteristics = ResolvedCharacteristics.FromProperties props; - InferredInformation = InferredCallableInformation.NoInformation - } - - let args = - [ - Result |> ResolvedType.New; - [ - ( - ( - typeParamT, - UnitType |> ResolvedType.New - ), - characteristics - ) |> Operation |> ResolvedType.New; - typeParamT - ].ToImmutableArray() |> TupleType |> ResolvedType.New - ].ToImmutableArray() |> TupleType |> ResolvedType.New - - ( - ( - args, - ResolvedType.New UnitType - ), - characteristics - ) |> Operation |> ResolvedType.New + // hard dependencies in Microsoft.Quantum.Simulation.QuantumProcessor.Extensions // This is expected to have type <'T>((Result, (('T => Unit), 'T)) => Unit) static member ApplyIfZero = { @@ -130,7 +95,6 @@ type BuiltIn = { Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) } - static member ApplyIfZeroResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfZero [] // This is expected to have type <'T>((Result, (('T => Unit is Adj), 'T)) => Unit is Adj) static member ApplyIfZeroA = { @@ -138,7 +102,6 @@ type BuiltIn = { Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) } - static member ApplyIfZeroAResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfZeroA [OpProperty.Adjointable] // This is expected to have type <'T>((Result, (('T => Unit is Ctl), 'T)) => Unit is Ctl) static member ApplyIfZeroC = { @@ -146,7 +109,6 @@ type BuiltIn = { Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) } - static member ApplyIfZeroCResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfZeroC [OpProperty.Controllable] // This is expected to have type <'T>((Result, (('T => Unit is Adj + Ctl), 'T)) => Unit is Adj + Ctl) static member ApplyIfZeroCA = { @@ -154,7 +116,6 @@ type BuiltIn = { Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) } - static member ApplyIfZeroCAResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfZeroCA [OpProperty.Adjointable; OpProperty.Controllable] // This is expected to have type <'T>((Result, (('T => Unit), 'T)) => Unit) static member ApplyIfOne = { @@ -162,7 +123,6 @@ type BuiltIn = { Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) } - static member ApplyIfOneResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfOne [] // This is expected to have type <'T>((Result, (('T => Unit is Adj), 'T)) => Unit is Adj) static member ApplyIfOneA = { @@ -170,7 +130,6 @@ type BuiltIn = { Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) } - static member ApplyIfOneAResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfOneA [OpProperty.Adjointable] // This is expected to have type <'T>((Result, (('T => Unit is Ctl), 'T)) => Unit is Ctl) static member ApplyIfOneC = { @@ -178,7 +137,6 @@ type BuiltIn = { Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) } - static member ApplyIfOneCResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfOneC [OpProperty.Controllable] // This is expected to have type <'T>((Result, (('T => Unit is Adj + Ctl), 'T)) => Unit is Adj + Ctl) static member ApplyIfOneCA = { @@ -186,61 +144,6 @@ type BuiltIn = { Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) } - static member ApplyIfOneCAResolvedType = BuiltIn._MakeResolvedType BuiltIn.ApplyIfOneCA [OpProperty.Adjointable; OpProperty.Controllable] - - static member private _MakeApplyIfElseResolvedType builtIn props = - let typeParamT = - { - Origin = {Namespace = builtIn.Namespace; Name = builtIn.Name}; - TypeName = builtIn.TypeParameters.[0]; - Range = QsRangeInfo.Null - } |> TypeParameter |> ResolvedType.New - - let typeParamU = - { - Origin = {Namespace = builtIn.Namespace; Name = builtIn.Name}; - TypeName = builtIn.TypeParameters.[1]; - Range = QsRangeInfo.Null - } |> TypeParameter |> ResolvedType.New - - let characteristics = - { - Characteristics = ResolvedCharacteristics.FromProperties props; - InferredInformation = InferredCallableInformation.NoInformation - } - - let args = - [ - Result |> ResolvedType.New; - [ - ( - ( - typeParamT, - UnitType |> ResolvedType.New - ), - characteristics - ) |> Operation |> ResolvedType.New; - typeParamT - ].ToImmutableArray() |> TupleType |> ResolvedType.New; - [ - ( - ( - typeParamU, - UnitType |> ResolvedType.New - ), - characteristics - ) |> Operation |> ResolvedType.New; - typeParamU - ].ToImmutableArray() |> TupleType |> ResolvedType.New - ].ToImmutableArray() |> TupleType |> ResolvedType.New - - ( - ( - args, - ResolvedType.New UnitType - ), - characteristics - ) |> Operation |> ResolvedType.New // This is expected to have type <'T, 'U>((Result, (('T => Unit), 'T), (('U => Unit), 'U)) => Unit) static member ApplyIfElseR = { @@ -248,7 +151,6 @@ type BuiltIn = { Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) } - static member ApplyIfElseRResolvedType = BuiltIn._MakeApplyIfElseResolvedType BuiltIn.ApplyIfElseR [] // 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 = { @@ -256,7 +158,6 @@ type BuiltIn = { Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) } - static member ApplyIfElseRAResolvedType = BuiltIn._MakeApplyIfElseResolvedType BuiltIn.ApplyIfElseRA [OpProperty.Adjointable] // 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 = { @@ -264,15 +165,13 @@ type BuiltIn = { Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) } - static member ApplyIfElseRCResolvedType = BuiltIn._MakeApplyIfElseResolvedType BuiltIn.ApplyIfElseRC [OpProperty.Controllable] // 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 ApplyIfElseCA = { - Name = "ApplyIfElseCA" |> NonNullable.New + static member ApplyIfElseRCA = { + Name = "ApplyIfElseRCA" |> NonNullable.New Namespace = BuiltIn.ClassicallyControlledNamespace TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) } - static member ApplyIfElseCAResolvedType = BuiltIn._MakeApplyIfElseResolvedType BuiltIn.ApplyIfElseCA [OpProperty.Adjointable; OpProperty.Controllable] // "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/Utils/Evaluation.fs b/src/QsCompiler/Optimizations/Utils/Evaluation.fs index b0e240f57b..3573f89b61 100644 --- a/src/QsCompiler/Optimizations/Utils/Evaluation.fs +++ b/src/QsCompiler/Optimizations/Utils/Evaluation.fs @@ -220,7 +220,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) @@ -230,7 +230,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) @@ -240,7 +240,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/Utils.fs b/src/QsCompiler/Optimizations/Utils/Utils.fs index 48563eb4b7..cfccb388e1 100644 --- a/src/QsCompiler/Optimizations/Utils/Utils.fs +++ b/src/QsCompiler/Optimizations/Utils/Utils.fs @@ -182,18 +182,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/Tests.Compiler/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index ddd534b179..a8ebac129f 100644 --- a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -15,6 +15,7 @@ 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)) @@ -24,6 +25,7 @@ type ClassicalControlTests () = 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 @@ -68,12 +70,13 @@ type ClassicalControlTests () = |> 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;$" call typeArgs args + let regex = sprintf @"^\s*%s\s*%s\s*%s;$" call typeArgs args let regexMatch = Regex.Match(input, regex) if regexMatch.Success then @@ -89,14 +92,13 @@ type ClassicalControlTests () = sprintf @"\(%s\s*%s,\s*%s\)" <| call <| typeArgs <| args let IsApplyIfArgMatch input resultVar (opName : QsQualifiedName) = - let regexMatch = Regex.Match(input, sprintf @"^%s,\s*%s$" <| Regex.Escape resultVar <| MakeApplicationRegex opName) + 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 @@ -249,7 +251,7 @@ type ClassicalControlTests () = // 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 @@ -257,10 +259,10 @@ type ClassicalControlTests () = [] [] - member this.``Don't Hoist Return Statments`` () = + member this.``Don't Hoist Return Statements`` () = CompileClassicalControlTest 5 |> ignore - [] + [] [] member this.``All-Or-None Hoisting`` () = CompileClassicalControlTest 6 |> ignore @@ -286,7 +288,7 @@ type ClassicalControlTests () = 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 @@ -333,7 +335,7 @@ type ClassicalControlTests () = 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" elifOp.FullName elseOp.FullName + IsApplyIfElseArgsMatch subArgs "r" elseOp.FullName elifOp.FullName // elif and else are swapped because second condition is against One |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg)) [] @@ -344,7 +346,7 @@ type ClassicalControlTests () = 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" ifOp elseOp + IsApplyIfElseArgsMatch subArgs "r" elseOp ifOp // if and else are swapped because second condition is against One |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg)) [] @@ -355,10 +357,10 @@ type ClassicalControlTests () = 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" ifOp elseOp + 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 @@ -1165,3 +1167,74 @@ type ClassicalControlTests () = 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/ExecutionTests.fs b/src/QsCompiler/Tests.Compiler/ExecutionTests.fs index a4a4db38cd..e123951e62 100644 --- a/src/QsCompiler/Tests.Compiler/ExecutionTests.fs +++ b/src/QsCompiler/Tests.Compiler/ExecutionTests.fs @@ -40,7 +40,7 @@ type ExecutionTests (output:ITestOutputHelper) = AssertEqual expectedOutput out - [] + //[] member this.``Specialization Generation for Conjugations`` () = ExecuteAndCompareOutput "ConjugationsInBody" " @@ -139,7 +139,7 @@ type ExecutionTests (output:ITestOutputHelper) = " - [] + //[] member this.``Referencing Projects and Packages`` () = ExecuteAndCompareOutput "PackageAndProjectReference" " diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs index 6930ce1002..cbe56be9c4 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -10,14 +10,10 @@ namespace SubOps { // ================================= +// Basic Hoist namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo() : Unit { let r = Zero; @@ -36,13 +32,9 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Hoist Loops namespace Microsoft.Quantum.Testing.ClassicalControl { - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo() : Unit { let r = Zero; @@ -64,14 +56,10 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Don't Hoist Single Call namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo() : Unit { let r = Zero; if (r == Zero) { @@ -83,14 +71,10 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Hoist Single Non-Call namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo() : Unit { let r = Zero; @@ -103,14 +87,10 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Don't Hoist Return Statements namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo() : Unit { let r = Zero; if (r == Zero) { @@ -123,16 +103,10 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// All-Or-None Hoisting namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - IfInvalid(); - ElseInvalid(); - BothInvalid(); - } - operation IfInvalid() : Unit { let r = Zero; if (r == Zero) { @@ -174,14 +148,10 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// ApplyIfZero And ApplyIfOne namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo() : Unit { let r = Zero; @@ -204,14 +174,10 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Apply If Zero Else One namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo() : Unit { let r = Zero; @@ -228,14 +194,10 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Apply If One Else Zero namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo() : Unit { let r = One; @@ -252,14 +214,10 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// If Elif namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo() : Unit { let r = Zero; @@ -279,14 +237,10 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// And Condition namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo() : Unit { let r = Zero; @@ -303,14 +257,10 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Or Condition namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo() : Unit { let r = Zero; @@ -327,37 +277,32 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Don't Hoist Functions namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } function Foo() : Unit { let r = Zero; if (r == Zero) { - SubOp1(); - SubOp2(); - SubOp3(); + SubFunc1(); + SubFunc2(); + SubFunc3(); } } + function SubFunc1() : Unit { } + function SubFunc2() : Unit { } + function SubFunc3() : Unit { } + } // ================================= +// Hoist Self-Contained Mutable namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - - function Foo() : Unit { + operation Foo() : Unit { let r = Zero; if (r == Zero) { @@ -370,15 +315,11 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Don't Hoist General Mutable namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - - function Foo() : Unit { + operation Foo() : Unit { let r = Zero; mutable temp = 3; @@ -391,14 +332,10 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Generics Support namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; - @EntryPoint() - operation ClassicalControlTestMain() : Unit { - Foo(); - } - operation Foo<'A, 'B, 'C>() : Unit { let r = Zero; @@ -412,6 +349,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Adjoint Support namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @@ -465,6 +403,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Controlled Support namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @@ -505,6 +444,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Controlled Adjoint Support - Provided namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @@ -629,6 +569,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Controlled Adjoint Support - Distribute namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @@ -724,6 +665,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Controlled Adjoint Support - Invert namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @@ -819,6 +761,7 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { // ================================= +// Controlled Adjoint Support - Self namespace Microsoft.Quantum.Testing.ClassicalControl { open SubOps; @@ -857,4 +800,86 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { 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 f8abaae641..7a183d5d5f 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -49,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 = @@ -191,95 +201,93 @@ let public IntrinsicResolutionSignatures = |] |> _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, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" - ClassicalControlNs, "Foo", [||], "Unit" // The original operation - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" // The generated operation + (_DefaultTypes, [| // Basic Hoist + ClassicalControlNs, "Foo", [||], "Unit"; // The original operation + ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; // The generated operation |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // Hoist Loops ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // Don't Hoist Single Call ClassicalControlNs, "Foo", [||], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // Hoist Single Non-Call ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // Don't Hoist Return Statements ClassicalControlNs, "Foo", [||], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // All-Or-None Hoisting ClassicalControlNs, "IfInvalid", [||], "Unit" ClassicalControlNs, "ElseInvalid", [||], "Unit" ClassicalControlNs, "BothInvalid", [||], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // ApplyIfZero And ApplyIfOne ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // Apply If Zero Else One ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // Apply If One Else Zero ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // If Elif ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // And Condition ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // Or Condition ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // Don't Hoist Functions ClassicalControlNs, "Foo", [||], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + ClassicalControlNs, "SubFunc1", [||], "Unit" + ClassicalControlNs, "SubFunc2", [||], "Unit" + ClassicalControlNs, "SubFunc3", [||], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // Hoist Self-Contained Mutable ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // Don't Hoist General Mutable ClassicalControlNs, "Foo", [||], "Unit" |]) - (_DefaultTypes, [| - ClassicalControlNs, "ClassicalControlTestMain", [||], "Unit" + (_DefaultTypes, [| // Generics Support ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| + (_DefaultTypes, [| // Adjoint Support ClassicalControlNs, "Provided", [||], "Unit" ClassicalControlNs, "Self", [||], "Unit" ClassicalControlNs, "Invert", [||], "Unit" @@ -288,14 +296,14 @@ let public ClassicalControlSignatures = ClassicalControlNs, "_Self", [|"Result"|], "Unit" ClassicalControlNs, "_Invert", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| + (_DefaultTypes, [| // Controlled Support ClassicalControlNs, "Provided", [||], "Unit" ClassicalControlNs, "Distribute", [||], "Unit" ClassicalControlNs, "_Provided", [|"Result"|], "Unit" ClassicalControlNs, "_Provided", [|"Result";"Qubit[]";"Unit"|], "Unit" ClassicalControlNs, "_Distribute", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| + (_DefaultTypes, [| // Controlled Adjoint Support - Provided ClassicalControlNs, "ProvidedBody", [||], "Unit" ClassicalControlNs, "ProvidedAdjoint", [||], "Unit" ClassicalControlNs, "ProvidedControlled", [||], "Unit" @@ -317,7 +325,7 @@ let public ClassicalControlSignatures = ClassicalControlNs, "_ProvidedAll", [|"Result";"Qubit[]";"Unit"|], "Unit" ClassicalControlNs, "_ProvidedAll", [|"Result";"Qubit[]";"Unit"|], "Unit" |]) - (_DefaultTypes, [| + (_DefaultTypes, [| // Controlled Adjoint Support - Distribute ClassicalControlNs, "DistributeBody", [||], "Unit" ClassicalControlNs, "DistributeAdjoint", [||], "Unit" ClassicalControlNs, "DistributeControlled", [||], "Unit" @@ -335,7 +343,7 @@ let public ClassicalControlSignatures = ClassicalControlNs, "_DistributeAll", [|"Result"|], "Unit" ClassicalControlNs, "_DistributeAll", [|"Result";"Qubit[]";"Unit"|], "Unit" |]) - (_DefaultTypes, [| + (_DefaultTypes, [| // Controlled Adjoint Support - Invert ClassicalControlNs, "InvertBody", [||], "Unit" ClassicalControlNs, "InvertAdjoint", [||], "Unit" ClassicalControlNs, "InvertControlled", [||], "Unit" @@ -353,7 +361,7 @@ let public ClassicalControlSignatures = ClassicalControlNs, "_InvertAll", [|"Result"|], "Unit" ClassicalControlNs, "_InvertAll", [|"Result";"Qubit[]";"Unit"|], "Unit" |]) - (_DefaultTypes, [| + (_DefaultTypes, [| // Controlled Adjoint Support - Self ClassicalControlNs, "SelfBody", [||], "Unit" ClassicalControlNs, "SelfControlled", [||], "Unit" @@ -362,5 +370,27 @@ let public ClassicalControlSignatures = 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 d96ff2484a..48dadf392e 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -23,6 +23,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -154,9 +157,6 @@ - - false - diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 8243f686f2..2ccc7433cb 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; +using System.Linq; using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; @@ -14,25 +14,26 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlledTran { using ExpressionKind = QsExpressionKind; using ResolvedTypeKind = QsTypeKind; - - // 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. + 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) { - var filter = new SyntaxTreeTransformation(new ClassicallyControlledScope()); - 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, - ImmutableArray, ResolvedType>> typeArgsMapping, ResolvedType resolvedType) => + TypeArgsResolution typeArgsMapping, ResolvedType resolvedType) => new TypedExpression ( ExpressionKind.NewIdentifier( @@ -52,78 +53,130 @@ private static TypedExpression CreateValueTupleExpression(params TypedExpression new TypedExpression ( ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), - ImmutableArray, ResolvedType>>.Empty, + 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 { - public ClassicallyControlledScope(NoExpressionTransformations expr = null) : base(expr ?? new NoExpressionTransformations()) { } + private QsCompilation _Compilation; - private (bool, QsResult, TypedExpression, QsScope, QsScope) IsConditionedOnResultLiteralStatement(QsStatement statement) + public ClassicallyControlledScope(QsCompilation compilation, NoExpressionTransformations expr = null) : base(expr ?? new NoExpressionTransformations()) { - 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; - - if (expression.Item1.Expression is ExpressionKind.ResultLiteral exp1) - { - return (true, exp1.Item, expression.Item2, scope, defaultScope); - } - else if (expression.Item2.Expression is ExpressionKind.ResultLiteral exp2) - { - return (true, exp2.Item, expression.Item1, scope, defaultScope); - } - } - } - - return (false, null, null, null, null); + _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) + 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); - // Merge the two lists into one list with distinct argument mappings, - // giving preference to the id's type arguments. - var mapping = callTypeArguments.ToDictionary(x => (x.Item1, x.Item2), x => x.Item3); - foreach (var arg in idTypeArguments) - { - mapping[(arg.Item1, arg.Item2)] = arg.Item3; - } - var combinedTypeArguments = mapping.Select(kvp => Tuple.Create(kvp.Key.Item1, kvp.Key.Item2, kvp.Value)).ToImmutableArray(); - + // This relies on anything having type parameters must be a global callable. var newExpr1 = call.Item1; - // ToDo: shouldn't rely on expr1 being identifier - if (combinedTypeArguments.Any() && newExpr1.Expression is ExpressionKind.Identifier id) + 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(combinedTypeArguments - .Select(arg => arg.Item3) - .ToImmutableArray())), + QsNullable>.NewValue( + callableTypeParameters + .Select(x => combinedTypeArguments.First(y => y.Item2.Equals(x.Item)).Item3).ToImmutableArray())), combinedTypeArguments, - ResolvedType.New(ResolvedTypeKind.UnitType), // ToDo: This is really wrong - // Need to replace all type-param types in resolved type with their resolution - // I hope this does not involve a whole new transformation :( - //call.Item1.ResolvedType, + call.Item1.ResolvedType, call.Item1.InferredInformation, call.Item1.Range); } @@ -134,11 +187,11 @@ scope.Statements[0].Statement is QsStatementKind.QsExpressionStatement expr && return (false, null, null); } - private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args) => + private TypedExpression CreateApplyIfCall(TypedExpression id, TypedExpression args, TypeArgsResolution typeRes) => new TypedExpression ( ExpressionKind.NewCallLikeExpression(id, args), - ImmutableArray, ResolvedType>>.Empty, + typeRes, ResolvedType.New(ResolvedTypeKind.UnitType), new InferredExpressionInformation(false, true), QsNullable>.Null @@ -163,21 +216,35 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul } } + 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; - ResolvedType controlOpType; TypedExpression controlArgs; ImmutableArray targetArgs; + + var props = ImmutableHashSet.Empty; + if (isCondValid) { // Get characteristic properties from global id - var props = condId.ResolvedType.Resolution is ResolvedTypeKind.Operation op - ? op.Item2.Characteristics.GetProperties() - : ImmutableHashSet.Empty; + 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)); @@ -185,29 +252,26 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co { if (adj && ctl) { - controlOpInfo = BuiltIn.ApplyIfElseCA; - controlOpType = BuiltIn.ApplyIfElseCAResolvedType; + controlOpInfo = BuiltIn.ApplyIfElseRCA; } else if (adj) { controlOpInfo = BuiltIn.ApplyIfElseRA; - controlOpType = BuiltIn.ApplyIfElseRAResolvedType; } else if (ctl) { controlOpInfo = BuiltIn.ApplyIfElseRC; - controlOpType = BuiltIn.ApplyIfElseRCResolvedType; } else { controlOpInfo = BuiltIn.ApplyIfElseR; - controlOpType = BuiltIn.ApplyIfElseRResolvedType; } - controlArgs = CreateValueTupleExpression( - conditionExpression, - CreateValueTupleExpression(condId, condArgs), - CreateValueTupleExpression(defaultId, defaultArgs)); + 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); } @@ -215,27 +279,27 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co { if (adj && ctl) { - (controlOpInfo, controlOpType) = (result == QsResult.One) - ? (BuiltIn.ApplyIfOneCA, BuiltIn.ApplyIfOneCAResolvedType) - : (BuiltIn.ApplyIfZeroCA, BuiltIn.ApplyIfZeroCAResolvedType); + controlOpInfo = (result == QsResult.Zero) + ? BuiltIn.ApplyIfZeroCA + : BuiltIn.ApplyIfOneCA; } else if (adj) { - (controlOpInfo, controlOpType) = (result == QsResult.One) - ? (BuiltIn.ApplyIfOneA, BuiltIn.ApplyIfOneAResolvedType) - : (BuiltIn.ApplyIfZeroA, BuiltIn.ApplyIfZeroAResolvedType); + controlOpInfo = (result == QsResult.Zero) + ? BuiltIn.ApplyIfZeroA + : BuiltIn.ApplyIfOneA; } else if (ctl) { - (controlOpInfo, controlOpType) = (result == QsResult.One) - ? (BuiltIn.ApplyIfOneC, BuiltIn.ApplyIfOneCResolvedType) - : (BuiltIn.ApplyIfZeroC, BuiltIn.ApplyIfZeroCResolvedType); + controlOpInfo = (result == QsResult.Zero) + ? BuiltIn.ApplyIfZeroC + : BuiltIn.ApplyIfOneC; } else { - (controlOpInfo, controlOpType) = (result == QsResult.One) - ? (BuiltIn.ApplyIfOne, BuiltIn.ApplyIfOneResolvedType) - : (BuiltIn.ApplyIfZero, BuiltIn.ApplyIfZeroResolvedType); + controlOpInfo = (result == QsResult.Zero) + ? BuiltIn.ApplyIfZero + : BuiltIn.ApplyIfOne; } controlArgs = CreateValueTupleExpression( @@ -260,10 +324,26 @@ private TypedExpression GetApplyIfExpression(QsResult result, TypedExpression co 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(), - controlOpType); + .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); + return CreateApplyIfCall(controlOpId, controlArgs, opTypeArgResolutions); } private (bool, QsConditionalStatement) ProcessElif(QsConditionalStatement cond) @@ -386,7 +466,7 @@ public override QsScope Transform(QsScope scope) stm = this.onStatement(stm); var (isCondition, result, conditionExpression, conditionScope, defaultScope) = IsConditionedOnResultLiteralStatement(stm); - + if (isCondition) { statements.Add(CreateApplyIfStatement(stm, result, conditionExpression, conditionScope, defaultScope)); @@ -565,6 +645,8 @@ public CallableDetails(QsCallable callable) private bool _InAdjoint = false; private bool _InControlled = false; + private bool _InWithinBlock = false; + public static QsCompilation Apply(QsCompilation compilation) { var filter = new HoistSyntax(new HoistTransformation()); @@ -591,11 +673,15 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature var ctl = _CurrentCallable.Controlled; var ctlAdj = _CurrentCallable.ControlledAdjoint; - // ToDo: I don't think you have to add the ControlledAdjoint if you add the Controlled and Adjoint specializations bool addAdjoint = false; bool addControlled = false; - //bool addControlledAdjoint = false; - if (_InBody) + + 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; @@ -646,18 +732,18 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); } - //if (addControlledAdjoint) - //{ - // specializations.Add(MakeSpec( - // QsSpecializationKind.QsControlledAdjoint, - // controlledSig, - // SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); - //} + if (addAdjoint && addControlled) + { + specializations.Add(MakeSpec( + QsSpecializationKind.QsControlledAdjoint, + controlledSig, + SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); + } return (newSig, specializations); } - private (QsQualifiedName, ResolvedType, CallableInformation) GenerateOperation(QsScope contents) + private (QsCallable, ResolvedType) GenerateOperation(QsScope contents) { var newName = new QsQualifiedName( _CurrentCallable.Callable.FullName.Namespace, @@ -703,9 +789,8 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature QsComments.Empty); var updatedCallable = UpdateGeneratedOpTransformation.Apply(controlCallable, knownVariables, _CurrentCallable.Callable.FullName, newName); - _ControlOperations.Add(updatedCallable); - return (newName, paramTypes, signature.Information); + return (updatedCallable, signature.ArgumentType); } private HoistTransformation() { } @@ -750,6 +835,8 @@ public override QsSpecialization onControlledSpecialization(QsSpecialization spe 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 @@ -765,41 +852,37 @@ private class HoistStatementKind : StatementKindTransformation scope) : base(scope) { _super = super; } - private QsStatement HoistIfContents(QsScope contents) + private (QsCallable, QsStatement) HoistIfContents(QsScope contents) { - var (targetName, targetParamType, callInfo) = _super.GenerateOperation(contents); + var (targetOp, originalArgumentType) = _super.GenerateOperation(contents); var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( Tuple.Create( - targetParamType, + originalArgumentType, ResolvedType.New(ResolvedTypeKind.UnitType)), - callInfo)); + targetOp.Signature.Information)); var targetTypeArgTypes = _super._CurrentCallable.TypeParamTypes; var targetOpId = new TypedExpression ( - ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetName), targetTypeArgTypes), + ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetOp.FullName), targetTypeArgTypes), targetTypeArgTypes.IsNull - ? ImmutableArray, ResolvedType>>.Empty + ? TypeArgsResolution.Empty : targetTypeArgTypes.Item - .Select(type => Tuple.Create(targetName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + .Select(type => Tuple.Create(targetOp.FullName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) .ToImmutableArray(), targetOpType, new InferredExpressionInformation(false, false), QsNullable>.Null ); - // ToDo: double-check this is necessary - var knownSymbols = contents.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : contents.KnownSymbols.Variables; + var knownSymbols = contents.KnownSymbols.Variables; TypedExpression targetArgs = null; if (knownSymbols.Any()) { targetArgs = CreateValueTupleExpression(knownSymbols.Select(var => CreateIdentifierExpression( - Identifier.NewLocalVariable(var.VariableName), - // ToDo: may need to be more careful here with the type argument mapping on the identifiers - ImmutableArray, ResolvedType>>.Empty, + Identifier.NewLocalVariable(var.VariableName), + TypeArgsResolution.Empty, var.Type)) .ToArray()); } @@ -808,7 +891,7 @@ private QsStatement HoistIfContents(QsScope contents) targetArgs = new TypedExpression ( ExpressionKind.UnitValue, - ImmutableArray, ResolvedType>>.Empty, + TypeArgsResolution.Empty, ResolvedType.New(ResolvedTypeKind.UnitType), new InferredExpressionInformation(false, false), QsNullable>.Null @@ -817,19 +900,44 @@ private QsStatement HoistIfContents(QsScope contents) var call = new TypedExpression ( - ExpressionKind.NewCallLikeExpression(targetOpId, targetArgs), - // All type parameters are resolved on the Identifier - ImmutableArray, ResolvedType>>.Empty, + 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 new QsStatement( + return (targetOp, new QsStatement( QsStatementKind.NewQsExpressionStatement(call), LocalDeclarations.Empty, QsNullable.Null, - QsComments.Empty); + 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(null, stm.OuterTransformation); // ToDo: null is probably bad here + _super._InWithinBlock = superInWithinBlock; + + var (_, inner) = this.onPositionedBlock(null, stm.InnerTransformation); // ToDo: null is probably bad here + + return QsStatementKind.NewQsConjugation(new QsConjugation(outer, inner)); } public override QsStatementKind onReturnStatement(TypedExpression ex) @@ -859,29 +967,43 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st var contextValidScope = _super._IsValidScope; var contextHoistParams = _super._CurrentHoistParams; - var newConditionBlocks = stm.ConditionalBlocks - .Select(condBlock => - { - _super._IsValidScope = true; - _super._CurrentHoistParams = condBlock.Item2.Body.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : condBlock.Item2.Body.KnownSymbols.Variables; - - var (expr, block) = this.onPositionedBlock(condBlock.Item1, condBlock.Item2); - if (_super._IsValidScope && block.Body.Statements.Length > 1) // if sub-scope is valid, hoist content - { - // Hoist the scope to its own operation - var call = HoistIfContents(block.Body); - block = new QsPositionedBlock( - new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), - block.Location, - block.Comments); - } - return Tuple.Create(expr.Value, block); // ToDo: .Value may be unnecessary in the future - }).ToImmutableArray(); + var isHoistValid = true; + + var newConditionBlocks = new List>(); + var generatedOperations = new List(); + foreach (var condBlock in stm.ConditionalBlocks) + { + _super._IsValidScope = true; + _super._CurrentHoistParams = condBlock.Item2.Body.KnownSymbols.IsEmpty + ? ImmutableArray>>.Empty + : condBlock.Item2.Body.KnownSymbols.Variables; + + var (expr, block) = this.onPositionedBlock(condBlock.Item1, condBlock.Item2); + + // ToDo: Reduce the number of unnecessary generated operations by generalizing + // the condition logic for the conversion and using that condition here + //var (isExprCond, _, _) = IsConditionedOnResultLiteralExpression(expr.Value); // ToDo: .Value may not be needed in the future + + if (block.Body.Statements.Length > 0 /*&& isExprCond*/ && _super._IsValidScope && !IsScopeSingleCall(block.Body)) // if sub-scope is valid, hoist content + { + // Hoist the scope to its own operation + var (callable, call) = HoistIfContents(block.Body); + block = new QsPositionedBlock( + new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), + block.Location, + block.Comments); + newConditionBlocks.Add(Tuple.Create(expr.Value,block)); + generatedOperations.Add(callable); + } + else + { + isHoistValid = false; + break; + } + } var newDefault = QsNullable.Null; - if (stm.Default.IsValue) + if (isHoistValid && stm.Default.IsValue) { _super._IsValidScope = true; _super._CurrentHoistParams = stm.Default.Item.Body.KnownSymbols.IsEmpty @@ -889,23 +1011,36 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st : stm.Default.Item.Body.KnownSymbols.Variables; var (_, block) = this.onPositionedBlock(null, stm.Default.Item); // ToDo: null is probably bad here - if (_super._IsValidScope && block.Body.Statements.Length > 1) // if sub-scope is valid, hoist content + if (block.Body.Statements.Length > 0 && _super._IsValidScope && !IsScopeSingleCall(block.Body)) // if sub-scope is valid, hoist content { // Hoist the scope to its own operation - var call = HoistIfContents(block.Body); + var (callable, call) = HoistIfContents(block.Body); block = new QsPositionedBlock( new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), block.Location, block.Comments); + newDefault = QsNullable.NewValue(block); + generatedOperations.Add(callable); } - newDefault = QsNullable.NewValue(block); + else + { + isHoistValid = false; + } + } + + if (isHoistValid) + { + _super._ControlOperations.AddRange(generatedOperations); } _super._CurrentHoistParams = contextHoistParams; _super._IsValidScope = contextValidScope; - return QsStatementKind.NewQsConditionalStatement( - new QsConditionalStatement(newConditionBlocks, newDefault)); + return isHoistValid + ? QsStatementKind.NewQsConditionalStatement( + new QsConditionalStatement(newConditionBlocks.ToImmutableArray(), newDefault)) + : QsStatementKind.NewQsConditionalStatement( + new QsConditionalStatement(stm.ConditionalBlocks, stm.Default)); } public override QsStatementKind Transform(QsStatementKind kind) diff --git a/src/QsCompiler/Transformations/CodeOutput.cs b/src/QsCompiler/Transformations/CodeOutput.cs index 8108a6fc63..c82cdfba06 100644 --- a/src/QsCompiler/Transformations/CodeOutput.cs +++ b/src/QsCompiler/Transformations/CodeOutput.cs @@ -818,10 +818,10 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st public override QsStatementKind onConjugation(QsConjugation stm) { - this._Scope.CurrentComments = stm.InnerTransformation.Comments; - this.AddBlockStatement(Keywords.qsWithin.id, stm.InnerTransformation.Body, true); this._Scope.CurrentComments = stm.OuterTransformation.Comments; - this.AddBlockStatement(Keywords.qsApply.id, stm.OuterTransformation.Body, false); + this.AddBlockStatement(Keywords.qsWithin.id, stm.OuterTransformation.Body, true); + this._Scope.CurrentComments = stm.InnerTransformation.Comments; + this.AddBlockStatement(Keywords.qsApply.id, stm.InnerTransformation.Body, false); return QsStatementKind.NewQsConjugation(stm); } From 487fe377529f07f9accb8e7a6c75a9173140677b Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 6 Feb 2020 18:53:05 -0800 Subject: [PATCH 46/53] fixing build failure due to option change --- .../ClassicallyControlledTransformation.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 2ccc7433cb..1e47f283b4 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -932,10 +932,10 @@ public override QsStatementKind onConjugation(QsConjugation stm) { var superInWithinBlock = _super._InWithinBlock; _super._InWithinBlock = true; - var (_, outer) = this.onPositionedBlock(null, stm.OuterTransformation); // ToDo: null is probably bad here + var (_, outer) = this.onPositionedBlock(QsNullable.Null, stm.OuterTransformation); // ToDo: null is probably bad here _super._InWithinBlock = superInWithinBlock; - var (_, inner) = this.onPositionedBlock(null, stm.InnerTransformation); // ToDo: null is probably bad here + var (_, inner) = this.onPositionedBlock(QsNullable.Null, stm.InnerTransformation); // ToDo: null is probably bad here return QsStatementKind.NewQsConjugation(new QsConjugation(outer, inner)); } @@ -962,8 +962,6 @@ public override QsStatementKind onValueUpdate(QsValueUpdate stm) public override QsStatementKind onConditionalStatement(QsConditionalStatement stm) { - // ToDo: Revisit this method when the F# Option type has been removed from the onPositionBlock function. - var contextValidScope = _super._IsValidScope; var contextHoistParams = _super._CurrentHoistParams; @@ -978,11 +976,11 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st ? ImmutableArray>>.Empty : condBlock.Item2.Body.KnownSymbols.Variables; - var (expr, block) = this.onPositionedBlock(condBlock.Item1, condBlock.Item2); + var (expr, block) = this.onPositionedBlock(QsNullable.NewValue(condBlock.Item1), condBlock.Item2); // ToDo: Reduce the number of unnecessary generated operations by generalizing // the condition logic for the conversion and using that condition here - //var (isExprCond, _, _) = IsConditionedOnResultLiteralExpression(expr.Value); // ToDo: .Value may not be needed in the future + //var (isExprCond, _, _) = IsConditionedOnResultLiteralExpression(expr.Item); if (block.Body.Statements.Length > 0 /*&& isExprCond*/ && _super._IsValidScope && !IsScopeSingleCall(block.Body)) // if sub-scope is valid, hoist content { @@ -992,7 +990,7 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), block.Location, block.Comments); - newConditionBlocks.Add(Tuple.Create(expr.Value,block)); + newConditionBlocks.Add(Tuple.Create(expr.Item,block)); generatedOperations.Add(callable); } else @@ -1010,7 +1008,7 @@ public override QsStatementKind onConditionalStatement(QsConditionalStatement st ? ImmutableArray>>.Empty : stm.Default.Item.Body.KnownSymbols.Variables; - var (_, block) = this.onPositionedBlock(null, stm.Default.Item); // ToDo: null is probably bad here + var (_, block) = this.onPositionedBlock(QsNullable.Null, stm.Default.Item); if (block.Body.Statements.Length > 0 && _super._IsValidScope && !IsScopeSingleCall(block.Body)) // if sub-scope is valid, hoist content { // Hoist the scope to its own operation From b522f7a3d096526b3190e84f0a0ac9eb85c6719e Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 6 Feb 2020 22:17:52 -0800 Subject: [PATCH 47/53] reenabling execution tests --- QsCompiler.sln | 15 +++++++++++++++ src/QsCompiler/Tests.Compiler/ExecutionTests.fs | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/QsCompiler.sln b/QsCompiler.sln index f7ee3f64dc..7a330f37f1 100644 --- a/QsCompiler.sln +++ b/QsCompiler.sln @@ -34,6 +34,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simulation", "Simulation", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Simulation", "src\QsCompiler\TestTargets\Simulation\Target\Simulation.csproj", "{D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "src\QsCompiler\TestTargets\Simulation\Example\Example.csproj", "{2E331781-F7ED-4EF1-8451-896636C6D93A}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{D2E36476-A65F-4310-9C4C-B721BCC47B00}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Library1", "src\QsCompiler\TestTargets\Libraries\Library1\Library1.csproj", "{DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}" @@ -220,6 +222,18 @@ Global {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x64.Build.0 = Release|Any CPU {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x86.ActiveCfg = Release|Any CPU {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x86.Build.0 = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x64.ActiveCfg = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x64.Build.0 = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x86.ActiveCfg = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x86.Build.0 = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|Any CPU.Build.0 = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x64.ActiveCfg = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x64.Build.0 = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x86.ActiveCfg = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x86.Build.0 = Release|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -254,6 +268,7 @@ Global {256A6275-FC7F-42E9-9931-BC6EA6D0F31A} = {B4A9484D-31FC-4A27-9E26-4C8DE3E02D77} {76BA96DA-DC1E-4315-A3ED-5F0700A79812} = {6077A717-50BF-4F87-B439-CA549AF6A4AE} {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1} = {76BA96DA-DC1E-4315-A3ED-5F0700A79812} + {2E331781-F7ED-4EF1-8451-896636C6D93A} = {76BA96DA-DC1E-4315-A3ED-5F0700A79812} {D2E36476-A65F-4310-9C4C-B721BCC47B00} = {6077A717-50BF-4F87-B439-CA549AF6A4AE} {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F} = {D2E36476-A65F-4310-9C4C-B721BCC47B00} EndGlobalSection diff --git a/src/QsCompiler/Tests.Compiler/ExecutionTests.fs b/src/QsCompiler/Tests.Compiler/ExecutionTests.fs index e123951e62..a4a4db38cd 100644 --- a/src/QsCompiler/Tests.Compiler/ExecutionTests.fs +++ b/src/QsCompiler/Tests.Compiler/ExecutionTests.fs @@ -40,7 +40,7 @@ type ExecutionTests (output:ITestOutputHelper) = AssertEqual expectedOutput out - //[] + [] member this.``Specialization Generation for Conjugations`` () = ExecuteAndCompareOutput "ConjugationsInBody" " @@ -139,7 +139,7 @@ type ExecutionTests (output:ITestOutputHelper) = " - //[] + [] member this.``Referencing Projects and Packages`` () = ExecuteAndCompareOutput "PackageAndProjectReference" " From 6599f61026c29508adba0e2219a7ccb5b2c9d65c Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 6 Feb 2020 22:55:15 -0800 Subject: [PATCH 48/53] forgot to actually update the packages --- src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj | 2 +- src/QsCompiler/TestTargets/Simulation/Example/Example.csproj | 3 ++- src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) 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 @@ - + From 0762467bf0ffb9c9eca9aca429a655a77b127d32 Mon Sep 17 00:00:00 2001 From: Scott Carda <55811729+ScottCarda-MS@users.noreply.github.com> Date: Fri, 7 Feb 2020 10:41:01 -0800 Subject: [PATCH 49/53] Removed completed ToDo's (#309) Removed completed ToDo's --- .../Transformations/ClassicallyControlledTransformation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index 1e47f283b4..e6b41521ab 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -932,10 +932,10 @@ public override QsStatementKind onConjugation(QsConjugation stm) { var superInWithinBlock = _super._InWithinBlock; _super._InWithinBlock = true; - var (_, outer) = this.onPositionedBlock(QsNullable.Null, stm.OuterTransformation); // ToDo: null is probably bad here + var (_, outer) = this.onPositionedBlock(QsNullable.Null, stm.OuterTransformation); _super._InWithinBlock = superInWithinBlock; - var (_, inner) = this.onPositionedBlock(QsNullable.Null, stm.InnerTransformation); // ToDo: null is probably bad here + var (_, inner) = this.onPositionedBlock(QsNullable.Null, stm.InnerTransformation); return QsStatementKind.NewQsConjugation(new QsConjugation(outer, inner)); } From 6b65781f1e3754e6bf9afc1416728b3ec8527388 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 7 Feb 2020 16:55:11 -0800 Subject: [PATCH 50/53] Added precondition to the rewrite step. --- src/QsCompiler/Compiler/PluginInterface.cs | 2 +- .../RewriteSteps/ClassicallyControlled.cs | 33 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) 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 index 98812e4e47..dd7088f28f 100644 --- a/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs +++ b/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs @@ -2,6 +2,8 @@ // 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; @@ -16,7 +18,7 @@ internal class ClassicallyControlled : IRewriteStep public IEnumerable GeneratedDiagnostics => null; public bool ImplementsTransformation => true; - public bool ImplementsPreconditionVerification => false; + public bool ImplementsPreconditionVerification => true; public bool ImplementsPostconditionVerification => false; public ClassicallyControlled() @@ -32,7 +34,34 @@ public bool Transformation(QsCompilation compilation, out QsCompilation transfor public bool PreconditionVerification(QsCompilation compilation) { - throw new System.NotImplementedException(); + 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) From 16ad138816918dc8c1f6ebdae88d00d47726ff50 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Sat, 8 Feb 2020 21:42:18 -0800 Subject: [PATCH 51/53] Removed extra onStatement call. --- .../Transformations/ClassicallyControlledTransformation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs index e6b41521ab..3068bdd70e 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledTransformation.cs @@ -473,7 +473,7 @@ public override QsScope Transform(QsScope scope) } else { - statements.Add(this.onStatement(stm)); + statements.Add(stm); } } else From e6c1a1ac238a4d75b394504266f5bdc890cc3c58 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Wed, 12 Feb 2020 09:45:00 -0800 Subject: [PATCH 52/53] Addressed PR comments --- src/QsCompiler/Compiler/CompilationLoader.cs | 1 + src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 69c48e4d19..0fc0a1aee0 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -214,6 +214,7 @@ internal bool Success(Configuration options, bool isExe) => 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) && diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 48dadf392e..b7f2118e59 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -157,6 +157,9 @@ + + false + From 93cfc4e98a8d2cea9cbf863b3d1795c1af4d0ff5 Mon Sep 17 00:00:00 2001 From: bettinaheim <34236215+bettinaheim@users.noreply.github.com> Date: Wed, 12 Feb 2020 09:48:43 -0800 Subject: [PATCH 53/53] Update src/QsCompiler/Compiler/CompilationLoader.cs --- src/QsCompiler/Compiler/CompilationLoader.cs | 1847 +++++++++--------- 1 file changed, 924 insertions(+), 923 deletions(-) diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 0fc0a1aee0..200969bb40 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -1,923 +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; - /// - /// 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)); - } -} +// 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)); + } +}