From cebe4e34b83866ca37493e623456e2e19a1c3b33 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 9 Apr 2020 22:32:46 -0700 Subject: [PATCH 01/25] prevent array of arrays in entry point arguments --- src/QsCompiler/Core/SymbolTable.fs | 9 +++++++-- src/QsCompiler/DataStructures/Diagnostics.fs | 16 +++++++++------- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 7 +++++++ .../TestCases/LinkingTests/InvalidEntryPoints.qs | 11 +++++++++++ .../TestCases/LinkingTests/ValidEntryPoints.qs | 8 ++++++++ 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index f54464be3f..a5e4e5da98 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -980,13 +980,18 @@ and NamespaceManager elif tId |> isBuiltIn BuiltIn.EntryPoint then match box decl.Defined with | :? CallableSignature as signature when not (signature.TypeParameters.Any()) -> - let validateArgAndReturnTypes preventTuples (qsType : QsType) = + let validateArgAndReturnTypes argumentRestriction (qsType : QsType) = + let rec IsArray = function + | ArrayType _ -> true + | TupleType (ts : ImmutableArray) when ts.Length = 1 -> IsArray ts.[0].Type + | _ -> false qsType.ExtractAll (fun t -> t.Type |> function // ExtractAll recurs on all subtypes (e.g. callable in- and output types as well) | Qubit -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.QubitTypeInEntryPointSignature, [])) |> Seq.singleton | QsTypeKind.Operation _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.CallableTypeInEntryPointSignature, [])) |> Seq.singleton | QsTypeKind.Function _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.CallableTypeInEntryPointSignature, [])) |> Seq.singleton | UserDefinedType _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UserDefinedTypeInEntryPointSignature, [])) |> Seq.singleton - | TupleType ts when ts.Length > 1 && preventTuples -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InnerTupleInEntryPointArgument, [])) |> Seq.singleton + | TupleType ts when ts.Length > 1 && argumentRestriction -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InnerTupleInEntryPointArgument, [])) |> Seq.singleton + | ArrayType bt when argumentRestriction && bt.Type |> IsArray -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.ArrayOfArrayInEntryPointArgument, [])) |> Seq.singleton | _ -> Seq.empty) let inErrs = signature.Argument.Items.Select(snd) |> Seq.collect (validateArgAndReturnTypes true) let outErrs = signature.ReturnType |> validateArgAndReturnTypes false diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 0d9d9972e5..2e4cf29168 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -235,13 +235,14 @@ type ErrorCode = | CallableTypeInEntryPointSignature = 6232 | UserDefinedTypeInEntryPointSignature = 6233 | InnerTupleInEntryPointArgument = 6234 - | MultipleEntryPoints = 6235 - | InvalidEntryPointSpecialization = 6236 - | InvalidTestAttributePlacement = 6237 - | InvalidExecutionTargetForTest = 6238 - | ExpectingFullNameAsAttributeArgument = 6239 - | AttributeInvalidOnSpecialization = 6240 - | AttributeInvalidOnCallable = 6241 + | ArrayOfArrayInEntryPointArgument = 6235 + | MultipleEntryPoints = 6236 + | InvalidEntryPointSpecialization = 6237 + | InvalidTestAttributePlacement = 6238 + | InvalidExecutionTargetForTest = 6239 + | ExpectingFullNameAsAttributeArgument = 6240 + | AttributeInvalidOnSpecialization = 6241 + | AttributeInvalidOnCallable = 6242 | TypeMismatchInReturn = 6301 | TypeMismatchInValueUpdate = 6302 @@ -603,6 +604,7 @@ type DiagnosticItem = | ErrorCode.CallableTypeInEntryPointSignature -> "Invalid entry point. Values of operation or function type may not be used as arguments or return values to entry points." | ErrorCode.UserDefinedTypeInEntryPointSignature -> "Invalid entry point. Values of user defined type may not be used as arguments or return values to entry points." | ErrorCode.InnerTupleInEntryPointArgument -> "Anonymous tuple items or arrays of tuples are not supported in entry point arguments. All items need to be named." + | ErrorCode.ArrayOfArrayInEntryPointArgument -> "Multi-dimensional arrays are not supported in entry point arguments." | ErrorCode.MultipleEntryPoints -> "Invalid entry point. An entry point {0} already exists in {1}." | ErrorCode.InvalidEntryPointSpecialization -> "Entry points cannot have any other specializations besides the default body." | ErrorCode.InvalidTestAttributePlacement -> "Invalid test attribute. Test attributes may only occur on callables that have no arguments and return Unit." diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index aee882e49f..4e456e67bf 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -237,6 +237,10 @@ type LinkingTests (output:ITestOutputHelper) = for entryPoint in LinkingTests.ReadAndChunkSourceFile "EntryPointSpecializations.qs" do this.CompileAndVerify entryPoint [Error ErrorCode.InvalidEntryPointSpecialization] + + [] + member this.``Entry point verification`` () = + for entryPoint in LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" do this.CompileAndVerify entryPoint [] @@ -303,6 +307,9 @@ type LinkingTests (output:ITestOutputHelper) = this.Expect "InvalidEntryPoint36" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] this.Expect "InvalidEntryPoint37" [Error ErrorCode.InnerTupleInEntryPointArgument] this.Expect "InvalidEntryPoint38" [Error ErrorCode.InnerTupleInEntryPointArgument] + this.Expect "InvalidEntryPoint39" [Error ErrorCode.ArrayOfArrayInEntryPointArgument] + this.Expect "InvalidEntryPoint40" [Error ErrorCode.ArrayOfArrayInEntryPointArgument] + this.Expect "InvalidEntryPoint41" [Error ErrorCode.ArrayOfArrayInEntryPointArgument] [] diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs index 88271c5867..4f956a135c 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs @@ -222,4 +222,15 @@ namespace Microsoft.Quantum.Testing.EntryPoints { @ EntryPoint() operation InvalidEntryPoint38(arg1 : (Pauli, Result)[], arg2 : Double) : Unit {} + // no arrays of arrays in entry point arguments + + @ EntryPoint() + operation InvalidEntryPoint39(arg : Int[][]) : Unit {} + + @ EntryPoint() + operation InvalidEntryPoint40(arg : (Int[])[]) : Unit {} + + @ EntryPoint() + operation InvalidEntryPoint41(a : Int[], (b : Int[][], c : Double)) : Unit {} + } diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ValidEntryPoints.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ValidEntryPoints.qs index c7af27373a..56f78c9257 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ValidEntryPoints.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ValidEntryPoints.qs @@ -127,3 +127,11 @@ namespace Microsoft.Quantum.Testing.EntryPoints { return Default<((Bool, String)[][], (Unit, Range))>(); } } + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation ValidEntryPoint15(a : Int[], (b : Double[], c : String[])) : Unit { } +} From 6057fa5e71a48810f8bfb547a0e4dcca4dac9eb9 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 9 Apr 2020 23:40:42 -0700 Subject: [PATCH 02/25] verifying entry point argument names --- src/QsCompiler/Core/Dependencies.fs | 5 +++ src/QsCompiler/Core/SymbolTable.fs | 34 +++++++++++++++---- src/QsCompiler/DataStructures/Diagnostics.fs | 14 +++++--- .../DataStructures/ReservedKeywords.fs | 5 +++ 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index 1a01f29fd2..6dc010eed0 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -38,6 +38,11 @@ type BuiltIn = { ["QuantumSimulator"; "ToffoliSimulator"; "ResourcesEstimator"] |> ImmutableHashSet.CreateRange + /// Returns the names of all reserved names for command line arguments. + static member internal ReserveCommandLineArguments = + [ReservedKeywords.CommandLineArguments.SimulatorOption] + |> ImmutableHashSet.CreateRange + /// Returns true if the given attribute marks the corresponding declaration as entry point. static member MarksEntryPoint (att : QsDeclarationAttribute) = att.TypeId |> function | Value tId -> tId.Namespace.Value = BuiltIn.EntryPoint.FullName.Namespace.Value && tId.Name.Value = BuiltIn.EntryPoint.FullName.Name.Value diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index a5e4e5da98..01fc6f2879 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -10,6 +10,7 @@ open System.Linq open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics +open Microsoft.Quantum.QsCompiler.ReservedKeywords open Microsoft.Quantum.QsCompiler.SymbolResolution open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxGenerator @@ -980,6 +981,15 @@ and NamespaceManager elif tId |> isBuiltIn BuiltIn.EntryPoint then match box decl.Defined with | :? CallableSignature as signature when not (signature.TypeParameters.Any()) -> + + // verify that the entry point has only a default body specialization + let hasCharacteristics = signature.Characteristics.Characteristics |> function | EmptySet | InvalidSetExpr -> false | _ -> true + match Namespaces.TryGetValue parent.Namespace with + | false, _ -> ArgumentException "no namespace with the given name exists" |> raise + | true, ns when not ((ns.SpecializationsDefinedInAllSources parent.Name).Any(fst >> (<>)QsBody) || hasCharacteristics) -> () + | _ -> errs.Add (decl.Position, signature.Characteristics.Range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.InvalidEntryPointSpecialization, [])) + + // validate entry point argument and return type let validateArgAndReturnTypes argumentRestriction (qsType : QsType) = let rec IsArray = function | ArrayType _ -> true @@ -993,14 +1003,25 @@ and NamespaceManager | TupleType ts when ts.Length > 1 && argumentRestriction -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InnerTupleInEntryPointArgument, [])) |> Seq.singleton | ArrayType bt when argumentRestriction && bt.Type |> IsArray -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.ArrayOfArrayInEntryPointArgument, [])) |> Seq.singleton | _ -> Seq.empty) - let inErrs = signature.Argument.Items.Select(snd) |> Seq.collect (validateArgAndReturnTypes true) + let inErrs = signature.Argument.Items.Select snd |> Seq.collect (validateArgAndReturnTypes true) let outErrs = signature.ReturnType |> validateArgAndReturnTypes false let signatureErrs = inErrs.Concat outErrs - let hasCharacteristics = signature.Characteristics.Characteristics |> function | EmptySet | InvalidSetExpr -> false | _ -> true - match Namespaces.TryGetValue parent.Namespace with - | false, _ -> ArgumentException "no namespace with the given name exists" |> raise - | true, ns when not ((ns.SpecializationsDefinedInAllSources parent.Name).Any(fst >> (<>)QsBody) || hasCharacteristics) -> () - | _ -> errs.Add (decl.Position, signature.Characteristics.Range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.InvalidEntryPointSpecialization, [])) + + // validate entry point argument names + let asCommandLineArg (str : string) = str.ToLowerInvariant() |> Seq.filter((<>)'_') |> String.Concat + let reservedCommandLineArgs = [CommandLineArguments.SimulatorOption] |> List.map asCommandLineArg + let nameAndRange (sym : QsSymbol) = sym.Symbol |> function + | Symbol name -> Some (asCommandLineArg name.Value, sym.Range) + | _ -> None + let simplifiedArgNames = signature.Argument.Items.Select fst |> Seq.choose nameAndRange |> Seq.toList + let verifyArgument i (arg, range : QsNullable<_>) = + if i > 0 && simplifiedArgNames.[..i-1] |> Seq.map fst |> Seq.contains arg + then errs.Add (decl.Position, range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.DuplicateEntryPointArgumentName, [])) + elif reservedCommandLineArgs.Contains arg + then errs.Add (decl.Position, range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.ReservedEntryPointArgumentName, [])) + simplifiedArgNames |> List.iteri verifyArgument + + // check that there is no more than one entry point if signatureErrs.Any() then returnInvalid signatureErrs else GetEntryPoints() |> Seq.tryHead |> function | None -> attributeHash :: alreadyDefined, att :: resAttr @@ -1020,6 +1041,7 @@ and NamespaceManager | [] -> signature.ReturnType |> isUnitType | [(_, argType)] -> argType |> isUnitType && signature.ReturnType |> isUnitType | _ -> false + match box decl.Defined with | :? CallableSignature as signature when signature |> isUnitToUnit && not (signature.TypeParameters.Any()) -> let arg = att.Argument |> AttributeAnnotation.NonInterpolatedStringArgument (fun ex -> ex.Expression) diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 2e4cf29168..5b8c090b91 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -238,11 +238,13 @@ type ErrorCode = | ArrayOfArrayInEntryPointArgument = 6235 | MultipleEntryPoints = 6236 | InvalidEntryPointSpecialization = 6237 - | InvalidTestAttributePlacement = 6238 - | InvalidExecutionTargetForTest = 6239 - | ExpectingFullNameAsAttributeArgument = 6240 - | AttributeInvalidOnSpecialization = 6241 - | AttributeInvalidOnCallable = 6242 + | DuplicateEntryPointArgumentName = 6238 + | ReservedEntryPointArgumentName = 6239 + | InvalidTestAttributePlacement = 6240 + | InvalidExecutionTargetForTest = 6241 + | ExpectingFullNameAsAttributeArgument = 6242 + | AttributeInvalidOnSpecialization = 6243 + | AttributeInvalidOnCallable = 6244 | TypeMismatchInReturn = 6301 | TypeMismatchInValueUpdate = 6302 @@ -607,6 +609,8 @@ type DiagnosticItem = | ErrorCode.ArrayOfArrayInEntryPointArgument -> "Multi-dimensional arrays are not supported in entry point arguments." | ErrorCode.MultipleEntryPoints -> "Invalid entry point. An entry point {0} already exists in {1}." | ErrorCode.InvalidEntryPointSpecialization -> "Entry points cannot have any other specializations besides the default body." + | ErrorCode.DuplicateEntryPointArgumentName -> "Invalid name for entry point argument. A similar argument name is already in use." + | ErrorCode.ReservedEntryPointArgumentName -> "Invalid name for entry point argument. The name has been reserved and cannot be used." | ErrorCode.InvalidTestAttributePlacement -> "Invalid test attribute. Test attributes may only occur on callables that have no arguments and return Unit." | ErrorCode.InvalidExecutionTargetForTest -> "Invalid execution target. Currently, valid execution targets for tests are the QuantumSimulator, the ToffoliSimulator, or the ResourcesEstimator." | ErrorCode.ExpectingFullNameAsAttributeArgument -> "Invalid attribute argument. Expecting a fully qualified name as argument to the {0} attribute." diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index 89ee4b8990..257c4d6e85 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -231,6 +231,11 @@ module GeneratedAttributes = let LoadedViaTestNameInsteadOf = "__LoadedViaTestNameInsteadOf__" +/// contains reserved names for command line arguments +module CommandLineArguments = + let SimulatorOption = "simulator" + + /// contains project specific settings specified during Q# compilation module AssemblyConstants = let OutputPath = "OutputPath" From cdf9628ab3d347a8abdcf86a2458f4e7216d8916 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 9 Apr 2020 23:47:11 -0700 Subject: [PATCH 03/25] ... and the corresponding tests --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 11 +++++++- .../LinkingTests/EntryPointDiagnostics.qs | 28 +++++++++++++++++++ .../LinkingTests/InvalidEntryPoints.qs | 1 + .../Tests.Compiler/Tests.Compiler.fsproj | 7 +++-- 4 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 4e456e67bf..a45a67bfe3 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -239,12 +239,21 @@ type LinkingTests (output:ITestOutputHelper) = [] - member this.``Entry point verification`` () = + member this.``Entry point validation`` () = for entryPoint in LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" do this.CompileAndVerify entryPoint [] + [] + member this.``Entry point argument name verification`` () = + + let tests = LinkingTests.ReadAndChunkSourceFile "EntryPointDiagnostics.qs" + this.CompileAndVerify tests.[0] [Error ErrorCode.DuplicateEntryPointArgumentName] + this.CompileAndVerify tests.[1] [Error ErrorCode.DuplicateEntryPointArgumentName] + this.CompileAndVerify tests.[2] [Error ErrorCode.ReservedEntryPointArgumentName] + + [] member this.``Entry point attribute placement verification`` () = diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs new file mode 100644 index 0000000000..59ff722816 --- /dev/null +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// The entry points in this file are all recognized as such. +// Correspondingly, checking the generated diagnostics requires to compile them separately. + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation InvalidEntryPoint42(argName : Int, arg_name : Int) : Unit {} +} + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation InvalidEntryPoint43(argName : Int, argname : Int) : Unit {} +} + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation InvalidEntryPoint44(simulator : Int) : Unit {} +} + diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs index 4f956a135c..a7198c1741 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs @@ -222,6 +222,7 @@ namespace Microsoft.Quantum.Testing.EntryPoints { @ EntryPoint() operation InvalidEntryPoint38(arg1 : (Pauli, Result)[], arg2 : Double) : Unit {} + // no arrays of arrays in entry point arguments @ EntryPoint() diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 879f089a5d..da351f97d3 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -38,6 +38,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -179,9 +182,7 @@ $(MSBuildThisFileDirectory)..\TestTargets\Simulation\Example\bin\$(Configuration)\netcoreapp3.1\Example.dll $(MSBuildThisFileDirectory)..\TestTargets\Libraries\Library1\bin\$(Configuration)\netcoreapp3.1\Library1.dll - + From c34b09bc66230fd3ed4b16e5146e732b01421183 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 9 Apr 2020 23:52:43 -0700 Subject: [PATCH 04/25] forgot to remove something --- src/QsCompiler/Core/Dependencies.fs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index 6dc010eed0..1a01f29fd2 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -38,11 +38,6 @@ type BuiltIn = { ["QuantumSimulator"; "ToffoliSimulator"; "ResourcesEstimator"] |> ImmutableHashSet.CreateRange - /// Returns the names of all reserved names for command line arguments. - static member internal ReserveCommandLineArguments = - [ReservedKeywords.CommandLineArguments.SimulatorOption] - |> ImmutableHashSet.CreateRange - /// Returns true if the given attribute marks the corresponding declaration as entry point. static member MarksEntryPoint (att : QsDeclarationAttribute) = att.TypeId |> function | Value tId -> tId.Namespace.Value = BuiltIn.EntryPoint.FullName.Namespace.Value && tId.Name.Value = BuiltIn.EntryPoint.FullName.Name.Value From e615d6a211ca3536339da027ddf2573667a19a7c Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 9 Apr 2020 23:55:44 -0700 Subject: [PATCH 05/25] one more test --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 3 ++- .../TestCases/LinkingTests/EntryPointDiagnostics.qs | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index a45a67bfe3..eae78bea7b 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -251,7 +251,8 @@ type LinkingTests (output:ITestOutputHelper) = let tests = LinkingTests.ReadAndChunkSourceFile "EntryPointDiagnostics.qs" this.CompileAndVerify tests.[0] [Error ErrorCode.DuplicateEntryPointArgumentName] this.CompileAndVerify tests.[1] [Error ErrorCode.DuplicateEntryPointArgumentName] - this.CompileAndVerify tests.[2] [Error ErrorCode.ReservedEntryPointArgumentName] + this.CompileAndVerify tests.[2] [Error ErrorCode.DuplicateEntryPointArgumentName] + this.CompileAndVerify tests.[3] [Error ErrorCode.ReservedEntryPointArgumentName] [] diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs index 59ff722816..28f6d0eb6c 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs @@ -15,7 +15,15 @@ namespace Microsoft.Quantum.Testing.EntryPoints { namespace Microsoft.Quantum.Testing.EntryPoints { @ EntryPoint() - operation InvalidEntryPoint43(argName : Int, argname : Int) : Unit {} + operation InvalidEntryPoint43(argName : Int, ArgName : Int) : Unit {} +} + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation InvalidEntryPoint43(argName : Int, Arg_Name : Int) : Unit {} } // ================================= From 736c0ab995470fbc85f298e8a04af1e206fc9fd1 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 10 Apr 2020 00:06:58 -0700 Subject: [PATCH 06/25] adding DefaultSimulator property as assembly constant --- src/QsCompiler/DataStructures/ReservedKeywords.fs | 1 + src/QuantumSdk/README.md | 3 +++ src/QuantumSdk/Sdk/Sdk.targets | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index 257c4d6e85..a76898d5c3 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -244,6 +244,7 @@ module AssemblyConstants = let AlfredProcessor = "AlfredProcessor" let BrunoProcessor = "BrunoProcessor" let ClementineProcessor = "ClementineProcessor" + let DefaultSimulator = "DefaultSimulator" /// contains specific names used within Q# dlls diff --git a/src/QuantumSdk/README.md b/src/QuantumSdk/README.md index e50d3f5621..6efb0f450e 100644 --- a/src/QuantumSdk/README.md +++ b/src/QuantumSdk/README.md @@ -109,6 +109,9 @@ May contain additional arguments to pass to the Q# command line compiler. Valid - `CsharpGeneration`: Specifies whether to generate C# code as part of the compilation process. Setting this property to false may prevent certain interoperability features or integration with other pieces of the Quantum Development Kit. +- `DefaultSimulator`: +Specifies the simulator to use by default for execution. + - `IncludeQsharpCorePackages`: Specifies whether the packages providing the basic language support for Q# are referenced. This property is set to true by default. If set to false, the Sdk will not reference any Q# libraries. diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index b2db5eb026..3fc78bd5f9 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -76,7 +76,9 @@ <_QscCommandTrimFlag Condition="'$(ResolvedQuantumProcessor)' == 'QPGen1'">--trim 2 <_QscCommandTargetPackageFlag Condition="'$(ResolvedTargetPackage)' != ''">--target-package "$(ResolvedTargetPackage)" <_QscCommandTestNamesFlag Condition="$(ExposeReferencesViaTestNames)">--load-test-names - <_QscCommandAssemblyPropertiesFlag>--assembly-properties ResolvedExecutionTarget:$(ResolvedQsharpExecutionTarget) $(QscCommandAssemblyProperties) + <_QscCommandPredefinedAssemblyProperties>ResolvedExecutionTarget:$(ResolvedQsharpExecutionTarget) + <_QscCommandPredefinedAssemblyProperties Condition="$(DefaultSimulator) != ''">$(_QscCommandPredefinedAssemblyProperties) DefaultSimulator:$(DefaultSimulator) + <_QscCommandAssemblyPropertiesFlag>--assembly-properties $(_QscCommandPredefinedAssemblyProperties) $(QscCommandAssemblyProperties) <_QscPackageLoadFallbackFoldersFlag Condition="@(ResolvedPackageLoadFallbackFolders->Count()) > 0">--package-load-fallback-folders "@(ResolvedPackageLoadFallbackFolders,'" "')" <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandTrimFlag) $(_QscCommandTargetPackageFlag) $(_QscPackageLoadFallbackFoldersFlag) $(_QscCommandTestNamesFlag) $(_QscCommandAssemblyPropertiesFlag) $(AdditionalQscArguments) <_QscCommandArgsFile>$(QscBuildConfigOutputPath)qsc.rsp From 69700f409984e99eab31d5eb987a344c500af4c4 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 10 Apr 2020 07:36:36 -0700 Subject: [PATCH 07/25] passing runtime capabilities as command line flag --- src/QsCompiler/CommandLineTool/Commands/Build.cs | 2 +- .../CommandLineTool/Commands/Diagnose.cs | 2 +- src/QsCompiler/CommandLineTool/Options.cs | 4 ++++ src/QsCompiler/Compiler/CompilationLoader.cs | 14 +++++++++++--- src/QsCompiler/DataStructures/ReservedKeywords.fs | 6 ++++++ src/QuantumSdk/DefaultItems/DefaultItems.targets | 10 +++++----- src/QuantumSdk/Sdk/Sdk.targets | 4 ++-- 7 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index 07c2505b9b..af86876beb 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -159,9 +159,9 @@ public static int Run(BuildOptions options, ConsoleLogger logger) ProjectName = options.ProjectName, AssemblyConstants = assemblyConstants, TargetPackageAssembly = options.GetTargetPackageAssemblyPath(logger), + RuntimeCapabilities = options.RuntimeCapabilites, GenerateFunctorSupport = true, SkipSyntaxTreeTrimming = options.TrimLevel == 0, - ConvertClassicalControl = options.TrimLevel >= 2, AttemptFullPreEvaluation = options.TrimLevel > 2, DocumentationOutputFolder = options.DocFolder, BuildOutputFolder = options.OutputFolder ?? (usesPlugins ? "." : null), diff --git a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs index 03ae109e33..1e68cacd4f 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs @@ -218,9 +218,9 @@ public static int Run(DiagnoseOptions options, ConsoleLogger logger) { AssemblyConstants = assemblyConstants, TargetPackageAssembly = options.GetTargetPackageAssemblyPath(logger), + RuntimeCapabilities = options.RuntimeCapabilites, GenerateFunctorSupport = true, SkipSyntaxTreeTrimming = options.TrimLevel == 0, - ConvertClassicalControl = options.TrimLevel >= 2, AttemptFullPreEvaluation = options.TrimLevel > 2, RewriteSteps = options.Plugins?.Select(step => (step, (string)null)) ?? ImmutableArray<(string, string)>.Empty, EnableAdditionalChecks = true, diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs index 595232b6ce..51f4bd6d1a 100644 --- a/src/QsCompiler/CommandLineTool/Options.cs +++ b/src/QsCompiler/CommandLineTool/Options.cs @@ -50,6 +50,10 @@ public class CompilationOptions : Options HelpText = "Additional properties to populate the AssemblyConstants dictionary with. Each item is expected to be of the form \"key:value\".")] public IEnumerable AdditionalAssemblyProperties { get; set; } + [Option("runtime", Required = false, SetName = CODE_MODE, + HelpText = "Specifies the classical capabilites of the runtime. Determines what QIR profile to compile to.")] + public AssemblyConstants.RuntimeCapabilities RuntimeCapabilites { get; set; } + /// /// Returns a dictionary with the specified assembly properties as out parameter. /// Returns a boolean indicating whether all specified properties were successfully added. diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 932e1b977d..a4749680cc 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -20,8 +20,10 @@ using Microsoft.Quantum.QsCompiler.Transformations.BasicTransformations; using Microsoft.VisualStudio.LanguageServer.Protocol; using Newtonsoft.Json.Bson; + using MetadataReference = Microsoft.CodeAnalysis.MetadataReference; using OptimizationLevel = Microsoft.CodeAnalysis.OptimizationLevel; +using RuntimeCapabilities = Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants.RuntimeCapabilities; namespace Microsoft.Quantum.QsCompiler @@ -104,10 +106,10 @@ public struct Configuration /// public bool AttemptFullPreEvaluation; /// - /// If set to true, the compiler will remove if-statements and replace them with calls to appropriate - /// intrinsic operations. + /// Specifies the capabilities of the runtime. + /// The specified capabilities determine what QIR profile to compile to. /// - public bool ConvertClassicalControl; + public RuntimeCapabilities RuntimeCapabilities; /// /// Unless this is set to true, all usages of type-parameterized callables are replaced with /// the concrete callable instantiation if an entry point is specified for the compilation. @@ -171,6 +173,12 @@ public struct Configuration internal bool SerializeSyntaxTree => BuildOutputFolder != null || DllOutputPath != null; + /// + /// Indicates whether the compiler will remove if-statements and replace them with calls to appropriate intrinsic operations. + /// + internal bool ConvertClassicalControl => + RuntimeCapabilities == RuntimeCapabilities.QPRGen1; + /// /// If the ProjectName does not have an ending "proj", appends a .qsproj ending to the project name. /// Returns null if the project name is null. diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index a76898d5c3..a6cc7db278 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -246,6 +246,12 @@ module AssemblyConstants = let ClementineProcessor = "ClementineProcessor" let DefaultSimulator = "DefaultSimulator" + // Note: The names of the capabilities here need to match the ones defined by the Sdk. + type RuntimeCapabilities = + | Unknown = 0 + | QPRGen0 = 1 + | QPRGen1 = 2 + /// contains specific names used within Q# dlls module DotnetCoreDll = diff --git a/src/QuantumSdk/DefaultItems/DefaultItems.targets b/src/QuantumSdk/DefaultItems/DefaultItems.targets index 7e8f137c2c..4f8243ac80 100644 --- a/src/QuantumSdk/DefaultItems/DefaultItems.targets +++ b/src/QuantumSdk/DefaultItems/DefaultItems.targets @@ -44,12 +44,12 @@ Default - + - QPGen1 - QPGen0 - QPGen1 - Unknown + QPRGen1 + QPRGen0 + QPRGen1 + Unknown diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index 3fc78bd5f9..19d3b7d621 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -73,14 +73,14 @@ <_QscCommandInputFlag Condition="@(QsharpCompile->Count()) > 0">--input "@(QsharpCompile,'" "')" <_QscCommandReferencesFlag Condition="@(ResolvedQsharpReferences->Count()) > 0">--references "@(ResolvedQsharpReferences,'" "')" <_QscCommandLoadFlag Condition="@(_PrioritizedResolvedQscReferences->Count()) > 0">--load "@(_PrioritizedResolvedQscReferences,'" "')" - <_QscCommandTrimFlag Condition="'$(ResolvedQuantumProcessor)' == 'QPGen1'">--trim 2 + <_QscCommandRuntimeFlag>--runtime $(ResolvedRuntimeCapabilites) <_QscCommandTargetPackageFlag Condition="'$(ResolvedTargetPackage)' != ''">--target-package "$(ResolvedTargetPackage)" <_QscCommandTestNamesFlag Condition="$(ExposeReferencesViaTestNames)">--load-test-names <_QscCommandPredefinedAssemblyProperties>ResolvedExecutionTarget:$(ResolvedQsharpExecutionTarget) <_QscCommandPredefinedAssemblyProperties Condition="$(DefaultSimulator) != ''">$(_QscCommandPredefinedAssemblyProperties) DefaultSimulator:$(DefaultSimulator) <_QscCommandAssemblyPropertiesFlag>--assembly-properties $(_QscCommandPredefinedAssemblyProperties) $(QscCommandAssemblyProperties) <_QscPackageLoadFallbackFoldersFlag Condition="@(ResolvedPackageLoadFallbackFolders->Count()) > 0">--package-load-fallback-folders "@(ResolvedPackageLoadFallbackFolders,'" "')" - <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandTrimFlag) $(_QscCommandTargetPackageFlag) $(_QscPackageLoadFallbackFoldersFlag) $(_QscCommandTestNamesFlag) $(_QscCommandAssemblyPropertiesFlag) $(AdditionalQscArguments) + <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandRuntimeFlag) $(_QscCommandTargetPackageFlag) $(_QscPackageLoadFallbackFoldersFlag) $(_QscCommandTestNamesFlag) $(_QscCommandAssemblyPropertiesFlag) $(AdditionalQscArguments) <_QscCommandArgsFile>$(QscBuildConfigOutputPath)qsc.rsp $(QscExe) build --format MsBuild $(_VerbosityFlag) --response-files $(_QscCommandArgsFile) From 2c82f3fd4161ab7b07e96b7ba771f7c29c4931c0 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 10 Apr 2020 07:48:49 -0700 Subject: [PATCH 08/25] propagating the information whether or not the project to build is a Q# command line application --- src/QsCompiler/CommandLineTool/Commands/Build.cs | 1 + src/QsCompiler/CommandLineTool/Commands/Diagnose.cs | 1 + src/QsCompiler/CommandLineTool/Options.cs | 4 ++++ src/QsCompiler/Compiler/CompilationLoader.cs | 6 ++++++ src/QuantumSdk/Sdk/Sdk.targets | 3 ++- 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index af86876beb..940363242d 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -166,6 +166,7 @@ public static int Run(BuildOptions options, ConsoleLogger logger) DocumentationOutputFolder = options.DocFolder, BuildOutputFolder = options.OutputFolder ?? (usesPlugins ? "." : null), DllOutputPath = options.EmitDll ? " " : null, // set to e.g. an empty space to generate the dll in the same location as the .bson file + IsExecutable = options.MakeExecutable, RewriteSteps = options.Plugins?.Select(step => (step, (string)null)) ?? ImmutableArray<(string, string)>.Empty, EnableAdditionalChecks = false, // todo: enable debug mode? ExposeReferencesViaTestNames = options.ExposeReferencesViaTestNames diff --git a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs index 1e68cacd4f..beefde2cbc 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs @@ -222,6 +222,7 @@ public static int Run(DiagnoseOptions options, ConsoleLogger logger) GenerateFunctorSupport = true, SkipSyntaxTreeTrimming = options.TrimLevel == 0, AttemptFullPreEvaluation = options.TrimLevel > 2, + IsExecutable = options.MakeExecutable, RewriteSteps = options.Plugins?.Select(step => (step, (string)null)) ?? ImmutableArray<(string, string)>.Empty, EnableAdditionalChecks = true, ExposeReferencesViaTestNames = options.ExposeReferencesViaTestNames diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs index 51f4bd6d1a..dbd9afa42d 100644 --- a/src/QsCompiler/CommandLineTool/Options.cs +++ b/src/QsCompiler/CommandLineTool/Options.cs @@ -54,6 +54,10 @@ public class CompilationOptions : Options HelpText = "Specifies the classical capabilites of the runtime. Determines what QIR profile to compile to.")] public AssemblyConstants.RuntimeCapabilities RuntimeCapabilites { get; set; } + [Option("build-exe", Required = false, Default = false, SetName = CODE_MODE, + HelpText = "Specifies whether to build a Q# command line application.")] + public bool MakeExecutable { get; set; } + /// /// Returns a dictionary with the specified assembly properties as out parameter. /// Returns a boolean indicating whether all specified properties were successfully added. diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index a4749680cc..e2837477cb 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -111,6 +111,12 @@ public struct Configuration /// public RuntimeCapabilities RuntimeCapabilities; /// + /// Specifies whether the project to build is a Q# command line application. + /// If set to true, a warning will be raised if no entry point is defined. + /// If set to false, then defined entry points will be ignored and a warning will be raised. + /// + public bool IsExecutable; + /// /// Unless this is set to true, all usages of type-parameterized callables are replaced with /// the concrete callable instantiation if an entry point is specified for the compilation. /// Removes all type-parameterizations in the syntax tree. diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index 19d3b7d621..9b8b732f22 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -68,6 +68,7 @@ + <_QscCommandIsExecutableFlag Condition="'$(ResolvedQsharpOutputType)' == 'QsharpExe'">--build-exe <_QscCommandOutputFlag>--output "$(GeneratedFilesOutputPath)" <_QscCommandDocsFlag Condition="$(QsharpDocsGeneration)">--doc "$(QsharpDocsOutputPath)" <_QscCommandInputFlag Condition="@(QsharpCompile->Count()) > 0">--input "@(QsharpCompile,'" "')" @@ -80,7 +81,7 @@ <_QscCommandPredefinedAssemblyProperties Condition="$(DefaultSimulator) != ''">$(_QscCommandPredefinedAssemblyProperties) DefaultSimulator:$(DefaultSimulator) <_QscCommandAssemblyPropertiesFlag>--assembly-properties $(_QscCommandPredefinedAssemblyProperties) $(QscCommandAssemblyProperties) <_QscPackageLoadFallbackFoldersFlag Condition="@(ResolvedPackageLoadFallbackFolders->Count()) > 0">--package-load-fallback-folders "@(ResolvedPackageLoadFallbackFolders,'" "')" - <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandRuntimeFlag) $(_QscCommandTargetPackageFlag) $(_QscPackageLoadFallbackFoldersFlag) $(_QscCommandTestNamesFlag) $(_QscCommandAssemblyPropertiesFlag) $(AdditionalQscArguments) + <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandIsExecutableFlag) $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandRuntimeFlag) $(_QscCommandTargetPackageFlag) $(_QscPackageLoadFallbackFoldersFlag) $(_QscCommandTestNamesFlag) $(_QscCommandAssemblyPropertiesFlag) $(AdditionalQscArguments) <_QscCommandArgsFile>$(QscBuildConfigOutputPath)qsc.rsp $(QscExe) build --format MsBuild $(_VerbosityFlag) --response-files $(_QscCommandArgsFile) From f931edf973c5dcc39a38bdf947e54f2a20416278 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 10 Apr 2020 08:26:34 -0700 Subject: [PATCH 09/25] warning for missing entry point --- src/QsCompiler/Compiler/CompilationLoader.cs | 24 ++++++++++++++++---- src/QsCompiler/DataStructures/Diagnostics.fs | 4 ++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index e2837477cb..3a4e3f2d8e 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -233,7 +233,7 @@ internal ExecutionStatus(IEnumerable externalRewriteSteps) => private bool WasSuccessful(bool run, Status code) => (run && code == Status.Succeeded) || (!run && code == Status.NotRun); - internal bool Success(Configuration options, bool isExe) => + internal bool Success(Configuration options) => this.SourceFileLoading <= 0 && this.ReferenceLoading <= 0 && WasSuccessful(true, this.Validation) && @@ -244,7 +244,7 @@ internal bool Success(Configuration options, bool isExe) => WasSuccessful(!options.SkipSyntaxTreeTrimming, this.TreeTrimming) && WasSuccessful(options.ConvertClassicalControl, this.ConvertClassicalControl) && - WasSuccessful(isExe && !options.SkipMonomorphization, this.Monomorphization) && + WasSuccessful(options.IsExecutable && !options.SkipMonomorphization, this.Monomorphization) && WasSuccessful(options.DocumentationOutputFolder != null, this.Documentation) && WasSuccessful(options.SerializeSyntaxTree, this.Serialization) && WasSuccessful(options.BuildOutputFolder != null, this.BinaryFormat) && @@ -337,7 +337,7 @@ public Status LoadedRewriteStep(string name, string source = null) /// Indicates the overall success of all compilation steps. /// The compilation is indicated as having been successful if all steps that were configured to execute completed successfully. /// - public bool Success => this.CompilationStatus.Success(this.Config, this.CompilationOutput?.EntryPoints.Length != 0); + public bool Success => this.CompilationStatus.Success(this.Config); /// @@ -446,6 +446,14 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference foreach (var diag in this.VerifiedCompilation?.Diagnostics() ?? Enumerable.Empty()) { this.LogAndUpdate(ref this.CompilationStatus.Validation, diag); } + if (this.Config.IsExecutable && this.CompilationOutput?.EntryPoints.Length == 0) + { + this.Logger?.Log(WarningCode.MissingEntryPoint, new string[0]); + } + // TODO: + // give warnings and ignore entry points in libraries, + // and check additional restriction on the return type for execution on quantum processors. + // executing the specified rewrite steps if (!Uri.TryCreate(Assembly.GetExecutingAssembly().CodeBase, UriKind.Absolute, out Uri thisDllUri)) @@ -462,7 +470,7 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference this.CompilationOutput = ExecuteAsAtomicTransformation(rewriteStep, ref this.CompilationStatus.ConvertClassicalControl); } - if (!this.Config.SkipMonomorphization && this.CompilationOutput?.EntryPoints.Length != 0) + if (!this.Config.SkipMonomorphization) { var rewriteStep = new RewriteSteps.LoadedStep(new Monomorphization(), typeof(IRewriteStep), thisDllUri); this.CompilationOutput = ExecuteAsAtomicTransformation(rewriteStep, ref this.CompilationStatus.Monomorphization); @@ -595,6 +603,14 @@ private void LogAndUpdate(ref Status current, ErrorCode code, IEnumerable + /// Logs a warning with the given warning code and message parameters, and updates the status passed as reference accordingly. + /// + private void LogAndUpdate(ref Status current, WarningCode code, IEnumerable args) + { + this.Logger?.Log(code, args); + } + /// /// Logs the given diagnostic and updates the status passed as reference accordingly. /// Adds the given diagnostic to the tracked load diagnostics. diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 5b8c090b91..b0832e6afd 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -326,6 +326,8 @@ type WarningCode = | NamespaceAliasIsAlreadyDefined = 6004 // same alias for the same namespace, hence (only) a warning | MissingBodyDeclaration = 6005 | DuplicateAttribute = 6201 + | MissingEntryPoint = 6202 + | IgnoredEntryPoint = 6203 | GeneratorDirectiveWillBeIgnored = 6301 | UnreachableCode = 6302 @@ -700,6 +702,8 @@ type DiagnosticItem = | WarningCode.NamespaceAliasIsAlreadyDefined -> "A short name for this namespace is already defined." | WarningCode.MissingBodyDeclaration -> "A body specification for this callable is missing. The callable is assumed to be intrinsic." | WarningCode.DuplicateAttribute -> "The attribute {0} is a duplication and will be ignored." + | WarningCode.MissingEntryPoint -> "The project is a Q# command line application but no entry point has been found." + | WarningCode.IgnoredEntryPoint -> "Entry point will be ignored. The project is a Q# library and cannot have any entry points." | WarningCode.GeneratorDirectiveWillBeIgnored -> "Generation directive ignored. A specialization of this callable has been declared as intrinsic." | WarningCode.UnreachableCode -> "This statement will never be executed." From 5cfb4bd22fac631252e0d40a2224aabc44d3ea72 Mon Sep 17 00:00:00 2001 From: bettinaheim <34236215+bettinaheim@users.noreply.github.com> Date: Mon, 13 Apr 2020 08:04:44 -0700 Subject: [PATCH 10/25] Update src/QsCompiler/Compiler/CompilationLoader.cs Co-Authored-By: Sarah Marshall <33814365+samarsha@users.noreply.github.com> --- src/QsCompiler/Compiler/CompilationLoader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 3a4e3f2d8e..cf6464c5b2 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -448,7 +448,8 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference if (this.Config.IsExecutable && this.CompilationOutput?.EntryPoints.Length == 0) { - this.Logger?.Log(WarningCode.MissingEntryPoint, new string[0]); + this.Logger?.Log(WarningCode.MissingEntryPoint, Array.Empty()); + } // TODO: // give warnings and ignore entry points in libraries, From a02469391d4891eabc0e9ec106f1cdb7dc53099d Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 09:13:24 -0700 Subject: [PATCH 11/25] addressing review comments --- src/QsCompiler/Compiler/CompilationLoader.cs | 11 +- src/QsCompiler/Core/SymbolTable.fs | 121 +++++++++++-------- src/QuantumSdk/README.md | 2 +- 3 files changed, 72 insertions(+), 62 deletions(-) diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 3a4e3f2d8e..06cd4624bf 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -243,7 +243,6 @@ internal bool Success(Configuration options) => WasSuccessful(options.AttemptFullPreEvaluation, this.PreEvaluation) && WasSuccessful(!options.SkipSyntaxTreeTrimming, this.TreeTrimming) && WasSuccessful(options.ConvertClassicalControl, this.ConvertClassicalControl) && - WasSuccessful(options.IsExecutable && !options.SkipMonomorphization, this.Monomorphization) && WasSuccessful(options.DocumentationOutputFolder != null, this.Documentation) && WasSuccessful(options.SerializeSyntaxTree, this.Serialization) && @@ -470,7 +469,7 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference this.CompilationOutput = ExecuteAsAtomicTransformation(rewriteStep, ref this.CompilationStatus.ConvertClassicalControl); } - if (!this.Config.SkipMonomorphization) + if (!this.Config.SkipMonomorphization && this.CompilationOutput?.EntryPoints.Length != 0) { var rewriteStep = new RewriteSteps.LoadedStep(new Monomorphization(), typeof(IRewriteStep), thisDllUri); this.CompilationOutput = ExecuteAsAtomicTransformation(rewriteStep, ref this.CompilationStatus.Monomorphization); @@ -603,14 +602,6 @@ private void LogAndUpdate(ref Status current, ErrorCode code, IEnumerable - /// Logs a warning with the given warning code and message parameters, and updates the status passed as reference accordingly. - /// - private void LogAndUpdate(ref Status current, WarningCode code, IEnumerable args) - { - this.Logger?.Log(code, args); - } - /// /// Logs the given diagnostic and updates the status passed as reference accordingly. /// Adds the given diagnostic to the tracked load diagnostics. diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 01fc6f2879..6be59add68 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -918,6 +918,70 @@ and NamespaceManager then [| QsCompilerDiagnostic.Error (code, [udt.Name.Value; parent.Value]) (udt.Range.ValueOr QsCompilerDiagnostic.DefaultRange) |] else [||] + /// Checks whether the given parent and declaration should recognized as an entry point. + /// Verifies the entry point signature and arguments, and generates and returns suitable diagnostics. + /// The given offset and range are used to generate diagnostics and should correspond to location of the entry point attribute. + /// Returns true if the declaration should be recognized as entry point, which may be the case even if errors have been generated. + /// Throws an ArgumentException if the parent namespace does not exist. + let validateEntryPoint (parent : QsQualifiedName) (offset, range) (decl : Resolution<'T,_>) = + let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange + let errs = new List<_>() + + match box decl.Defined with + | :? CallableSignature as signature when not (signature.TypeParameters.Any()) -> + + // verify that the entry point has only a default body specialization + let hasCharacteristics = signature.Characteristics.Characteristics |> function | EmptySet | InvalidSetExpr -> false | _ -> true + match Namespaces.TryGetValue parent.Namespace with + | false, _ -> ArgumentException "no namespace with the given name exists" |> raise + | true, ns when not ((ns.SpecializationsDefinedInAllSources parent.Name).Any(fst >> (<>)QsBody) || hasCharacteristics) -> () + | _ -> errs.Add (decl.Position, signature.Characteristics.Range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.InvalidEntryPointSpecialization, [])) + + // validate entry point argument and return type + let validateArgAndReturnTypes argumentRestriction (qsType : QsType) = + let rec IsArray = function + | ArrayType _ -> true + | TupleType (ts : ImmutableArray) when ts.Length = 1 -> IsArray ts.[0].Type + | _ -> false + qsType.ExtractAll (fun t -> t.Type |> function // ExtractAll recurs on all subtypes (e.g. callable in- and output types as well) + | Qubit -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.QubitTypeInEntryPointSignature, [])) |> Seq.singleton + | QsTypeKind.Operation _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.CallableTypeInEntryPointSignature, [])) |> Seq.singleton + | QsTypeKind.Function _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.CallableTypeInEntryPointSignature, [])) |> Seq.singleton + | UserDefinedType _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UserDefinedTypeInEntryPointSignature, [])) |> Seq.singleton + | TupleType ts when ts.Length > 1 && argumentRestriction -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InnerTupleInEntryPointArgument, [])) |> Seq.singleton + | ArrayType bt when argumentRestriction && bt.Type |> IsArray -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.ArrayOfArrayInEntryPointArgument, [])) |> Seq.singleton + | _ -> Seq.empty) + let inErrs = signature.Argument.Items.Select snd |> Seq.collect (validateArgAndReturnTypes true) + let outErrs = signature.ReturnType |> validateArgAndReturnTypes false + let signatureErrs = inErrs.Concat outErrs + errs.AddRange signatureErrs + + // validate entry point argument names + let asCommandLineArg (str : string) = str.ToLowerInvariant() |> Seq.filter((<>)'_') |> String.Concat + let reservedCommandLineArgs = [CommandLineArguments.SimulatorOption] |> List.map asCommandLineArg + let nameAndRange (sym : QsSymbol) = sym.Symbol |> function + | Symbol name -> Some (asCommandLineArg name.Value, sym.Range) + | _ -> None + let simplifiedArgNames = signature.Argument.Items.Select fst |> Seq.choose nameAndRange |> Seq.toList + let verifyArgument i (arg, range : QsNullable<_>) = + if i > 0 && simplifiedArgNames.[..i-1] |> Seq.map fst |> Seq.contains arg + then errs.Add (decl.Position, range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.DuplicateEntryPointArgumentName, [])) + elif reservedCommandLineArgs.Contains arg + then errs.Add (decl.Position, range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.ReservedEntryPointArgumentName, [])) + simplifiedArgNames |> List.iteri verifyArgument + + // check that there is no more than one entry point + if signatureErrs.Any() then false, errs + else GetEntryPoints() |> Seq.tryHead |> function + | None -> true, errs + | Some (epName, epSource) -> + let msgArgs = [sprintf "%s.%s" epName.Namespace.Value epName.Name.Value; epSource.Value] + errs.Add (offset, range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.MultipleEntryPoints, msgArgs)) + false, errs + | _ -> + errs.Add (offset, range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidEntryPointPlacement, [])) + false, errs + /// Given the name of the namespace as well as the source file in which the attribute occurs, resolves the given /// attribute. @@ -956,7 +1020,9 @@ and NamespaceManager let isBuiltIn (builtIn : BuiltIn) (tId : UserDefinedType) = tId.Namespace.Value = builtIn.FullName.Namespace.Value && tId.Name.Value = builtIn.FullName.Name.Value let attr, msgs = decl.DefinedAttributes |> Seq.map (this.ResolveAttribute (parent.Namespace, source)) |> Seq.toList |> List.unzip + let errs = new List<_>(msgs |> Seq.collect id) + let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange let validateAttributes (alreadyDefined : int list, resAttr) (att : QsDeclarationAttribute) = let returnInvalid msg = errs.AddRange msg @@ -965,7 +1031,6 @@ and NamespaceManager // known attribute | Value tId -> - let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange let attributeHash = if tId |> isBuiltIn BuiltIn.Deprecated then hash (tId.Namespace.Value, tId.Name.Value) elif tId |> isBuiltIn BuiltIn.EnableTestingViaName then hash (tId.Namespace.Value, tId.Name.Value) @@ -979,56 +1044,10 @@ and NamespaceManager // the attribute marks an entry point elif tId |> isBuiltIn BuiltIn.EntryPoint then - match box decl.Defined with - | :? CallableSignature as signature when not (signature.TypeParameters.Any()) -> - - // verify that the entry point has only a default body specialization - let hasCharacteristics = signature.Characteristics.Characteristics |> function | EmptySet | InvalidSetExpr -> false | _ -> true - match Namespaces.TryGetValue parent.Namespace with - | false, _ -> ArgumentException "no namespace with the given name exists" |> raise - | true, ns when not ((ns.SpecializationsDefinedInAllSources parent.Name).Any(fst >> (<>)QsBody) || hasCharacteristics) -> () - | _ -> errs.Add (decl.Position, signature.Characteristics.Range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.InvalidEntryPointSpecialization, [])) - - // validate entry point argument and return type - let validateArgAndReturnTypes argumentRestriction (qsType : QsType) = - let rec IsArray = function - | ArrayType _ -> true - | TupleType (ts : ImmutableArray) when ts.Length = 1 -> IsArray ts.[0].Type - | _ -> false - qsType.ExtractAll (fun t -> t.Type |> function // ExtractAll recurs on all subtypes (e.g. callable in- and output types as well) - | Qubit -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.QubitTypeInEntryPointSignature, [])) |> Seq.singleton - | QsTypeKind.Operation _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.CallableTypeInEntryPointSignature, [])) |> Seq.singleton - | QsTypeKind.Function _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.CallableTypeInEntryPointSignature, [])) |> Seq.singleton - | UserDefinedType _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UserDefinedTypeInEntryPointSignature, [])) |> Seq.singleton - | TupleType ts when ts.Length > 1 && argumentRestriction -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InnerTupleInEntryPointArgument, [])) |> Seq.singleton - | ArrayType bt when argumentRestriction && bt.Type |> IsArray -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.ArrayOfArrayInEntryPointArgument, [])) |> Seq.singleton - | _ -> Seq.empty) - let inErrs = signature.Argument.Items.Select snd |> Seq.collect (validateArgAndReturnTypes true) - let outErrs = signature.ReturnType |> validateArgAndReturnTypes false - let signatureErrs = inErrs.Concat outErrs - - // validate entry point argument names - let asCommandLineArg (str : string) = str.ToLowerInvariant() |> Seq.filter((<>)'_') |> String.Concat - let reservedCommandLineArgs = [CommandLineArguments.SimulatorOption] |> List.map asCommandLineArg - let nameAndRange (sym : QsSymbol) = sym.Symbol |> function - | Symbol name -> Some (asCommandLineArg name.Value, sym.Range) - | _ -> None - let simplifiedArgNames = signature.Argument.Items.Select fst |> Seq.choose nameAndRange |> Seq.toList - let verifyArgument i (arg, range : QsNullable<_>) = - if i > 0 && simplifiedArgNames.[..i-1] |> Seq.map fst |> Seq.contains arg - then errs.Add (decl.Position, range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.DuplicateEntryPointArgumentName, [])) - elif reservedCommandLineArgs.Contains arg - then errs.Add (decl.Position, range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.ReservedEntryPointArgumentName, [])) - simplifiedArgNames |> List.iteri verifyArgument - - // check that there is no more than one entry point - if signatureErrs.Any() then returnInvalid signatureErrs - else GetEntryPoints() |> Seq.tryHead |> function - | None -> attributeHash :: alreadyDefined, att :: resAttr - | Some (epName, epSource) -> - let msgArgs = [sprintf "%s.%s" epName.Namespace.Value epName.Name.Value; epSource.Value] - (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.MultipleEntryPoints, msgArgs)) |> Seq.singleton |> returnInvalid - | _ -> (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidEntryPointPlacement, [])) |> Seq.singleton |> returnInvalid + let register, msgs = validateEntryPoint parent (att.Offset, tId.Range) decl + errs.AddRange msgs + if register then attributeHash :: alreadyDefined, att :: resAttr + else alreadyDefined, {att with TypeId = Null} :: resAttr // the attribute marks a unit test elif tId |> isBuiltIn BuiltIn.Test then diff --git a/src/QuantumSdk/README.md b/src/QuantumSdk/README.md index 6efb0f450e..682fab4c86 100644 --- a/src/QuantumSdk/README.md +++ b/src/QuantumSdk/README.md @@ -110,7 +110,7 @@ May contain additional arguments to pass to the Q# command line compiler. Valid Specifies whether to generate C# code as part of the compilation process. Setting this property to false may prevent certain interoperability features or integration with other pieces of the Quantum Development Kit. - `DefaultSimulator`: -Specifies the simulator to use by default for execution. +Specifies the simulator to use by default for execution. Valid values are QuantumSimulator, ToffoliSimulator, ResourcesEstimator, or the fully qualified name of a custom simulator. - `IncludeQsharpCorePackages`: Specifies whether the packages providing the basic language support for Q# are referenced. This property is set to true by default. If set to false, the Sdk will not reference any Q# libraries. From 08adec52c4fb51305417ebfd82c8e338df2f633c Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 09:55:15 -0700 Subject: [PATCH 12/25] some keywords etc --- src/QsCompiler/Core/Dependencies.fs | 6 ------ src/QsCompiler/Core/SymbolResolution.fs | 3 ++- src/QsCompiler/Core/SymbolTable.fs | 16 ++++++++------- src/QsCompiler/DataStructures/Diagnostics.fs | 2 +- .../DataStructures/ReservedKeywords.fs | 20 +++++++++++++------ 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index 1a01f29fd2..eebb9436fd 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -32,12 +32,6 @@ type BuiltIn = { /// Returns the set of namespaces that is automatically opened for each compilation. static member NamespacesToAutoOpen = ImmutableHashSet.Create (BuiltIn.CoreNamespace) - /// Returns all valid targets for executing Q# code. - static member ValidExecutionTargets = - // Note: If this is adapted, then the error message for InvalidExecutionTargetForTest needs to be adapted as well. - ["QuantumSimulator"; "ToffoliSimulator"; "ResourcesEstimator"] - |> ImmutableHashSet.CreateRange - /// Returns true if the given attribute marks the corresponding declaration as entry point. static member MarksEntryPoint (att : QsDeclarationAttribute) = att.TypeId |> function | Value tId -> tId.Namespace.Value = BuiltIn.EntryPoint.FullName.Namespace.Value && tId.Name.Value = BuiltIn.EntryPoint.FullName.Name.Value diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index 59080a6be3..9102decd17 100644 --- a/src/QsCompiler/Core/SymbolResolution.fs +++ b/src/QsCompiler/Core/SymbolResolution.fs @@ -9,6 +9,7 @@ open System.Collections.Immutable open System.Linq open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics +open Microsoft.Quantum.QsCompiler.ReservedKeywords open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTokens open Microsoft.Quantum.QsCompiler.SyntaxTree @@ -231,7 +232,7 @@ module SymbolResolution = /// The returned sequence is empty if the declaration does not contain a unit test. let TryFindTestTargets attributes = let getTarget (att : QsDeclarationAttribute) = if att |> BuiltIn.MarksTestOperation then Some att.Argument else None - let validTargets = BuiltIn.ValidExecutionTargets.ToImmutableDictionary(fun t -> t.ToLowerInvariant()) + let validTargets = CommandLineArguments.BuiltInSimulators.ToImmutableDictionary(fun t -> t.ToLowerInvariant()) let targetName (target : string) = if target = null then null elif SyntaxGenerator.FullyQualifiedName.IsMatch target then target diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 6be59add68..d6f48ef7cf 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -934,11 +934,13 @@ and NamespaceManager let hasCharacteristics = signature.Characteristics.Characteristics |> function | EmptySet | InvalidSetExpr -> false | _ -> true match Namespaces.TryGetValue parent.Namespace with | false, _ -> ArgumentException "no namespace with the given name exists" |> raise - | true, ns when not ((ns.SpecializationsDefinedInAllSources parent.Name).Any(fst >> (<>)QsBody) || hasCharacteristics) -> () - | _ -> errs.Add (decl.Position, signature.Characteristics.Range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.InvalidEntryPointSpecialization, [])) + | true, ns -> + let specializations = ns.SpecializationsDefinedInAllSources parent.Name + if hasCharacteristics || specializations.Any(fst >> (<>)QsBody) then + errs.Add (decl.Position, signature.Characteristics.Range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.InvalidEntryPointSpecialization, [])) // validate entry point argument and return type - let validateArgAndReturnTypes argumentRestriction (qsType : QsType) = + let validateArgAndReturnTypes isArg (qsType : QsType) = let rec IsArray = function | ArrayType _ -> true | TupleType (ts : ImmutableArray) when ts.Length = 1 -> IsArray ts.[0].Type @@ -948,8 +950,8 @@ and NamespaceManager | QsTypeKind.Operation _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.CallableTypeInEntryPointSignature, [])) |> Seq.singleton | QsTypeKind.Function _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.CallableTypeInEntryPointSignature, [])) |> Seq.singleton | UserDefinedType _ -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UserDefinedTypeInEntryPointSignature, [])) |> Seq.singleton - | TupleType ts when ts.Length > 1 && argumentRestriction -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InnerTupleInEntryPointArgument, [])) |> Seq.singleton - | ArrayType bt when argumentRestriction && bt.Type |> IsArray -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.ArrayOfArrayInEntryPointArgument, [])) |> Seq.singleton + | TupleType ts when ts.Length > 1 && isArg -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InnerTupleInEntryPointArgument, [])) |> Seq.singleton + | ArrayType bt when isArg && bt.Type |> IsArray -> (decl.Position, t.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.ArrayOfArrayInEntryPointArgument, [])) |> Seq.singleton | _ -> Seq.empty) let inErrs = signature.Argument.Items.Select snd |> Seq.collect (validateArgAndReturnTypes true) let outErrs = signature.ReturnType |> validateArgAndReturnTypes false @@ -958,7 +960,7 @@ and NamespaceManager // validate entry point argument names let asCommandLineArg (str : string) = str.ToLowerInvariant() |> Seq.filter((<>)'_') |> String.Concat - let reservedCommandLineArgs = [CommandLineArguments.SimulatorOption] |> List.map asCommandLineArg + let reservedCommandLineArgs = CommandLineArguments.ReservedArguments |> Seq.map asCommandLineArg |> Seq.toArray let nameAndRange (sym : QsSymbol) = sym.Symbol |> function | Symbol name -> Some (asCommandLineArg name.Value, sym.Range) | _ -> None @@ -1064,7 +1066,7 @@ and NamespaceManager match box decl.Defined with | :? CallableSignature as signature when signature |> isUnitToUnit && not (signature.TypeParameters.Any()) -> let arg = att.Argument |> AttributeAnnotation.NonInterpolatedStringArgument (fun ex -> ex.Expression) - let validExecutionTargets = BuiltIn.ValidExecutionTargets |> Seq.map (fun x -> x.ToLowerInvariant()) + let validExecutionTargets = CommandLineArguments.BuiltInSimulators |> Seq.map (fun x -> x.ToLowerInvariant()) if arg <> null && (validExecutionTargets |> Seq.contains (arg.ToLowerInvariant()) || SyntaxGenerator.FullyQualifiedName.IsMatch arg) then attributeHash :: alreadyDefined, att :: resAttr else (att.Offset, att.Argument.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidExecutionTargetForTest, [])) |> Seq.singleton |> returnInvalid diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index b0832e6afd..224ef65e6d 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -702,7 +702,7 @@ type DiagnosticItem = | WarningCode.NamespaceAliasIsAlreadyDefined -> "A short name for this namespace is already defined." | WarningCode.MissingBodyDeclaration -> "A body specification for this callable is missing. The callable is assumed to be intrinsic." | WarningCode.DuplicateAttribute -> "The attribute {0} is a duplication and will be ignored." - | WarningCode.MissingEntryPoint -> "The project is a Q# command line application but no entry point has been found." + | WarningCode.MissingEntryPoint -> "The project is a Q# command line application but no entry point has been found. The project should be a library, and any C# driver code should be defined in a separate project." | WarningCode.IgnoredEntryPoint -> "Entry point will be ignored. The project is a Q# library and cannot have any entry points." | WarningCode.GeneratorDirectiveWillBeIgnored -> "Generation directive ignored. A specialization of this callable has been declared as intrinsic." | WarningCode.UnreachableCode -> "This statement will never be executed." diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index a6cc7db278..05b983d8fd 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -230,12 +230,6 @@ module GeneratedAttributes = let Namespace = "Microsoft.Quantum.QsCompiler.Metadata.Attributes" let LoadedViaTestNameInsteadOf = "__LoadedViaTestNameInsteadOf__" - -/// contains reserved names for command line arguments -module CommandLineArguments = - let SimulatorOption = "simulator" - - /// contains project specific settings specified during Q# compilation module AssemblyConstants = let OutputPath = "OutputPath" @@ -245,6 +239,9 @@ module AssemblyConstants = let BrunoProcessor = "BrunoProcessor" let ClementineProcessor = "ClementineProcessor" let DefaultSimulator = "DefaultSimulator" + let QuantumSimulator = "QuantumSimulator" + let ToffoliSimulator = "ToffoliSimulator" + let ResourcesEstimator = "ResourcesEstimator" // Note: The names of the capabilities here need to match the ones defined by the Sdk. type RuntimeCapabilities = @@ -253,6 +250,17 @@ module AssemblyConstants = | QPRGen1 = 2 +/// contains reserved names for command line arguments +module CommandLineArguments = + let SimulatorOption = "simulator" + let ReservedArguments = ImmutableArray.Create SimulatorOption + + let BuiltInSimulators = + // Note: If this is adapted, then the error message for InvalidExecutionTargetForTest needs to be adapted as well. + [AssemblyConstants.QuantumSimulator; AssemblyConstants.ToffoliSimulator; AssemblyConstants.ResourcesEstimator] + |> ImmutableHashSet.CreateRange + + /// contains specific names used within Q# dlls module DotnetCoreDll = let ResourceName = "__qsharp_data__.bson" From db9a5a2859e9349c42ba19b6260c8ad6f413169e Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 10:13:58 -0700 Subject: [PATCH 13/25] warning for clashes with reserved arg names --- src/QsCompiler/Core/SymbolTable.fs | 6 +++--- src/QsCompiler/DataStructures/Diagnostics.fs | 14 +++++++------- src/QsCompiler/DataStructures/ReservedKeywords.fs | 1 + src/QsCompiler/Tests.Compiler/LinkingTests.fs | 3 ++- .../LinkingTests/EntryPointDiagnostics.qs | 7 +++++++ 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index d6f48ef7cf..dcbdcc8551 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -959,7 +959,7 @@ and NamespaceManager errs.AddRange signatureErrs // validate entry point argument names - let asCommandLineArg (str : string) = str.ToLowerInvariant() |> Seq.filter((<>)'_') |> String.Concat + let asCommandLineArg (str : string) = str.ToLowerInvariant() |> String.filter((<>)'_') let reservedCommandLineArgs = CommandLineArguments.ReservedArguments |> Seq.map asCommandLineArg |> Seq.toArray let nameAndRange (sym : QsSymbol) = sym.Symbol |> function | Symbol name -> Some (asCommandLineArg name.Value, sym.Range) @@ -968,8 +968,8 @@ and NamespaceManager let verifyArgument i (arg, range : QsNullable<_>) = if i > 0 && simplifiedArgNames.[..i-1] |> Seq.map fst |> Seq.contains arg then errs.Add (decl.Position, range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.DuplicateEntryPointArgumentName, [])) - elif reservedCommandLineArgs.Contains arg - then errs.Add (decl.Position, range.ValueOr decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.ReservedEntryPointArgumentName, [])) + elif reservedCommandLineArgs.Contains arg || (arg.Length = 1 && CommandLineArguments.ReservedArgumentAbbreviations.Contains arg.[0]) + then errs.Add (decl.Position, range.ValueOr decl.Range |> QsCompilerDiagnostic.Warning (WarningCode.ReservedEntryPointArgumentName, [])) simplifiedArgNames |> List.iteri verifyArgument // check that there is no more than one entry point diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 224ef65e6d..9c2df007e8 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -239,12 +239,11 @@ type ErrorCode = | MultipleEntryPoints = 6236 | InvalidEntryPointSpecialization = 6237 | DuplicateEntryPointArgumentName = 6238 - | ReservedEntryPointArgumentName = 6239 - | InvalidTestAttributePlacement = 6240 - | InvalidExecutionTargetForTest = 6241 - | ExpectingFullNameAsAttributeArgument = 6242 - | AttributeInvalidOnSpecialization = 6243 - | AttributeInvalidOnCallable = 6244 + | InvalidTestAttributePlacement = 6239 + | InvalidExecutionTargetForTest = 6240 + | ExpectingFullNameAsAttributeArgument = 6241 + | AttributeInvalidOnSpecialization = 6242 + | AttributeInvalidOnCallable = 6243 | TypeMismatchInReturn = 6301 | TypeMismatchInValueUpdate = 6302 @@ -328,6 +327,7 @@ type WarningCode = | DuplicateAttribute = 6201 | MissingEntryPoint = 6202 | IgnoredEntryPoint = 6203 + | ReservedEntryPointArgumentName = 6204 | GeneratorDirectiveWillBeIgnored = 6301 | UnreachableCode = 6302 @@ -612,7 +612,6 @@ type DiagnosticItem = | ErrorCode.MultipleEntryPoints -> "Invalid entry point. An entry point {0} already exists in {1}." | ErrorCode.InvalidEntryPointSpecialization -> "Entry points cannot have any other specializations besides the default body." | ErrorCode.DuplicateEntryPointArgumentName -> "Invalid name for entry point argument. A similar argument name is already in use." - | ErrorCode.ReservedEntryPointArgumentName -> "Invalid name for entry point argument. The name has been reserved and cannot be used." | ErrorCode.InvalidTestAttributePlacement -> "Invalid test attribute. Test attributes may only occur on callables that have no arguments and return Unit." | ErrorCode.InvalidExecutionTargetForTest -> "Invalid execution target. Currently, valid execution targets for tests are the QuantumSimulator, the ToffoliSimulator, or the ResourcesEstimator." | ErrorCode.ExpectingFullNameAsAttributeArgument -> "Invalid attribute argument. Expecting a fully qualified name as argument to the {0} attribute." @@ -704,6 +703,7 @@ type DiagnosticItem = | WarningCode.DuplicateAttribute -> "The attribute {0} is a duplication and will be ignored." | WarningCode.MissingEntryPoint -> "The project is a Q# command line application but no entry point has been found. The project should be a library, and any C# driver code should be defined in a separate project." | WarningCode.IgnoredEntryPoint -> "Entry point will be ignored. The project is a Q# library and cannot have any entry points." + | WarningCode.ReservedEntryPointArgumentName -> "The argument name conflicts with a default argument for a Q# command line application." | WarningCode.GeneratorDirectiveWillBeIgnored -> "Generation directive ignored. A specialization of this callable has been declared as intrinsic." | WarningCode.UnreachableCode -> "This statement will never be executed." diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index 05b983d8fd..f086e7d553 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -254,6 +254,7 @@ module AssemblyConstants = module CommandLineArguments = let SimulatorOption = "simulator" let ReservedArguments = ImmutableArray.Create SimulatorOption + let ReservedArgumentAbbreviations = ImmutableArray.Create SimulatorOption.[0] let BuiltInSimulators = // Note: If this is adapted, then the error message for InvalidExecutionTargetForTest needs to be adapted as well. diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index eae78bea7b..191b4891f7 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -252,7 +252,8 @@ type LinkingTests (output:ITestOutputHelper) = this.CompileAndVerify tests.[0] [Error ErrorCode.DuplicateEntryPointArgumentName] this.CompileAndVerify tests.[1] [Error ErrorCode.DuplicateEntryPointArgumentName] this.CompileAndVerify tests.[2] [Error ErrorCode.DuplicateEntryPointArgumentName] - this.CompileAndVerify tests.[3] [Error ErrorCode.ReservedEntryPointArgumentName] + this.CompileAndVerify tests.[3] [Warning WarningCode.ReservedEntryPointArgumentName] + this.CompileAndVerify tests.[4] [Warning WarningCode.ReservedEntryPointArgumentName] [] diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs index 28f6d0eb6c..7d712670a0 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs @@ -34,3 +34,10 @@ namespace Microsoft.Quantum.Testing.EntryPoints { operation InvalidEntryPoint44(simulator : Int) : Unit {} } +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation InvalidEntryPoint45(s : Int) : Unit {} +} From bfcf9c815497433a7b2712fc69926cb910b5f4de Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 17:37:13 -0700 Subject: [PATCH 14/25] removing a comment --- src/QsCompiler/DataStructures/ReservedKeywords.fs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index f086e7d553..b636ebd09e 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -257,7 +257,6 @@ module CommandLineArguments = let ReservedArgumentAbbreviations = ImmutableArray.Create SimulatorOption.[0] let BuiltInSimulators = - // Note: If this is adapted, then the error message for InvalidExecutionTargetForTest needs to be adapted as well. [AssemblyConstants.QuantumSimulator; AssemblyConstants.ToffoliSimulator; AssemblyConstants.ResourcesEstimator] |> ImmutableHashSet.CreateRange From 9278c8b1c7a7827abc9398826bb8878ca902f922 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 17:45:28 -0700 Subject: [PATCH 15/25] comment --- src/QsCompiler/DataStructures/ReservedKeywords.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index b636ebd09e..e3be83ee00 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -250,7 +250,7 @@ module AssemblyConstants = | QPRGen1 = 2 -/// contains reserved names for command line arguments +/// contains reserved names for command line arguments of Q# projects module CommandLineArguments = let SimulatorOption = "simulator" let ReservedArguments = ImmutableArray.Create SimulatorOption From f579682c81b78429ab0820fa5a7dbc542c205c61 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 20:11:53 -0700 Subject: [PATCH 16/25] dragging project information into the comilation manager --- .../CompilationManager/CompilationUnit.cs | 8 +-- .../CompilationUnitManager.cs | 7 ++- .../CompilationManager/ProjectManager.cs | 60 ++++++++++++------- src/QsCompiler/LanguageServer/EditorState.cs | 10 +++- .../DefaultItems/DefaultItems.targets | 8 +-- src/QuantumSdk/Sdk/Sdk.targets | 2 +- 6 files changed, 61 insertions(+), 34 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 9aaf1e6ea5..b4cc72500f 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -139,7 +139,7 @@ static QsDeclarationAttribute Renamed(QsQualifiedName originalName, Tuple, Headers> Declarations; public static References Empty = - new References(ImmutableDictionary, Headers>.Empty); + new References(ImmutableDictionary, Headers>.Empty, false); /// /// Combines the current references with the given references, and verifies that there are no conflicts. @@ -152,7 +152,7 @@ internal References CombineWith(References other, Action on { if (other == null) throw new ArgumentNullException(nameof(other)); if (this.Declarations.Keys.Intersect(other.Declarations.Keys).Any()) throw new ArgumentException("common references exist"); - return new References (this.Declarations.AddRange(other.Declarations), onError: onError); + return new References (this.Declarations.AddRange(other.Declarations), false, onError: onError); } /// @@ -163,7 +163,7 @@ internal References CombineWith(References other, Action on /// Throws an ArgumentNullException if the given diagnostics are null. /// internal References Remove(NonNullable source, Action onError = null) => - new References(this.Declarations.Remove(source), onError: onError); + new References(this.Declarations.Remove(source), false, onError: onError); /// /// Given a dictionary that maps the ids of dll files to the corresponding headers, @@ -174,7 +174,7 @@ internal References Remove(NonNullable source, Action - public References(ImmutableDictionary, Headers> refs, bool loadTestNames = false, Action onError = null) + public References(ImmutableDictionary, Headers> refs, bool loadTestNames, Action onError = null) { this.Declarations = refs ?? throw new ArgumentNullException(nameof(refs)); if (loadTestNames) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index d25e0b5693..0495b5df31 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -58,11 +58,12 @@ public class CompilationUnitManager : IDisposable protected readonly ProcessingQueue Processing; /// - /// Initializes a CompilationUnitManager instance. - /// If an for publishing diagnostics is given and is not null, + /// Initializes a CompilationUnitManager instance for a project with the given properties. + /// If an for publishing diagnostics is given and is not null, /// that action is called whenever diagnostics within a file have changed and are ready for publishing. /// - public CompilationUnitManager(Action exceptionLogger = null, Action publishDiagnostics = null, bool syntaxCheckOnly = false) + public CompilationUnitManager(ProjectInformation.Properties projectProperties, + Action exceptionLogger = null, Action publishDiagnostics = null, bool syntaxCheckOnly = false) { this.EnableVerification = !syntaxCheckOnly; this.CompilationUnit = new CompilationUnit(); diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index d42cb675f0..d125aad86b 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.Diagnostics; +using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -18,24 +19,41 @@ namespace Microsoft.Quantum.QsCompiler.CompilationBuilder { public class ProjectInformation { + public class Properties + { + public readonly string Version; + public readonly string OutputPath; + public readonly AssemblyConstants.RuntimeCapabilities RuntimeCapabilities; + public readonly bool IsExecutable; + public readonly bool ExposeReferencesViaTestNames; + + internal static Properties Default => + new Properties("Latest", "", AssemblyConstants.RuntimeCapabilities.Unknown, false, false); + + public Properties(string version, string outputPath, AssemblyConstants.RuntimeCapabilities runtimeCapabilities, bool isExecutable, bool loadTestNames) + { + this.Version = version ?? ""; + this.OutputPath = outputPath ?? throw new ArgumentNullException(nameof(outputPath)); + this.RuntimeCapabilities = runtimeCapabilities; + this.IsExecutable = isExecutable; + this.ExposeReferencesViaTestNames = loadTestNames; + } + } + public delegate bool Loader(Uri projectFile, out ProjectInformation projectInfo); - public readonly string Version; - public readonly string OutputPath; - public readonly bool ExposeReferencesViaTestNames; + public readonly Properties ProjectProperties; public readonly ImmutableArray SourceFiles; public readonly ImmutableArray ProjectReferences; public readonly ImmutableArray References; - internal static ProjectInformation Empty(string version, string outputPath) => - new ProjectInformation(version, outputPath, false, Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()); + internal static ProjectInformation Empty(string version, string outputPath, AssemblyConstants.RuntimeCapabilities runtimeCapabilities) => + new ProjectInformation(version, outputPath, runtimeCapabilities, false, false, Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()); - public ProjectInformation(string version, string outputPath, bool loadTestNames, + public ProjectInformation(string version, string outputPath, AssemblyConstants.RuntimeCapabilities runtimeCapabilities, bool isExecutable, bool loadTestNames, IEnumerable sourceFiles, IEnumerable projectReferences, IEnumerable references) { - this.Version = version ?? ""; - this.OutputPath = outputPath ?? throw new ArgumentNullException(nameof(outputPath)); - this.ExposeReferencesViaTestNames = loadTestNames; + this.ProjectProperties = new Properties(version, outputPath, runtimeCapabilities, isExecutable, loadTestNames); this.SourceFiles = sourceFiles?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(sourceFiles)); this.ProjectReferences = projectReferences?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(projectReferences)); this.References = references?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(references)); @@ -48,7 +66,7 @@ private class Project : IDisposable { public readonly Uri ProjectFile; public Uri OutputPath { get; private set; } - private bool ExposeReferencesViaTestNames; + public ProjectInformation.Properties Properties { get; private set; } private bool IsLoaded; /// @@ -120,14 +138,14 @@ internal Project(Uri projectFile, ProjectInformation projectInfo, this.ProjectFile = projectFile ?? throw new ArgumentNullException(nameof(projectFile)); this.SetProjectInformation(projectInfo ?? throw new ArgumentNullException(nameof(projectInfo))); - var version = Version.TryParse(projectInfo.Version, out Version v) ? v : null; - if (projectInfo.Version.Equals("Latest", StringComparison.InvariantCultureIgnoreCase)) version = new Version(0, 3); + var version = Version.TryParse(projectInfo.ProjectProperties.Version, out Version v) ? v : null; + if (projectInfo.ProjectProperties.Version.Equals("Latest", StringComparison.InvariantCultureIgnoreCase)) version = new Version(0, 3); var ignore = version == null || version < new Version(0, 3) ? true : false; // We track the file contents for unsupported projects in case the files are migrated to newer projects while editing, // but we don't do any semantic verification, and we don't publish diagnostics for them. this.Processing = new ProcessingQueue(onException); - this.Manager = new CompilationUnitManager(onException, ignore ? null : publishDiagnostics, syntaxCheckOnly: ignore); + this.Manager = new CompilationUnitManager(this.Properties, onException, ignore ? null : publishDiagnostics, syntaxCheckOnly: ignore); this.Log = log ?? ((msg, severity) => Console.WriteLine($"{severity}: {msg}")); this.LoadedSourceFiles = ImmutableHashSet.Empty; @@ -144,10 +162,10 @@ internal Project(Uri projectFile, ProjectInformation projectInfo, private void SetProjectInformation(ProjectInformation projectInfo) { if (projectInfo == null) throw new ArgumentNullException(nameof(projectInfo)); - this.ExposeReferencesViaTestNames = projectInfo.ExposeReferencesViaTestNames; + this.Properties = projectInfo.ProjectProperties; this.IsLoaded = false; - var outputPath = projectInfo.OutputPath; + var outputPath = projectInfo.ProjectProperties.OutputPath; try { outputPath = Path.GetFullPath(outputPath); } catch { outputPath = null; } var outputUri = Uri.TryCreate(outputPath, UriKind.Absolute, out Uri uri) ? uri : null; @@ -261,7 +279,7 @@ private Task LoadProjectReferencesAsync( projectReferences, GetProjectOutputPath(projectOutputPaths, diagnostics), diagnostics.Add, this.Manager.LogException); - this.LoadedProjectReferences = new References(loadedHeaders); + this.LoadedProjectReferences = new References(loadedHeaders, this.Properties.ExposeReferencesViaTestNames); var importedDeclarations = this.LoadedReferences.CombineWith(this.LoadedProjectReferences, (code,args) => diagnostics.Add(Errors.LoadError(code, args, MessageSource(this.ProjectFile)))); this.ProjectReferenceDiagnostics = diagnostics.ToImmutableArray(); @@ -288,7 +306,7 @@ private void ReloadProjectReference(IDictionary projectOutputPaths, Ur var loadedHeaders = ProjectManager.LoadProjectReferences( new string[] { projectReference.LocalPath }, GetProjectOutputPath(projectOutputPaths, diagnostics), diagnostics.Add, this.Manager.LogException); - var loaded = new References(loadedHeaders); + var loaded = new References(loadedHeaders, this.Properties.ExposeReferencesViaTestNames); QsCompilerError.Verify(!loaded.Declarations.Any() || (loaded.Declarations.Count == 1 && loaded.Declarations.First().Key.Value == projRefId.Value), @@ -320,7 +338,7 @@ private Task LoadReferencedAssembliesAsync(IEnumerable references, bool var loadedHeaders = ProjectManager.LoadReferencedAssemblies(references, diagnostics.Add, this.Manager.LogException); - this.LoadedReferences = new References(loadedHeaders); + this.LoadedReferences = new References(loadedHeaders, this.Properties.ExposeReferencesViaTestNames); var importedDeclarations = this.LoadedReferences.CombineWith(this.LoadedProjectReferences, (code, args) => diagnostics.Add(Errors.LoadError(code, args, MessageSource(this.ProjectFile)))); this.ReferenceDiagnostics = diagnostics.ToImmutableArray(); @@ -342,7 +360,7 @@ private void ReloadReferencedAssembly(Uri reference) var diagnostics = new List(); var loadedHeaders = ProjectManager.LoadReferencedAssemblies(new string[] { reference.LocalPath }, diagnostics.Add, this.Manager.LogException); - var loaded = new References(loadedHeaders); + var loaded = new References(loadedHeaders, this.Properties.ExposeReferencesViaTestNames); QsCompilerError.Verify(!loaded.Declarations.Any() || (loaded.Declarations.Count == 1 && loaded.Declarations.First().Key.Value == refId.Value), @@ -568,7 +586,7 @@ public ProjectManager(Action exceptionLogger, Action(); - this.DefaultManager = new CompilationUnitManager(exceptionLogger, publishDiagnostics, syntaxCheckOnly: true); + this.DefaultManager = new CompilationUnitManager(ProjectInformation.Properties.Default, exceptionLogger, publishDiagnostics, syntaxCheckOnly: true); this.PublishDiagnostics = publishDiagnostics; this.LogException = exceptionLogger; this.Log = log; @@ -687,7 +705,7 @@ public Task ProjectChangedOnDiskAsync(Uri projectFile, ProjectInformation.Loader if (!loaded) { existing?.LoadProjectAsync(ImmutableDictionary.Empty, null, MigrateToDefaultManager(openInEditor), - ProjectInformation.Empty("Latest", existing.OutputPath.LocalPath))?.Wait(); // does need to block, or the call to the DefaultManager in ManagerTaskAsync needs to be adapted + ProjectInformation.Empty("Latest", existing.OutputPath.LocalPath, AssemblyConstants.RuntimeCapabilities.Unknown))?.Wait(); // does need to block, or the call to the DefaultManager in ManagerTaskAsync needs to be adapted if (existing != null) this.ProjectReferenceChangedOnDiskChangeAsync(projectFile); return; } diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index 3a7e9decf6..e214fd4512 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.Build.Execution; using Microsoft.Quantum.QsCompiler.CompilationBuilder; +using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -104,15 +105,22 @@ internal bool QsProjectLoader(Uri projectFile, out ProjectInformation info) var targetFile = projectInstance.GetPropertyValue("TargetFileName"); var outputPath = Path.Combine(projectInstance.Directory, outputDir, targetFile); + var resRuntimeCapability = projectInstance.GetPropertyValue("ResolvedRuntimeCapabilities"); + var runtimeCapabilities = Enum.TryParse(resRuntimeCapability, out AssemblyConstants.RuntimeCapabilities capability) + ? capability + : AssemblyConstants.RuntimeCapabilities.Unknown; + var sourceFiles = GetItemsByType(projectInstance, "QsharpCompile"); var projectReferences = GetItemsByType(projectInstance, "ProjectReference"); var references = GetItemsByType(projectInstance, "Reference"); + var version = projectInstance.GetPropertyValue("QsharpLangVersion"); + var isExecutable = "QsharpExe".Equals(projectInstance.GetPropertyValue("ResolvedQsharpOutputType"), StringComparison.InvariantCultureIgnoreCase); var loadTestNames = "true".Equals(projectInstance.GetPropertyValue("ExposeReferencesViaTestNames"), StringComparison.InvariantCultureIgnoreCase); var telemetryMeas = new Dictionary { ["sources"] = sourceFiles.Count() }; this.SendTelemetry("project-load", telemetryProps, telemetryMeas); // does not send anything unless the corresponding flag is defined upon compilation - info = new ProjectInformation(version, outputPath, loadTestNames, sourceFiles, projectReferences, references); + info = new ProjectInformation(version, outputPath, runtimeCapabilities, isExecutable, loadTestNames, sourceFiles, projectReferences, references); return true; } diff --git a/src/QuantumSdk/DefaultItems/DefaultItems.targets b/src/QuantumSdk/DefaultItems/DefaultItems.targets index 4f8243ac80..aaed1d27ab 100644 --- a/src/QuantumSdk/DefaultItems/DefaultItems.targets +++ b/src/QuantumSdk/DefaultItems/DefaultItems.targets @@ -46,10 +46,10 @@ - QPRGen1 - QPRGen0 - QPRGen1 - Unknown + QPRGen1 + QPRGen0 + QPRGen1 + Unknown diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index 9b8b732f22..ba633b3e5a 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -74,7 +74,7 @@ <_QscCommandInputFlag Condition="@(QsharpCompile->Count()) > 0">--input "@(QsharpCompile,'" "')" <_QscCommandReferencesFlag Condition="@(ResolvedQsharpReferences->Count()) > 0">--references "@(ResolvedQsharpReferences,'" "')" <_QscCommandLoadFlag Condition="@(_PrioritizedResolvedQscReferences->Count()) > 0">--load "@(_PrioritizedResolvedQscReferences,'" "')" - <_QscCommandRuntimeFlag>--runtime $(ResolvedRuntimeCapabilites) + <_QscCommandRuntimeFlag>--runtime $(ResolvedRuntimeCapabilities) <_QscCommandTargetPackageFlag Condition="'$(ResolvedTargetPackage)' != ''">--target-package "$(ResolvedTargetPackage)" <_QscCommandTestNamesFlag Condition="$(ExposeReferencesViaTestNames)">--load-test-names <_QscCommandPredefinedAssemblyProperties>ResolvedExecutionTarget:$(ResolvedQsharpExecutionTarget) From b4364016e3976c0f8a1fe02e4077736a0d557453 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 20:12:15 -0700 Subject: [PATCH 17/25] first draft, not actually using the info yet --- .../CompilationManager/CompilationUnit.cs | 2 +- src/QsCompiler/Compiler/CompilationLoader.cs | 5 ++- .../Tests.Compiler/ClassicalControlTests.fs | 2 +- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 2 +- .../Tests.Compiler/OptimizationTests.fs | 2 +- .../TestUtils/SetupVerificationTests.fs | 2 +- .../Tests.Compiler/TransformationTests.fs | 2 +- .../ProjectLoaderTests.cs | 36 +++++++++---------- 8 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index b4cc72500f..bd76c5e7b7 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -174,7 +174,7 @@ internal References Remove(NonNullable source, Action - public References(ImmutableDictionary, Headers> refs, bool loadTestNames, Action onError = null) + public References(ImmutableDictionary, Headers> refs, bool loadTestNames = false, Action onError = null) { this.Declarations = refs ?? throw new ArgumentNullException(nameof(refs)); if (loadTestNames) diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 71e3c7172a..1d2eae0ac1 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -435,7 +435,10 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference RaiseCompilationTaskStart("OverallCompilation", "Build"); this.CompilationStatus.Validation = Status.Succeeded; var files = CompilationUnitManager.InitializeFileManagers(sourceFiles, null, this.OnCompilerException); // do *not* live track (i.e. use publishing) here! - var compilationManager = new CompilationUnitManager(this.OnCompilerException); + + // FIXME: INSTEAD OF PASSING PROJECT PROPERTIES, IT'S PROBABLY BETTER TO JUST PASS THE TWO CONFIGS NEEDED ... + var projectProps = new ProjectInformation.Properties("Latest", this.DllOutputPath ?? "", this.Config.RuntimeCapabilities, this.Config.IsExecutable, this.Config.ExposeReferencesViaTestNames); + var compilationManager = new CompilationUnitManager(projectProps, this.OnCompilerException); compilationManager.UpdateReferencesAsync(references); compilationManager.AddOrUpdateSourceFilesAsync(files); this.VerifiedCompilation = compilationManager.Build(); diff --git a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index 454ff162a2..9b2e939056 100644 --- a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -18,7 +18,7 @@ open Xunit type ClassicalControlTests () = - let compilationManager = new CompilationUnitManager(new Action (fun ex -> failwith ex.Message)) + let compilationManager = new CompilationUnitManager(ProjectInformation.Properties.Default, 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) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 191b4891f7..8d066ade0a 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -26,7 +26,7 @@ open Xunit.Abstractions type LinkingTests (output:ITestOutputHelper) = inherit CompilerTests(CompilerTests.Compile (Path.Combine ("TestCases", "LinkingTests" )) ["Core.qs"; "InvalidEntryPoints.qs"] [], output) - let compilationManager = new CompilationUnitManager(new Action (fun ex -> failwith ex.Message)) + let compilationManager = new CompilationUnitManager(ProjectInformation.Properties.Default, new Action (fun ex -> failwith ex.Message)) // The file name needs to end in ".qs" so that it isn't ignored by the References.Headers class during the internal renaming tests. let getTempFile () = Path.GetRandomFileName () + ".qs" |> Path.GetFullPath |> Uri diff --git a/src/QsCompiler/Tests.Compiler/OptimizationTests.fs b/src/QsCompiler/Tests.Compiler/OptimizationTests.fs index f1b296247e..773c8c4684 100644 --- a/src/QsCompiler/Tests.Compiler/OptimizationTests.fs +++ b/src/QsCompiler/Tests.Compiler/OptimizationTests.fs @@ -15,7 +15,7 @@ open Xunit /// Given a string of valid Q# code, outputs the AST and the callables dictionary let private buildCompilation code = let fileId = new Uri(Path.GetFullPath "test-file.qs") - let compilationUnit = new CompilationUnitManager(fun ex -> failwith ex.Message) + let compilationUnit = new CompilationUnitManager(ProjectInformation.Properties.Default, fun ex -> failwith ex.Message) let file = CompilationUnitManager.InitializeFileManager(fileId, code) compilationUnit.AddOrUpdateSourceFileAsync file |> ignore // spawns a task that modifies the current compilation let mutable compilation = compilationUnit.Build().BuiltCompilation // will wait for any current tasks to finish diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs index eb03d7079a..4c77ead28d 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs @@ -92,7 +92,7 @@ type CompilerTests (compilation : CompilationUnitManager.Compilation, output:ITe static member Compile srcFolder files references = let compileFiles (files : IEnumerable<_>) = - let mgr = new CompilationUnitManager(fun ex -> failwith ex.Message) + let mgr = new CompilationUnitManager(ProjectInformation.Properties.Default, fun ex -> failwith ex.Message) files.ToImmutableDictionary(Path.GetFullPath >> Uri, File.ReadAllText) |> CompilationUnitManager.InitializeFileManagers |> mgr.AddOrUpdateSourceFilesAsync diff --git a/src/QsCompiler/Tests.Compiler/TransformationTests.fs b/src/QsCompiler/Tests.Compiler/TransformationTests.fs index 2a376638b5..ea214abfbc 100644 --- a/src/QsCompiler/Tests.Compiler/TransformationTests.fs +++ b/src/QsCompiler/Tests.Compiler/TransformationTests.fs @@ -73,7 +73,7 @@ and private SyntaxCounterExpressionKinds(parent : SyntaxCounter) = let private buildSyntaxTree code = let fileId = new Uri(Path.GetFullPath "test-file.qs") - let compilationUnit = new CompilationUnitManager(fun ex -> failwith ex.Message) + let compilationUnit = new CompilationUnitManager(ProjectInformation.Properties.Default, fun ex -> failwith ex.Message) let file = CompilationUnitManager.InitializeFileManager(fileId, code) compilationUnit.AddOrUpdateSourceFileAsync file |> ignore // spawns a task that modifies the current compilation let mutable syntaxTree = compilationUnit.Build().BuiltCompilation // will wait for any current tasks to finish diff --git a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs index c4e98fd1e0..f9192927ae 100644 --- a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs +++ b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs @@ -113,8 +113,8 @@ public void LoadOutdatedQsharpProject() var (projectFile, context) = Context("test9"); var projDir = Path.GetDirectoryName(projectFile); Assert.IsNotNull(context); - Assert.AreEqual("test9.dll", Path.GetFileName(context.OutputPath)); - Assert.IsTrue(Path.GetDirectoryName(context.OutputPath).StartsWith(projDir)); + Assert.AreEqual("test9.dll", Path.GetFileName(context.ProjectProperties.OutputPath)); + Assert.IsTrue(Path.GetDirectoryName(context.ProjectProperties.OutputPath).StartsWith(projDir)); var qsFiles = new string[] { @@ -133,8 +133,8 @@ public void LoadQsharpCoreLibraries() var (projectFile, context) = Context("test3"); var projDir = Path.GetDirectoryName(projectFile); Assert.IsNotNull(context); - Assert.AreEqual("test3.dll", Path.GetFileName(context.OutputPath)); - Assert.IsTrue(Path.GetDirectoryName(context.OutputPath).StartsWith(projDir)); + Assert.AreEqual("test3.dll", Path.GetFileName(context.ProjectProperties.OutputPath)); + Assert.IsTrue(Path.GetDirectoryName(context.ProjectProperties.OutputPath).StartsWith(projDir)); var qsFiles = new string[] { @@ -152,8 +152,8 @@ public void LoadQsharpCoreLibraries() (projectFile, context) = Context("test12"); projDir = Path.GetDirectoryName(projectFile); Assert.IsNotNull(context); - Assert.AreEqual("test12.dll", Path.GetFileName(context.OutputPath)); - Assert.IsTrue(Path.GetDirectoryName(context.OutputPath).StartsWith(projDir)); + Assert.AreEqual("test12.dll", Path.GetFileName(context.ProjectProperties.OutputPath)); + Assert.IsTrue(Path.GetDirectoryName(context.ProjectProperties.OutputPath).StartsWith(projDir)); qsFiles = new string[] { @@ -175,8 +175,8 @@ public void LoadQsharpFrameworkLibrary() var (projectFile, context) = Context("test7"); var projDir = Path.GetDirectoryName(projectFile); Assert.IsNotNull(context); - Assert.AreEqual("test7.dll", Path.GetFileName(context.OutputPath)); - Assert.IsTrue(Path.GetDirectoryName(context.OutputPath).StartsWith(projDir)); + Assert.AreEqual("test7.dll", Path.GetFileName(context.ProjectProperties.OutputPath)); + Assert.IsTrue(Path.GetDirectoryName(context.ProjectProperties.OutputPath).StartsWith(projDir)); var qsFiles = new string[] { @@ -195,8 +195,8 @@ public void LoadQsharpConsoleApps() var (projectFile, context) = Context("test4"); var projDir = Path.GetDirectoryName(projectFile); Assert.IsNotNull(context); - Assert.AreEqual("test4.dll", Path.GetFileName(context.OutputPath)); - Assert.IsTrue(Path.GetDirectoryName(context.OutputPath).StartsWith(projDir)); + Assert.AreEqual("test4.dll", Path.GetFileName(context.ProjectProperties.OutputPath)); + Assert.IsTrue(Path.GetDirectoryName(context.ProjectProperties.OutputPath).StartsWith(projDir)); var qsFiles = new string[] { @@ -212,8 +212,8 @@ public void LoadQsharpConsoleApps() (projectFile, context) = Context("test10"); projDir = Path.GetDirectoryName(projectFile); Assert.IsNotNull(context); - Assert.AreEqual("test10.dll", Path.GetFileName(context.OutputPath)); - Assert.IsTrue(Path.GetDirectoryName(context.OutputPath).StartsWith(projDir)); + Assert.AreEqual("test10.dll", Path.GetFileName(context.ProjectProperties.OutputPath)); + Assert.IsTrue(Path.GetDirectoryName(context.ProjectProperties.OutputPath).StartsWith(projDir)); qsFiles = new string[] { @@ -227,8 +227,8 @@ public void LoadQsharpConsoleApps() (projectFile, context) = Context("test11"); projDir = Path.GetDirectoryName(projectFile); Assert.IsNotNull(context); - Assert.AreEqual("test11.dll", Path.GetFileName(context.OutputPath)); - Assert.IsTrue(Path.GetDirectoryName(context.OutputPath).StartsWith(projDir)); + Assert.AreEqual("test11.dll", Path.GetFileName(context.ProjectProperties.OutputPath)); + Assert.IsTrue(Path.GetDirectoryName(context.ProjectProperties.OutputPath).StartsWith(projDir)); qsFiles = new string[] { @@ -246,8 +246,8 @@ public void LoadQsharpUnitTest() var (projectFile, context) = Context("test5"); var projDir = Path.GetDirectoryName(projectFile); Assert.IsNotNull(context); - Assert.AreEqual("test5.dll", Path.GetFileName(context.OutputPath)); - Assert.IsTrue(Path.GetDirectoryName(context.OutputPath).StartsWith(projDir)); + Assert.AreEqual("test5.dll", Path.GetFileName(context.ProjectProperties.OutputPath)); + Assert.IsTrue(Path.GetDirectoryName(context.ProjectProperties.OutputPath).StartsWith(projDir)); var qsFiles = new string[] { @@ -270,8 +270,8 @@ public void LoadQsharpMultiFrameworkLibrary() var (projectFile, context) = Context("test6"); var projDir = Path.GetDirectoryName(projectFile); Assert.IsNotNull(context); - Assert.AreEqual("test6.dll", Path.GetFileName(context.OutputPath)); - Assert.IsTrue(Path.GetDirectoryName(context.OutputPath).StartsWith(projDir)); + Assert.AreEqual("test6.dll", Path.GetFileName(context.ProjectProperties.OutputPath)); + Assert.IsTrue(Path.GetDirectoryName(context.ProjectProperties.OutputPath).StartsWith(projDir)); var qsFiles = new string[] { From f6c8dc0fc516944cae76e2216dbf4fa234233510 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 20:27:00 -0700 Subject: [PATCH 18/25] going with two additional arguments for the manager only --- .../CompilationManager/CompilationUnitManager.cs | 6 ++++-- src/QsCompiler/CompilationManager/ProjectManager.cs | 10 ++++++---- .../CompilationManager/Properties/AssemblyInfo.cs | 1 + src/QsCompiler/Compiler/CompilationLoader.cs | 5 +---- src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs | 2 +- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 3 +-- src/QsCompiler/Tests.Compiler/OptimizationTests.fs | 2 +- .../Tests.Compiler/TestUtils/SetupVerificationTests.fs | 2 +- src/QsCompiler/Tests.Compiler/TransformationTests.fs | 2 +- 9 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 0495b5df31..34616ebd23 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.Quantum.QsCompiler.CompilationBuilder.DataStructures; using Microsoft.Quantum.QsCompiler.DataTypes; +using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.Quantum.QsCompiler.SyntaxProcessing; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; @@ -62,8 +63,9 @@ public class CompilationUnitManager : IDisposable /// If an for publishing diagnostics is given and is not null, /// that action is called whenever diagnostics within a file have changed and are ready for publishing. /// - public CompilationUnitManager(ProjectInformation.Properties projectProperties, - Action exceptionLogger = null, Action publishDiagnostics = null, bool syntaxCheckOnly = false) + public CompilationUnitManager( + Action exceptionLogger = null, Action publishDiagnostics = null, bool syntaxCheckOnly = false, + AssemblyConstants.RuntimeCapabilities capabilities = AssemblyConstants.RuntimeCapabilities.Unknown, bool isExecutable = false) { this.EnableVerification = !syntaxCheckOnly; this.CompilationUnit = new CompilationUnit(); diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index d125aad86b..f8e56056c4 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -19,7 +19,7 @@ namespace Microsoft.Quantum.QsCompiler.CompilationBuilder { public class ProjectInformation { - public class Properties + internal class Properties { public readonly string Version; public readonly string OutputPath; @@ -42,7 +42,7 @@ public Properties(string version, string outputPath, AssemblyConstants.RuntimeCa public delegate bool Loader(Uri projectFile, out ProjectInformation projectInfo); - public readonly Properties ProjectProperties; + internal readonly Properties ProjectProperties; public readonly ImmutableArray SourceFiles; public readonly ImmutableArray ProjectReferences; public readonly ImmutableArray References; @@ -145,7 +145,9 @@ internal Project(Uri projectFile, ProjectInformation projectInfo, // We track the file contents for unsupported projects in case the files are migrated to newer projects while editing, // but we don't do any semantic verification, and we don't publish diagnostics for them. this.Processing = new ProcessingQueue(onException); - this.Manager = new CompilationUnitManager(this.Properties, onException, ignore ? null : publishDiagnostics, syntaxCheckOnly: ignore); + this.Manager = new CompilationUnitManager( + onException, ignore ? null : publishDiagnostics, syntaxCheckOnly: ignore, + this.Properties.RuntimeCapabilities, this.Properties.IsExecutable); this.Log = log ?? ((msg, severity) => Console.WriteLine($"{severity}: {msg}")); this.LoadedSourceFiles = ImmutableHashSet.Empty; @@ -586,7 +588,7 @@ public ProjectManager(Action exceptionLogger, Action(); - this.DefaultManager = new CompilationUnitManager(ProjectInformation.Properties.Default, exceptionLogger, publishDiagnostics, syntaxCheckOnly: true); + this.DefaultManager = new CompilationUnitManager(exceptionLogger, publishDiagnostics, syntaxCheckOnly: true); this.PublishDiagnostics = publishDiagnostics; this.LogException = exceptionLogger; this.Log = log; diff --git a/src/QsCompiler/CompilationManager/Properties/AssemblyInfo.cs b/src/QsCompiler/CompilationManager/Properties/AssemblyInfo.cs index 8413ca8700..08ea392bdb 100644 --- a/src/QsCompiler/CompilationManager/Properties/AssemblyInfo.cs +++ b/src/QsCompiler/CompilationManager/Properties/AssemblyInfo.cs @@ -6,3 +6,4 @@ // Allow the test assembly to use our internal methods [assembly: InternalsVisibleTo("Tests.Microsoft.Quantum.QsCompiler" + SigningConstants.PUBLIC_KEY)] +[assembly: InternalsVisibleTo("Tests.Microsoft.Quantum.QsLanguageServer" + SigningConstants.PUBLIC_KEY)] diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 1d2eae0ac1..38338d20fe 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -435,10 +435,7 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference RaiseCompilationTaskStart("OverallCompilation", "Build"); this.CompilationStatus.Validation = Status.Succeeded; var files = CompilationUnitManager.InitializeFileManagers(sourceFiles, null, this.OnCompilerException); // do *not* live track (i.e. use publishing) here! - - // FIXME: INSTEAD OF PASSING PROJECT PROPERTIES, IT'S PROBABLY BETTER TO JUST PASS THE TWO CONFIGS NEEDED ... - var projectProps = new ProjectInformation.Properties("Latest", this.DllOutputPath ?? "", this.Config.RuntimeCapabilities, this.Config.IsExecutable, this.Config.ExposeReferencesViaTestNames); - var compilationManager = new CompilationUnitManager(projectProps, this.OnCompilerException); + var compilationManager = new CompilationUnitManager(this.OnCompilerException, capabilities: this.Config.RuntimeCapabilities, isExecutable: this.Config.IsExecutable); compilationManager.UpdateReferencesAsync(references); compilationManager.AddOrUpdateSourceFilesAsync(files); this.VerifiedCompilation = compilationManager.Build(); diff --git a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index 9b2e939056..454ff162a2 100644 --- a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -18,7 +18,7 @@ open Xunit type ClassicalControlTests () = - let compilationManager = new CompilationUnitManager(ProjectInformation.Properties.Default, new Action (fun ex -> failwith ex.Message)) + 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) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 8d066ade0a..dde3c1bdf4 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -7,7 +7,6 @@ open System open System.Collections.Generic open System.Collections.Immutable open System.IO -open System.Linq open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.CompilationBuilder open Microsoft.Quantum.QsCompiler.DataTypes @@ -26,7 +25,7 @@ open Xunit.Abstractions type LinkingTests (output:ITestOutputHelper) = inherit CompilerTests(CompilerTests.Compile (Path.Combine ("TestCases", "LinkingTests" )) ["Core.qs"; "InvalidEntryPoints.qs"] [], output) - let compilationManager = new CompilationUnitManager(ProjectInformation.Properties.Default, new Action (fun ex -> failwith ex.Message)) + let compilationManager = new CompilationUnitManager(new Action (fun ex -> failwith ex.Message)) // The file name needs to end in ".qs" so that it isn't ignored by the References.Headers class during the internal renaming tests. let getTempFile () = Path.GetRandomFileName () + ".qs" |> Path.GetFullPath |> Uri diff --git a/src/QsCompiler/Tests.Compiler/OptimizationTests.fs b/src/QsCompiler/Tests.Compiler/OptimizationTests.fs index 773c8c4684..f1b296247e 100644 --- a/src/QsCompiler/Tests.Compiler/OptimizationTests.fs +++ b/src/QsCompiler/Tests.Compiler/OptimizationTests.fs @@ -15,7 +15,7 @@ open Xunit /// Given a string of valid Q# code, outputs the AST and the callables dictionary let private buildCompilation code = let fileId = new Uri(Path.GetFullPath "test-file.qs") - let compilationUnit = new CompilationUnitManager(ProjectInformation.Properties.Default, fun ex -> failwith ex.Message) + let compilationUnit = new CompilationUnitManager(fun ex -> failwith ex.Message) let file = CompilationUnitManager.InitializeFileManager(fileId, code) compilationUnit.AddOrUpdateSourceFileAsync file |> ignore // spawns a task that modifies the current compilation let mutable compilation = compilationUnit.Build().BuiltCompilation // will wait for any current tasks to finish diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs index 4c77ead28d..eb03d7079a 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs @@ -92,7 +92,7 @@ type CompilerTests (compilation : CompilationUnitManager.Compilation, output:ITe static member Compile srcFolder files references = let compileFiles (files : IEnumerable<_>) = - let mgr = new CompilationUnitManager(ProjectInformation.Properties.Default, fun ex -> failwith ex.Message) + let mgr = new CompilationUnitManager(fun ex -> failwith ex.Message) files.ToImmutableDictionary(Path.GetFullPath >> Uri, File.ReadAllText) |> CompilationUnitManager.InitializeFileManagers |> mgr.AddOrUpdateSourceFilesAsync diff --git a/src/QsCompiler/Tests.Compiler/TransformationTests.fs b/src/QsCompiler/Tests.Compiler/TransformationTests.fs index ea214abfbc..2a376638b5 100644 --- a/src/QsCompiler/Tests.Compiler/TransformationTests.fs +++ b/src/QsCompiler/Tests.Compiler/TransformationTests.fs @@ -73,7 +73,7 @@ and private SyntaxCounterExpressionKinds(parent : SyntaxCounter) = let private buildSyntaxTree code = let fileId = new Uri(Path.GetFullPath "test-file.qs") - let compilationUnit = new CompilationUnitManager(ProjectInformation.Properties.Default, fun ex -> failwith ex.Message) + let compilationUnit = new CompilationUnitManager(fun ex -> failwith ex.Message) let file = CompilationUnitManager.InitializeFileManager(fileId, code) compilationUnit.AddOrUpdateSourceFileAsync file |> ignore // spawns a task that modifies the current compilation let mutable syntaxTree = compilationUnit.Build().BuiltCompilation // will wait for any current tasks to finish From 5e029193ec3b201dd84aad7a5eed1766c3797eb4 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 23:00:57 -0700 Subject: [PATCH 19/25] error for entry point in library --- .../CompilationManager/CompilationUnit.cs | 15 +++++++++++++-- .../CompilationManager/CompilationUnitManager.cs | 4 ++-- src/QsCompiler/Core/SymbolTable.fs | 10 +++++++--- src/QsCompiler/DataStructures/Diagnostics.fs | 12 +++++++----- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 3 ++- .../TestCases/LinkingTests/InvalidEntryPoints.qs | 3 +++ 6 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index bd76c5e7b7..4609611006 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -221,11 +221,16 @@ public class CompilationUnit : IReaderWriterLock, IDisposable internal References Externals { get; private set; } internal NamespaceManager GlobalSymbols { get; private set; } + private readonly Dictionary CompiledCallables; private readonly Dictionary CompiledTypes; + private readonly ReaderWriterLockSlim SyncRoot; private readonly HashSet DependentLocks; + internal readonly AssemblyConstants.RuntimeCapabilities RuntimeCapabilities; + internal readonly bool IsExecutable; + public void Dispose() { this.SyncRoot.Dispose(); } @@ -234,13 +239,18 @@ public void Dispose() /// with the given sequence of locks registered as dependent locks if the sequence is not null. /// Throws an ArgumentNullException if any of the given locks is. /// - internal CompilationUnit(References externals = null, IEnumerable dependentLocks = null) + internal CompilationUnit(AssemblyConstants.RuntimeCapabilities capabilities, bool isExecutable, + References externals = null, IEnumerable dependentLocks = null) { this.SyncRoot = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); this.DependentLocks = dependentLocks == null ? new HashSet() : new HashSet(dependentLocks); if (dependentLocks?.Contains(null) ?? false) throw new ArgumentNullException(nameof(dependentLocks), "one or more of the given locks is null"); + + this.RuntimeCapabilities = capabilities; + this.IsExecutable = isExecutable; + this.CompiledCallables = new Dictionary(); this.CompiledTypes = new Dictionary(); this.UpdateReferences(externals ?? References.Empty); @@ -260,7 +270,8 @@ internal void UpdateReferences(References externals) this.GlobalSymbols = new NamespaceManager(this, this.Externals.Declarations.Values.SelectMany(h => h.Callables), this.Externals.Declarations.Values.SelectMany(h => h.Specializations.Select(t => new Tuple(t.Item1, t.Item2))), - this.Externals.Declarations.Values.SelectMany(h => h.Types)); + this.Externals.Declarations.Values.SelectMany(h => h.Types), + this.RuntimeCapabilities, this.IsExecutable); } finally { this.ExitWriteLock(); } } diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 34616ebd23..757e094801 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -68,7 +68,7 @@ public CompilationUnitManager( AssemblyConstants.RuntimeCapabilities capabilities = AssemblyConstants.RuntimeCapabilities.Unknown, bool isExecutable = false) { this.EnableVerification = !syntaxCheckOnly; - this.CompilationUnit = new CompilationUnit(); + this.CompilationUnit = new CompilationUnit(capabilities, isExecutable); this.FileContentManagers = new ConcurrentDictionary, FileContentManager>(); this.ChangedFiles = new ManagedHashSet>(new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion)); this.PublishDiagnostics = publishDiagnostics ?? (_ => { }); @@ -440,7 +440,7 @@ private Task SpawnGlobalTypeCheckingAsync(bool runSynchronously = false) // work with a separate compilation unit instance such that processing of all further edits can go on in parallel var sourceFiles = this.FileContentManagers.Values.OrderBy(m => m.FileName); this.ChangedFiles.RemoveAll(f => sourceFiles.Any(m => m.FileName.Value == f.Value)); - var compilation = new CompilationUnit(this.CompilationUnit.Externals, sourceFiles.Select(file => file.SyncRoot)); + var compilation = new CompilationUnit(this.CompilationUnit.RuntimeCapabilities, this.CompilationUnit.IsExecutable, this.CompilationUnit.Externals, sourceFiles.Select(file => file.SyncRoot)); var content = compilation.UpdateGlobalSymbolsFor(sourceFiles); foreach (var file in sourceFiles) this.PublishDiagnostics(file.Diagnostics()); diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index dcbdcc8551..1dc0acf667 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -741,7 +741,8 @@ and NamespaceManager (syncRoot : IReaderWriterLock, callablesInRefs : IEnumerable, specializationsInRefs : IEnumerable, - typesInRefs : IEnumerable) = + typesInRefs : IEnumerable, + runtimeCapabilites, isExecutable) = // This class itself does not use any concurrency, // so anything that is accessible within the class only does not apply any locks. // IMPORTANT: the syncRoot is intentionally not exposed externally, since with this class supporting mutation @@ -972,10 +973,13 @@ and NamespaceManager then errs.Add (decl.Position, range.ValueOr decl.Range |> QsCompilerDiagnostic.Warning (WarningCode.ReservedEntryPointArgumentName, [])) simplifiedArgNames |> List.iteri verifyArgument - // check that there is no more than one entry point + // check that there is no more than one entry point, and no entry point if the project is not executable if signatureErrs.Any() then false, errs + elif not isExecutable then + errs.Add (offset, range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.EntryPointInLibrary, [])) + false, errs else GetEntryPoints() |> Seq.tryHead |> function - | None -> true, errs + | None -> isExecutable, errs | Some (epName, epSource) -> let msgArgs = [sprintf "%s.%s" epName.Namespace.Value epName.Name.Value; epSource.Value] errs.Add (offset, range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.MultipleEntryPoints, msgArgs)) diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 9c2df007e8..752c369505 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -239,11 +239,12 @@ type ErrorCode = | MultipleEntryPoints = 6236 | InvalidEntryPointSpecialization = 6237 | DuplicateEntryPointArgumentName = 6238 - | InvalidTestAttributePlacement = 6239 - | InvalidExecutionTargetForTest = 6240 - | ExpectingFullNameAsAttributeArgument = 6241 - | AttributeInvalidOnSpecialization = 6242 - | AttributeInvalidOnCallable = 6243 + | EntryPointInLibrary = 6239 + | InvalidTestAttributePlacement = 6240 + | InvalidExecutionTargetForTest = 6241 + | ExpectingFullNameAsAttributeArgument = 6242 + | AttributeInvalidOnSpecialization = 6243 + | AttributeInvalidOnCallable = 6244 | TypeMismatchInReturn = 6301 | TypeMismatchInValueUpdate = 6302 @@ -612,6 +613,7 @@ type DiagnosticItem = | ErrorCode.MultipleEntryPoints -> "Invalid entry point. An entry point {0} already exists in {1}." | ErrorCode.InvalidEntryPointSpecialization -> "Entry points cannot have any other specializations besides the default body." | ErrorCode.DuplicateEntryPointArgumentName -> "Invalid name for entry point argument. A similar argument name is already in use." + | ErrorCode.EntryPointInLibrary -> "Invalid entry point. Only Q# command line applications can have entry points." | ErrorCode.InvalidTestAttributePlacement -> "Invalid test attribute. Test attributes may only occur on callables that have no arguments and return Unit." | ErrorCode.InvalidExecutionTargetForTest -> "Invalid execution target. Currently, valid execution targets for tests are the QuantumSimulator, the ToffoliSimulator, or the ResourcesEstimator." | ErrorCode.ExpectingFullNameAsAttributeArgument -> "Invalid attribute argument. Expecting a fully qualified name as argument to the {0} attribute." diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index dde3c1bdf4..0c932b3337 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -25,7 +25,7 @@ open Xunit.Abstractions type LinkingTests (output:ITestOutputHelper) = inherit CompilerTests(CompilerTests.Compile (Path.Combine ("TestCases", "LinkingTests" )) ["Core.qs"; "InvalidEntryPoints.qs"] [], output) - let compilationManager = new CompilationUnitManager(new Action (fun ex -> failwith ex.Message)) + let compilationManager = new CompilationUnitManager(new Action (fun ex -> failwith ex.Message), isExecutable = true) // The file name needs to end in ".qs" so that it isn't ignored by the References.Headers class during the internal renaming tests. let getTempFile () = Path.GetRandomFileName () + ".qs" |> Path.GetFullPath |> Uri @@ -242,6 +242,7 @@ type LinkingTests (output:ITestOutputHelper) = for entryPoint in LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" do this.CompileAndVerify entryPoint [] + this.Expect "EntryPointInLibrary" [Error ErrorCode.EntryPointInLibrary] [] diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs index a7198c1741..8dfbb83f54 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InvalidEntryPoints.qs @@ -8,6 +8,9 @@ namespace Microsoft.Quantum.Testing.EntryPoints { // tests related to entry point placement verification + @ EntryPoint() + operation EntryPointInLibrary() : Unit { } + @ EntryPoint() newtype InvalidEntryPointPlacement1 = Int; From 7c19c79a891ac39b4460190bc3d88f650dc013c5 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 23:45:27 -0700 Subject: [PATCH 20/25] warning for non-result types in return value --- src/QsCompiler/Core/SymbolTable.fs | 7 ++ src/QsCompiler/DataStructures/Diagnostics.fs | 2 + src/QsCompiler/Tests.Compiler/LinkingTests.fs | 49 ++++++++---- .../LinkingTests/EntryPointDiagnostics.qs | 79 +++++++++++++++++++ 4 files changed, 122 insertions(+), 15 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 1dc0acf667..912e653f6b 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -959,6 +959,13 @@ and NamespaceManager let signatureErrs = inErrs.Concat outErrs errs.AddRange signatureErrs + // currently, only return values of type Result, Result[], and tuples thereof are supported on quantum processors + if runtimeCapabilites = AssemblyConstants.RuntimeCapabilities.QPRGen0 || runtimeCapabilites = AssemblyConstants.RuntimeCapabilities.QPRGen1 then + let invalid = signature.ReturnType.ExtractAll (fun t -> t.Type |> function + | Result | ArrayType _ | TupleType _ | InvalidType -> Seq.empty + | _ -> Seq.singleton t) + if invalid.Any() then errs.Add (decl.Position, signature.ReturnType.Range |> orDefault |> QsCompilerDiagnostic.Warning (WarningCode.NonResultTypeReturnedInEntryPoint, [])) + // validate entry point argument names let asCommandLineArg (str : string) = str.ToLowerInvariant() |> String.filter((<>)'_') let reservedCommandLineArgs = CommandLineArguments.ReservedArguments |> Seq.map asCommandLineArg |> Seq.toArray diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 752c369505..79ab89602c 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -329,6 +329,7 @@ type WarningCode = | MissingEntryPoint = 6202 | IgnoredEntryPoint = 6203 | ReservedEntryPointArgumentName = 6204 + | NonResultTypeReturnedInEntryPoint = 6205 | GeneratorDirectiveWillBeIgnored = 6301 | UnreachableCode = 6302 @@ -706,6 +707,7 @@ type DiagnosticItem = | WarningCode.MissingEntryPoint -> "The project is a Q# command line application but no entry point has been found. The project should be a library, and any C# driver code should be defined in a separate project." | WarningCode.IgnoredEntryPoint -> "Entry point will be ignored. The project is a Q# library and cannot have any entry points." | WarningCode.ReservedEntryPointArgumentName -> "The argument name conflicts with a default argument for a Q# command line application." + | WarningCode.NonResultTypeReturnedInEntryPoint -> "Only values of type Result, Result[], and tuples thereof can be returned when executing on a quantum processor." | WarningCode.GeneratorDirectiveWillBeIgnored -> "Generation directive ignored. A specialization of this callable has been declared as intrinsic." | WarningCode.UnreachableCode -> "This statement will never be executed." diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 0c932b3337..af92541db6 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -11,6 +11,7 @@ open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.CompilationBuilder open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics +open Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTree open Microsoft.Quantum.QsCompiler.Transformations.IntrinsicResolution @@ -66,7 +67,7 @@ type LinkingTests (output:ITestOutputHelper) = let name = name |> NonNullable<_>.New this.Verify (QsQualifiedName.New (ns, name), diag) - member private this.CompileAndVerify input (diag : DiagnosticItem seq) = + member private this.CompileAndVerify (compilationManager : CompilationUnitManager) input (diag : DiagnosticItem seq) = let fileId = getTempFile() let file = getManager fileId input @@ -226,22 +227,15 @@ type LinkingTests (output:ITestOutputHelper) = let fileId = getTempFile() let file = getManager fileId entryPoints.[0] compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore - this.CompileAndVerify entryPoints.[1] [Error ErrorCode.MultipleEntryPoints] + this.CompileAndVerify compilationManager entryPoints.[1] [Error ErrorCode.MultipleEntryPoints] compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore - [] - member this.``Entry point specialization verification`` () = - - for entryPoint in LinkingTests.ReadAndChunkSourceFile "EntryPointSpecializations.qs" do - this.CompileAndVerify entryPoint [Error ErrorCode.InvalidEntryPointSpecialization] - - [] member this.``Entry point validation`` () = for entryPoint in LinkingTests.ReadAndChunkSourceFile "ValidEntryPoints.qs" do - this.CompileAndVerify entryPoint [] + this.CompileAndVerify compilationManager entryPoint [] this.Expect "EntryPointInLibrary" [Error ErrorCode.EntryPointInLibrary] @@ -249,11 +243,18 @@ type LinkingTests (output:ITestOutputHelper) = member this.``Entry point argument name verification`` () = let tests = LinkingTests.ReadAndChunkSourceFile "EntryPointDiagnostics.qs" - this.CompileAndVerify tests.[0] [Error ErrorCode.DuplicateEntryPointArgumentName] - this.CompileAndVerify tests.[1] [Error ErrorCode.DuplicateEntryPointArgumentName] - this.CompileAndVerify tests.[2] [Error ErrorCode.DuplicateEntryPointArgumentName] - this.CompileAndVerify tests.[3] [Warning WarningCode.ReservedEntryPointArgumentName] - this.CompileAndVerify tests.[4] [Warning WarningCode.ReservedEntryPointArgumentName] + this.CompileAndVerify compilationManager tests.[0] [Error ErrorCode.DuplicateEntryPointArgumentName] + this.CompileAndVerify compilationManager tests.[1] [Error ErrorCode.DuplicateEntryPointArgumentName] + this.CompileAndVerify compilationManager tests.[2] [Error ErrorCode.DuplicateEntryPointArgumentName] + this.CompileAndVerify compilationManager tests.[3] [Warning WarningCode.ReservedEntryPointArgumentName] + this.CompileAndVerify compilationManager tests.[4] [Warning WarningCode.ReservedEntryPointArgumentName] + + + [] + member this.``Entry point specialization verification`` () = + + for entryPoint in LinkingTests.ReadAndChunkSourceFile "EntryPointSpecializations.qs" do + this.CompileAndVerify compilationManager entryPoint [Error ErrorCode.InvalidEntryPointSpecialization] [] @@ -271,6 +272,24 @@ type LinkingTests (output:ITestOutputHelper) = this.Expect "InvalidEntryPointPlacement7" [Error ErrorCode.MisplacedDeclarationAttribute] + [] + member this.``Entry point return type restriction for quantum processors`` () = + + let tests = LinkingTests.ReadAndChunkSourceFile "EntryPointDiagnostics.qs" + let compilationManager = new CompilationUnitManager(new Action (fun ex -> failwith ex.Message), isExecutable = true, capabilities = RuntimeCapabilities.QPRGen0) + let addOrUpdateSourceFile filePath = getManager (new Uri(filePath)) (File.ReadAllText filePath) |> compilationManager.AddOrUpdateSourceFileAsync |> ignore + Path.Combine ("TestCases", "LinkingTests", "Core.qs") |> Path.GetFullPath |> addOrUpdateSourceFile + + this.CompileAndVerify compilationManager tests.[5] [] + this.CompileAndVerify compilationManager tests.[6] [] + this.CompileAndVerify compilationManager tests.[7] [] + this.CompileAndVerify compilationManager tests.[8] [] + this.CompileAndVerify compilationManager tests.[9] [Warning WarningCode.NonResultTypeReturnedInEntryPoint] + this.CompileAndVerify compilationManager tests.[10] [Warning WarningCode.NonResultTypeReturnedInEntryPoint] + this.CompileAndVerify compilationManager tests.[11] [Warning WarningCode.NonResultTypeReturnedInEntryPoint] + this.CompileAndVerify compilationManager tests.[12] [Warning WarningCode.NonResultTypeReturnedInEntryPoint] + + [] member this.``Entry point argument and return type verification`` () = diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs index 7d712670a0..57f7b28c6e 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/EntryPointDiagnostics.qs @@ -41,3 +41,82 @@ namespace Microsoft.Quantum.Testing.EntryPoints { @ EntryPoint() operation InvalidEntryPoint45(s : Int) : Unit {} } + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation EntryPoint1() : Result { + return Zero; + } +} + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation EntryPoint2() : Result[] { + return [Zero]; + } +} + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation EntryPoint3() : (Result, Result[]) { + return (Zero, [Zero]); + } +} + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation EntryPoint4() : ((Result, Result), Result[]) { + return ((Zero, Zero), [Zero]); + } +} + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation EntryPoint5() : Unit {} +} + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation EntryPoint6() : (Int, Result) { + return (0, Zero); + } +} + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation EntryPoint7() : String[] { + return [""]; + } +} + +// ================================= + +namespace Microsoft.Quantum.Testing.EntryPoints { + + @ EntryPoint() + operation EntryPoint8() : ((Result[], (Result, Int)[]), Result) { + return (([Zero], [(Zero, 0)]), Zero); + } +} + From a23aa365a0139af036fab72472c18b71ba8d063b Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 23:46:39 -0700 Subject: [PATCH 21/25] removing comment --- src/QsCompiler/Compiler/CompilationLoader.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 38338d20fe..03e6900227 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -450,9 +450,6 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference this.Logger?.Log(WarningCode.MissingEntryPoint, Array.Empty()); } - // TODO: - // give warnings and ignore entry points in libraries, - // and check additional restriction on the return type for execution on quantum processors. // executing the specified rewrite steps From 82dc9b5d455461d0fab6344bb6683f5c4b847e6a Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Apr 2020 23:59:54 -0700 Subject: [PATCH 22/25] forgot something --- src/QsCompiler/CompilationManager/CompilationUnit.cs | 6 +++--- src/QuantumSdk/Sdk/Sdk.props | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 4609611006..a7fef3023b 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -139,7 +139,7 @@ static QsDeclarationAttribute Renamed(QsQualifiedName originalName, Tuple, Headers> Declarations; public static References Empty = - new References(ImmutableDictionary, Headers>.Empty, false); + new References(ImmutableDictionary, Headers>.Empty); /// /// Combines the current references with the given references, and verifies that there are no conflicts. @@ -152,7 +152,7 @@ internal References CombineWith(References other, Action on { if (other == null) throw new ArgumentNullException(nameof(other)); if (this.Declarations.Keys.Intersect(other.Declarations.Keys).Any()) throw new ArgumentException("common references exist"); - return new References (this.Declarations.AddRange(other.Declarations), false, onError: onError); + return new References (this.Declarations.AddRange(other.Declarations), onError: onError); } /// @@ -163,7 +163,7 @@ internal References CombineWith(References other, Action on /// Throws an ArgumentNullException if the given diagnostics are null. /// internal References Remove(NonNullable source, Action onError = null) => - new References(this.Declarations.Remove(source), false, onError: onError); + new References(this.Declarations.Remove(source), onError: onError); /// /// Given a dictionary that maps the ids of dll files to the corresponding headers, diff --git a/src/QuantumSdk/Sdk/Sdk.props b/src/QuantumSdk/Sdk/Sdk.props index 7f9bd490b9..9c0384e810 100644 --- a/src/QuantumSdk/Sdk/Sdk.props +++ b/src/QuantumSdk/Sdk/Sdk.props @@ -30,6 +30,7 @@ +