diff --git a/build/build.ps1 b/build/build.ps1 index 167428063d..2d28dce3dc 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -51,7 +51,7 @@ function Build-VSCode() { } } Catch { Write-Host "##vso[task.logissue type=error;]Failed to build VS Code extension." - $all_ok = $False + $script:all_ok = $False } } else { Write-Host "##vso[task.logissue type=warning;]npm not installed. Will skip creation of VS Code extension" @@ -92,7 +92,7 @@ function Build-VS() { } } Catch { Write-Host "##vso[task.logissue type=error;]Failed to build VS extension." - $all_ok = $False + $script:all_ok = $False } } else { Write-Host "msbuild not installed. Will skip building the VisualStudio extension" diff --git a/build/pack.ps1 b/build/pack.ps1 index c414aab6a3..4d9f728b4d 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -168,7 +168,7 @@ function Pack-VSCode() { } } Catch { Write-Host "##vso[task.logissue type=error;]Failed to pack VS Code extension." - $all_ok = $False + $Script:all_ok = $False } } else { Write-Host "##vso[task.logissue type=warning;]vsce not installed. Will skip creation of VS Code extension package" @@ -195,7 +195,7 @@ function Pack-VS() { } } Catch { Write-Host "##vso[task.logissue type=error;]Failed to pack VS extension." - $all_ok = $False + $Script:all_ok = $False } } else { Write-Host "msbuild not installed. Will skip creation of VisualStudio extension package" diff --git a/omnisharp.json b/omnisharp.json index ec1a2fe03e..2bcb6eaf77 100644 --- a/omnisharp.json +++ b/omnisharp.json @@ -3,6 +3,7 @@ "enableEditorConfigSupport": true }, "RoslynExtensionsOptions": { - "enableAnalyzersSupport": true + "enableAnalyzersSupport": true, + "enableDecompilationSupport": true } } diff --git a/src/QsCompiler/CommandLineTool/CompilationTracker.cs b/src/QsCompiler/CommandLineTool/CompilationTracker.cs index fe0824bb53..b2d96d8632 100644 --- a/src/QsCompiler/CommandLineTool/CompilationTracker.cs +++ b/src/QsCompiler/CommandLineTool/CompilationTracker.cs @@ -5,8 +5,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; +using System.Text.Json; using System.Threading; -using Newtonsoft.Json; namespace Microsoft.Quantum.QsCompiler.CommandLineCompiler { @@ -105,6 +106,21 @@ public CompilationTaskNode(CompilationTask task) this.Task = task; this.Children = new Dictionary(); } + + public void WriteToJson(Utf8JsonWriter jsonWriter, string prefix) + { + var preparedPrefix = ""; + if (!string.IsNullOrEmpty(prefix)) { + preparedPrefix = $"{prefix}."; + } + + var propertyName = $"{preparedPrefix}{this.Task.Name}"; + jsonWriter.WriteNumber(propertyName, this.Task.DurationInMs ?? -1); + foreach (var entry in this.Children.OrderBy(e => e.Key)) + { + entry.Value.WriteToJson(jsonWriter, propertyName); + } + } } /// @@ -294,24 +310,27 @@ public static void PublishResults(string outputFolder) Directory.CreateDirectory(outputPath); using (var file = File.CreateText(Path.Combine(outputPath, CompilationPerfDataFileName))) { - var serializer = new JsonSerializer + var jsonWriterOptions = new JsonWriterOptions() { - Formatting = Formatting.Indented + Indented = true }; - serializer.Serialize(file, compilationProcessesForest); + var jsonWriter = new Utf8JsonWriter(file.BaseStream, jsonWriterOptions); + jsonWriter.WriteStartObject(); + foreach (var tree in compilationProcessesForest.OrderBy(t => t.Task.Name)) + { + tree.WriteToJson(jsonWriter, null); + } + + jsonWriter.WriteEndObject(); + jsonWriter.Flush(); } if (Warnings.Count > 0) { using (var file = File.CreateText(Path.Combine(outputPath, CompilationPerfWarningsFileName))) { - var serializer = new JsonSerializer - { - Formatting = Formatting.Indented - }; - - serializer.Serialize(file, Warnings); + JsonSerializer.SerializeAsync(file.BaseStream, Warnings).Wait(); } } } diff --git a/src/QsCompiler/Compiler/FindNuspecReferences.ps1 b/src/QsCompiler/Compiler/FindNuspecReferences.ps1 index 49c9c23160..57e66d2781 100644 --- a/src/QsCompiler/Compiler/FindNuspecReferences.ps1 +++ b/src/QsCompiler/Compiler/FindNuspecReferences.ps1 @@ -37,7 +37,7 @@ function Add-NuGetDependencyFromCsprojToNuspec($PathToCsproj) # Check if package already added as dependency, only add if new: $added = $dep.dependency | Where { $_.id -eq $id } - if (!$added) { + if (!$added -and $_.PrivateAssets -ne "All") { Write-Host "Adding $id" $onedependency = $dep.AppendChild($nuspec.CreateElement('dependency', $nuspec.package.metadata.NamespaceURI)) $onedependency.SetAttribute('id', $id) @@ -58,4 +58,3 @@ Add-NuGetDependencyFromCsprojToNuspec "Compiler.csproj" $dep # Save into .nuspec file: $nuspec.package.metadata.AppendChild($dep) $nuspec.Save("$PSScriptRoot\Compiler.nuspec") - diff --git a/src/QsCompiler/Core/ConstructorExtensions.fs b/src/QsCompiler/Core/ConstructorExtensions.fs index 328c39562d..45263ccc5a 100644 --- a/src/QsCompiler/Core/ConstructorExtensions.fs +++ b/src/QsCompiler/Core/ConstructorExtensions.fs @@ -83,7 +83,7 @@ type TypedExpression with /// the ResolvedType is set to the type constructed by resolving it using ResolveTypeParameters and the given look-up. static member New (expr, typeParamResolutions : ImmutableDictionary<_,_>, exType, exInfo, range) = { Expression = expr - TypeArguments = typeParamResolutions |> TypedExpression.AsTypeArguments + TypeArguments = TypedExpression.AsTypeArguments typeParamResolutions ResolvedType = ResolvedType.ResolveTypeParameters typeParamResolutions exType InferredInformation = exInfo Range = range diff --git a/src/QsCompiler/Tests.Compiler/SyntaxTests.fs b/src/QsCompiler/Tests.Compiler/SyntaxTests.fs index c80e320dfc..66a64c89e6 100644 --- a/src/QsCompiler/Tests.Compiler/SyntaxTests.fs +++ b/src/QsCompiler/Tests.Compiler/SyntaxTests.fs @@ -159,10 +159,10 @@ let ``Expression literal tests`` () = let doubleString (n : IFormattable) = n.ToString ("R", CultureInfo.InvariantCulture) // constants that should be handled - let minInt = System.Int64.MinValue // -9223372036854775808L - let maxInt = System.Int64.MaxValue // 9223372036854775807L - let minDouble = System.Double.MinValue // -1.7976931348623157E+308 - let maxDouble = System.Double.MaxValue // 1.7976931348623157E+308 + let minInt = Int64.MinValue // -9223372036854775808L + let maxInt = Int64.MaxValue // 9223372036854775807L + let minDouble = Double.MinValue // -1.7976931348623157E+308 + let maxDouble = Double.MaxValue // 1.7976931348623157E+308 Assert.Equal(minInt, -(-minInt)) // constants that should raise an error @@ -287,7 +287,7 @@ let ``Simple comparison expression tests`` () = [] let ``Identifier tests`` () = [ - ("x", true, toSymbol "x", []); + ("x", true, toIdentifier "x", []); ("a.b.c", true, toExpr (Identifier ({Symbol=QualifiedSymbol("a.b" |> NonNullable.New, "c" |> NonNullable.New); Range=Null}, Null)), []); @@ -299,21 +299,21 @@ let ``Identifier tests`` () = let ``Complex literal tests`` () = [ ("[1,2,3]", true, toArray [ toInt 1; toInt 2; toInt 3 ], []); - ("[1,x,3]", true, toArray [ toInt 1; toSymbol "x"; toInt 3 ], []); + ("[1,x,3]", true, toArray [ toInt 1; toIdentifier "x"; toInt 3 ], []); ("(1,2,3)", true, toTuple [ toInt 1; toInt 2; toInt 3 ], []); - ("(x,2,3)", true, toTuple [ toSymbol "x"; toInt 2; toInt 3 ], []); + ("(x,2,3)", true, toTuple [ toIdentifier "x"; toInt 2; toInt 3 ], []); ("1..2", true, toExpr (RangeLiteral (toInt 1, toInt 2)), []); ("1..2..3", true, toExpr (RangeLiteral (RangeLiteral (toInt 1, toInt 2) |> toExpr, toInt 3)), []); ("1..2..x", true, toExpr (RangeLiteral (RangeLiteral (toInt 1, toInt 2) |> toExpr, - toSymbol "x")), []); - ("1..x..3", true, toExpr (RangeLiteral (RangeLiteral (toInt 1, toSymbol "x") |> toExpr, toInt 3)), []); - ("x..2..3", true, toExpr (RangeLiteral (RangeLiteral (toSymbol "x", toInt 2) |> toExpr, + toIdentifier "x")), []); + ("1..x..3", true, toExpr (RangeLiteral (RangeLiteral (toInt 1, toIdentifier "x") |> toExpr, toInt 3)), []); + ("x..2..3", true, toExpr (RangeLiteral (RangeLiteral (toIdentifier "x", toInt 2) |> toExpr, toInt 3)), []); - ("1..x", true, toExpr (RangeLiteral (toInt 1, toSymbol "x")), []); - ("x..2", true, toExpr (RangeLiteral (toSymbol "x", toInt 2)), []); - ("x..y", true, toExpr (RangeLiteral (toSymbol "x", toSymbol "y")), []); + ("1..x", true, toExpr (RangeLiteral (toInt 1, toIdentifier "x")), []); + ("x..2", true, toExpr (RangeLiteral (toIdentifier "x", toInt 2)), []); + ("x..y", true, toExpr (RangeLiteral (toIdentifier "x", toIdentifier "y")), []); ] |> List.iter testExpr @@ -321,31 +321,31 @@ let ``Complex literal tests`` () = [] let ``Call tests`` () = [ - ("x()", true, toExpr (CallLikeExpression (toSymbol "x", UnitValue |> toExpr)), []); - ("x(1,2)", true, toExpr (CallLikeExpression (toSymbol "x", + ("x()", true, toExpr (CallLikeExpression (toIdentifier "x", UnitValue |> toExpr)), []); + ("x(1,2)", true, toExpr (CallLikeExpression (toIdentifier "x", toTuple [ toInt 1; toInt 2])), []); - ("Adjoint x()", true, toExpr (CallLikeExpression (AdjointApplication (toSymbol "x") |> toExpr, + ("Adjoint x()", true, toExpr (CallLikeExpression (AdjointApplication (toIdentifier "x") |> toExpr, UnitValue |> toExpr)), []); - ("Controlled x()", true, toExpr (CallLikeExpression (ControlledApplication (toSymbol "x") |> toExpr, + ("Controlled x()", true, toExpr (CallLikeExpression (ControlledApplication (toIdentifier "x") |> toExpr, UnitValue |> toExpr)), []); - ("(x(_,1))(2)", true, toExpr (CallLikeExpression (toTuple [toExpr (CallLikeExpression (toSymbol "x", + ("(x(_,1))(2)", true, toExpr (CallLikeExpression (toTuple [toExpr (CallLikeExpression (toIdentifier "x", toTuple [ MissingExpr |> toExpr; toInt 1]) )], toTuple [ toInt 2 ])), []); - ("(x(_,1))(1,2)", true, toExpr (CallLikeExpression (toTuple [toExpr (CallLikeExpression (toSymbol "x", + ("(x(_,1))(1,2)", true, toExpr (CallLikeExpression (toTuple [toExpr (CallLikeExpression (toIdentifier "x", toTuple [ MissingExpr |> toExpr; toInt 1]) )], toTuple [ toInt 1; toInt 2 ])), []); - ("(x(1,(2, _)))(2)", true, toExpr (CallLikeExpression (toTuple [toExpr (CallLikeExpression (toSymbol "x", + ("(x(1,(2, _)))(2)", true, toExpr (CallLikeExpression (toTuple [toExpr (CallLikeExpression (toIdentifier "x", toTuple [ toInt 1; toTuple [ toInt 2; MissingExpr |> toExpr]; ]) )], toTuple [ toInt 2 ])), []); - ("(x(_,(2, _)))(1,2)", true, toExpr (CallLikeExpression (toTuple [toExpr (CallLikeExpression (toSymbol "x", + ("(x(_,(2, _)))(1,2)", true, toExpr (CallLikeExpression (toTuple [toExpr (CallLikeExpression (toIdentifier "x", toTuple [ MissingExpr |> toExpr; toTuple [ toInt 2; MissingExpr |> toExpr]; @@ -360,50 +360,72 @@ let ``Call tests`` () = let ``Modifier tests`` () = // modifiers can only be applied to identifiers, arity-1 tuples, and array item expressions [ // modifiers on identifiers: - ("ab!", true, toExpr (UnwrapApplication (toSymbol "ab")), []); - ("!ab!", true, toExpr (NOT (UnwrapApplication (toSymbol "ab") |> toExpr)), [Warning WarningCode.DeprecatedNOToperator]); - ("ab!!", true, toExpr (UnwrapApplication (UnwrapApplication (toSymbol "ab") |> toExpr)), []); - ("Adjoint x", true, toExpr (AdjointApplication (toSymbol "x")), []); - ("Controlled Adjoint x", true, toExpr (ControlledApplication (AdjointApplication (toSymbol "x") |> toExpr)), []); - ("ab! ()", true, toExpr (CallLikeExpression (UnwrapApplication (toSymbol "ab") |> toExpr, + ("ab!", true, toExpr (UnwrapApplication (toIdentifier "ab")), []); + ("!ab!", true, toExpr (NOT (UnwrapApplication (toIdentifier "ab") |> toExpr)), [Warning WarningCode.DeprecatedNOToperator]); + ("ab!!", true, toExpr (UnwrapApplication (UnwrapApplication (toIdentifier "ab") |> toExpr)), []); + ("Adjoint x", true, toExpr (AdjointApplication (toIdentifier "x")), []); + ("Controlled Adjoint x", true, toExpr (ControlledApplication (AdjointApplication (toIdentifier "x") |> toExpr)), []); + ("ab! ()", true, toExpr (CallLikeExpression (UnwrapApplication (toIdentifier "ab") |> toExpr, UnitValue |> toExpr)), []); - ("ab!! ()", true, toExpr (CallLikeExpression (UnwrapApplication (UnwrapApplication (toSymbol "ab") |> toExpr) |> toExpr, + ("ab!! ()", true, toExpr (CallLikeExpression (UnwrapApplication (UnwrapApplication (toIdentifier "ab") |> toExpr) |> toExpr, UnitValue |> toExpr)), []); - ("Adjoint x()", true, toExpr (CallLikeExpression (AdjointApplication (toSymbol "x") |> toExpr, + ("Adjoint x()", true, toExpr (CallLikeExpression (AdjointApplication (toIdentifier "x") |> toExpr, UnitValue |> toExpr)), []); - ("Adjoint Controlled x()", true, toExpr (CallLikeExpression (AdjointApplication (ControlledApplication (toSymbol "x") |> toExpr) |> toExpr, + ("Adjoint Controlled x()", true, toExpr (CallLikeExpression (AdjointApplication (ControlledApplication (toIdentifier "x") |> toExpr) |> toExpr, UnitValue |> toExpr)), []); - ("Adjoint x! ()", true, toExpr (CallLikeExpression (AdjointApplication (UnwrapApplication (toSymbol "x") |> toExpr) |> toExpr, + ("Adjoint x! ()", true, toExpr (CallLikeExpression (AdjointApplication (UnwrapApplication (toIdentifier "x") |> toExpr) |> toExpr, UnitValue |> toExpr)), []); // modifiers on arity-1 tuples: - ("(udt(x))!", true, toExpr (UnwrapApplication (toTuple [(CallLikeExpression (toSymbol "udt", - toTuple [toSymbol "x"]) |> toExpr)])), []); + ("(udt(x))!", true, toExpr (UnwrapApplication (toTuple [(CallLikeExpression (toIdentifier "udt", + toTuple [toIdentifier "x"]) |> toExpr)])), []); ("(udt(x))! ()", true, toExpr (CallLikeExpression ((UnwrapApplication (toTuple [CallLikeExpression ( - toSymbol "udt", - toTuple [toSymbol "x"]) |> toExpr])) |> toExpr, + toIdentifier "udt", + toTuple [toIdentifier "x"]) |> toExpr])) |> toExpr, UnitValue |> toExpr)), []); ("Controlled (udt(x))! ()", true, toExpr (CallLikeExpression (ControlledApplication ((UnwrapApplication (toTuple [CallLikeExpression ( - toSymbol "udt", - toTuple [toSymbol "x"]) |> toExpr])) |> toExpr) |> toExpr, + toIdentifier "udt", + toTuple [toIdentifier "x"]) |> toExpr])) |> toExpr) |> toExpr, UnitValue |> toExpr)), []); ("(Controlled (udt(x))!)()", true, toExpr (CallLikeExpression (toTuple [ControlledApplication ((UnwrapApplication (toTuple [CallLikeExpression ( - toSymbol "udt", - toTuple [toSymbol "x"]) |> toExpr])) |> toExpr) |> toExpr], + toIdentifier "udt", + toTuple [toIdentifier "x"]) |> toExpr])) |> toExpr) |> toExpr], UnitValue |> toExpr)), []); // modifiers on array item expressions - ("x[i]!", true, toExpr (UnwrapApplication (ArrayItem(toSymbol "x", - toSymbol "i") |> toExpr)), []); - ("Adjoint x[i]", true, toExpr (AdjointApplication (ArrayItem(toSymbol "x", - toSymbol "i") |> toExpr)), []); - ("x[i]! ()", true, toExpr (CallLikeExpression (UnwrapApplication (ArrayItem(toSymbol "x", - toSymbol "i") |> toExpr) |> toExpr, + ("x[i]!", true, toExpr (UnwrapApplication (ArrayItem(toIdentifier "x", + toIdentifier "i") |> toExpr)), []); + ("Adjoint x[i]", true, toExpr (AdjointApplication (ArrayItem(toIdentifier "x", + toIdentifier "i") |> toExpr)), []); + ("x[i]! ()", true, toExpr (CallLikeExpression (UnwrapApplication (ArrayItem(toIdentifier "x", + toIdentifier "i") |> toExpr) |> toExpr, UnitValue |> toExpr)), []); - ("Adjoint x[i] ()", true, toExpr (CallLikeExpression (AdjointApplication (ArrayItem(toSymbol "x", - toSymbol "i") |> toExpr) |> toExpr, + ("Adjoint x[i] ()", true, toExpr (CallLikeExpression (AdjointApplication (ArrayItem(toIdentifier "x", + toIdentifier "i") |> toExpr) |> toExpr, UnitValue |> toExpr)), []); - ("Controlled x[i]! ()", true, toExpr (CallLikeExpression (ControlledApplication (UnwrapApplication (ArrayItem(toSymbol "x", - toSymbol "i") |> toExpr) |> toExpr) |> toExpr, + ("Controlled x[i]! ()", true, toExpr (CallLikeExpression (ControlledApplication (UnwrapApplication (ArrayItem(toIdentifier "x", + toIdentifier "i") |> toExpr) |> toExpr) |> toExpr, UnitValue |> toExpr)), []); + // modifiers combined with named and array item access + ("x[i]::Re", true, toExpr (NamedItem (ArrayItem(toIdentifier "x", + toIdentifier "i") |> toExpr, + toSymbol "Re")), []); + ("x[i]!::Re", true, toExpr (NamedItem (UnwrapApplication (ArrayItem(toIdentifier "x", + toIdentifier "i") |> toExpr) |> toExpr, + toSymbol "Re")), []); + ("x[i]::Re!", true, toExpr (UnwrapApplication (NamedItem (ArrayItem(toIdentifier "x", + toIdentifier "i") |> toExpr, + toSymbol "Re") |> toExpr)), []); + ("x[i]![j]", true, toExpr (ArrayItem (UnwrapApplication (ArrayItem(toIdentifier "x", + toIdentifier "i") |> toExpr) |> toExpr, + toIdentifier "j")), []); + ("x::Re![j]", true, toExpr (ArrayItem (UnwrapApplication (NamedItem(toIdentifier "x", + toSymbol "Re") |> toExpr) |> toExpr, + toIdentifier "j")), []); + ("x::Re!::Im", true, toExpr (NamedItem (UnwrapApplication (NamedItem(toIdentifier "x", + toSymbol "Re") |> toExpr) |> toExpr, + toSymbol "Im")), []); + ("x::Re!!::Im", true, toExpr (NamedItem (UnwrapApplication (UnwrapApplication (NamedItem(toIdentifier "x", + toSymbol "Re") |> toExpr) |> toExpr) |> toExpr, + toSymbol "Im")), []); // Note that since we do not currently have the means to support a left-recursive grammar (would require a different approach to parsing), // we have to prioritize in order to stick with a non-left-recursive grammar (there are algorithms that translate a left-recursive grammar into a non-left-recursive one, @@ -466,26 +488,26 @@ let ``Operation type tests`` () = [] let ``Other expression tests`` () = [ - ("x?y|z", true, toExpr (CONDITIONAL (toSymbol "x", toSymbol "y", toSymbol "z")), []); - ("a[1]", true, toExpr (ArrayItem (toSymbol "a", toInt 1)), []); - ("a[0..2]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (toInt 0, toInt 2) |> toExpr)), []); - ("a[]", true, toExpr (ArrayItem (toSymbol "a", InvalidExpr |> toExpr)), [Error ErrorCode.MissingExpression]); - ("a[0...]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (toInt 0, toExpr MissingExpr) |> toExpr)), []); - ("a[0 ... ]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (toInt 0, toExpr MissingExpr) |> toExpr)), []); - ("a[0... ]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (toInt 0, toExpr MissingExpr) |> toExpr)), []); - ("a[0..2...]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (RangeLiteral (toInt 0, toInt 2) |> toExpr, toExpr MissingExpr) |> toExpr)), []); - ("a[0 .. 2 ... ]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (RangeLiteral (toInt 0, toInt 2) |> toExpr, toExpr MissingExpr) |> toExpr)), []); - ("a[0..2 ...]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (RangeLiteral (toInt 0, toInt 2) |> toExpr, toExpr MissingExpr) |> toExpr)), []); - ("a[...0]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (toExpr MissingExpr, toInt 0) |> toExpr)), []); - ("a[ ...0]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (toExpr MissingExpr, toInt 0) |> toExpr)), []); - ("a[ ... 0]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (toExpr MissingExpr, toInt 0) |> toExpr)), []); - ("a[...2..0]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (RangeLiteral (toExpr MissingExpr, toInt 2) |> toExpr, toInt 0) |> toExpr)), []); - ("a[ ...2 .. 0]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (RangeLiteral (toExpr MissingExpr, toInt 2) |> toExpr, toInt 0) |> toExpr)), []); - ("a[ ... 2 ..0]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (RangeLiteral (toExpr MissingExpr, toInt 2) |> toExpr, toInt 0) |> toExpr)), []); - ("a[...-1...]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (RangeLiteral (toExpr MissingExpr, NEG (toInt 1) |> toExpr) |> toExpr, toExpr MissingExpr) |> toExpr)), []); - ("a[ ... -1 ... ]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (RangeLiteral (toExpr MissingExpr, NEG (toInt 1) |> toExpr) |> toExpr, toExpr MissingExpr) |> toExpr)), []); - ("a[...]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (toExpr MissingExpr, toExpr MissingExpr) |> toExpr)), []); - ("a[ ... ]", true, toExpr (ArrayItem (toSymbol "a", RangeLiteral (toExpr MissingExpr, toExpr MissingExpr) |> toExpr)), []); + ("x?y|z", true, toExpr (CONDITIONAL (toIdentifier "x", toIdentifier "y", toIdentifier "z")), []); + ("a[1]", true, toExpr (ArrayItem (toIdentifier "a", toInt 1)), []); + ("a[0..2]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (toInt 0, toInt 2) |> toExpr)), []); + ("a[]", true, toExpr (ArrayItem (toIdentifier "a", InvalidExpr |> toExpr)), [Error ErrorCode.MissingExpression]); + ("a[0...]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (toInt 0, toExpr MissingExpr) |> toExpr)), []); + ("a[0 ... ]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (toInt 0, toExpr MissingExpr) |> toExpr)), []); + ("a[0... ]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (toInt 0, toExpr MissingExpr) |> toExpr)), []); + ("a[0..2...]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (RangeLiteral (toInt 0, toInt 2) |> toExpr, toExpr MissingExpr) |> toExpr)), []); + ("a[0 .. 2 ... ]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (RangeLiteral (toInt 0, toInt 2) |> toExpr, toExpr MissingExpr) |> toExpr)), []); + ("a[0..2 ...]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (RangeLiteral (toInt 0, toInt 2) |> toExpr, toExpr MissingExpr) |> toExpr)), []); + ("a[...0]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (toExpr MissingExpr, toInt 0) |> toExpr)), []); + ("a[ ...0]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (toExpr MissingExpr, toInt 0) |> toExpr)), []); + ("a[ ... 0]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (toExpr MissingExpr, toInt 0) |> toExpr)), []); + ("a[...2..0]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (RangeLiteral (toExpr MissingExpr, toInt 2) |> toExpr, toInt 0) |> toExpr)), []); + ("a[ ...2 .. 0]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (RangeLiteral (toExpr MissingExpr, toInt 2) |> toExpr, toInt 0) |> toExpr)), []); + ("a[ ... 2 ..0]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (RangeLiteral (toExpr MissingExpr, toInt 2) |> toExpr, toInt 0) |> toExpr)), []); + ("a[...-1...]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (RangeLiteral (toExpr MissingExpr, NEG (toInt 1) |> toExpr) |> toExpr, toExpr MissingExpr) |> toExpr)), []); + ("a[ ... -1 ... ]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (RangeLiteral (toExpr MissingExpr, NEG (toInt 1) |> toExpr) |> toExpr, toExpr MissingExpr) |> toExpr)), []); + ("a[...]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (toExpr MissingExpr, toExpr MissingExpr) |> toExpr)), []); + ("a[ ... ]", true, toExpr (ArrayItem (toIdentifier "a", RangeLiteral (toExpr MissingExpr, toExpr MissingExpr) |> toExpr)), []); ] |> List.iter testExpr @@ -515,10 +537,10 @@ let ``Operator precendence tests`` () = toInt 2) |> toExpr, (MUL (toInt 2, toInt 3) |> toExpr))), []); - ("A(5+7)?2^3|B(3)/2", true, toExpr (CONDITIONAL (CallLikeExpression (toSymbol "A", + ("A(5+7)?2^3|B(3)/2", true, toExpr (CONDITIONAL (CallLikeExpression (toIdentifier "A", [ADD(toInt 5, toInt 7) |> toExpr] |> toTuple) |> toExpr, POW(toInt 2, toInt 3) |> toExpr, - DIV(CallLikeExpression (toSymbol "B", [toInt 3] |> toTuple) |> toExpr, + DIV(CallLikeExpression (toIdentifier "B", [toInt 3] |> toTuple) |> toExpr, toInt 2) |> toExpr)), []); ] |> List.iter testExpr diff --git a/src/QsCompiler/Tests.Compiler/TestCases/TypeParameter.qs b/src/QsCompiler/Tests.Compiler/TestCases/TypeParameter.qs new file mode 100644 index 0000000000..c91be7ec96 --- /dev/null +++ b/src/QsCompiler/Tests.Compiler/TestCases/TypeParameter.qs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + + +// Identifier Resolution +namespace Microsoft.Quantum.Testing.TypeParameter { + + operation Main() : Unit { + Foo<_, Int, String>(1.0, 2, "Three"); + } + + operation Foo<'A, 'B, 'C>(a : 'A, b : 'B, c : 'C) : Unit { } +} + +// ================================= + +// Adjoint Application Resolution +namespace Microsoft.Quantum.Testing.TypeParameter { + + operation Main() : Unit { + (Adjoint (Foo(1.0, 2, _)))("Three"); + } + + operation Foo<'A, 'B, 'C>(a : 'A, b : 'B, c : 'C) : Unit is Adj { } +} + +// ================================= + +// Controlled Application Resolution +namespace Microsoft.Quantum.Testing.TypeParameter { + + operation Main(qs : Qubit) : Unit { + (Controlled (Foo(1.0, 2, _)))([qs], "Three"); + } + + operation Foo<'A, 'B, 'C>(a : 'A, b : 'B, c : 'C) : Unit is Ctl { } +} + +// ================================= + +// Partial Application Resolution +namespace Microsoft.Quantum.Testing.TypeParameter { + + operation Main() : Unit { + (Foo(_, 3, "Hi"))(1.0); + } + + operation Foo<'A, 'B, 'C>(a : 'A, b : 'B, c : 'C) : Unit { } +} + +// ================================= + +// Sub-call Resolution +namespace Microsoft.Quantum.Testing.TypeParameter { + + operation Main() : Unit { + (Foo(1, 2, 3))(4); + } + + operation Foo<'A, 'B, 'C>(a : 'A, b : 'B, c : 'C) : ('C => Unit) { return Bar<'A, 'B, 'C>(a, b, _); } + + operation Bar<'A, 'B, 'C>(a : 'A, b : 'B, c : 'C) : Unit { } +} + +// ================================= + +// Argument Sub-call Resolution +namespace Microsoft.Quantum.Testing.TypeParameter { + + operation Main() : Unit { + Foo(1.0, Bar(2), "Three"); + } + + operation Foo<'A, 'B, 'C>(a : 'A, b : 'B, c : 'C) : Unit { } + + operation Bar<'A>(a : 'A) : 'A { return a; } +} diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/TestUtils.fs b/src/QsCompiler/Tests.Compiler/TestUtils/TestUtils.fs index 3ee7fdd5fe..0c0fdc9231 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/TestUtils.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/TestUtils.fs @@ -75,7 +75,10 @@ let toBigInt b = BigIntLiteral (System.Numerics.BigInteger.Parse b) |> toExpr let toSymbol s = - (Identifier ({Symbol=Symbol (s |> NonNullable.New); Range=Null}, Null)) |> toExpr + {Symbol=Symbol (s |> NonNullable.New); Range=Null} + +let toIdentifier s = + (Identifier (toSymbol s, Null)) |> toExpr let toTuple (es : QsExpression seq) = ValueTuple (es.ToImmutableArray()) |> toExpr diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index d1dee4e3b5..3d9bf60271 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -27,8 +27,8 @@ PreserveNewest - - PreserveNewest + + PreserveNewest PreserveNewest @@ -145,11 +145,14 @@ PreserveNewest - - PreserveNewest + + PreserveNewest - - PreserveNewest + + PreserveNewest + + + PreserveNewest @@ -165,6 +168,7 @@ + diff --git a/src/QsCompiler/Tests.Compiler/TypeParameterTests.fs b/src/QsCompiler/Tests.Compiler/TypeParameterTests.fs new file mode 100644 index 0000000000..97ac80ea44 --- /dev/null +++ b/src/QsCompiler/Tests.Compiler/TypeParameterTests.fs @@ -0,0 +1,621 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.QsCompiler.Testing + +open System +open System.Collections.Immutable +open System.IO +open Microsoft.Quantum.QsCompiler +open Microsoft.Quantum.QsCompiler.CompilationBuilder +open Microsoft.Quantum.QsCompiler.DataTypes +open Microsoft.Quantum.QsCompiler.SyntaxExtensions +open Microsoft.Quantum.QsCompiler.SyntaxTokens +open Microsoft.Quantum.QsCompiler.SyntaxTree +open Xunit + + +type TypeParameterTests () = + + let TypeParameterNS = "Microsoft.Quantum.Testing.TypeParameter" + + let qualifiedName name = + (TypeParameterNS |> NonNullable.New, name |> NonNullable.New) |> QsQualifiedName.New + + let typeParameter (id : string) = + let pieces = id.Split(".") + Assert.True(pieces.Length = 2) + let parent = qualifiedName pieces.[0] + let name = pieces.[1] |> NonNullable.New + QsTypeParameter.New (parent, name, Null) + + let FooA = typeParameter "Foo.A" + let FooB = typeParameter "Foo.B" + let FooC = typeParameter "Foo.C" + let BarA = typeParameter "Bar.A" + let BarB = typeParameter "Bar.B" + let BazA = typeParameter "Baz.A" + + let MakeTupleType types = + types |> Seq.map ResolvedType.New |> ImmutableArray.CreateRange |> TupleType + + let ResolutionFromParam (res : (QsTypeParameter * QsTypeKind<_,_,_,_>) list) = + res.ToImmutableDictionary((fun (tp,_) -> tp.Origin, tp.TypeName), snd >> ResolvedType.New) + + let CheckResolutionMatch (res1 : ImmutableDictionary<_,_>) (res2 : ImmutableDictionary<_,_> ) = + let keysMismatch = ImmutableHashSet.CreateRange(res1.Keys).SymmetricExcept res2.Keys + keysMismatch.Count = 0 && res1 |> Seq.exists (fun kv -> res2.[kv.Key] <> kv.Value) |> not + + let AssertExpectedResolution expected given = + Assert.True(CheckResolutionMatch expected given, "Given resolutions did not match the expected resolutions.") + + let CheckCombinedResolution expected (resolutions : ImmutableDictionary<(QsQualifiedName*NonNullable),ResolvedType> []) = + let combination = TypeResolutionCombination(resolutions) + AssertExpectedResolution expected combination.CombinedResolutionDictionary + combination.IsValid + + let AssertCombinedResolution expected resolutions = + let success = CheckCombinedResolution expected resolutions + Assert.True(success, "Combining type resolutions was not successful.") + + let AssertCombinedResolutionFailure expected resolutions = + let success = CheckCombinedResolution expected resolutions + Assert.False(success, "Combining type resolutions should have failed.") + + 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) + + let ReadAndChunkSourceFile fileName = + let sourceInput = Path.Combine ("TestCases", 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 CompileTypeParameterTest testNumber = + let srcChunks = ReadAndChunkSourceFile "TypeParameter.qs" + srcChunks.Length >= testNumber |> Assert.True + let compilationDataStructures = BuildContent <| srcChunks.[testNumber-1] + let processedCompilation = compilationDataStructures.BuiltCompilation + Assert.NotNull processedCompilation + processedCompilation + + 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 GetMainExpression (compilation : QsCompilation) = + let mainCallable = GetCallableWithName compilation TypeParameterNS "Main" + let body = + mainCallable.Specializations + |> Seq.find (fun x -> x.Kind = QsSpecializationKind.QsBody) + |> fun x -> match x.Implementation with + | Provided (_, body) -> body + | _ -> failwith "Expected but did not find Provided Implementation" + Assert.True(body.Statements.Length = 1) + match body.Statements.[0].Statement with + | QsExpressionStatement expression -> expression + | _ -> failwith "Expected but did not find an Expression Statement" + + + [] + [] + member this.``Resolution to Concrete`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + (FooB, Int) + ] + ResolutionFromParam [ + (BarA, Int) + ] + |] + let expected = ResolutionFromParam [ + (FooA, Int) + (FooB, Int) + (BarA, Int) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Resolution to Type Parameter`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, BazA |> TypeParameter) + ] + |] + let expected = ResolutionFromParam [ + (FooA, BazA |> TypeParameter) + (BarA, BazA |> TypeParameter) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Resolution via Identity Mapping`` () = + + let given = [| + ResolutionFromParam [ + (FooA, FooA |> TypeParameter) + ] + ResolutionFromParam [ + (FooA, Int) + ] + |] + let expected = ResolutionFromParam [ + (FooA, Int) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Multi-Stage Resolution`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, Int) + (FooB, Int) + ] + |] + let expected = ResolutionFromParam [ + (FooA, Int) + (FooB, Int) + (BarA, Int) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Multiple Resolutions to Concrete`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + (FooB, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, Int) + ] + |] + let expected = ResolutionFromParam [ + (FooA, Int) + (FooB, Int) + (BarA, Int) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Multiple Resolutions to Type Parameter`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + (FooB, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, BazA |> TypeParameter) + ] + ResolutionFromParam [ + (BazA, Double) + ] + |] + let expected = ResolutionFromParam [ + (FooA, Double) + (FooB, Double) + (BarA, Double) + (BazA, Double) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Multi-Stage Resolution of Multiple Resolutions to Concrete`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (FooB, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, Int) + ] + |] + let expected = ResolutionFromParam [ + (FooA, Int) + (FooB, Int) + (BarA, Int) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Multi-Stage Resolution of Multiple Resolutions to Type Parameter`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, BazA |> TypeParameter) + (FooB, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BazA, Int) + ] + |] + let expected = ResolutionFromParam [ + (FooA, Int) + (FooB, Int) + (BarA, Int) + (BazA, Int) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Redundant Resolution to Concrete`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, Int) + (FooA, Int) + ] + |] + let expected = ResolutionFromParam [ + (FooA, Int) + (BarA, Int) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Redundant Resolution to Type Parameter`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, BazA |> TypeParameter) + ] + ResolutionFromParam [ + (FooA, BazA |> TypeParameter) + ] + |] + let expected = ResolutionFromParam [ + (FooA, BazA |> TypeParameter) + (BarA, BazA |> TypeParameter) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Conflicting Resolution to Concrete`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, Int) + (FooA, Double) + ] + |] + let expected = ResolutionFromParam [ + (FooA, Double) + (BarA, Int) + ] + + AssertCombinedResolutionFailure expected given + + [] + [] + member this.``Conflicting Resolution to Type Parameter`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, BazA |> TypeParameter) + (FooA, BarB |> TypeParameter) + ] + |] + let expected = ResolutionFromParam [ + (FooA, BarB |> TypeParameter) + (BarA, BazA |> TypeParameter) + ] + + AssertCombinedResolutionFailure expected given + + [] + [] + member this.``Direct Resolution to Native`` () = + + let given = [| + ResolutionFromParam [ + (FooA, FooA |> TypeParameter) + ] + |] + let expected = ResolutionFromParam [ + (FooA, FooA |> TypeParameter) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Indirect Resolution to Native`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, BazA |> TypeParameter) + ] + ResolutionFromParam [ + (BazA, FooA |> TypeParameter) + ] + |] + let expected = ResolutionFromParam [ + (FooA, FooA |> TypeParameter) + (BarA, FooA |> TypeParameter) + (BazA, FooA |> TypeParameter) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Direct Resolution Constrains Native`` () = + + let given = [| + ResolutionFromParam [ + (FooA, FooB |> TypeParameter) + ] + |] + let expected = ResolutionFromParam [ + (FooA, FooB |> TypeParameter) + ] + + AssertCombinedResolutionFailure expected given + + [] + [] + member this.``Indirect Resolution Constrains Native`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, BazA |> TypeParameter) + ] + ResolutionFromParam [ + (BazA, FooB |> TypeParameter) + ] + |] + let expected = ResolutionFromParam [ + (FooA, FooB |> TypeParameter) + (BarA, FooB |> TypeParameter) + (BazA, FooB |> TypeParameter) + ] + + AssertCombinedResolutionFailure expected given + + [] + [] + member this.``Inner Cycle Constrains Type Parameter`` () = + + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + ] + ResolutionFromParam [ + (BarA, BazA |> TypeParameter) + ] + ResolutionFromParam [ + (BazA, BarB |> TypeParameter) + ] + |] + let expected = ResolutionFromParam [ + (FooA, BarB |> TypeParameter) + (BarA, BarB |> TypeParameter) + (BazA, BarB |> TypeParameter) + ] + + AssertCombinedResolutionFailure expected given + + [] + [] + member this.``Nested Type Paramter Resolution`` () = + let given = [| + ResolutionFromParam [ + (FooA, [BarA |> TypeParameter; Int] |> MakeTupleType) + ] + ResolutionFromParam [ + (BarA, [String; Double] |> MakeTupleType) + ] + |] + let expected = ResolutionFromParam [ + (FooA, [[String; Double] |> MakeTupleType; Int] |> MakeTupleType) + (BarA, [String; Double] |> MakeTupleType) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Nested Constricted Resolution`` () = + let given = [| + ResolutionFromParam [ + (FooA, [FooB |> TypeParameter; Int] |> MakeTupleType) + ] + |] + let expected = ResolutionFromParam [ + (FooA, [FooB |> TypeParameter; Int] |> MakeTupleType) + ] + + AssertCombinedResolutionFailure expected given + + [] + [] + member this.``Nested Self Resolution`` () = + let given = [| + ResolutionFromParam [ + (FooA, [FooA |> TypeParameter; BarA |> TypeParameter] |> MakeTupleType) + ] + ResolutionFromParam [ + (BarA, Int) + ] + |] + let expected = ResolutionFromParam [ + (FooA, [FooA |> TypeParameter; Int] |> MakeTupleType) + (BarA, Int) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Single Dictonary Resolution`` () = + let given = [| + ResolutionFromParam [ + (FooA, BarA |> TypeParameter) + (BarA, Int) + ] + |] + let expected = ResolutionFromParam [ + (FooA, Int) + (BarA, Int) + ] + + AssertCombinedResolution expected given + + [] + [] + member this.``Identifier Resolution`` () = + let expression = CompileTypeParameterTest 1 |> GetMainExpression + + let combination = TypeResolutionCombination(expression) + let given = combination.CombinedResolutionDictionary + let expected = ResolutionFromParam [ + (FooA, Double) + (FooB, Int) + (FooC, String) + ] + + AssertExpectedResolution expected given + + [] + [] + member this.``Adjoint Application Resolution`` () = + let expression = CompileTypeParameterTest 2 |> GetMainExpression + + let combination = TypeResolutionCombination(expression) + let given = combination.CombinedResolutionDictionary + let expected = ResolutionFromParam [ + (FooA, Double) + (FooB, Int) + (FooC, String) + ] + + AssertExpectedResolution expected given + + [] + [] + member this.``Controlled Application Resolution`` () = + let expression = CompileTypeParameterTest 3 |> GetMainExpression + + let combination = TypeResolutionCombination(expression) + let given = combination.CombinedResolutionDictionary + let expected = ResolutionFromParam [ + (FooA, Double) + (FooB, Int) + (FooC, String) + ] + + AssertExpectedResolution expected given + + [] + [] + member this.``Partial Application Resolution`` () = + let expression = CompileTypeParameterTest 4 |> GetMainExpression + + let combination = TypeResolutionCombination(expression) + let given = combination.CombinedResolutionDictionary + let expected = ResolutionFromParam [ + (FooA, Double) + (FooB, Int) + (FooC, String) + ] + + AssertExpectedResolution expected given + + [] + [] + member this.``Sub-call Resolution`` () = + let expression = CompileTypeParameterTest 5 |> GetMainExpression + + let combination = TypeResolutionCombination(expression) + let given = combination.CombinedResolutionDictionary + let expected = ResolutionFromParam [ ] + + AssertExpectedResolution expected given + + [] + [] + member this.``Argument Sub-call Resolution`` () = + let expression = CompileTypeParameterTest 6 |> GetMainExpression + + let combination = TypeResolutionCombination(expression) + let given = combination.CombinedResolutionDictionary + let expected = ResolutionFromParam [ + (FooA, Double) + (FooB, Int) + (FooC, String) + ] + + AssertExpectedResolution expected given diff --git a/src/QsCompiler/TextProcessor/QsExpressionParsing.fs b/src/QsCompiler/TextProcessor/QsExpressionParsing.fs index 7a59458a91..d4dc0a15b1 100644 --- a/src/QsCompiler/TextProcessor/QsExpressionParsing.fs +++ b/src/QsCompiler/TextProcessor/QsExpressionParsing.fs @@ -72,10 +72,10 @@ qsExpression.AddOperator (InfixOperator (qsMULop.op , emptySpace qsExpression.AddOperator (InfixOperator (qsMODop.op , emptySpace , qsMODop.prec , qsMODop.Associativity , (), applyBinary MOD )) qsExpression.AddOperator (InfixOperator (qsDIVop.op , notFollowedBy (pchar '/') >>. emptySpace , qsDIVop.prec , qsDIVop.Associativity , (), applyBinary DIV )) qsExpression.AddOperator (InfixOperator (qsPOWop.op , emptySpace , qsPOWop.prec , qsPOWop.Associativity , (), applyBinary POW )) -qsExpression.AddOperator (PrefixOperator (qsBNOTop.op , emptySpace , qsBNOTop.prec , qsBNOTop.isLeftAssociative , (), applyUnary BNOT )) -qsExpression.AddOperator (PrefixOperator (qsNOTop.op , notFollowedBy (many1Satisfy isSymbolContinuation) >>. emptySpace , qsNOTop.prec , qsNOTop.isLeftAssociative , (), applyUnary NOT )) -qsExpression.AddOperator (PrefixOperator (qsNEGop.op , emptySpace , qsNEGop.prec , qsNEGop.isLeftAssociative , (), applyUnary NEG )) -qsExpression.AddOperator (PrefixOperator ("!" , "!" |> deprecatedOp WarningCode.DeprecatedNOToperator >>. emptySpace , qsNOTop.prec , qsNOTop.isLeftAssociative , (), applyUnary NOT )) +qsExpression.AddOperator (PrefixOperator (qsBNOTop.op , emptySpace , qsBNOTop.prec , true , (), applyUnary BNOT )) +qsExpression.AddOperator (PrefixOperator (qsNOTop.op , notFollowedBy (many1Satisfy isSymbolContinuation) >>. emptySpace , qsNOTop.prec , true , (), applyUnary NOT )) +qsExpression.AddOperator (PrefixOperator (qsNEGop.op , emptySpace , qsNEGop.prec , true , (), applyUnary NEG )) +qsExpression.AddOperator (PrefixOperator ("!" , "!" |> deprecatedOp WarningCode.DeprecatedNOToperator >>. emptySpace , qsNOTop.prec , true , (), applyUnary NOT )) qsExpression.AddOperator (InfixOperator ("||" , "||" |> deprecatedOp WarningCode.DeprecatedORoperator >>. emptySpace , qsORop.prec , qsORop.Associativity , (), applyBinary OR )) qsExpression.AddOperator (InfixOperator ("&&" , "&&" |> deprecatedOp WarningCode.DeprecatedANDoperator >>. emptySpace , qsANDop.prec , qsANDop.Associativity , (), applyBinary AND )) @@ -85,6 +85,12 @@ for op in qsExpression.Operators do qsArgument.AddOperator op // processing modifiers (functor application and unwrap directives) // -> modifiers basically act as unary operators with infinite precedence that can only be applied to certain expressions +/// Parses a postfix modifer (unwrap operator) as term and returns its range, +/// i.e. fails without consuming input if there is no postfix modifier to parse. +let private postFixModifier = + term (pstring qsUnwrapModifier.op .>> notFollowedBy (pchar '=')) + |>> snd |>> QsPositionInfo.Range + /// Given an expression which (potentially) supports the application of modifiers, /// processes the expression and all its leading and trailing modifiers, applies all modifiers, and builds the corresponding Q# expression. /// Expression modifiers are functor application and/or unwrap directives. @@ -94,7 +100,6 @@ let private withModifiers modifiableExpr = let rec applyUnwraps unwraps (core : QsExpression) = unwraps |> function | [] -> core | range :: tail -> buildCombinedExpr (UnwrapApplication core) (core.Range, range) |> applyUnwraps tail - let unwrapOperator = term (pstring qsUnwrapModifier.op .>> notFollowedBy (pchar '=')) |>> snd |>> QsPositionInfo.Range let rec applyFunctors functors (core : QsExpression) = functors |> function | [] -> core | (range, kind) :: tail -> buildCombinedExpr (kind core) (QsPositionInfo.Range range, core.Range) |> applyFunctors tail @@ -102,7 +107,7 @@ let private withModifiers modifiableExpr = let adjointApplication = qsAdjointFunctor.parse .>>. preturn AdjointApplication let controlledApplication = qsControlledFunctor.parse .>>. preturn ControlledApplication adjointApplication <|> controlledApplication - attempt (many functorApplication .>>. modifiableExpr) .>>. many unwrapOperator // NOTE: do *not* replace by an expected expression even if there are preceding functors! + attempt (many functorApplication .>>. modifiableExpr) .>>. many postFixModifier // NOTE: do *not* replace by an expected expression even if there are preceding functors! |>> fun ((functors, ex), unwraps) -> applyUnwraps unwraps ex |> applyFunctors (functors |> List.rev) @@ -304,15 +309,23 @@ type private ItemAccessor = /// Note that this parser has a dependency on the identifier, tupleItem expr, and valueArray parsers - /// meaning they process the left-most part of the array item expression and thus need to be evaluated *after* the arrayItemExpr parser. let private itemAccessExpr = + let rec applyPostfixModifiers ex = function + | [] -> ex + | (range : QsRangeInfo) :: tail -> + let ex = (UnwrapApplication (ex), range) |> QsExpression.New + applyPostfixModifiers ex tail let rec applyAccessors (ex : QsExpression, item) = + let recur (accessEx, mods) tail = + let accessExWithMods = applyPostfixModifiers accessEx mods + applyAccessors (accessExWithMods, tail) match item with | [] -> ex - | ArrayItemAccessor (idx, range) :: tail -> + | (ArrayItemAccessor (idx, range), postfixMod) :: tail -> let arrItemEx = buildCombinedExpr (ArrayItem (ex, idx)) (ex.Range, range |> QsPositionInfo.Range) - applyAccessors (arrItemEx, tail) - | NamedItemAccessor sym :: tail -> + recur (arrItemEx, postfixMod) tail + | (NamedItemAccessor sym, postfixMod) :: tail -> let namedItemEx = buildCombinedExpr (NamedItem (ex, sym)) (ex.Range, sym.Range) - applyAccessors (namedItemEx, tail) + recur (namedItemEx, postfixMod) tail let accessor = let missingEx pos = (MissingExpr, (pos, pos)) |> QsExpression.New let openRange = pstring qsOpenRangeOp.op |> term @@ -340,7 +353,7 @@ let private itemAccessExpr = | None -> core |> applyPost let arrayItemAccess = arrayBrackets (fullyOpenRange <|> closedOrHalfOpenRange) |>> ArrayItemAccessor let namedItemAccess = term (pstring qsNamedItemCombinator.op) >>. symbolLike ErrorCode.ExpectingUnqualifiedSymbol |>> NamedItemAccessor - arrayItemAccess <|> namedItemAccess + (arrayItemAccess <|> namedItemAccess) .>>. many postFixModifier let arrItem = // ideally, this would also "depend" on callLikeExpression and arrayItemExpr (i.e. try them as an lhs expression) // but that requires handling the cyclic dependency ... diff --git a/src/QsCompiler/TextProcessor/QsKeywords.fs b/src/QsCompiler/TextProcessor/QsKeywords.fs index 911860400b..00749b648f 100644 --- a/src/QsCompiler/TextProcessor/QsKeywords.fs +++ b/src/QsCompiler/TextProcessor/QsKeywords.fs @@ -270,9 +270,9 @@ let qsMULop = QsOperator.New("*" , 35, true) let qsMODop = QsOperator.New("%" , 35, true) let qsDIVop = QsOperator.New("/" , 35, true) let qsPOWop = QsOperator.New("^" , 40, false) -let qsBNOTop = QsOperator.New("~~~" , 45, true) -let qsNOTop = QsOperator.New(notOperator.id, 45, true) -let qsNEGop = QsOperator.New("-" , 45, true) +let qsBNOTop = QsOperator.New("~~~" , 45, false) +let qsNOTop = QsOperator.New(notOperator.id, 45, false) +let qsNEGop = QsOperator.New("-" , 45, false) let qsSetUnion = QsOperator.New("+" , 10, true) let qsSetIntersection = QsOperator.New("*" , 20, true) @@ -289,4 +289,5 @@ let qsAdjointModifier = QsOperator.New(qsAdjointFunctor.id , 950 , fals let qsControlledModifier = QsOperator.New(qsControlledFunctor.id , 951 , false) let qsUnwrapModifier = QsOperator.New("!" , 1000, true) let qsArrayAccessCombinator = QsOperator.New("[", "]" , 1100, true) // arr[i][j] is fine -let qsNamedItemCombinator = QsOperator.New("::" , 1100, true) // any combination of named and array item acces is fine \ No newline at end of file +let qsNamedItemCombinator = QsOperator.New("::" , 1100, true) // any combination of named and array item acces is fine + diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index 7009a2f3d8..b2f7c933d7 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.Quantum.QsCompiler.DataTypes; -using Microsoft.Quantum.QsCompiler.DependencyAnalysis; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; using Microsoft.Quantum.QsCompiler.Transformations.Core; @@ -250,29 +249,6 @@ public StatementTransformation(SyntaxTreeTransformation par { } - /// - /// Get the combined type resolutions for a pair of nested resolutions, - /// resolving references in the inner resolutions to the outer resolutions. - /// - private TypeArgsResolution GetCombinedTypeResolution(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(); - } - /// /// Checks if the scope is valid for conversion to an operation call from the conditional control API. /// It is valid if there is exactly one statement in it and that statement is a call like expression statement. @@ -300,12 +276,9 @@ private TypeArgsResolution GetCombinedTypeResolution(TypeArgsResolution outer, T { // 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 combined = TypeParamUtils.TryCombineTypeResolutionsForTarget( - global.Item, - out var combinedTypeArguments, - newCallIdentifier.TypeParameterResolutions, - callTypeArguments); - QsCompilerError.Verify(combined, "failed to combine type parameter resolution"); + var combination = new TypeResolutionCombination(expr.Item); + var combinedTypeArguments = combination.CombinedResolutionDictionary.Where(kvp => kvp.Key.Item1.Equals(global.Item)).ToImmutableDictionary(); + QsCompilerError.Verify(combination.IsValid, "failed to combine type parameter resolution"); var globalCallable = this.SharedState.Compilation.Namespaces .Where(ns => ns.Name.Equals(global.Item.Namespace)) diff --git a/src/QsCompiler/Transformations/TypeResolutionCombination.cs b/src/QsCompiler/Transformations/TypeResolutionCombination.cs new file mode 100644 index 0000000000..88f210dfe5 --- /dev/null +++ b/src/QsCompiler/Transformations/TypeResolutionCombination.cs @@ -0,0 +1,397 @@ +// Copyright (c) Microsoft Corporation. +// 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; +using Microsoft.Quantum.QsCompiler.Transformations.Core; + +#nullable enable + +namespace Microsoft.Quantum.QsCompiler +{ + using ExpressionKind = QsExpressionKind; + using ResolvedTypeKind = QsTypeKind; + // Type Parameters are frequently referenced by the callable of the type parameter followed by the name of the specific type parameter. + using TypeParameterName = Tuple>; + using TypeParameterResolutions = ImmutableDictionary>, ResolvedType>; + + /// + /// Combines a series of type parameter resolution dictionaries, IndependentResolutionDictionaries, + /// into one resolution dictionary, CombinedResolutionDictionary, containing the ultimate type + /// resolutions for all the type parameters found in the dictionaries. Validation is done on the + /// resolutions, which can be checked through the IsValid flag. + /// + public class TypeResolutionCombination + { + // Static Members + + /// + /// Checks if the given type parameter directly resolves to itself. + /// + private static bool IsSelfResolution(TypeParameterName typeParam, ResolvedType res) + { + return res.Resolution is ResolvedTypeKind.TypeParameter tp + && tp.Item.Origin.Equals(typeParam.Item1) + && tp.Item.TypeName.Equals(typeParam.Item2); + } + + /// + /// Reverses the dependencies of type parameters resolving to other type parameters in the given + /// dictionary to create a lookup whose keys are type parameters and whose values are all the type + /// parameters that can be updated by knowing the resolution of the lookup's associated key. + /// + private static ILookup GetReplaceable(TypeParameterResolutions.Builder typeParamResolutions) + { + return typeParamResolutions + .Select(kvp => (kvp.Key, GetTypeParameters.Apply(kvp.Value))) // Get any type parameters in the resolution type. + .SelectMany(tup => tup.Item2.Select(value => (tup.Key, value))) // For each type parameter found, match it to the dictionary key. + .ToLookup(// Reverse the keys and resulting type parameters to make the lookup. + kvp => kvp.value, + kvp => kvp.Key); + } + + // Fields and Properties + + /// + /// Array of all the type parameter resolution dictionaries that are combined in this combination. + /// The items are ordered such that dictionaries containing type parameters resolutions that + /// reference type parameters in other dictionaries appear before those dictionaries containing + /// the referenced type parameters. I.e., dictionary A depends on dictionary B, so A should come before B. + /// + public readonly ImmutableArray IndependentResolutionDictionaries; + + /// + /// The resulting resolution dictionary from combining all the input resolutions in + /// IndependentResolutionDictionaries. Represents a combination of all the type parameters + /// found and their ultimate type resolutions. + /// + public TypeParameterResolutions CombinedResolutionDictionary { get; private set; } = TypeParameterResolutions.Empty; + + /// + /// Flag for if there were any invalid scenarios encountered while creating the combination. + /// Invalid scenarios include a type parameter being assigned to multiple conflicting types + /// and a type parameter being assigned to a type referencing a different type parameter of + /// the same callable. Has value true if no invalid scenarios were encountered. + /// + public bool IsValid => !this.combinesOverConflictingResolution && !this.combinesOverParameterConstriction; + + /// + /// Flag for if, at any time in the creation of the combination, there was a type parameter that + /// was assigned conflicting type resolutions. Has value true if a conflict was encountered. + /// + private bool combinesOverConflictingResolution = false; + + /// + /// Flag for if, at any time in the creation of the combination, there was a type parameter that + /// was assigned a type resolution referencing a different type parameter of the same callable. + /// + private bool combinesOverParameterConstriction = false; + + // Constructors + + /// + /// Creates a type parameter resolution combination from the independent type parameter resolutions + /// found in the given typed expression and its sub expressions. Only sub-expressions whose + /// type parameter resolutions are relevant to the given expression's type parameter resolutions + /// are considered. + /// + public TypeResolutionCombination(TypedExpression expression) : this(GetTypeParameterResolutions.Apply(expression)) + { + } + + /// + /// Creates a type parameter resolution combination from independent type parameter resolution dictionaries. + /// The given resolutions are expected to be ordered such that dictionaries containing type parameters resolutions that + /// reference type parameters in other dictionaries appear before those dictionaries containing the referenced type parameters. + /// I.e., dictionary A depends on dictionary B, so A should come before B. When using this method to resolve + /// the resolutions of a nested expression, this means that the innermost resolutions should come first, followed by + /// the next innermost, and so on until the outermost expression is given last. Empty and null dictionaries are ignored. + /// + internal TypeResolutionCombination(params TypeParameterResolutions[] independentResolutionDictionaries) + { + // Filter out empty dictionaries + this.IndependentResolutionDictionaries = independentResolutionDictionaries.Where(res => !(res is null || res.IsEmpty)).ToImmutableArray(); + + if (this.IndependentResolutionDictionaries.Any()) + { + this.CombineTypeResolutions(); + } + } + + // Methods + + /// + /// Updates the combinesOverParameterConstriction flag. If the flag is already set to true, + /// nothing will be done. If not, the given type parameter will be checked against the given + /// resolution for type parameter constriction, which is when one type parameter is dependent + /// on another type parameter of the same callable. + /// + private void UpdateConstrictionFlag(TypeParameterName typeParamName, ResolvedType typeParamResolution) + { + this.combinesOverParameterConstriction = this.combinesOverParameterConstriction + || CheckForConstriction.Apply(typeParamName, typeParamResolution); + } + + /// + /// Uses the given lookup, mayBeReplaced, to determine what records in the combinedBuilder can be updated + /// from the given type parameter, typeParam, and its resolution, paramRes. Then updates the combinedBuilder + /// appropriately. + /// + private void UpdatedReplaceableResolutions( + ILookup mayBeReplaced, + TypeParameterResolutions.Builder combinedBuilder, + TypeParameterName typeParam, + ResolvedType paramRes) + { + // Create a dictionary with just the current resolution in it. + var singleResolution = new[] { 0 }.ToImmutableDictionary(_ => typeParam, _ => paramRes); + + // Get all the parameters whose value is dependent on the current resolution's type parameter, + // and update their values with this resolution's value. + foreach (var keyInCombined in mayBeReplaced[typeParam]) + { + // Check that we are not constricting a type parameter to another type parameter of the same callable. + this.UpdateConstrictionFlag(keyInCombined, paramRes); + combinedBuilder[keyInCombined] = ResolvedType.ResolveTypeParameters(singleResolution, combinedBuilder[keyInCombined]); + } + } + + /// + /// Combines independent resolutions in a disjointed dictionary, resulting in a + /// resolution dictionary that has type parameter keys that are not referenced + /// in its values. Null mappings are removed in the resulting dictionary. + /// Returns the resulting dictionary. + /// + private TypeParameterResolutions CombineTypeResolutionDictionary(TypeParameterResolutions independentResolutions) + { + var combinedBuilder = ImmutableDictionary.CreateBuilder(); + + foreach (var (typeParam, paramRes) in independentResolutions) + { + // Skip any null mappings + if (paramRes is null) + { + continue; + } + + // Contains a lookup of all the keys in the combined resolutions whose value needs to be updated + // if a certain type parameter is resolved by the currently processed dictionary. + var mayBeReplaced = GetReplaceable(combinedBuilder); + + // Check that we are not constricting a type parameter to another type parameter of the same callable + // both before and after updating the current value with the resolutions processed so far. + this.UpdateConstrictionFlag(typeParam, paramRes); + var resolvedParamRes = ResolvedType.ResolveTypeParameters(combinedBuilder.ToImmutable(), paramRes); + this.UpdateConstrictionFlag(typeParam, resolvedParamRes); + + // Do any replacements for type parameters that may be replaced with the current resolution. + this.UpdatedReplaceableResolutions(mayBeReplaced, combinedBuilder, typeParam, resolvedParamRes); + + // Add the resolution to the current dictionary. + combinedBuilder[typeParam] = resolvedParamRes; + } + + return combinedBuilder.ToImmutable(); + } + + /// + /// Combines the resolution dictionaries in the combination into one resolution dictionary containing + /// the resolutions for all the type parameters found. + /// Updates the combination with the constructed dictionary. Updates the validation flags accordingly. + /// + private void CombineTypeResolutions() + { + var combinedBuilder = ImmutableDictionary.CreateBuilder(); + + foreach (var resolutionDictionary in this.IndependentResolutionDictionaries) + { + var resolvedDictionary = this.CombineTypeResolutionDictionary(resolutionDictionary); + + // Contains a lookup of all the keys in the combined resolutions whose value needs to be updated + // if a certain type parameter is resolved by the currently processed dictionary. + var mayBeReplaced = GetReplaceable(combinedBuilder); + + // Do any replacements for type parameters that may be replaced with values in the current dictionary. + // This needs to be done first to cover an edge case. + foreach (var (typeParam, paramRes) in resolvedDictionary.Where(entry => mayBeReplaced.Contains(entry.Key))) + { + this.UpdatedReplaceableResolutions(mayBeReplaced, combinedBuilder, typeParam, paramRes); + } + + // Validate and add each resolution to the result. + foreach (var (typeParam, paramRes) in resolvedDictionary) + { + // Check that we are not constricting a type parameter to another type parameter of the same callable. + this.UpdateConstrictionFlag(typeParam, paramRes); + + // Check that there is no conflicting resolution already defined. + if (!this.combinesOverConflictingResolution) + { + this.combinesOverConflictingResolution = combinedBuilder.TryGetValue(typeParam, out var current) + && !current.Equals(paramRes) && !IsSelfResolution(typeParam, current); + } + + // Add the resolution to the current dictionary. + combinedBuilder[typeParam] = paramRes; + } + } + + this.CombinedResolutionDictionary = combinedBuilder.ToImmutable(); + } + + // Nested Classes + + /// + /// Walker that collects all of the type parameter references for a given ResolvedType + /// and returns them as a HashSet. + /// + private class GetTypeParameters : TypeTransformation + { + /// + /// Walks the given ResolvedType and returns all of the type parameters referenced. + /// + public static HashSet Apply(ResolvedType res) + { + var walker = new GetTypeParameters(); + walker.OnType(res); + return walker.SharedState.TypeParams; + } + + internal class TransformationState + { + public HashSet TypeParams = new HashSet(); + } + + private GetTypeParameters() : base(new TransformationState(), TransformationOptions.NoRebuild) + { + } + + private static TypeParameterName AsTypeResolutionKey(QsTypeParameter tp) => Tuple.Create(tp.Origin, tp.TypeName); + + public override ResolvedTypeKind OnTypeParameter(QsTypeParameter tp) + { + this.SharedState.TypeParams.Add(AsTypeResolutionKey(tp)); + return base.OnTypeParameter(tp); + } + } + + /// + /// Walker that checks a given type parameter resolution to see if it constricts + /// the type parameter to another type parameter of the same callable. + /// + private class CheckForConstriction : TypeTransformation + { + private readonly TypeParameterName typeParamName; + + /// + /// Walks the given ResolvedType, typeParamRes, and returns true if there is a reference + /// to a different type parameter of the same callable as the given type parameter, typeParam. + /// Otherwise returns false. + /// + public static bool Apply(TypeParameterName typeParam, ResolvedType typeParamRes) + { + var walker = new CheckForConstriction(typeParam); + walker.OnType(typeParamRes); + return walker.SharedState.IsConstrictive; + } + + internal class TransformationState + { + public bool IsConstrictive = false; + } + + private CheckForConstriction(TypeParameterName typeParamName) + : base(new TransformationState(), TransformationOptions.NoRebuild) + { + this.typeParamName = typeParamName; + } + + public new ResolvedType OnType(ResolvedType t) + { + // Short-circuit if we already know the type is constrictive. + if (!this.SharedState.IsConstrictive) + { + base.OnType(t); + } + + // It doesn't matter what we return because this is a walker. + return t; + } + + public override ResolvedTypeKind OnTypeParameter(QsTypeParameter tp) + { + // If the type parameter is from the same callable, but is a different parameter, + // then the type resolution is constrictive. + if (tp.Origin.Equals(this.typeParamName.Item1) && !tp.TypeName.Equals(this.typeParamName.Item2)) + { + this.SharedState.IsConstrictive = true; + } + + return base.OnTypeParameter(tp); + } + } + + /// + /// Walker that returns the relevant type parameter resolution dictionaries from a given + /// TypedExpression and its sub-expressions. + /// + private class GetTypeParameterResolutions : ExpressionTransformation + { + /// + /// Walk the given TypedExpression, collecting type parameter resolution dictionaries relevant to + /// the type parameter resolutions of the topmost expression. Returns the resolution dictionaries + /// ordered from the innermost expression's resolutions to the outermost expression's resolutions. + /// + public static TypeParameterResolutions[] Apply(TypedExpression expression) + { + var walker = new GetTypeParameterResolutions(); + walker.OnTypedExpression(expression); + return walker.SharedState.Resolutions.ToArray(); + } + + internal class TransformationState + { + public List Resolutions = new List(); + public bool InCallLike = false; + } + + private GetTypeParameterResolutions() : base(new TransformationState(), TransformationOptions.NoRebuild) + { + } + + public override TypedExpression OnTypedExpression(TypedExpression ex) + { + if (ex.Expression is ExpressionKind.CallLikeExpression call) + { + if (!this.SharedState.InCallLike || TypedExpression.IsPartialApplication(call)) + { + var contextInCallLike = this.SharedState.InCallLike; + this.SharedState.InCallLike = true; + this.OnTypedExpression(call.Item1); + this.SharedState.Resolutions.Add(ex.TypeParameterResolutions); + this.SharedState.InCallLike = contextInCallLike; + } + } + else if (ex.Expression is ExpressionKind.AdjointApplication adj) + { + this.OnTypedExpression(adj.Item); + } + else if (ex.Expression is ExpressionKind.ControlledApplication ctrl) + { + this.OnTypedExpression(ctrl.Item); + } + else + { + this.SharedState.Resolutions.Add(ex.TypeParameterResolutions); + } + + return ex; + } + } + } +} diff --git a/src/VSCodeExtension/package.json.v.template b/src/VSCodeExtension/package.json.v.template index ddcd4518ab..ac9756756d 100644 --- a/src/VSCodeExtension/package.json.v.template +++ b/src/VSCodeExtension/package.json.v.template @@ -124,17 +124,17 @@ "typescript": "^2.6.1", "tslint": "^5.8.0", "mocha": "^5.2.0", - "@types/yeoman-generator": "^3.1.4", - "@types/yosay": "^0.0.29", "yeoman-test": "^1.7.0", "yeoman-assert": "^3.1.0", - "@types/yeoman-environment": "^2.3.3", "@types/node": "^9.6.5", "@types/mocha": "^5.2.0", "@types/which": "1.3.1", "@types/tmp": "0.0.33", "@types/semver": "^6.0.0", - "@types/request": "^2.48.3" + "@types/request": "^2.48.3", + "@types/yeoman-environment": "2.3.3", + "@types/yeoman-generator": "3.1.4", + "@types/yosay": "0.0.29" }, "blobs": { "win32": {