From 451fe2e6ac7997d2ced3712fa9275e1bae9216d9 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Wed, 26 Feb 2020 16:38:29 -0800 Subject: [PATCH 1/6] Added logic for NEQ support. --- .../Transformations/ClassicallyControlled.cs | 92 ++++++++++++++----- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index 850b2f12db..01b9420390 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -171,32 +171,41 @@ x.Resolution is ResolvedTypeKind.TupleType tup /// /// Creates an operation call from the conditional control API for non-literal Result comparisons. /// - private TypedExpression CreateApplyConditionallyExpression(TypedExpression conditionExpr1, TypedExpression conditionExpr2, QsScope conditionScope, QsScope defaultScope) + private TypedExpression CreateApplyConditionallyExpression(TypedExpression conditionExpr1, TypedExpression conditionExpr2, QsScope equalityScope, QsScope inequalityScope) { - var (isConditionValid, conditionId, conditionArgs) = IsValidScope(conditionScope); - var (isDefaultValid, defaultId, defaultArgs) = IsValidScope(defaultScope); + if (equalityScope == null && inequalityScope == null) + { + return null; // Not sure how this would happen + } - if (!isConditionValid) + var (isEqualityValid, equalityId, equalityArgs) = IsValidScope(equalityScope); + var (isInequaltiyValid, inequalityId, inequalityArgs) = IsValidScope(inequalityScope); + + if (!isEqualityValid && equalityScope != null) { - return null; // ToDo: Diagnostic message - condition block not valid + return null; // ToDo: Diagnostic message - equality block exists, but is not valid } - if (!isDefaultValid && defaultScope != null) + if (!isInequaltiyValid && inequalityScope != null) { - return null; // ToDo: Diagnostic message - default block exists, but is not valid + return null; // ToDo: Diagnostic message - inequality block exists, but is not valid } - if (defaultScope == null) + if (equalityScope == null) { - (defaultId, defaultArgs) = Utils.GetNoOp(); + (equalityId, equalityArgs) = Utils.GetNoOp(); + } + else if (inequalityScope == null) + { + (inequalityId, inequalityArgs) = Utils.GetNoOp(); } // Get characteristic properties from global id var props = ImmutableHashSet.Empty; - if (conditionId.ResolvedType.Resolution is ResolvedTypeKind.Operation op) + if (equalityId.ResolvedType.Resolution is ResolvedTypeKind.Operation op) { props = op.Item2.Characteristics.GetProperties(); - if (defaultId != null && defaultId.ResolvedType.Resolution is ResolvedTypeKind.Operation defaultOp) + if (inequalityId != null && inequalityId.ResolvedType.Resolution is ResolvedTypeKind.Operation defaultOp) { props = props.Intersect(defaultOp.Item2.Characteristics.GetProperties()); } @@ -221,10 +230,10 @@ private TypedExpression CreateApplyConditionallyExpression(TypedExpression condi controlOpInfo = BuiltIn.ApplyConditionally; } - var equality = Utils.CreateValueTupleExpression(conditionId, conditionArgs); - var inequality = Utils.CreateValueTupleExpression(defaultId, defaultArgs); + var equality = Utils.CreateValueTupleExpression(equalityId, equalityArgs); + var inequality = Utils.CreateValueTupleExpression(inequalityId, inequalityArgs); var controlArgs = Utils.CreateValueTupleExpression(Utils.CreateValueArray(conditionExpr1), Utils.CreateValueArray(conditionExpr2), equality, inequality); - var targetArgsTypes = ImmutableArray.Create(conditionArgs.ResolvedType, defaultArgs.ResolvedType); + var targetArgsTypes = ImmutableArray.Create(equalityArgs.ResolvedType, inequalityArgs.ResolvedType); return CreateControlCall(controlOpInfo, props, controlArgs, targetArgsTypes); } @@ -374,13 +383,20 @@ private QsStatement ConvertConditionalToControlCall(QsStatement statement) } else { - var (isCompareNonLiteral, conditionExpr1, conditionExpr2) = IsConditionedOnResultEqualityExpression(condition); - if (isCompareNonLiteral) + var (isNonLiteralEquality, conditionExpr1, conditionExpr2) = IsConditionedOnResultEqualityExpression(condition); + if (isNonLiteralEquality) { return CreateControlStatement(statement, CreateApplyConditionallyExpression(conditionExpr1, conditionExpr2, conditionScope, defaultScope)); } else { + bool isNonLiteralInequality; + (isNonLiteralInequality, conditionExpr1, conditionExpr2) = IsConditionedOnResultInequalityExpression(condition); + if (isNonLiteralInequality) + { + return CreateControlStatement(statement, CreateApplyConditionallyExpression(conditionExpr1, conditionExpr2, defaultScope, conditionScope)); + } + // ToDo: Diagnostic message return statement; // The condition does not fit a supported format. } @@ -413,21 +429,35 @@ private QsStatement ConvertConditionalToControlCall(QsStatement statement) } /// - /// Checks if the expression is an equality comparison where one side is a Result literal. - /// If it is, returns true along with the Result literal and the other expression in the - /// equality, otherwise returns false with nulls. + /// Checks if the expression is an equality or inequality comparison where one side is a + /// Result literal. If it is, returns true along with the Result literal and the other + /// expression in the (in)equality, otherwise returns false with nulls. If it is an + /// inequality, the returned result value will be the opposite of the result literal found. /// private (bool, QsResult, TypedExpression) IsConditionedOnResultLiteralExpression(TypedExpression expression) { if (expression.Expression is ExpressionKind.EQ eq) { - if (eq.Item1.Expression is ExpressionKind.ResultLiteral exp1) + if (eq.Item1.Expression is ExpressionKind.ResultLiteral literal1) + { + return (true, literal1.Item, eq.Item2); + } + else if (eq.Item2.Expression is ExpressionKind.ResultLiteral literal2) + { + return (true, literal2.Item, eq.Item1); + } + } + else if (expression.Expression is ExpressionKind.NEQ neq) + { + QsResult FlipResult(QsResult result) => result.IsZero ? QsResult.One : QsResult.Zero; + + if (neq.Item1.Expression is ExpressionKind.ResultLiteral literal1) { - return (true, exp1.Item, eq.Item2); + return (true, FlipResult(literal1.Item), neq.Item2); } - else if (eq.Item2.Expression is ExpressionKind.ResultLiteral exp2) + else if (neq.Item2.Expression is ExpressionKind.ResultLiteral literal2) { - return (true, exp2.Item, eq.Item1); + return (true, FlipResult(literal2.Item), neq.Item1); } } @@ -450,6 +480,22 @@ private QsStatement ConvertConditionalToControlCall(QsStatement statement) return (false, null, null); } + /// + /// Checks if the expression is an inequality comparison between two Result-typed expressions. + /// If it is, returns true along with the two expressions, otherwise returns false with nulls. + /// + private (bool, TypedExpression, TypedExpression) IsConditionedOnResultInequalityExpression(TypedExpression expression) + { + if (expression.Expression is ExpressionKind.NEQ neq + && neq.Item1.ResolvedType.Resolution == ResolvedTypeKind.Result + && neq.Item2.ResolvedType.Resolution == ResolvedTypeKind.Result) + { + return (true, neq.Item1, neq.Item2); + } + + return (false, null, null); + } + #endregion #region Condition Reshaping Logic From 6e75bc1de39b75601dd76b92475e0c0ce4c420e4 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Wed, 26 Feb 2020 16:50:16 -0800 Subject: [PATCH 2/6] Cleaned up a nested if structure by changing the condition checking logic methods from returning tuples to using out parameters. --- .../Transformations/ClassicallyControlled.cs | 77 +++++++++++-------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index 01b9420390..fb6bb78814 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using Microsoft.FSharp.Core; using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; @@ -376,31 +377,22 @@ private QsStatement ConvertConditionalToControlCall(QsStatement statement) if (isCondition) { - var (isCompareLiteral, literal, nonLiteral) = IsConditionedOnResultLiteralExpression(condition); - if (isCompareLiteral) + if (IsConditionedOnResultLiteralExpression(condition, out var literal, out var nonLiteral)) { return CreateControlStatement(statement, CreateApplyIfExpression(literal, nonLiteral, conditionScope, defaultScope)); } - else + else if (IsConditionedOnResultEqualityExpression(condition, out var lhsConditionExpression, out var rhsConditionExpression)) { - var (isNonLiteralEquality, conditionExpr1, conditionExpr2) = IsConditionedOnResultEqualityExpression(condition); - if (isNonLiteralEquality) - { - return CreateControlStatement(statement, CreateApplyConditionallyExpression(conditionExpr1, conditionExpr2, conditionScope, defaultScope)); - } - else - { - bool isNonLiteralInequality; - (isNonLiteralInequality, conditionExpr1, conditionExpr2) = IsConditionedOnResultInequalityExpression(condition); - if (isNonLiteralInequality) - { - return CreateControlStatement(statement, CreateApplyConditionallyExpression(conditionExpr1, conditionExpr2, defaultScope, conditionScope)); - } - - // ToDo: Diagnostic message - return statement; // The condition does not fit a supported format. - } + return CreateControlStatement(statement, CreateApplyConditionallyExpression(lhsConditionExpression, rhsConditionExpression, conditionScope, defaultScope)); + } + else if (IsConditionedOnResultInequalityExpression(condition, out lhsConditionExpression, out rhsConditionExpression)) + { + // The scope arguments are reversed to account for the negation of the NEQ + return CreateControlStatement(statement, CreateApplyConditionallyExpression(lhsConditionExpression, rhsConditionExpression, defaultScope, conditionScope)); } + + // ToDo: Diagnostic message + return statement; // The condition does not fit a supported format. } else { @@ -434,17 +426,24 @@ private QsStatement ConvertConditionalToControlCall(QsStatement statement) /// expression in the (in)equality, otherwise returns false with nulls. If it is an /// inequality, the returned result value will be the opposite of the result literal found. /// - private (bool, QsResult, TypedExpression) IsConditionedOnResultLiteralExpression(TypedExpression expression) + private bool IsConditionedOnResultLiteralExpression(TypedExpression expression, out QsResult literal, out TypedExpression nonLiteral) { + literal = null; + nonLiteral = null; + if (expression.Expression is ExpressionKind.EQ eq) { if (eq.Item1.Expression is ExpressionKind.ResultLiteral literal1) { - return (true, literal1.Item, eq.Item2); + literal = literal1.Item; + nonLiteral = eq.Item2; + return true; } else if (eq.Item2.Expression is ExpressionKind.ResultLiteral literal2) { - return (true, literal2.Item, eq.Item1); + literal = literal2.Item; + nonLiteral = eq.Item1; + return true; } } else if (expression.Expression is ExpressionKind.NEQ neq) @@ -453,47 +452,61 @@ private QsStatement ConvertConditionalToControlCall(QsStatement statement) if (neq.Item1.Expression is ExpressionKind.ResultLiteral literal1) { - return (true, FlipResult(literal1.Item), neq.Item2); + literal = FlipResult(literal1.Item); + nonLiteral = neq.Item2; + return true; } else if (neq.Item2.Expression is ExpressionKind.ResultLiteral literal2) { - return (true, FlipResult(literal2.Item), neq.Item1); + literal = FlipResult(literal2.Item); + nonLiteral = neq.Item1; + return true; } } - return (false, null, null); + return false; } /// /// Checks if the expression is an equality comparison between two Result-typed expressions. /// If it is, returns true along with the two expressions, otherwise returns false with nulls. /// - private (bool, TypedExpression, TypedExpression) IsConditionedOnResultEqualityExpression(TypedExpression expression) + private bool IsConditionedOnResultEqualityExpression(TypedExpression expression, out TypedExpression lhs, out TypedExpression rhs) { + lhs = null; + rhs = null; + if (expression.Expression is ExpressionKind.EQ eq && eq.Item1.ResolvedType.Resolution == ResolvedTypeKind.Result && eq.Item2.ResolvedType.Resolution == ResolvedTypeKind.Result) { - return (true, eq.Item1, eq.Item2); + lhs = eq.Item1; + rhs = eq.Item2; + return true; } - return (false, null, null); + return false; } /// /// Checks if the expression is an inequality comparison between two Result-typed expressions. /// If it is, returns true along with the two expressions, otherwise returns false with nulls. /// - private (bool, TypedExpression, TypedExpression) IsConditionedOnResultInequalityExpression(TypedExpression expression) + private bool IsConditionedOnResultInequalityExpression(TypedExpression expression, out TypedExpression lhs, out TypedExpression rhs) { + lhs = null; + rhs = null; + if (expression.Expression is ExpressionKind.NEQ neq && neq.Item1.ResolvedType.Resolution == ResolvedTypeKind.Result && neq.Item2.ResolvedType.Resolution == ResolvedTypeKind.Result) { - return (true, neq.Item1, neq.Item2); + lhs = neq.Item1; + rhs = neq.Item2; + return true; } - return (false, null, null); + return false; } #endregion From 011976962c2ed19751b5ad7ac2873f12c5c13e3e Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 28 Feb 2020 10:10:02 -0800 Subject: [PATCH 3/6] Removed unnecessary reference to F# in transformation. --- src/QsCompiler/Transformations/ClassicallyControlled.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index fb6bb78814..d0744744d1 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using Microsoft.FSharp.Core; using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; From 41d9280f60b30f2605c65e4843f5c5d8322fbb4a Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 28 Feb 2020 10:50:38 -0800 Subject: [PATCH 4/6] Unit tests for NEQ support. --- .../Tests.Compiler/ClassicalControlTests.fs | 71 ++++++++++++++- .../LinkingTests/ClassicalControl.qs | 91 +++++++++++++++++++ .../Tests.Compiler/TestUtils/Signatures.fs | 18 ++++ 3 files changed, 179 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index f4a82d0eef..4311997713 100644 --- a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -1265,4 +1265,73 @@ type ClassicalControlTests () = IsApplyIfElseArgsMatch args "[r1], [r2]" Bar NoOp |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyConditionally did not have the correct arguments")) - Assert.True(IsTypeArgsMatch targs "Result, Unit", "ApplyConditionally did not have the correct type arguments") \ No newline at end of file + Assert.True(IsTypeArgsMatch targs "Result, Unit", "ApplyConditionally did not have the correct type arguments") + + [] + [] + member this.``Inequality with ApplyConditionally`` () = + let result = CompileClassicalControlTest 31 + + let original = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable + let lines = original |> GetLinesFromSpecialization + + Assert.True(3 = Seq.length lines, sprintf "Callable %O(%A) did not have the expected number of statements" original.Parent original.Kind) + + let (success, targs, args) = CheckIfLineIsCall BuiltIn.ApplyConditionally.Namespace.Value BuiltIn.ApplyConditionally.Name.Value lines.[2] + Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent original.Kind) + + let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} + let SubOp1 = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} + + IsApplyIfElseArgsMatch args "[r1], [r2]" SubOp1 Bar + |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyConditionally did not have the correct arguments")) + + Assert.True(IsTypeArgsMatch targs "Unit, Result", "ApplyConditionally did not have the correct type arguments") + + [] + [] + member this.``Inequality with Apply If One Else Zero`` () = + let (targs, args) = CompileClassicalControlTest 32 |> ApplyIfElseTest + + let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} + let SubOp1 = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} + + IsApplyIfElseArgsMatch args "r" SubOp1 Bar + |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) + + Assert.True(IsTypeArgsMatch targs "Unit, Result", "ApplyIfElse did not have the correct type arguments") + + [] + [] + member this.``Inequality with Apply If Zero Else One`` () = + let (targs, args) = CompileClassicalControlTest 33 |> ApplyIfElseTest + + let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} + let SubOp1 = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} + + IsApplyIfElseArgsMatch args "r" Bar SubOp1 + |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) + + Assert.True(IsTypeArgsMatch targs "Result, Unit", "ApplyIfElse did not have the correct type arguments") + + [] + [] + member this.``Inequality with ApplyIfOne`` () = + let result = CompileClassicalControlTest 34 + + let originalOp = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable + + [ (1, BuiltIn.ApplyIfOne) ] + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls originalOp + + [] + [] + member this.``Inequality with ApplyIfZero`` () = + let result = CompileClassicalControlTest 35 + + let originalOp = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable + + [ (1, BuiltIn.ApplyIfZero) ] + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls originalOp \ No newline at end of file diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs index 09086d6b29..4c2b585cf6 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -909,4 +909,95 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { Bar(r1); } } +} + +// ================================= + +// Inequality with ApplyConditionally +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Bar(r : Result) : Unit { } + + operation Foo() : Unit { + let r1 = Zero; + let r2 = Zero; + + if (r1 != r2) { + Bar(r1); + } + else { + SubOp1(); + } + } +} + +// ================================= + +// Inequality with Apply If One Else Zero +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Bar(r : Result) : Unit { } + + operation Foo() : Unit { + let r = Zero; + + if (r != Zero) { + Bar(r); + } + else { + SubOp1(); + } + } +} + +// ================================= + +// Inequality with Apply If Zero Else One +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Bar(r : Result) : Unit { } + + operation Foo() : Unit { + let r = Zero; + + if (r != One) { + Bar(r); + } + else { + SubOp1(); + } + } +} + +// ================================= + +// Inequality with ApplyIfOne +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (r != Zero) { + SubOp1(); + } + } +} + +// ================================= + +// Inequality with ApplyIfZero +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + 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 6931520202..45f59407df 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -393,5 +393,23 @@ let public ClassicalControlSignatures = ClassicalControlNs, "Bar", [|"Result"|], "Unit" ClassicalControlNs, "Foo", [||], "Unit" |]) + (_DefaultTypes, [| // Inequality with ApplyConditionally + ClassicalControlNs, "Bar", [|"Result"|], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + |]) + (_DefaultTypes, [| // Inequality with Apply If One Else Zero + ClassicalControlNs, "Bar", [|"Result"|], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + |]) + (_DefaultTypes, [| // Inequality with Apply If Zero Else One + ClassicalControlNs, "Bar", [|"Result"|], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + |]) + (_DefaultTypes, [| // Inequality with ApplyIfOne + ClassicalControlNs, "Foo", [||], "Unit" + |]) + (_DefaultTypes, [| // Inequality with ApplyIfZero + ClassicalControlNs, "Foo", [||], "Unit" + |]) |] |> _MakeSignatures \ No newline at end of file From 46a65b7362933493bbadb524350c8db671067f2c Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 3 Mar 2020 14:37:45 -0800 Subject: [PATCH 5/6] Changed a check to an assert. --- src/QsCompiler/Transformations/ClassicallyControlled.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index d0744744d1..cd618d2128 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -170,13 +170,11 @@ x.Resolution is ResolvedTypeKind.TupleType tup /// /// Creates an operation call from the conditional control API for non-literal Result comparisons. + /// The equalityScope and inequalityScope cannot both be null. /// private TypedExpression CreateApplyConditionallyExpression(TypedExpression conditionExpr1, TypedExpression conditionExpr2, QsScope equalityScope, QsScope inequalityScope) { - if (equalityScope == null && inequalityScope == null) - { - return null; // Not sure how this would happen - } + QsCompilerError.Verify(equalityScope != null || inequalityScope != null, $"Cannot have null for both equality and inequality scopes when creating ApplyConditionally expressions."); var (isEqualityValid, equalityId, equalityArgs) = IsValidScope(equalityScope); var (isInequaltiyValid, inequalityId, inequalityArgs) = IsValidScope(inequalityScope); From 1613b581d2e55cf224ef9f4e9b6130d1b3749173 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 3 Mar 2020 15:06:07 -0800 Subject: [PATCH 6/6] Added test for checking literals on the left of an equality comparison. --- .../Tests.Compiler/ClassicalControlTests.fs | 11 +++++++++++ .../TestCases/LinkingTests/ClassicalControl.qs | 15 +++++++++++++++ .../Tests.Compiler/TestUtils/Signatures.fs | 3 +++ .../Transformations/ClassicallyControlled.cs | 16 ++++++++-------- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index 4311997713..d5f6ec5a25 100644 --- a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -1334,4 +1334,15 @@ type ClassicalControlTests () = [ (1, BuiltIn.ApplyIfZero) ] |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls originalOp + + [] + [] + member this.``Literal on the Left`` () = + let result = CompileClassicalControlTest 36 + + let originalOp = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable + + [(1, BuiltIn.ApplyIfZero)] + |> Seq.map ExpandBuiltInQualifiedSymbol |> AssertSpecializationHasCalls originalOp \ No newline at end of file diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs index 4c2b585cf6..fcfcfd5db8 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs @@ -1000,4 +1000,19 @@ namespace Microsoft.Quantum.Testing.ClassicalControl { SubOp1(); } } +} + +// ================================= + +// Literal on the Left +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (Zero == r) { + 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 45f59407df..c4d7f6efed 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -411,5 +411,8 @@ let public ClassicalControlSignatures = (_DefaultTypes, [| // Inequality with ApplyIfZero ClassicalControlNs, "Foo", [||], "Unit" |]) + (_DefaultTypes, [| // Literal on the Left + ClassicalControlNs, "Foo", [||], "Unit" + |]) |] |> _MakeSignatures \ No newline at end of file diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index cd618d2128..b8d47bc3d1 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -374,9 +374,9 @@ private QsStatement ConvertConditionalToControlCall(QsStatement statement) if (isCondition) { - if (IsConditionedOnResultLiteralExpression(condition, out var literal, out var nonLiteral)) + if (IsConditionedOnResultLiteralExpression(condition, out var literal, out var conditionExpression)) { - return CreateControlStatement(statement, CreateApplyIfExpression(literal, nonLiteral, conditionScope, defaultScope)); + return CreateControlStatement(statement, CreateApplyIfExpression(literal, conditionExpression, conditionScope, defaultScope)); } else if (IsConditionedOnResultEqualityExpression(condition, out var lhsConditionExpression, out var rhsConditionExpression)) { @@ -423,23 +423,23 @@ private QsStatement ConvertConditionalToControlCall(QsStatement statement) /// expression in the (in)equality, otherwise returns false with nulls. If it is an /// inequality, the returned result value will be the opposite of the result literal found. /// - private bool IsConditionedOnResultLiteralExpression(TypedExpression expression, out QsResult literal, out TypedExpression nonLiteral) + private bool IsConditionedOnResultLiteralExpression(TypedExpression expression, out QsResult literal, out TypedExpression conditionExpression) { literal = null; - nonLiteral = null; + conditionExpression = null; if (expression.Expression is ExpressionKind.EQ eq) { if (eq.Item1.Expression is ExpressionKind.ResultLiteral literal1) { literal = literal1.Item; - nonLiteral = eq.Item2; + conditionExpression = eq.Item2; return true; } else if (eq.Item2.Expression is ExpressionKind.ResultLiteral literal2) { literal = literal2.Item; - nonLiteral = eq.Item1; + conditionExpression = eq.Item1; return true; } } @@ -450,13 +450,13 @@ private bool IsConditionedOnResultLiteralExpression(TypedExpression expression, if (neq.Item1.Expression is ExpressionKind.ResultLiteral literal1) { literal = FlipResult(literal1.Item); - nonLiteral = neq.Item2; + conditionExpression = neq.Item2; return true; } else if (neq.Item2.Expression is ExpressionKind.ResultLiteral literal2) { literal = FlipResult(literal2.Item); - nonLiteral = neq.Item1; + conditionExpression = neq.Item1; return true; } }