From f2b65d7b937443e0f55da4f443330dc964207798 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 21 Jan 2020 17:54:28 -0800 Subject: [PATCH 001/135] Keep track of modifiers for callables and types --- .../CompilationManager/CompilationUnit.cs | 14 ++++-- .../EditorSupport/CodeActions.cs | 9 ++-- .../CompilationManager/TypeChecking.cs | 2 +- src/QsCompiler/Core/ConstructorExtensions.fs | 7 +-- src/QsCompiler/Core/DeclarationHeaders.fs | 5 ++- src/QsCompiler/Core/SymbolResolution.fs | 8 +++- src/QsCompiler/Core/SymbolTable.fs | 43 +++++++++++-------- src/QsCompiler/Core/SyntaxGenerator.fs | 3 +- src/QsCompiler/Core/TreeTransformation.fs | 11 +++-- src/QsCompiler/Core/TreeWalker.fs | 3 +- src/QsCompiler/DataStructures/SyntaxTokens.fs | 30 ++++++++++++- src/QsCompiler/DataStructures/SyntaxTree.fs | 29 ++++++++----- src/QsCompiler/DocumentationParser/Utils.cs | 3 +- .../SyntaxProcessor/SymbolTracker.fs | 2 +- .../SyntaxProcessor/SyntaxExtensions.fs | 14 +++--- .../SyntaxProcessor/TreeVerification.fs | 4 +- .../Tests.Compiler/SerializationTests.fs | 7 ++- .../Tests.DocGenerator/DocParsingTests.cs | 8 +++- .../TextProcessor/QsFragmentParsing.fs | 16 +++++-- .../TextProcessor/SyntaxExtensions.fs | 9 ++-- .../Transformations/Monomorphization.cs | 3 +- 21 files changed, 161 insertions(+), 69 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 139c6302a1..330f4aa450 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -344,8 +344,16 @@ internal void UpdateTypes(IEnumerable updates) var compilationExists = this.CompiledTypes.TryGetValue(fullName, out QsCustomType compiled); if (!compilationExists) continue; // may happen if a file has been modified during global type checking - var type = new QsCustomType(compiled.FullName, compiled.Attributes, compiled.SourceFile, header.Location, - compiled.Type, compiled.TypeItems, compiled.Documentation, compiled.Comments); + var type = new QsCustomType( + compiled.FullName, + compiled.Attributes, + compiled.SourceFile, + header.Location, + compiled.Type, + compiled.TypeItems, + compiled.Documentation, + compiled.Comments + ); this.CompiledTypes[fullName] = type; } } @@ -465,7 +473,7 @@ private QsCallable GetImportedCallable(CallableDeclarationHeader header) private QsCustomType GetImportedType(TypeDeclarationHeader header) { if (header == null) throw new ArgumentNullException(nameof(header)); - return new QsCustomType(header.QualifiedName, header.Attributes, header.SourceFile, header.Location, + return new QsCustomType(header.QualifiedName, header.Attributes, header.SourceFile, header.Location, header.Type, header.TypeItems, header.Documentation, QsComments.Empty); } diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index a605170f0e..916057ff6b 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -235,7 +235,7 @@ IEnumerable GetCharacteristics(QsTuple> var characteristicsInFragment = fragment?.Kind is QsFragmentKind.FunctionDeclaration function ? GetCharacteristics(function.Item2.Argument) : fragment?.Kind is QsFragmentKind.OperationDeclaration operation ? GetCharacteristics(operation.Item2.Argument) : - fragment?.Kind is QsFragmentKind.TypeDefinition type ? GetCharacteristics(type.Item2) : + fragment?.Kind is QsFragmentKind.TypeDefinition type ? GetCharacteristics(type.Item2.Items) : Enumerable.Empty(); //var symbolInfo = file.TryGetQsSymbolInfo(d.Range.Start, false, out var fragment); @@ -437,9 +437,10 @@ bool EmptyOrFirstAttribute(IEnumerable line, out CodeFragment att) var docString = $"{docPrefix}# Summary{endLine}{docPrefix}{endLine}"; var (argTuple, typeParams) = - callableDecl.IsValue ? (callableDecl.Item.Item2.Item2.Argument, callableDecl.Item.Item2.Item2.TypeParameters) : - typeDecl.IsValue ? (typeDecl.Item.Item2, ImmutableArray.Empty) : - (null, ImmutableArray.Empty); + callableDecl.IsValue ? (callableDecl.Item.Item2.Item2.Argument, + callableDecl.Item.Item2.Item2.TypeParameters) + : typeDecl.IsValue ? (typeDecl.Item.Item2.Items, ImmutableArray.Empty) + : (null, ImmutableArray.Empty); var hasOutput = callableDecl.IsValue && !callableDecl.Item.Item2.Item2.ReturnType.Type.IsUnitType; var args = argTuple == null ? ImmutableArray>.Empty : SyntaxGenerator.ExtractItems(argTuple); diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index 56bd6507d9..972dcd4f9d 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -111,7 +111,7 @@ internal static ImmutableArray DocumentingComments(this FileContentManag /// /// Returns the HeaderItems corresponding to all type declarations with a valid name in the given file, or null if the given file is null. /// - private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry>>)> GetTypeDeclarationHeaderItems + private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry)> GetTypeDeclarationHeaderItems (this FileContentManager file) => file.GetHeaderItems(file?.TypeDeclarationTokens(), frag => frag.Kind.DeclaredType(), null); /// diff --git a/src/QsCompiler/Core/ConstructorExtensions.fs b/src/QsCompiler/Core/ConstructorExtensions.fs index ce3f4f7964..0918f91d70 100644 --- a/src/QsCompiler/Core/ConstructorExtensions.fs +++ b/src/QsCompiler/Core/ConstructorExtensions.fs @@ -169,11 +169,12 @@ type QsStatement with } type ResolvedSignature with - static member New ((argType, returnType), info, typeParams : IEnumerable<_>) = { + static member New ((argType, returnType), info, typeParams : IEnumerable<_>, modifiers) = { TypeParameters = typeParams.ToImmutableArray() ArgumentType = argType ReturnType = returnType Information = info + Modifiers = modifiers } type QsSpecialization with @@ -212,12 +213,12 @@ type QsCallable with static member NewTypeConstructor = QsCallable.New QsCallableKind.TypeConstructor type QsCustomType with - static member New (source, location) (name, attributes, items, underlyingType, documentation, comments) = { + static member New (source, location) (name, attributes, items, typeSignature, documentation, comments) = { FullName = name Attributes = attributes SourceFile = source Location = location - Type = underlyingType + Type = typeSignature TypeItems = items Documentation = documentation Comments = comments diff --git a/src/QsCompiler/Core/DeclarationHeaders.fs b/src/QsCompiler/Core/DeclarationHeaders.fs index 7ea14d39bc..9e087d6b48 100644 --- a/src/QsCompiler/Core/DeclarationHeaders.fs +++ b/src/QsCompiler/Core/DeclarationHeaders.fs @@ -99,7 +99,7 @@ type TypeDeclarationHeader = { SourceFile : NonNullable Position : DeclarationHeader.Offset SymbolRange : DeclarationHeader.Range - Type : ResolvedType + Type : ResolvedTypeSignature TypeItems : QsTuple Documentation : ImmutableArray } @@ -124,7 +124,8 @@ type TypeDeclarationHeader = { let attributesAreNullOrDefault = Object.ReferenceEquals(header.Attributes, null) || header.Attributes.IsDefault let header = if attributesAreNullOrDefault then {header with Attributes = ImmutableArray.Empty} else header // no reason to raise an error if not (Object.ReferenceEquals(header.TypeItems, null)) then success, header - else false, {header with TypeItems = ImmutableArray.Create (header.Type |> Anonymous |> QsTupleItem) |> QsTuple} + else false, {header with TypeItems = header.Type.UnderlyingType + |> Anonymous |> QsTupleItem |> ImmutableArray.Create |> QsTuple} member this.ToJson() : string = DeclarationHeader.ToJson this diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index 921ecd96f5..1481239512 100644 --- a/src/QsCompiler/Core/SymbolResolution.fs +++ b/src/QsCompiler/Core/SymbolResolution.fs @@ -235,7 +235,13 @@ module SymbolResolution = let errs = TypeParameterResolutionWarnings argType (returnType, signature.ReturnType.Range |> orDefault) typeParams (typeParams |> Seq.map fst).ToImmutableArray(), errs let callableInfo = CallableInformation.Common specBundleInfos - let resolvedSig = { TypeParameters = resolvedParams; ArgumentType = argType; ReturnType = returnType; Information = callableInfo } + let resolvedSig = { + TypeParameters = resolvedParams + ArgumentType = argType + ReturnType = returnType + Modifiers = signature.Modifiers + Information = callableInfo + } (resolvedSig, argTuple), [inErr; outErr; resErrs; tpErrs] |> Array.concat /// Give a routine for type resolution, fully resolves the given user defined type as well as its items. diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 8026c9cd27..d7cf8929f9 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -24,7 +24,7 @@ type private PartialNamespace private source : NonNullable, documentation : IEnumerable>, openNS : IEnumerable, string>>, - typeDecl : IEnumerable, Resolution, ResolvedType * QsTuple<_>>>>, + typeDecl : IEnumerable, Resolution>>>, callableDecl : IEnumerable, QsCallableKind * Resolution>>>, specializations : IEnumerable, List>>>) = @@ -130,7 +130,7 @@ type private PartialNamespace private /// Adds the corresponding type constructor to the dictionary of declared callables. /// The given location is associated with both the type constructur and the type itself and accessible via the record properties Position and SymbolRange. /// -> Note that this routine will fail with the standard dictionary.Add error if either a type or a callable with that name already exists. - member this.AddType (location : QsLocation) (tName, typeTuple, attributes, documentation) = + member this.AddType (location : QsLocation) (tName, typeSignature, attributes, documentation) = let mutable anonItemId = 0 let withoutRange sym = {Symbol = sym; Range = Null} let replaceAnonymous (itemName : QsSymbol, itemType) = // positional info for types in type constructors is removed upon resolution @@ -146,11 +146,17 @@ type private PartialNamespace private let rec buildItem = function | QsTuple args -> (args |> Seq.map buildItem).ToImmutableArray() |> QsTuple | QsTupleItem (n, t) -> replaceAnonymous (n, t) - match typeTuple with + match typeSignature.Items with | QsTupleItem (n, t) -> ImmutableArray.Create (replaceAnonymous (n, t)) |> QsTuple - | QsTuple _ -> buildItem typeTuple + | QsTuple _ -> buildItem typeSignature.Items let returnType = {Type = UserDefinedType (QualifiedSymbol (this.Name, tName) |> withoutRange); Range = Null} - {TypeParameters = ImmutableArray.Empty; Argument = constructorArgument; ReturnType = returnType; Characteristics = {Characteristics = EmptySet; Range = Null}} + { + TypeParameters = ImmutableArray.Empty + Argument = constructorArgument + ReturnType = returnType + Characteristics = {Characteristics = EmptySet; Range = Null} + Modifiers = typeSignature.Modifiers + } // There are a couple of reasons not just blindly attach all attributes associated with the type to the constructor: // For one, we would need to make sure that the range information for duplications is stripped such that e.g. rename commands are not executed multiple times. @@ -165,7 +171,7 @@ type private PartialNamespace private if attributes |> Seq.exists (SymbolResolution.IndicatesDeprecation validDeprecatedQualification) then ImmutableArray.Create deprecationWithoutRedirect else ImmutableArray.Empty - TypeDeclarations.Add(tName, (typeTuple, attributes, documentation) |> unresolved location) + TypeDeclarations.Add(tName, (typeSignature, attributes, documentation) |> unresolved location) this.AddCallableDeclaration location (tName, (TypeConstructor, constructorSignature), constructorAttr, ImmutableArray.Empty) let bodyGen = {TypeArguments = Null; Generator = QsSpecializationGeneratorKind.Intrinsic; Range = Value location.Range} this.AddCallableSpecialization location QsBody (tName, bodyGen, ImmutableArray.Empty, ImmutableArray.Empty) @@ -348,7 +354,10 @@ and Namespace private | false, _ -> ArgumentException "given source file is not listed as source of the namespace" |> raise | true, partialNS -> partialNS.TryGetType attName |> function | true, resolution when resolution.DefinedAttributes |> Seq.exists compareAttributeName -> - resolution.Resolved.ValueOrApply missingResolutionException |> fst |> Some + Some { + UnderlyingType = resolution.Resolved.ValueOrApply missingResolutionException |> fst + Modifiers = resolution.Defined.Modifiers + } | _ -> None /// Returns the type with the given name defined in the given source file within this namespace. @@ -535,12 +544,12 @@ and Namespace private /// The given location is associated with both the type constructur and the type itself and accessible via the record properties Position and SymbolRange. /// If a type or callable with that name already exists, returns an array of suitable diagnostics. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. - member this.TryAddType (source, location) ((tName, tRange), typeTuple, attributes, documentation) : QsCompilerDiagnostic[] = + member this.TryAddType (source, location) ((tName, tRange), typeSignature, attributes, documentation) : QsCompilerDiagnostic[] = match Parts.TryGetValue source with | true, partial when not (IsDefined tName) -> TypesDefinedInAllSourcesCache <- null CallablesDefinedInAllSourcesCache <- null - partial.AddType location (tName, typeTuple, attributes, documentation); [||] + partial.AddType location (tName, typeSignature, attributes, documentation); [||] | true, _ -> this.ContainsType tName |> function | Value _ -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeRedefinition, [tName.Value]) |] | Null -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeConstructorOverlapWithCallable, [tName.Value]) |] @@ -773,7 +782,7 @@ and NamespaceManager match Namespaces.TryGetValue udt.Namespace with | true, ns -> ns.TryGetAttributeDeclaredIn declSource (udt.Name, validQualifications) |> function | None -> None, [| symRange.ValueOr QsCompilerDiagnostic.DefaultRange |> QsCompilerDiagnostic.Error (ErrorCode.NotMarkedAsAttribute, [fullName]) |] - | Some argType -> Some (udt, argType), errs + | Some argType -> Some (udt, argType.UnderlyingType), errs | false, _ -> QsCompilerError.Raise "namespace for defined type not found"; None, errs | None, errs -> None, errs let resolved, msgs = SymbolResolution.ResolveAttribute getAttribute attribute @@ -925,7 +934,7 @@ and NamespaceManager ns.TypesDefinedInAllSources() |> Seq.collect (fun kvPair -> let tName, (source, qsType) = kvPair.Key, kvPair.Value let fullName = {Namespace = ns.Name; Name = tName} - let resolved, msgs = qsType.Defined |> this.ResolveTypeDeclaration (fullName, source) + let resolved, msgs = qsType.Defined.Items |> this.ResolveTypeDeclaration (fullName, source) ns.SetTypeResolution source (tName, resolved |> Value, ImmutableArray.Empty) msgs |> Array.map (fun msg -> source, (qsType.Position, msg)))) // ... before we can resolve the corresponding attributes. @@ -1139,8 +1148,8 @@ and NamespaceManager Attributes = qsType.ResolvedAttributes SourceFile = source Position = DeclarationHeader.Offset.Defined qsType.Position - SymbolRange = DeclarationHeader.Range.Defined qsType.Range - Type = underlyingType + SymbolRange = DeclarationHeader.Range.Defined qsType.Range + Type = {UnderlyingType = underlyingType; Modifiers = qsType.Defined.Modifiers} TypeItems = items Documentation = qsType.Documentation })) @@ -1277,16 +1286,16 @@ and NamespaceManager /// throws the corresponding exception if no such type exists in that file. /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. member private this.TryGetTypeHeader (typeName : QsQualifiedName, declSource) (nsName, source) = - let BuildHeader fullName (source, declaration) = - let fallback () = declaration.Defined |> this.ResolveTypeDeclaration (typeName, source) |> fst + let BuildHeader fullName (source, declaration) = + let fallback () = declaration.Defined.Items |> this.ResolveTypeDeclaration (typeName, source) |> fst let underlyingType, items = declaration.Resolved.ValueOrApply fallback Value { QualifiedName = fullName Attributes = declaration.ResolvedAttributes SourceFile = source Position = DeclarationHeader.Offset.Defined declaration.Position - SymbolRange = DeclarationHeader.Range.Defined declaration.Range - Type = underlyingType + SymbolRange = DeclarationHeader.Range.Defined declaration.Range + Type = {UnderlyingType = underlyingType; Modifiers = declaration.Defined.Modifiers} TypeItems = items Documentation = declaration.Documentation } diff --git a/src/QsCompiler/Core/SyntaxGenerator.fs b/src/QsCompiler/Core/SyntaxGenerator.fs index a9a1c40fc7..8754f51dc0 100644 --- a/src/QsCompiler/Core/SyntaxGenerator.fs +++ b/src/QsCompiler/Core/SyntaxGenerator.fs @@ -201,7 +201,8 @@ module SyntaxGenerator = let WithoutRangeInfo (signature : ResolvedSignature) = let argType = signature.ArgumentType |> StripPositionInfo.Apply let returnType = signature.ReturnType |> StripPositionInfo.Apply - ResolvedSignature.New ((argType, returnType), signature.Information, signature.TypeParameters) + ResolvedSignature.New ((argType, returnType), + signature.Information, signature.TypeParameters, signature.Modifiers) /// Given the resolved argument type of an operation, returns the argument type of its controlled version. let AddControlQubits (argT : ResolvedType) = diff --git a/src/QsCompiler/Core/TreeTransformation.fs b/src/QsCompiler/Core/TreeTransformation.fs index 351caa9c18..d9499ec6ef 100644 --- a/src/QsCompiler/Core/TreeTransformation.fs +++ b/src/QsCompiler/Core/TreeTransformation.fs @@ -78,7 +78,8 @@ type SyntaxTreeTransformation() = let argType = this.Scope.Expression.Type.Transform s.ArgumentType let returnType = this.Scope.Expression.Type.Transform s.ReturnType let info = this.Scope.Expression.Type.onCallableInformation s.Information - ResolvedSignature.New ((argType, returnType), info, typeParams) + // TODO: Should modifiers also be transformed? + ResolvedSignature.New ((argType, returnType), info, typeParams, s.Modifiers) abstract member onExternalImplementation : unit -> unit @@ -158,11 +159,13 @@ type SyntaxTreeTransformation() = let source = this.onSourceFile t.SourceFile let loc = this.onLocation t.Location let attributes = t.Attributes |> Seq.map this.onAttribute |> ImmutableArray.CreateRange - let underlyingType = this.Scope.Expression.Type.Transform t.Type + let underlyingType = this.Scope.Expression.Type.Transform t.Type.UnderlyingType + // TODO: Should modifiers also be transformed? + let typeSignature = { UnderlyingType = underlyingType; Modifiers = t.Type.Modifiers } let typeItems = this.onTypeItems t.TypeItems let doc = this.onDocumentation t.Documentation - let comments = t.Comments - QsCustomType.New (source, loc) (t.FullName, attributes, typeItems, underlyingType, doc, comments) + QsCustomType.New + (source, loc) (t.FullName, attributes, typeItems, typeSignature, doc, t.Comments) abstract member onCallableImplementation : QsCallable -> QsCallable default this.onCallableImplementation (c : QsCallable) = diff --git a/src/QsCompiler/Core/TreeWalker.fs b/src/QsCompiler/Core/TreeWalker.fs index 0626d25192..523096efa2 100644 --- a/src/QsCompiler/Core/TreeWalker.fs +++ b/src/QsCompiler/Core/TreeWalker.fs @@ -148,7 +148,8 @@ type SyntaxTreeWalker() = this.onSourceFile t.SourceFile this.onLocation t.Location t.Attributes |> Seq.iter this.onAttribute - this.Scope.Expression.Type.Walk t.Type + // TODO: Should modifiers have their own walker? + this.Scope.Expression.Type.Walk t.Type.UnderlyingType this.onTypeItems t.TypeItems this.onDocumentation t.Documentation diff --git a/src/QsCompiler/DataStructures/SyntaxTokens.fs b/src/QsCompiler/DataStructures/SyntaxTokens.fs index 4af35c0fed..e7aa3fc6fd 100644 --- a/src/QsCompiler/DataStructures/SyntaxTokens.fs +++ b/src/QsCompiler/DataStructures/SyntaxTokens.fs @@ -185,11 +185,39 @@ type QsTuple<'Item> = | QsTupleItem of 'Item | QsTuple of ImmutableArray> +/// Defines where a global declaration may be accessed. +[] +type AccessModifier = + /// The default access modifier is public, which means the type or callable can be used from anywhere. + | DefaultAccess + /// Internal access means that a type or callable may only be used from within the compilation unit in which it is + /// declared. + | Internal + /// Private access means that a type or callable may only be used from within the compilation unit and namespace in + /// which it is declared. + | Private + +/// Used to represent Q# keywords that may be attached to a declaration to modify its visibility or behavior. +type Modifiers = { + /// Defines where a global declaration may be accessed. + Access : AccessModifier +} + type CallableSignature = { TypeParameters : ImmutableArray Argument : QsTuple ReturnType : QsType Characteristics : Characteristics + /// The modifiers that have been applied to this callable. + Modifiers : Modifiers +} + +// The signature of a user-defined type. +type TypeSignature = { + /// The underlying type and any named items. + Items : QsTuple + /// The modifiers that have been applied to this type. + Modifiers : Modifiers } type QsFragmentKind = @@ -216,7 +244,7 @@ type QsFragmentKind = | ControlledAdjointDeclaration of QsSpecializationGenerator | OperationDeclaration of QsSymbol * CallableSignature | FunctionDeclaration of QsSymbol * CallableSignature -| TypeDefinition of QsSymbol * QsTuple +| TypeDefinition of QsSymbol * TypeSignature | DeclarationAttribute of QsSymbol * QsExpression | OpenDirective of QsSymbol * QsNullable | NamespaceDeclaration of QsSymbol diff --git a/src/QsCompiler/DataStructures/SyntaxTree.fs b/src/QsCompiler/DataStructures/SyntaxTree.fs index 7f05775360..96a3431c81 100644 --- a/src/QsCompiler/DataStructures/SyntaxTree.fs +++ b/src/QsCompiler/DataStructures/SyntaxTree.fs @@ -602,6 +602,8 @@ type ResolvedSignature = { ReturnType : ResolvedType /// contains the functors that the callable supports (necessarily empty for functions) Information : CallableInformation + /// Represents the Q# keywords attached to the declaration that modify its behavior. + Modifiers : Modifiers } @@ -696,6 +698,16 @@ type QsTypeItem = | Anonymous of ResolvedType +/// Used to represent type and access information for a user defined type. +type ResolvedTypeSignature = { + /// Contains the underlying type, i.e. the argument type of the (auto-generated) type constructor associated with + /// the user defined type. + UnderlyingType : ResolvedType + /// Represents the Q# keywords attached to the declaration that modify its behavior. + Modifiers : Modifiers +} + + /// describes a Q# user defined type type QsCustomType = { /// contains the name of the type @@ -709,11 +721,11 @@ type QsCustomType = { /// and the range contains the range occupied by the type name relative to that position. /// The location is Null for auto-generated types defined by the compiler. Location : QsNullable - /// Contains the underlying Q# type. - /// Note that a user defined type is *not* considered to be a subtype of its underlying type, - /// but rather its own, entirely distinct type, - /// and the underlying type is merely the argument type of the (auto-generated) type constructor associated with the user defined type. - Type : ResolvedType + /// Contains the type and access information for the user defined type. + /// Note that a user defined type is *not* considered to be a subtype of its underlying type, but rather its own, + /// entirely distinct type, and the underlying type is merely the argument type of the (auto-generated) type + /// constructor associated with the user defined type. + Type : ResolvedTypeSignature /// contains the type tuple defining the named and anonymous items of the type TypeItems : QsTuple /// content of documenting comments associated with the type @@ -767,9 +779,4 @@ type QsCompilation = { /// Contains the names of all entry points of the compilation. /// In the case of a library the array is empty. EntryPoints : ImmutableArray -} - - - - - +} \ No newline at end of file diff --git a/src/QsCompiler/DocumentationParser/Utils.cs b/src/QsCompiler/DocumentationParser/Utils.cs index 357937a559..57cc84729e 100644 --- a/src/QsCompiler/DocumentationParser/Utils.cs +++ b/src/QsCompiler/DocumentationParser/Utils.cs @@ -344,11 +344,12 @@ internal static string CallableToSyntax(QsCallable callable) /// The syntax string internal static string CustomTypeToSyntax(QsCustomType customType) { + // TODO: Include modifiers. var sb = new StringBuilder(); sb.Append("newtype "); sb.Append(customType.FullName.Name.Value); sb.Append(" = "); - sb.Append(ResolvedTypeToString(customType.Type)); + sb.Append(ResolvedTypeToString(customType.Type.UnderlyingType)); sb.Append(";"); return sb.ToString(); } diff --git a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs index ad1333f429..006d67e6d6 100644 --- a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs +++ b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs @@ -258,7 +258,7 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif /// Adds a suitable diagnostic and returns an invalid type if the underlying type could not be determined. member this.GetUnderlyingType addError (udt : UserDefinedType) = match this.TryGetTypeDeclaration addError udt with - | Value decl -> decl.Type |> StripPositionInfo.Apply + | Value decl -> decl.Type.UnderlyingType |> StripPositionInfo.Apply | Null -> InvalidType |> ResolvedType.New /// Given the fully qualified name of a user defined type as well as the identifier specifying an item, diff --git a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs index 0bea65010e..17627e6e83 100644 --- a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs +++ b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs @@ -175,7 +175,7 @@ let public SymbolInformation fragmentKind = | QsFragmentKind.ControlledAdjointDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) | QsFragmentKind.OperationDeclaration (n, signature) -> (n, signature) |> SymbolsInCallableDeclaration | QsFragmentKind.FunctionDeclaration (n, signature) -> (n, signature) |> SymbolsInCallableDeclaration - | QsFragmentKind.TypeDefinition (sym, t) -> (sym, t) |> SymbolsInArgumentTuple + | QsFragmentKind.TypeDefinition (sym, t) -> (sym, t.Items) |> SymbolsInArgumentTuple | QsFragmentKind.DeclarationAttribute (sym, ex) -> [], ([AttributeAsCallExpr (sym, ex)], []) |> collectWith SymbolsFromExpr |> addVariable sym | QsFragmentKind.NamespaceDeclaration sym -> sym |> SymbolDeclarations, ([], [], []) | QsFragmentKind.OpenDirective (nsName, alias) -> [alias] |> chooseValues, ([nsName], [], []) @@ -280,10 +280,11 @@ let private CharacteristicsAnnotation (ex, format) = let public TypeInfo (symbolTable : NamespaceManager) (currentNS, source) (qsType : QsType) markdown = let udtInfo udt = match udt |> globalTypeResolution symbolTable (currentNS, source) with - | Some decl, _ -> + | Some decl, _ -> + // TODO: Include modifiers. let name = decl.QualifiedName.Name.Value |> withNewLine - let ns = sprintf "Namespace: %s" decl.QualifiedName.Namespace.Value |> withNewLine - let info = sprintf "Underlying type: %s" (TypeName decl.Type) + let ns = sprintf "Namespace: %s" decl.QualifiedName.Namespace.Value |> withNewLine + let info = sprintf "Underlying type: %s" (TypeName decl.Type.UnderlyingType) let doc = PrintSummary decl.Documentation markdown sprintf "User defined type %s%s%s%s" name ns info doc | None, Some sym -> sprintf "Type %s" sym.Value @@ -380,10 +381,11 @@ let public DeclarationInfo symbolTable (locals : LocalDeclarations) (currentNS, sprintf "Declaration of %s variable %s%s" kind name info | false, _ -> match qsSym |> globalTypeResolution symbolTable (currentNS, source) with // needs to be before querying callables - | Some decl, _ -> + | Some decl, _ -> + // TODO: Include modifiers. let name = decl.QualifiedName.Name.Value |> withNewLine let ns = sprintf "Namespace: %s" decl.QualifiedName.Namespace.Value |> withNewLine - let info = sprintf "Underlying type: %s" (decl.Type |> TypeName) + let info = sprintf "Underlying type: %s" (decl.Type.UnderlyingType |> TypeName) let doc = PrintSummary decl.Documentation markdown sprintf "Declaration of user defined type %s%s%s%s" name ns info doc | None, _ -> diff --git a/src/QsCompiler/SyntaxProcessor/TreeVerification.fs b/src/QsCompiler/SyntaxProcessor/TreeVerification.fs index ce81a02b12..f19150f45f 100644 --- a/src/QsCompiler/SyntaxProcessor/TreeVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/TreeVerification.fs @@ -151,7 +151,9 @@ let CheckDefinedTypesForCycles (definitions : ImmutableArray [it; ot] | QsTypeKind.TupleType vtypeList -> vtypeList |> Seq.toList - | QsTypeKind.UserDefinedType udt -> updateContainedReferences rootIndex (location, QsQualifiedName.New(udt.Namespace, udt.Name)) + | QsTypeKind.UserDefinedType udt -> + updateContainedReferences rootIndex (location, QsQualifiedName.New(udt.Namespace, udt.Name)) + |> List.map (fun s -> s.UnderlyingType) | _ -> [] let walk_udts () = // builds up containedTypes and containedIn diff --git a/src/QsCompiler/Tests.Compiler/SerializationTests.fs b/src/QsCompiler/Tests.Compiler/SerializationTests.fs index 354d580967..137ed6e3e8 100644 --- a/src/QsCompiler/Tests.Compiler/SerializationTests.fs +++ b/src/QsCompiler/Tests.Compiler/SerializationTests.fs @@ -52,6 +52,7 @@ module SerializationTests = ArgumentType = argType |> ResolvedType.New ReturnType = rType |> ResolvedType.New Information = CallableInformation.New(ResolvedCharacteristics.FromProperties props, InferredCallableInformation.NoInformation) + Modifiers = {Access = DefaultAccess} } let varDecl name t (s, e) = { @@ -210,7 +211,8 @@ module SerializationTests = SourceFile = "%%%" |> NonNullable.New Position = (2,4) |> DeclarationHeader.Offset.Defined SymbolRange = ({Line = 1; Column = 9}, {Line = 1; Column = 13}) |> DeclarationHeader.Range.Defined - Type = tupleIntIntType |> ResolvedType.New + Type = {UnderlyingType = ResolvedType.New tupleIntIntType + Modifiers = {Access = DefaultAccess}} TypeItems = intIntTypeItems Documentation = ImmutableArray.Empty } @@ -222,7 +224,8 @@ module SerializationTests = SourceFile = "%%%" |> NonNullable.New Position = (3,4) |> DeclarationHeader.Offset.Defined SymbolRange = ({Line = 1; Column = 9}, {Line = 1; Column = 15}) |> DeclarationHeader.Range.Defined - Type = tupleIntIntType |> ResolvedType.New + Type = {UnderlyingType = ResolvedType.New tupleIntIntType + Modifiers = {Access = DefaultAccess}} TypeItems = intIntTypeItems Documentation = ImmutableArray.Empty } diff --git a/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs b/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs index a4e8d649a1..0e13f98d19 100644 --- a/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs +++ b/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs @@ -160,7 +160,10 @@ element indexes the subsystem on which the generator acts on. ImmutableArray.Empty, NonNullable.New("GeneratorRepresentation.qs"), ZeroLocation, - baseType, + new ResolvedTypeSignature( + baseType, + new Modifiers(AccessModifier.DefaultAccess) + ), typeItems, comments.ToImmutableArray(), QsComments.Empty); @@ -324,7 +327,8 @@ of the generator represented by $U$. var typeParams = new QsLocalSymbol[] { }.ToImmutableArray(); var argTypes = new ResolvedType[] { qubitToUnitOp, qubitToUnitOp, qubitToUnitOpAC, phaseEstOp, qubitArrayType }.ToImmutableArray(); var argTupleType = ResolvedType.New(QsType.NewTupleType(argTypes)); - var signature = new ResolvedSignature(typeParams, argTupleType, doubleType, noInfo); + var noModifiers = new Modifiers(AccessModifier.DefaultAccess); + var signature = new ResolvedSignature(typeParams, argTupleType, doubleType, noInfo, noModifiers); var args = new List { BuildArgument("statePrepUnitary", qubitToUnitOp), BuildArgument("adiabaticUnitary", qubitToUnitOp), diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 28dd79d042..94f56090bc 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -148,7 +148,13 @@ let private signature = let symbolDeclaration = expectedIdentifierDeclaration (lAngle <|> lTuple) let returnTypeAnnotation = expected (typeAnnotation eof) ErrorCode.InvalidReturnTypeAnnotation ErrorCode.MissingReturnTypeAnnotation invalidType eof let characteristicsAnnotation = opt (qsCharacteristics.parse >>. expectedCharacteristics eof) |>> Option.defaultValue ((EmptySet, Null) |> Characteristics.New) - let signature = genericParamList .>>. (argumentTuple .>>. returnTypeAnnotation) .>>. characteristicsAnnotation |>> CallableSignature.New + // TODO: Parse modifiers. + let signature = + genericParamList + .>>. (argumentTuple .>>. returnTypeAnnotation) + .>>. characteristicsAnnotation + .>>. (preturn { Access = DefaultAccess }) + |>> CallableSignature.New symbolDeclaration .>>. signature /// Parses a Q# functor generator directive. @@ -285,7 +291,8 @@ and private functionDeclaration = /// Uses buildFragment to parse a Q# TypeDefinition as QsFragment. and private udtDeclaration = - let invalid = TypeDefinition (invalidSymbol, invalidArgTupleItem) + let invalid = TypeDefinition (invalidSymbol, + { Items = invalidArgTupleItem; Modifiers = { Access = DefaultAccess } }) let udtTuple = // not unified with the argument tuple for callable declarations, since the error handling needs to be different let asAnonymousItem t = QsTupleItem ((MissingSymbol, Null) |> QsSymbol.New, t) let namedItem = @@ -298,7 +305,10 @@ and private udtDeclaration = buildTupleItem tupleItem (fst >> QsTuple) ErrorCode.InvalidUdtItemDeclaration ErrorCode.MissingUdtItemDeclaration invalidArgTupleItem eof let invalidNamedSingle = followedBy namedItem >>. optTupleBrackets namedItem |>> fst invalidNamedSingle <|> udtTupleItem // require parenthesis for a single named item - let declBody = expectedIdentifierDeclaration equal .>> equal .>>. udtTuple + // TODO: Parse modifiers. + let declBody = + expectedIdentifierDeclaration equal .>> equal + .>>. (udtTuple |>> (fun items -> { Items = items; Modifiers = { Access = DefaultAccess } })) buildFragment typeDeclHeader.parse declBody invalid TypeDefinition eof diff --git a/src/QsCompiler/TextProcessor/SyntaxExtensions.fs b/src/QsCompiler/TextProcessor/SyntaxExtensions.fs index b90542b2a8..4bd1e818ae 100644 --- a/src/QsCompiler/TextProcessor/SyntaxExtensions.fs +++ b/src/QsCompiler/TextProcessor/SyntaxExtensions.fs @@ -98,19 +98,22 @@ type QsCompilerDiagnostic with type CallableSignature with - static member internal New ((typeParams, (argType, returnType)), characteristics) = { + static member internal New (((typeParams, (argType, returnType)), characteristics), modifiers) = { TypeParameters = typeParams Argument = argType ReturnType = returnType Characteristics = characteristics + Modifiers = modifiers } static member internal Invalid = let invalidType = (InvalidType, Null) |> QsType.New let invalidSymbol = (InvalidSymbol, Null) |> QsSymbol.New let invalidArg = QsTuple ([QsTupleItem (invalidSymbol, invalidType)].ToImmutableArray()) - ((ImmutableArray.Empty, (invalidArg, invalidType)), (InvalidSetExpr, Null) |> Characteristics.New) - |> CallableSignature.New + CallableSignature.New (((ImmutableArray.Empty, + (invalidArg, invalidType)), + (InvalidSetExpr, Null) |> Characteristics.New), + { Access = DefaultAccess }) type QsFragment with diff --git a/src/QsCompiler/Transformations/Monomorphization.cs b/src/QsCompiler/Transformations/Monomorphization.cs index 504df2f1d2..fbfb0d5a11 100644 --- a/src/QsCompiler/Transformations/Monomorphization.cs +++ b/src/QsCompiler/Transformations/Monomorphization.cs @@ -224,7 +224,8 @@ public override ResolvedSignature onSignature(ResolvedSignature s) ImmutableArray.Empty, s.ArgumentType, s.ReturnType, - s.Information + s.Information, + s.Modifiers ); return base.onSignature(s); } From 14e89cf41e7f7e65655b867cd0e460c8e521f61e Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 22 Jan 2020 13:57:59 -0800 Subject: [PATCH 002/135] Make Modifiers a struct so serialization works --- src/QsCompiler/DataStructures/SyntaxTokens.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/QsCompiler/DataStructures/SyntaxTokens.fs b/src/QsCompiler/DataStructures/SyntaxTokens.fs index e7aa3fc6fd..4591d75f39 100644 --- a/src/QsCompiler/DataStructures/SyntaxTokens.fs +++ b/src/QsCompiler/DataStructures/SyntaxTokens.fs @@ -198,6 +198,7 @@ type AccessModifier = | Private /// Used to represent Q# keywords that may be attached to a declaration to modify its visibility or behavior. +[] type Modifiers = { /// Defines where a global declaration may be accessed. Access : AccessModifier From 99a57317a327cd3ba648eb1caf7973b70ca41573 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 22 Jan 2020 14:04:36 -0800 Subject: [PATCH 003/135] Remove runtime tests until compiler version is updated --- QsCompiler.sln | 15 --------------- src/QsCompiler/Tests.Compiler/ExecutionTests.fs | 4 ++-- .../Tests.Compiler/Tests.Compiler.fsproj | 3 --- 3 files changed, 2 insertions(+), 20 deletions(-) diff --git a/QsCompiler.sln b/QsCompiler.sln index 7a330f37f1..f7ee3f64dc 100644 --- a/QsCompiler.sln +++ b/QsCompiler.sln @@ -34,8 +34,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simulation", "Simulation", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Simulation", "src\QsCompiler\TestTargets\Simulation\Target\Simulation.csproj", "{D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "src\QsCompiler\TestTargets\Simulation\Example\Example.csproj", "{2E331781-F7ED-4EF1-8451-896636C6D93A}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{D2E36476-A65F-4310-9C4C-B721BCC47B00}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Library1", "src\QsCompiler\TestTargets\Libraries\Library1\Library1.csproj", "{DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}" @@ -222,18 +220,6 @@ Global {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x64.Build.0 = Release|Any CPU {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x86.ActiveCfg = Release|Any CPU {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x86.Build.0 = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x64.ActiveCfg = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x64.Build.0 = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x86.ActiveCfg = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x86.Build.0 = Debug|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|Any CPU.Build.0 = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x64.ActiveCfg = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x64.Build.0 = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x86.ActiveCfg = Release|Any CPU - {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x86.Build.0 = Release|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -268,7 +254,6 @@ Global {256A6275-FC7F-42E9-9931-BC6EA6D0F31A} = {B4A9484D-31FC-4A27-9E26-4C8DE3E02D77} {76BA96DA-DC1E-4315-A3ED-5F0700A79812} = {6077A717-50BF-4F87-B439-CA549AF6A4AE} {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1} = {76BA96DA-DC1E-4315-A3ED-5F0700A79812} - {2E331781-F7ED-4EF1-8451-896636C6D93A} = {76BA96DA-DC1E-4315-A3ED-5F0700A79812} {D2E36476-A65F-4310-9C4C-B721BCC47B00} = {6077A717-50BF-4F87-B439-CA549AF6A4AE} {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F} = {D2E36476-A65F-4310-9C4C-B721BCC47B00} EndGlobalSection diff --git a/src/QsCompiler/Tests.Compiler/ExecutionTests.fs b/src/QsCompiler/Tests.Compiler/ExecutionTests.fs index a4a4db38cd..f1e47bf5b3 100644 --- a/src/QsCompiler/Tests.Compiler/ExecutionTests.fs +++ b/src/QsCompiler/Tests.Compiler/ExecutionTests.fs @@ -40,7 +40,7 @@ type ExecutionTests (output:ITestOutputHelper) = AssertEqual expectedOutput out - [] + // TODO: [] member this.``Specialization Generation for Conjugations`` () = ExecuteAndCompareOutput "ConjugationsInBody" " @@ -139,7 +139,7 @@ type ExecutionTests (output:ITestOutputHelper) = " - [] + // TODO: [] member this.``Referencing Projects and Packages`` () = ExecuteAndCompareOutput "PackageAndProjectReference" " diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 2f3634b115..cd7685cc7a 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -150,9 +150,6 @@ - - false - From 0bbbba52722dca878c3d9d9df563a768ad01dfa6 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 22 Jan 2020 17:43:40 -0800 Subject: [PATCH 004/135] Add converter and update serialization test --- .../DataStructures/Serialization.fs | 25 +++++++++++++++++++ .../Tests.Compiler/SerializationTests.fs | 8 +++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/DataStructures/Serialization.fs b/src/QsCompiler/DataStructures/Serialization.fs index 3652fd9ea0..7607bf9e37 100644 --- a/src/QsCompiler/DataStructures/Serialization.fs +++ b/src/QsCompiler/DataStructures/Serialization.fs @@ -69,6 +69,30 @@ type ResolvedTypeConverter(?ignoreSerializationException) = serializer.Serialize(writer, value.Resolution) +type ResolvedTypeSignatureConverter(?ignoreSerializationException) = + inherit JsonConverter() + let ignoreSerializationException = defaultArg ignoreSerializationException false + + /// For backwards compatibility, support deserializing either a ResolvedTypeSignture or a ResolvedType (with default + /// modifiers). + override this.ReadJson(reader : JsonReader, + objectType : Type, + existingValue : ResolvedTypeSignature, + hasExistingValue : bool, + serializer : JsonSerializer) = + let jobject = JObject.Load(reader) + let (underlyingType, modifiers) = jobject.ToObject(serializer) + { + UnderlyingType = + if obj.ReferenceEquals(underlyingType, null) then jobject.ToObject(serializer) + else underlyingType + Modifiers = modifiers + } + + override this.WriteJson(writer : JsonWriter, value : ResolvedTypeSignature, serializer : JsonSerializer) = + serializer.Serialize(writer, (value.UnderlyingType, value.Modifiers)) + + type ResolvedCharacteristicsConverter(?ignoreSerializationException) = inherit JsonConverter() let ignoreSerializationException = defaultArg ignoreSerializationException false @@ -139,6 +163,7 @@ module Json = new NonNullableConverter() :> JsonConverter new QsNullableLocationConverter(ignoreSerializationException) :> JsonConverter new ResolvedTypeConverter(ignoreSerializationException) :> JsonConverter + new ResolvedTypeSignatureConverter(ignoreSerializationException) :> JsonConverter new ResolvedCharacteristicsConverter(ignoreSerializationException) :> JsonConverter new TypedExpressionConverter() :> JsonConverter new ResolvedInitializerConverter() :> JsonConverter diff --git a/src/QsCompiler/Tests.Compiler/SerializationTests.fs b/src/QsCompiler/Tests.Compiler/SerializationTests.fs index 137ed6e3e8..57964756c2 100644 --- a/src/QsCompiler/Tests.Compiler/SerializationTests.fs +++ b/src/QsCompiler/Tests.Compiler/SerializationTests.fs @@ -232,15 +232,15 @@ module SerializationTests = |> testOne [] - let CALLABLE_1 = "{\"Kind\":{\"Case\":\"TypeConstructor\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":2,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":9},\"Item2\":{\"Line\":1,\"Column\":13}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"__Item1__\"]},\"Type\":{\"Case\":\"Int\"},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":1}}}]},{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"__Item2__\"]},\"Type\":{\"Case\":\"Int\"},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":1}}}]}]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"TupleType\",\"Fields\":[[{\"Case\":\"Int\"},{\"Case\":\"Int\"}]]},\"ReturnType\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":true}}},\"Documentation\":[\"type constructor for user defined type\"]}" + let CALLABLE_1 = "{\"Kind\":{\"Case\":\"TypeConstructor\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":2,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":9},\"Item2\":{\"Line\":1,\"Column\":13}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"__Item1__\"]},\"Type\":{\"Case\":\"Int\"},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":1}}}]},{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"__Item2__\"]},\"Type\":{\"Case\":\"Int\"},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":1}}}]}]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"TupleType\",\"Fields\":[[{\"Case\":\"Int\"},{\"Case\":\"Int\"}]]},\"ReturnType\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":true}},\"Modifiers\":{\"Access\":{\"Case\":\"DefaultAccess\"}}},\"Documentation\":[\"type constructor for user defined type\"]}" [] - let TYPE_1 = "{\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":2,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":9},\"Item2\":{\"Line\":1,\"Column\":13}},\"Type\":{\"Case\":\"TupleType\",\"Fields\":[[{\"Case\":\"Int\"},{\"Case\":\"Int\"}]]},\"TypeItems\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"Case\":\"Anonymous\",\"Fields\":[{\"Case\":\"Int\"}]}]},{\"Case\":\"QsTupleItem\",\"Fields\":[{\"Case\":\"Anonymous\",\"Fields\":[{\"Case\":\"Int\"}]}]}]]},\"Documentation\":[]}" + let TYPE_1 = "{\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":2,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":9},\"Item2\":{\"Line\":1,\"Column\":13}},\"Type\":{\"Item1\":{\"Case\":\"TupleType\",\"Fields\":[[{\"Case\":\"Int\"},{\"Case\":\"Int\"}]]},\"Item2\":{\"Access\":{\"Case\":\"DefaultAccess\"}}},\"TypeItems\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"Case\":\"Anonymous\",\"Fields\":[{\"Case\":\"Int\"}]}]},{\"Case\":\"QsTupleItem\",\"Fields\":[{\"Case\":\"Anonymous\",\"Fields\":[{\"Case\":\"Int\"}]}]}]]},\"Documentation\":[]}" [] - let CALLABLE_2 = "{\"Kind\":{\"Case\":\"Function\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyFunction\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":4,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":10},\"Item2\":{\"Line\":1,\"Column\":23}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"p\"]},\"Type\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":25},\"Item2\":{\"Line\":1,\"Column\":26}}}]}]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"ReturnType\":{\"Case\":\"UnitType\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}}},\"Documentation\":[]}" + let CALLABLE_2 = "{\"Kind\":{\"Case\":\"Function\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyFunction\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":4,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":10},\"Item2\":{\"Line\":1,\"Column\":23}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"p\"]},\"Type\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":25},\"Item2\":{\"Line\":1,\"Column\":26}}}]}]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"ReturnType\":{\"Case\":\"UnitType\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}},\"Modifiers\":{\"Access\":{\"Case\":\"DefaultAccess\"}}},\"Documentation\":[]}" [] let SPECIALIZATION_1 = "{\"Kind\":{\"Case\":\"QsBody\"},\"TypeArguments\":{\"Case\":\"Null\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}},\"Parent\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyFunction\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":4,\"Item2\":43},\"HeaderRange\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":5}},\"Documentation\":[]}" [] - let CALLABLE_3 = "{\"Kind\":{\"Case\":\"Operation\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyOperation\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":5,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":11},\"Item2\":{\"Line\":1,\"Column\":25}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"UnitType\"},\"ReturnType\":{\"Case\":\"UnitType\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}}},\"Documentation\":[]}" + let CALLABLE_3 = "{\"Kind\":{\"Case\":\"Operation\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyOperation\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":5,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":11},\"Item2\":{\"Line\":1,\"Column\":25}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"UnitType\"},\"ReturnType\":{\"Case\":\"UnitType\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}},\"Modifiers\":{\"Access\":{\"Case\":\"DefaultAccess\"}}},\"Documentation\":[]}" [] let SPECIALIZATION_3 = "{\"Kind\":{\"Case\":\"QsBody\"},\"TypeArguments\":{\"Case\":\"Null\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}},\"Parent\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyOperation\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":5,\"Item2\":39},\"HeaderRange\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":5}},\"Documentation\":[]}" From 0b02a69d3d3f6d13f9f520adbf89da2bd6a8f5cc Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 23 Jan 2020 13:28:09 -0800 Subject: [PATCH 005/135] Update transformation and walker --- src/QsCompiler/Core/TreeTransformation.fs | 21 ++++++++++++++------- src/QsCompiler/Core/TreeWalker.fs | 12 ++++++++++-- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/QsCompiler/Core/TreeTransformation.fs b/src/QsCompiler/Core/TreeTransformation.fs index d9499ec6ef..54ad2372ad 100644 --- a/src/QsCompiler/Core/TreeTransformation.fs +++ b/src/QsCompiler/Core/TreeTransformation.fs @@ -49,6 +49,9 @@ type SyntaxTreeTransformation() = abstract member onSourceFile : NonNullable -> NonNullable default this.onSourceFile f = f + abstract member onModifiers : Modifiers -> Modifiers + default this.onModifiers m = m + abstract member onTypeItems : QsTuple -> QsTuple default this.onTypeItems tItem = match tItem with @@ -78,8 +81,8 @@ type SyntaxTreeTransformation() = let argType = this.Scope.Expression.Type.Transform s.ArgumentType let returnType = this.Scope.Expression.Type.Transform s.ReturnType let info = this.Scope.Expression.Type.onCallableInformation s.Information - // TODO: Should modifiers also be transformed? - ResolvedSignature.New ((argType, returnType), info, typeParams, s.Modifiers) + let modifiers = this.onModifiers s.Modifiers + ResolvedSignature.New ((argType, returnType), info, typeParams, modifiers) abstract member onExternalImplementation : unit -> unit @@ -159,13 +162,17 @@ type SyntaxTreeTransformation() = let source = this.onSourceFile t.SourceFile let loc = this.onLocation t.Location let attributes = t.Attributes |> Seq.map this.onAttribute |> ImmutableArray.CreateRange - let underlyingType = this.Scope.Expression.Type.Transform t.Type.UnderlyingType - // TODO: Should modifiers also be transformed? - let typeSignature = { UnderlyingType = underlyingType; Modifiers = t.Type.Modifiers } + let typeSignature = this.onTypeSignature t.Type let typeItems = this.onTypeItems t.TypeItems let doc = this.onDocumentation t.Documentation - QsCustomType.New - (source, loc) (t.FullName, attributes, typeItems, typeSignature, doc, t.Comments) + QsCustomType.New (source, loc) (t.FullName, attributes, typeItems, typeSignature, doc, t.Comments) + + abstract member onTypeSignature : ResolvedTypeSignature -> ResolvedTypeSignature + default this.onTypeSignature s = + { + UnderlyingType = this.Scope.Expression.Type.Transform s.UnderlyingType + Modifiers = this.onModifiers s.Modifiers + } abstract member onCallableImplementation : QsCallable -> QsCallable default this.onCallableImplementation (c : QsCallable) = diff --git a/src/QsCompiler/Core/TreeWalker.fs b/src/QsCompiler/Core/TreeWalker.fs index 523096efa2..d860b58d47 100644 --- a/src/QsCompiler/Core/TreeWalker.fs +++ b/src/QsCompiler/Core/TreeWalker.fs @@ -52,6 +52,9 @@ type SyntaxTreeWalker() = abstract member onSourceFile : NonNullable -> unit default this.onSourceFile f = () + abstract member onModifiers : Modifiers -> unit + default this.onModifiers m = () + abstract member onTypeItems : QsTuple -> unit default this.onTypeItems tItem = match tItem with @@ -70,6 +73,7 @@ type SyntaxTreeWalker() = this.Scope.Expression.Type.Walk s.ArgumentType this.Scope.Expression.Type.Walk s.ReturnType this.Scope.Expression.Type.onCallableInformation s.Information + this.onModifiers s.Modifiers abstract member onExternalImplementation : unit -> unit @@ -148,11 +152,15 @@ type SyntaxTreeWalker() = this.onSourceFile t.SourceFile this.onLocation t.Location t.Attributes |> Seq.iter this.onAttribute - // TODO: Should modifiers have their own walker? - this.Scope.Expression.Type.Walk t.Type.UnderlyingType + this.onTypeSignature t.Type this.onTypeItems t.TypeItems this.onDocumentation t.Documentation + abstract member onTypeSignature : ResolvedTypeSignature -> unit + default this.onTypeSignature s = + this.Scope.Expression.Type.Walk s.UnderlyingType + this.onModifiers s.Modifiers + abstract member onCallableImplementation : QsCallable -> unit default this.onCallableImplementation (c : QsCallable) = this.onSourceFile c.SourceFile From 088d7c5ba85609819adc76d1c96b2474bc83beb9 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 23 Jan 2020 18:34:36 -0800 Subject: [PATCH 006/135] Move modifiers out of the signatures --- .../CompilationManager/CompilationUnit.cs | 59 +++++++++++++--- .../EditorSupport/CodeActions.cs | 4 +- .../CompilationManager/TypeChecking.cs | 39 ++++++++--- src/QsCompiler/Core/ConstructorExtensions.fs | 9 +-- src/QsCompiler/Core/DeclarationHeaders.fs | 10 ++- src/QsCompiler/Core/SymbolResolution.fs | 2 +- src/QsCompiler/Core/SymbolTable.fs | 57 +++++++-------- src/QsCompiler/Core/SyntaxGenerator.fs | 3 +- src/QsCompiler/Core/TreeTransformation.fs | 19 +++-- src/QsCompiler/Core/TreeWalker.fs | 10 +-- .../DataStructures/Serialization.fs | 25 ------- src/QsCompiler/DataStructures/SyntaxTokens.fs | 30 +++----- src/QsCompiler/DataStructures/SyntaxTree.fs | 18 ++--- src/QsCompiler/DocumentationParser/Utils.cs | 2 +- .../DeclarationVerification.fs | 10 +-- .../SyntaxProcessor/SymbolTracker.fs | 2 +- .../SyntaxProcessor/SyntaxExtensions.fs | 70 ++++++++++--------- .../SyntaxProcessor/TreeVerification.fs | 1 - .../Tests.Compiler/SerializationTests.fs | 13 ++-- .../Tests.DocGenerator/DocParsingTests.cs | 10 ++- .../TextProcessor/QsFragmentParsing.fs | 21 +++--- .../TextProcessor/SyntaxExtensions.fs | 9 +-- .../Transformations/Monomorphization.cs | 5 +- 23 files changed, 221 insertions(+), 207 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 330f4aa450..d300341b85 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -347,6 +347,7 @@ internal void UpdateTypes(IEnumerable updates) var type = new QsCustomType( compiled.FullName, compiled.Attributes, + compiled.Modifiers, compiled.SourceFile, header.Location, compiled.Type, @@ -404,8 +405,19 @@ internal void UpdateCallables(IEnumerable updates) var defaultSpec = new QsSpecialization(QsSpecializationKind.QsBody, header.QualifiedName, header.Attributes, header.SourceFile, header.Location, QsNullable>.Null, header.Signature, SpecializationImplementation.Intrinsic, ImmutableArray.Empty, QsComments.Empty); - this.CompiledCallables[fullName] = new QsCallable(header.Kind, header.QualifiedName, header.Attributes, header.SourceFile, header.Location, - header.Signature, header.ArgumentTuple, ImmutableArray.Create(defaultSpec), header.Documentation, QsComments.Empty); + this.CompiledCallables[fullName] = new QsCallable( + header.Kind, + header.QualifiedName, + header.Attributes, + header.Modifiers, + header.SourceFile, + header.Location, + header.Signature, + header.ArgumentTuple, + ImmutableArray.Create(defaultSpec), + header.Documentation, + QsComments.Empty + ); continue; } @@ -430,8 +442,19 @@ internal void UpdateCallables(IEnumerable updates) }) .Where(spec => spec != null).ToImmutableArray(); - var callable = new QsCallable(compiled.Kind, compiled.FullName, compiled.Attributes, compiled.SourceFile, header.Location, - compiled.Signature, compiled.ArgumentTuple, specializations, compiled.Documentation, compiled.Comments); + var callable = new QsCallable( + compiled.Kind, + compiled.FullName, + compiled.Attributes, + compiled.Modifiers, + compiled.SourceFile, + header.Location, + compiled.Signature, + compiled.ArgumentTuple, + specializations, + compiled.Documentation, + compiled.Comments + ); this.CompiledCallables[fullName] = callable; } } @@ -462,8 +485,19 @@ private QsCallable GetImportedCallable(CallableDeclarationHeader header) implementation, specHeader.Documentation, QsComments.Empty); }) .ToImmutableArray(); - return new QsCallable(header.Kind, header.QualifiedName, header.Attributes, header.SourceFile, header.Location, - header.Signature, header.ArgumentTuple, specializations, header.Documentation, QsComments.Empty); + return new QsCallable( + header.Kind, + header.QualifiedName, + header.Attributes, + header.Modifiers, + header.SourceFile, + header.Location, + header.Signature, + header.ArgumentTuple, + specializations, + header.Documentation, + QsComments.Empty + ); } /// @@ -473,8 +507,17 @@ private QsCallable GetImportedCallable(CallableDeclarationHeader header) private QsCustomType GetImportedType(TypeDeclarationHeader header) { if (header == null) throw new ArgumentNullException(nameof(header)); - return new QsCustomType(header.QualifiedName, header.Attributes, header.SourceFile, header.Location, - header.Type, header.TypeItems, header.Documentation, QsComments.Empty); + return new QsCustomType( + header.QualifiedName, + header.Attributes, + header.Modifiers, + header.SourceFile, + header.Location, + header.Type, + header.TypeItems, + header.Documentation, + QsComments.Empty + ); } /// diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index 916057ff6b..a6b0946ed4 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -235,7 +235,7 @@ IEnumerable GetCharacteristics(QsTuple> var characteristicsInFragment = fragment?.Kind is QsFragmentKind.FunctionDeclaration function ? GetCharacteristics(function.Item2.Argument) : fragment?.Kind is QsFragmentKind.OperationDeclaration operation ? GetCharacteristics(operation.Item2.Argument) : - fragment?.Kind is QsFragmentKind.TypeDefinition type ? GetCharacteristics(type.Item2.Items) : + fragment?.Kind is QsFragmentKind.TypeDefinition type ? GetCharacteristics(type.Item2) : Enumerable.Empty(); //var symbolInfo = file.TryGetQsSymbolInfo(d.Range.Start, false, out var fragment); @@ -439,7 +439,7 @@ bool EmptyOrFirstAttribute(IEnumerable line, out CodeFragment att) var (argTuple, typeParams) = callableDecl.IsValue ? (callableDecl.Item.Item2.Item2.Argument, callableDecl.Item.Item2.Item2.TypeParameters) - : typeDecl.IsValue ? (typeDecl.Item.Item2.Items, ImmutableArray.Empty) + : typeDecl.IsValue ? (typeDecl.Item.Item2.Item1, ImmutableArray.Empty) : (null, ImmutableArray.Empty); var hasOutput = callableDecl.IsValue && !callableDecl.Item.Item2.Item2.ReturnType.Type.IsUnitType; diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index 972dcd4f9d..beba4e80b0 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -111,13 +111,13 @@ internal static ImmutableArray DocumentingComments(this FileContentManag /// /// Returns the HeaderItems corresponding to all type declarations with a valid name in the given file, or null if the given file is null. /// - private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry)> GetTypeDeclarationHeaderItems + private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry>, Modifiers>>)> GetTypeDeclarationHeaderItems (this FileContentManager file) => file.GetHeaderItems(file?.TypeDeclarationTokens(), frag => frag.Kind.DeclaredType(), null); /// /// Returns the HeaderItems corresponding to all callable declarations with a valid name in the given file, or null if the given file is null. /// - private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry>)> GetCallableDeclarationHeaderItems + private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry>)> GetCallableDeclarationHeaderItems (this FileContentManager file) => file.GetHeaderItems(file?.CallableDeclarationTokens(), frag => frag.Kind.DeclaredCallable(), null); /// @@ -128,7 +128,7 @@ internal static ImmutableArray DocumentingComments(this FileContentManag /// The function returns Null if the Kind of the given fragment is null. /// private static QsNullable)>> SpecializationDeclaration - (HeaderEntry> parent, CodeFragment fragment) + (HeaderEntry> parent, CodeFragment fragment) { var specDecl = fragment.Kind?.DeclaredSpecialization(); var Null = QsNullable)>>.Null; @@ -226,7 +226,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position // add all type declarations var typesToCompile = AddItems(file.GetTypeDeclarationHeaderItems(), - (pos, name, decl, att, doc) => (ContainingParent(pos, namespaces)).TryAddType(file.FileName, Location(pos, name.Item2), name, decl, att, doc), + (pos, name, decl, att, doc) => (ContainingParent(pos, namespaces)).TryAddType(file.FileName, Location(pos, name.Item2), name, decl.Item1, att, decl.Item2, doc), file.FileName.Value, diagnostics); var tokensToCompile = new List<(QsQualifiedName, (QsComments, IEnumerable))>(); @@ -238,7 +238,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position // add all callable declarations var callablesToCompile = AddItems(file.GetCallableDeclarationHeaderItems(), - (pos, name, decl, att, doc) => (ContainingParent(pos, namespaces)).TryAddCallableDeclaration(file.FileName, Location(pos, name.Item2), name, decl, att, doc), + (pos, name, decl, att, doc) => (ContainingParent(pos, namespaces)).TryAddCallableDeclaration(file.FileName, Location(pos, name.Item2), name, Tuple.Create(decl.Item1, decl.Item2), att, decl.Item3, doc), file.FileName.Value, diagnostics); // add all callable specilizations -> TOOD: needs to be adapted for specializations outside the declaration body (not yet supported) @@ -271,7 +271,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position /// Throws an ArgumentNullException if either the given namespace or diagnostics are null. /// private static List AddSpecializationsToNamespace(FileContentManager file, Namespace ns, - (CodeFragment.TokenIndex, HeaderEntry>) parent, List diagnostics) + (CodeFragment.TokenIndex, HeaderEntry>) parent, List diagnostics) { if (ns == null) throw new ArgumentNullException(nameof(ns)); if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); @@ -1389,14 +1389,33 @@ QsCallable GetCallable((QsQualifiedName, ImmutableArray) specI } symbolTracker.EndScope(); QsCompilerError.Verify(symbolTracker.AllScopesClosed, "all scopes should be closed"); - return new QsCallable(info.Kind, parent, info.Attributes, info.SourceFile, QsNullable.Null, - info.Signature, info.ArgumentTuple, specs, info.Documentation, roots[parent].Item1); + return new QsCallable( + info.Kind, + parent, + info.Attributes, + info.Modifiers, + info.SourceFile, + QsNullable.Null, + info.Signature, + info.ArgumentTuple, + specs, + info.Documentation, + roots[parent].Item1 + ); } var callables = callableRoots.Select(GetSpecializations).Select(GetCallable).ToImmutableArray(); var types = typeDeclarations.Select(decl => new QsCustomType( - decl.Key, decl.Value.Attributes, decl.Value.SourceFile, decl.Value.Location, - decl.Value.Type, decl.Value.TypeItems, decl.Value.Documentation, roots[decl.Key].Item1)).ToImmutableArray(); + decl.Key, + decl.Value.Attributes, + decl.Value.Modifiers, + decl.Value.SourceFile, + decl.Value.Location, + decl.Value.Type, + decl.Value.TypeItems, + decl.Value.Documentation, + roots[decl.Key].Item1 + )).ToImmutableArray(); if (cancellationToken.IsCancellationRequested) return null; compilation.UpdateCallables(callables); diff --git a/src/QsCompiler/Core/ConstructorExtensions.fs b/src/QsCompiler/Core/ConstructorExtensions.fs index 0918f91d70..a373186bfe 100644 --- a/src/QsCompiler/Core/ConstructorExtensions.fs +++ b/src/QsCompiler/Core/ConstructorExtensions.fs @@ -169,12 +169,11 @@ type QsStatement with } type ResolvedSignature with - static member New ((argType, returnType), info, typeParams : IEnumerable<_>, modifiers) = { + static member New ((argType, returnType), info, typeParams : IEnumerable<_>) = { TypeParameters = typeParams.ToImmutableArray() ArgumentType = argType ReturnType = returnType Information = info - Modifiers = modifiers } type QsSpecialization with @@ -196,10 +195,11 @@ type QsSpecialization with static member NewControlledAdjoint = QsSpecialization.New QsControlledAdjoint type QsCallable with - static member New kind (source, location) (name, attributes, argTuple, signature, specializations : IEnumerable<_>, documentation, comments) = { + static member New kind (source, location) (name, attributes, modifiers, argTuple, signature, specializations : IEnumerable<_>, documentation, comments) = { Kind = kind FullName = name Attributes = attributes + Modifiers = modifiers SourceFile = source Location = location Signature = signature @@ -213,9 +213,10 @@ type QsCallable with static member NewTypeConstructor = QsCallable.New QsCallableKind.TypeConstructor type QsCustomType with - static member New (source, location) (name, attributes, items, typeSignature, documentation, comments) = { + static member New (source, location) (name, attributes, modifiers, items, typeSignature, documentation, comments) = { FullName = name Attributes = attributes + Modifiers = modifiers SourceFile = source Location = location Type = typeSignature diff --git a/src/QsCompiler/Core/DeclarationHeaders.fs b/src/QsCompiler/Core/DeclarationHeaders.fs index 9e087d6b48..9fd83377a7 100644 --- a/src/QsCompiler/Core/DeclarationHeaders.fs +++ b/src/QsCompiler/Core/DeclarationHeaders.fs @@ -96,10 +96,11 @@ module DeclarationHeader = type TypeDeclarationHeader = { QualifiedName : QsQualifiedName Attributes : ImmutableArray + Modifiers : Modifiers SourceFile : NonNullable Position : DeclarationHeader.Offset SymbolRange : DeclarationHeader.Range - Type : ResolvedTypeSignature + Type : ResolvedType TypeItems : QsTuple Documentation : ImmutableArray } @@ -111,6 +112,7 @@ type TypeDeclarationHeader = { static member New (customType : QsCustomType) = { QualifiedName = customType.FullName Attributes = customType.Attributes + Modifiers = customType.Modifiers SourceFile = customType.SourceFile Position = customType.Location |> DeclarationHeader.CreateOffset SymbolRange = customType.Location |> DeclarationHeader.CreateRange @@ -124,8 +126,8 @@ type TypeDeclarationHeader = { let attributesAreNullOrDefault = Object.ReferenceEquals(header.Attributes, null) || header.Attributes.IsDefault let header = if attributesAreNullOrDefault then {header with Attributes = ImmutableArray.Empty} else header // no reason to raise an error if not (Object.ReferenceEquals(header.TypeItems, null)) then success, header - else false, {header with TypeItems = header.Type.UnderlyingType - |> Anonymous |> QsTupleItem |> ImmutableArray.Create |> QsTuple} + else false, {header with + TypeItems = header.Type |> Anonymous |> QsTupleItem |> ImmutableArray.Create |> QsTuple} member this.ToJson() : string = DeclarationHeader.ToJson this @@ -136,6 +138,7 @@ type CallableDeclarationHeader = { Kind : QsCallableKind QualifiedName : QsQualifiedName Attributes : ImmutableArray + Modifiers : Modifiers SourceFile : NonNullable Position : DeclarationHeader.Offset SymbolRange : DeclarationHeader.Range @@ -152,6 +155,7 @@ type CallableDeclarationHeader = { Kind = callable.Kind QualifiedName = callable.FullName Attributes = callable.Attributes + Modifiers = callable.Modifiers SourceFile = callable.SourceFile Position = callable.Location |> DeclarationHeader.CreateOffset SymbolRange = callable.Location |> DeclarationHeader.CreateRange diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index 1481239512..1768de51a0 100644 --- a/src/QsCompiler/Core/SymbolResolution.fs +++ b/src/QsCompiler/Core/SymbolResolution.fs @@ -37,6 +37,7 @@ type internal Resolution<'T,'R> = internal { Resolved : QsNullable<'R> DefinedAttributes : ImmutableArray ResolvedAttributes : ImmutableArray + Modifiers : Modifiers Documentation : ImmutableArray } @@ -239,7 +240,6 @@ module SymbolResolution = TypeParameters = resolvedParams ArgumentType = argType ReturnType = returnType - Modifiers = signature.Modifiers Information = callableInfo } (resolvedSig, argTuple), [inErr; outErr; resErrs; tpErrs] |> Array.concat diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index d7cf8929f9..36a7caa927 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -24,19 +24,20 @@ type private PartialNamespace private source : NonNullable, documentation : IEnumerable>, openNS : IEnumerable, string>>, - typeDecl : IEnumerable, Resolution>>>, + typeDecl : IEnumerable, Resolution, ResolvedType * QsTuple<_>>>>, callableDecl : IEnumerable, QsCallableKind * Resolution>>>, specializations : IEnumerable, List>>>) = let keySelector (item : KeyValuePair<'k,'v>) = item.Key let valueSelector (item : KeyValuePair<'k,'v>) = item.Value - let unresolved (location : QsLocation) (definition, attributes, doc) = { - Defined = definition; + let unresolved (location : QsLocation) (definition, attributes, modifiers, doc) = { + Defined = definition DefinedAttributes = attributes - Resolved = Null; + Resolved = Null ResolvedAttributes = ImmutableArray.Empty - Position = location.Offset; - Range = location.Range; + Modifiers = modifiers + Position = location.Offset + Range = location.Range Documentation = doc } @@ -130,7 +131,7 @@ type private PartialNamespace private /// Adds the corresponding type constructor to the dictionary of declared callables. /// The given location is associated with both the type constructur and the type itself and accessible via the record properties Position and SymbolRange. /// -> Note that this routine will fail with the standard dictionary.Add error if either a type or a callable with that name already exists. - member this.AddType (location : QsLocation) (tName, typeSignature, attributes, documentation) = + member this.AddType (location : QsLocation) (tName, typeTuple, attributes, modifiers, documentation) = let mutable anonItemId = 0 let withoutRange sym = {Symbol = sym; Range = Null} let replaceAnonymous (itemName : QsSymbol, itemType) = // positional info for types in type constructors is removed upon resolution @@ -146,16 +147,15 @@ type private PartialNamespace private let rec buildItem = function | QsTuple args -> (args |> Seq.map buildItem).ToImmutableArray() |> QsTuple | QsTupleItem (n, t) -> replaceAnonymous (n, t) - match typeSignature.Items with + match typeTuple with | QsTupleItem (n, t) -> ImmutableArray.Create (replaceAnonymous (n, t)) |> QsTuple - | QsTuple _ -> buildItem typeSignature.Items + | QsTuple _ -> buildItem typeTuple let returnType = {Type = UserDefinedType (QualifiedSymbol (this.Name, tName) |> withoutRange); Range = Null} { TypeParameters = ImmutableArray.Empty Argument = constructorArgument ReturnType = returnType Characteristics = {Characteristics = EmptySet; Range = Null} - Modifiers = typeSignature.Modifiers } // There are a couple of reasons not just blindly attach all attributes associated with the type to the constructor: @@ -171,8 +171,8 @@ type private PartialNamespace private if attributes |> Seq.exists (SymbolResolution.IndicatesDeprecation validDeprecatedQualification) then ImmutableArray.Create deprecationWithoutRedirect else ImmutableArray.Empty - TypeDeclarations.Add(tName, (typeSignature, attributes, documentation) |> unresolved location) - this.AddCallableDeclaration location (tName, (TypeConstructor, constructorSignature), constructorAttr, ImmutableArray.Empty) + TypeDeclarations.Add(tName, (typeTuple, attributes, modifiers, documentation) |> unresolved location) + this.AddCallableDeclaration location (tName, (TypeConstructor, constructorSignature), constructorAttr, modifiers, ImmutableArray.Empty) let bodyGen = {TypeArguments = Null; Generator = QsSpecializationGeneratorKind.Intrinsic; Range = Value location.Range} this.AddCallableSpecialization location QsBody (tName, bodyGen, ImmutableArray.Empty, ImmutableArray.Empty) @@ -180,8 +180,8 @@ type private PartialNamespace private /// with the given callable name and signature to the dictionary of declared callables. /// The given location is associated with the callable declaration and accessible via the record properties Position and SymbolRange. /// -> Note that this routine will fail with the standard dictionary.Add error if a callable with that name already exists. - member this.AddCallableDeclaration location (cName, (kind, signature), attributes, documentation) = - CallableDeclarations.Add(cName, (kind, (signature, attributes, documentation) |> unresolved location)) + member this.AddCallableDeclaration location (cName, (kind, signature), attributes, modifiers, documentation) = + CallableDeclarations.Add(cName, (kind, (signature, attributes, modifiers, documentation) |> unresolved location)) /// Adds the callable specialization defined by the given kind and generator for the callable of the given name to the dictionary of declared specializations. /// The given location is associated with the given specialization and accessible via the record properties Position and HeaderRange. @@ -192,7 +192,7 @@ type private PartialNamespace private member this.AddCallableSpecialization location kind (cName, generator : QsSpecializationGenerator, attributes, documentation) = // NOTE: all types that are not specialized need to be resolved according to the file in which the callable is declared, // but all specialized types need to be resolved according to *this* file - let spec = kind, (generator, attributes, documentation) |> unresolved location + let spec = kind, (generator, attributes, {Access = DefaultAccess}, documentation) |> unresolved location match CallableSpecializations.TryGetValue cName with | true, specs -> specs.Add spec // it is up to the namespace to verify the type specializations | false, _ -> CallableSpecializations.Add(cName, new List<_>([spec])) @@ -354,10 +354,7 @@ and Namespace private | false, _ -> ArgumentException "given source file is not listed as source of the namespace" |> raise | true, partialNS -> partialNS.TryGetType attName |> function | true, resolution when resolution.DefinedAttributes |> Seq.exists compareAttributeName -> - Some { - UnderlyingType = resolution.Resolved.ValueOrApply missingResolutionException |> fst - Modifiers = resolution.Defined.Modifiers - } + resolution.Resolved.ValueOrApply missingResolutionException |> fst |> Some | _ -> None /// Returns the type with the given name defined in the given source file within this namespace. @@ -544,12 +541,12 @@ and Namespace private /// The given location is associated with both the type constructur and the type itself and accessible via the record properties Position and SymbolRange. /// If a type or callable with that name already exists, returns an array of suitable diagnostics. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. - member this.TryAddType (source, location) ((tName, tRange), typeSignature, attributes, documentation) : QsCompilerDiagnostic[] = + member this.TryAddType (source, location) ((tName, tRange), typeTuple, attributes, modifiers, documentation) : QsCompilerDiagnostic[] = match Parts.TryGetValue source with | true, partial when not (IsDefined tName) -> TypesDefinedInAllSourcesCache <- null CallablesDefinedInAllSourcesCache <- null - partial.AddType location (tName, typeSignature, attributes, documentation); [||] + partial.AddType location (tName, typeTuple, attributes, modifiers, documentation); [||] | true, _ -> this.ContainsType tName |> function | Value _ -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeRedefinition, [tName.Value]) |] | Null -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeConstructorOverlapWithCallable, [tName.Value]) |] @@ -561,11 +558,11 @@ and Namespace private /// The given location is associated with the callable declaration and accessible via the record properties Position and SymbolRange. /// If a callable with that name already exists, returns an array of suitable diagnostics. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. - member this.TryAddCallableDeclaration (source, location) ((cName, cRange), (kind, signature), attributes, documentation) = + member this.TryAddCallableDeclaration (source, location) ((cName, cRange), (kind, signature), attributes, modifiers, documentation) = match Parts.TryGetValue source with | true, partial when not (IsDefined cName) -> CallablesDefinedInAllSourcesCache <- null - partial.AddCallableDeclaration location (cName, (kind, signature), attributes, documentation); [||] + partial.AddCallableDeclaration location (cName, (kind, signature), attributes, modifiers, documentation); [||] | true, _ -> this.ContainsType cName |> function | Value _ -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableOverlapWithTypeConstructor, [cName.Value]) |] | Null -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableRedefinition, [cName.Value]) |] @@ -782,7 +779,7 @@ and NamespaceManager match Namespaces.TryGetValue udt.Namespace with | true, ns -> ns.TryGetAttributeDeclaredIn declSource (udt.Name, validQualifications) |> function | None -> None, [| symRange.ValueOr QsCompilerDiagnostic.DefaultRange |> QsCompilerDiagnostic.Error (ErrorCode.NotMarkedAsAttribute, [fullName]) |] - | Some argType -> Some (udt, argType.UnderlyingType), errs + | Some argType -> Some (udt, argType), errs | false, _ -> QsCompilerError.Raise "namespace for defined type not found"; None, errs | None, errs -> None, errs let resolved, msgs = SymbolResolution.ResolveAttribute getAttribute attribute @@ -934,7 +931,7 @@ and NamespaceManager ns.TypesDefinedInAllSources() |> Seq.collect (fun kvPair -> let tName, (source, qsType) = kvPair.Key, kvPair.Value let fullName = {Namespace = ns.Name; Name = tName} - let resolved, msgs = qsType.Defined.Items |> this.ResolveTypeDeclaration (fullName, source) + let resolved, msgs = qsType.Defined |> this.ResolveTypeDeclaration (fullName, source) ns.SetTypeResolution source (tName, resolved |> Value, ImmutableArray.Empty) msgs |> Array.map (fun msg -> source, (qsType.Position, msg)))) // ... before we can resolve the corresponding attributes. @@ -1115,6 +1112,7 @@ and NamespaceManager Kind = kind QualifiedName = {Namespace = ns.Name; Name = cName} Attributes = declaration.ResolvedAttributes + Modifiers = declaration.Modifiers SourceFile = source Position = DeclarationHeader.Offset.Defined declaration.Position SymbolRange = DeclarationHeader.Range.Defined declaration.Range @@ -1146,10 +1144,11 @@ and NamespaceManager | Value (underlyingType, items) -> Some { QualifiedName = {Namespace = ns.Name; Name = tName} Attributes = qsType.ResolvedAttributes + Modifiers = qsType.Modifiers SourceFile = source Position = DeclarationHeader.Offset.Defined qsType.Position SymbolRange = DeclarationHeader.Range.Defined qsType.Range - Type = {UnderlyingType = underlyingType; Modifiers = qsType.Defined.Modifiers} + Type = underlyingType TypeItems = items Documentation = qsType.Documentation })) @@ -1241,6 +1240,7 @@ and NamespaceManager Kind = kind QualifiedName = fullName Attributes = declaration.ResolvedAttributes + Modifiers = declaration.Modifiers SourceFile = source Position = DeclarationHeader.Offset.Defined declaration.Position SymbolRange = DeclarationHeader.Range.Defined declaration.Range @@ -1287,15 +1287,16 @@ and NamespaceManager /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. member private this.TryGetTypeHeader (typeName : QsQualifiedName, declSource) (nsName, source) = let BuildHeader fullName (source, declaration) = - let fallback () = declaration.Defined.Items |> this.ResolveTypeDeclaration (typeName, source) |> fst + let fallback () = declaration.Defined |> this.ResolveTypeDeclaration (typeName, source) |> fst let underlyingType, items = declaration.Resolved.ValueOrApply fallback Value { QualifiedName = fullName Attributes = declaration.ResolvedAttributes + Modifiers = declaration.Modifiers SourceFile = source Position = DeclarationHeader.Offset.Defined declaration.Position SymbolRange = DeclarationHeader.Range.Defined declaration.Range - Type = {UnderlyingType = underlyingType; Modifiers = declaration.Defined.Modifiers} + Type = underlyingType TypeItems = items Documentation = declaration.Documentation } diff --git a/src/QsCompiler/Core/SyntaxGenerator.fs b/src/QsCompiler/Core/SyntaxGenerator.fs index 8754f51dc0..a9a1c40fc7 100644 --- a/src/QsCompiler/Core/SyntaxGenerator.fs +++ b/src/QsCompiler/Core/SyntaxGenerator.fs @@ -201,8 +201,7 @@ module SyntaxGenerator = let WithoutRangeInfo (signature : ResolvedSignature) = let argType = signature.ArgumentType |> StripPositionInfo.Apply let returnType = signature.ReturnType |> StripPositionInfo.Apply - ResolvedSignature.New ((argType, returnType), - signature.Information, signature.TypeParameters, signature.Modifiers) + ResolvedSignature.New ((argType, returnType), signature.Information, signature.TypeParameters) /// Given the resolved argument type of an operation, returns the argument type of its controlled version. let AddControlQubits (argT : ResolvedType) = diff --git a/src/QsCompiler/Core/TreeTransformation.fs b/src/QsCompiler/Core/TreeTransformation.fs index 54ad2372ad..3468219968 100644 --- a/src/QsCompiler/Core/TreeTransformation.fs +++ b/src/QsCompiler/Core/TreeTransformation.fs @@ -81,8 +81,7 @@ type SyntaxTreeTransformation() = let argType = this.Scope.Expression.Type.Transform s.ArgumentType let returnType = this.Scope.Expression.Type.Transform s.ReturnType let info = this.Scope.Expression.Type.onCallableInformation s.Information - let modifiers = this.onModifiers s.Modifiers - ResolvedSignature.New ((argType, returnType), info, typeParams, modifiers) + ResolvedSignature.New ((argType, returnType), info, typeParams) abstract member onExternalImplementation : unit -> unit @@ -162,29 +161,27 @@ type SyntaxTreeTransformation() = let source = this.onSourceFile t.SourceFile let loc = this.onLocation t.Location let attributes = t.Attributes |> Seq.map this.onAttribute |> ImmutableArray.CreateRange - let typeSignature = this.onTypeSignature t.Type + let modifiers = this.onModifiers t.Modifiers + let typeTuple = this.Scope.Expression.Type.Transform t.Type let typeItems = this.onTypeItems t.TypeItems let doc = this.onDocumentation t.Documentation - QsCustomType.New (source, loc) (t.FullName, attributes, typeItems, typeSignature, doc, t.Comments) + QsCustomType.New (source, loc) (t.FullName, attributes, modifiers, typeItems, typeTuple, doc, t.Comments) - abstract member onTypeSignature : ResolvedTypeSignature -> ResolvedTypeSignature - default this.onTypeSignature s = - { - UnderlyingType = this.Scope.Expression.Type.Transform s.UnderlyingType - Modifiers = this.onModifiers s.Modifiers - } abstract member onCallableImplementation : QsCallable -> QsCallable default this.onCallableImplementation (c : QsCallable) = let source = this.onSourceFile c.SourceFile let loc = this.onLocation c.Location let attributes = c.Attributes |> Seq.map this.onAttribute |> ImmutableArray.CreateRange + let modifiers = this.onModifiers c.Modifiers let signature = this.onSignature c.Signature let argTuple = this.onArgumentTuple c.ArgumentTuple let specializations = c.Specializations |> Seq.map this.dispatchSpecialization let doc = this.onDocumentation c.Documentation let comments = c.Comments - QsCallable.New c.Kind (source, loc) (c.FullName, attributes, argTuple, signature, specializations, doc, comments) + QsCallable.New c.Kind + (source, loc) + (c.FullName, attributes, modifiers, argTuple, signature, specializations, doc, comments) abstract member onOperation : QsCallable -> QsCallable default this.onOperation c = this.onCallableImplementation c diff --git a/src/QsCompiler/Core/TreeWalker.fs b/src/QsCompiler/Core/TreeWalker.fs index d860b58d47..1cf5178265 100644 --- a/src/QsCompiler/Core/TreeWalker.fs +++ b/src/QsCompiler/Core/TreeWalker.fs @@ -73,7 +73,6 @@ type SyntaxTreeWalker() = this.Scope.Expression.Type.Walk s.ArgumentType this.Scope.Expression.Type.Walk s.ReturnType this.Scope.Expression.Type.onCallableInformation s.Information - this.onModifiers s.Modifiers abstract member onExternalImplementation : unit -> unit @@ -152,20 +151,17 @@ type SyntaxTreeWalker() = this.onSourceFile t.SourceFile this.onLocation t.Location t.Attributes |> Seq.iter this.onAttribute - this.onTypeSignature t.Type + this.onModifiers t.Modifiers + this.Scope.Expression.Type.Walk t.Type this.onTypeItems t.TypeItems this.onDocumentation t.Documentation - abstract member onTypeSignature : ResolvedTypeSignature -> unit - default this.onTypeSignature s = - this.Scope.Expression.Type.Walk s.UnderlyingType - this.onModifiers s.Modifiers - abstract member onCallableImplementation : QsCallable -> unit default this.onCallableImplementation (c : QsCallable) = this.onSourceFile c.SourceFile this.onLocation c.Location c.Attributes |> Seq.iter this.onAttribute + this.onModifiers c.Modifiers this.onSignature c.Signature this.onArgumentTuple c.ArgumentTuple c.Specializations |> Seq.iter this.dispatchSpecialization diff --git a/src/QsCompiler/DataStructures/Serialization.fs b/src/QsCompiler/DataStructures/Serialization.fs index 7607bf9e37..3652fd9ea0 100644 --- a/src/QsCompiler/DataStructures/Serialization.fs +++ b/src/QsCompiler/DataStructures/Serialization.fs @@ -69,30 +69,6 @@ type ResolvedTypeConverter(?ignoreSerializationException) = serializer.Serialize(writer, value.Resolution) -type ResolvedTypeSignatureConverter(?ignoreSerializationException) = - inherit JsonConverter() - let ignoreSerializationException = defaultArg ignoreSerializationException false - - /// For backwards compatibility, support deserializing either a ResolvedTypeSignture or a ResolvedType (with default - /// modifiers). - override this.ReadJson(reader : JsonReader, - objectType : Type, - existingValue : ResolvedTypeSignature, - hasExistingValue : bool, - serializer : JsonSerializer) = - let jobject = JObject.Load(reader) - let (underlyingType, modifiers) = jobject.ToObject(serializer) - { - UnderlyingType = - if obj.ReferenceEquals(underlyingType, null) then jobject.ToObject(serializer) - else underlyingType - Modifiers = modifiers - } - - override this.WriteJson(writer : JsonWriter, value : ResolvedTypeSignature, serializer : JsonSerializer) = - serializer.Serialize(writer, (value.UnderlyingType, value.Modifiers)) - - type ResolvedCharacteristicsConverter(?ignoreSerializationException) = inherit JsonConverter() let ignoreSerializationException = defaultArg ignoreSerializationException false @@ -163,7 +139,6 @@ module Json = new NonNullableConverter() :> JsonConverter new QsNullableLocationConverter(ignoreSerializationException) :> JsonConverter new ResolvedTypeConverter(ignoreSerializationException) :> JsonConverter - new ResolvedTypeSignatureConverter(ignoreSerializationException) :> JsonConverter new ResolvedCharacteristicsConverter(ignoreSerializationException) :> JsonConverter new TypedExpressionConverter() :> JsonConverter new ResolvedInitializerConverter() :> JsonConverter diff --git a/src/QsCompiler/DataStructures/SyntaxTokens.fs b/src/QsCompiler/DataStructures/SyntaxTokens.fs index 4591d75f39..9029b48b1c 100644 --- a/src/QsCompiler/DataStructures/SyntaxTokens.fs +++ b/src/QsCompiler/DataStructures/SyntaxTokens.fs @@ -185,6 +185,13 @@ type QsTuple<'Item> = | QsTupleItem of 'Item | QsTuple of ImmutableArray> +type CallableSignature = { + TypeParameters : ImmutableArray + Argument : QsTuple + ReturnType : QsType + Characteristics : Characteristics +} + /// Defines where a global declaration may be accessed. [] type AccessModifier = @@ -204,23 +211,6 @@ type Modifiers = { Access : AccessModifier } -type CallableSignature = { - TypeParameters : ImmutableArray - Argument : QsTuple - ReturnType : QsType - Characteristics : Characteristics - /// The modifiers that have been applied to this callable. - Modifiers : Modifiers -} - -// The signature of a user-defined type. -type TypeSignature = { - /// The underlying type and any named items. - Items : QsTuple - /// The modifiers that have been applied to this type. - Modifiers : Modifiers -} - type QsFragmentKind = | ExpressionStatement of QsExpression | ReturnStatement of QsExpression @@ -243,9 +233,9 @@ type QsFragmentKind = | AdjointDeclaration of QsSpecializationGenerator | ControlledDeclaration of QsSpecializationGenerator | ControlledAdjointDeclaration of QsSpecializationGenerator -| OperationDeclaration of QsSymbol * CallableSignature -| FunctionDeclaration of QsSymbol * CallableSignature -| TypeDefinition of QsSymbol * TypeSignature +| OperationDeclaration of QsSymbol * CallableSignature * Modifiers +| FunctionDeclaration of QsSymbol * CallableSignature * Modifiers +| TypeDefinition of QsSymbol * QsTuple * Modifiers | DeclarationAttribute of QsSymbol * QsExpression | OpenDirective of QsSymbol * QsNullable | NamespaceDeclaration of QsSymbol diff --git a/src/QsCompiler/DataStructures/SyntaxTree.fs b/src/QsCompiler/DataStructures/SyntaxTree.fs index 96a3431c81..f6bd4929a0 100644 --- a/src/QsCompiler/DataStructures/SyntaxTree.fs +++ b/src/QsCompiler/DataStructures/SyntaxTree.fs @@ -602,8 +602,6 @@ type ResolvedSignature = { ReturnType : ResolvedType /// contains the functors that the callable supports (necessarily empty for functions) Information : CallableInformation - /// Represents the Q# keywords attached to the declaration that modify its behavior. - Modifiers : Modifiers } @@ -661,6 +659,8 @@ type QsCallable = { FullName : QsQualifiedName /// contains all attributes associated with the callable Attributes : ImmutableArray + /// Represents the Q# keywords attached to the declaration that modify its behavior. + Modifiers : Modifiers /// identifier for the file the callable is declared in SourceFile : NonNullable /// Contains the location information for the declared callable. @@ -698,22 +698,14 @@ type QsTypeItem = | Anonymous of ResolvedType -/// Used to represent type and access information for a user defined type. -type ResolvedTypeSignature = { - /// Contains the underlying type, i.e. the argument type of the (auto-generated) type constructor associated with - /// the user defined type. - UnderlyingType : ResolvedType - /// Represents the Q# keywords attached to the declaration that modify its behavior. - Modifiers : Modifiers -} - - /// describes a Q# user defined type type QsCustomType = { /// contains the name of the type FullName : QsQualifiedName /// contains all attributes associated with the type Attributes : ImmutableArray + /// Represents the Q# keywords attached to the declaration that modify its behavior. + Modifiers : Modifiers /// identifier for the file the type is declared in SourceFile : NonNullable /// Contains the location information for the declared type. @@ -725,7 +717,7 @@ type QsCustomType = { /// Note that a user defined type is *not* considered to be a subtype of its underlying type, but rather its own, /// entirely distinct type, and the underlying type is merely the argument type of the (auto-generated) type /// constructor associated with the user defined type. - Type : ResolvedTypeSignature + Type : ResolvedType /// contains the type tuple defining the named and anonymous items of the type TypeItems : QsTuple /// content of documenting comments associated with the type diff --git a/src/QsCompiler/DocumentationParser/Utils.cs b/src/QsCompiler/DocumentationParser/Utils.cs index 57cc84729e..e915a49164 100644 --- a/src/QsCompiler/DocumentationParser/Utils.cs +++ b/src/QsCompiler/DocumentationParser/Utils.cs @@ -349,7 +349,7 @@ internal static string CustomTypeToSyntax(QsCustomType customType) sb.Append("newtype "); sb.Append(customType.FullName.Name.Value); sb.Append(" = "); - sb.Append(ResolvedTypeToString(customType.Type.UnderlyingType)); + sb.Append(ResolvedTypeToString(customType.Type)); sb.Append(";"); return sb.ToString(); } diff --git a/src/QsCompiler/SyntaxProcessor/DeclarationVerification.fs b/src/QsCompiler/SyntaxProcessor/DeclarationVerification.fs index 3abaa9e83a..7d167547a4 100644 --- a/src/QsCompiler/SyntaxProcessor/DeclarationVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/DeclarationVerification.fs @@ -66,12 +66,12 @@ let public OpenedNamespaceName this onInvalid = this |> OpenedNamespace |> NameOnly onInvalid /// If the given fragment kind is a type declaration, -/// returns the symbol for the type as well as the declared underlying type as Value. +/// returns the symbol for the type as well as the declared underlying type and modifiers as Value. /// Returns Null otherwise. [] let public DeclaredType this = match this with - | TypeDefinition (sym, decl) -> (sym, decl) |> Value + | TypeDefinition (sym, decl, modifiers) -> (sym, (decl, modifiers)) |> Value | _ -> Null /// If the given fragment kind is a type declaration, @@ -83,13 +83,13 @@ let public DeclaredTypeName this onInvalid = this |> DeclaredType |> NameOnly onInvalid /// If the given fragment kind is a callable declaration, -/// returns the symbol for the callable as well as its declared kind and signature as Value. +/// returns the symbol for the callable as well as its declared kind, signature and modifiers as Value. /// Returns Null otherwise. [] let public DeclaredCallable this = match this with - | FunctionDeclaration (sym, decl) -> (sym, (QsCallableKind.Function, decl)) |> Value - | OperationDeclaration (sym, decl) -> (sym, (QsCallableKind.Operation, decl)) |> Value + | FunctionDeclaration (sym, decl, modifiers) -> (sym, (QsCallableKind.Function, decl, modifiers)) |> Value + | OperationDeclaration (sym, decl, modifiers) -> (sym, (QsCallableKind.Operation, decl, modifiers)) |> Value | _ -> Null /// If the given fragment kind is a callable declaration, diff --git a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs index 006d67e6d6..ad1333f429 100644 --- a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs +++ b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs @@ -258,7 +258,7 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif /// Adds a suitable diagnostic and returns an invalid type if the underlying type could not be determined. member this.GetUnderlyingType addError (udt : UserDefinedType) = match this.TryGetTypeDeclaration addError udt with - | Value decl -> decl.Type.UnderlyingType |> StripPositionInfo.Apply + | Value decl -> decl.Type |> StripPositionInfo.Apply | Null -> InvalidType |> ResolvedType.New /// Given the fully qualified name of a user defined type as well as the identifier specifying an item, diff --git a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs index 17627e6e83..a11614af44 100644 --- a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs +++ b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs @@ -152,34 +152,34 @@ let public SymbolInformation fragmentKind = let chooseValues = QsNullable<_>.Choose id >> Seq.toList let addVariable var (syms, ts, exs) = var :: syms, ts, exs fragmentKind |> function - | QsFragmentKind.ExpressionStatement ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr - | QsFragmentKind.ReturnStatement ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr - | QsFragmentKind.FailStatement ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr - | QsFragmentKind.MutableBinding (sym, ex) -> sym |> SymbolDeclarations, ([ex] , []) |> collectWith SymbolsFromExpr - | QsFragmentKind.ImmutableBinding (sym, ex) -> sym |> SymbolDeclarations, ([ex] , []) |> collectWith SymbolsFromExpr - | QsFragmentKind.ValueUpdate (lhs, rhs) -> [], ([lhs;rhs], []) |> collectWith SymbolsFromExpr - | QsFragmentKind.IfClause ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr - | QsFragmentKind.ElifClause ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr - | QsFragmentKind.ElseClause -> [], ([] , [], []) - | QsFragmentKind.ForLoopIntro (sym, ex) -> sym |> SymbolDeclarations, ([ex] , []) |> collectWith SymbolsFromExpr - | QsFragmentKind.WhileLoopIntro ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr - | QsFragmentKind.RepeatIntro -> [], ([] , [], []) - | QsFragmentKind.UntilSuccess (ex,_) -> [], ([ex] , []) |> collectWith SymbolsFromExpr - | QsFragmentKind.WithinBlockIntro -> [], ([] , [], []) - | QsFragmentKind.ApplyBlockIntro -> [], ([] , [], []) - | QsFragmentKind.UsingBlockIntro (sym, init) -> sym |> SymbolDeclarations, init |> VariablesInInitializer - | QsFragmentKind.BorrowingBlockIntro (sym, init) -> sym |> SymbolDeclarations, init |> VariablesInInitializer - | QsFragmentKind.BodyDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) - | QsFragmentKind.AdjointDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) - | QsFragmentKind.ControlledDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) - | QsFragmentKind.ControlledAdjointDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) - | QsFragmentKind.OperationDeclaration (n, signature) -> (n, signature) |> SymbolsInCallableDeclaration - | QsFragmentKind.FunctionDeclaration (n, signature) -> (n, signature) |> SymbolsInCallableDeclaration - | QsFragmentKind.TypeDefinition (sym, t) -> (sym, t.Items) |> SymbolsInArgumentTuple - | QsFragmentKind.DeclarationAttribute (sym, ex) -> [], ([AttributeAsCallExpr (sym, ex)], []) |> collectWith SymbolsFromExpr |> addVariable sym - | QsFragmentKind.NamespaceDeclaration sym -> sym |> SymbolDeclarations, ([], [], []) - | QsFragmentKind.OpenDirective (nsName, alias) -> [alias] |> chooseValues, ([nsName], [], []) - | QsFragmentKind.InvalidFragment _ -> [], ([], [], []) + | QsFragmentKind.ExpressionStatement ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr + | QsFragmentKind.ReturnStatement ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr + | QsFragmentKind.FailStatement ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr + | QsFragmentKind.MutableBinding (sym, ex) -> sym |> SymbolDeclarations, ([ex] , []) |> collectWith SymbolsFromExpr + | QsFragmentKind.ImmutableBinding (sym, ex) -> sym |> SymbolDeclarations, ([ex] , []) |> collectWith SymbolsFromExpr + | QsFragmentKind.ValueUpdate (lhs, rhs) -> [], ([lhs;rhs], []) |> collectWith SymbolsFromExpr + | QsFragmentKind.IfClause ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr + | QsFragmentKind.ElifClause ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr + | QsFragmentKind.ElseClause -> [], ([] , [], []) + | QsFragmentKind.ForLoopIntro (sym, ex) -> sym |> SymbolDeclarations, ([ex] , []) |> collectWith SymbolsFromExpr + | QsFragmentKind.WhileLoopIntro ex -> [], ([ex] , []) |> collectWith SymbolsFromExpr + | QsFragmentKind.RepeatIntro -> [], ([] , [], []) + | QsFragmentKind.UntilSuccess (ex,_) -> [], ([ex] , []) |> collectWith SymbolsFromExpr + | QsFragmentKind.WithinBlockIntro -> [], ([] , [], []) + | QsFragmentKind.ApplyBlockIntro -> [], ([] , [], []) + | QsFragmentKind.UsingBlockIntro (sym, init) -> sym |> SymbolDeclarations, init |> VariablesInInitializer + | QsFragmentKind.BorrowingBlockIntro (sym, init) -> sym |> SymbolDeclarations, init |> VariablesInInitializer + | QsFragmentKind.BodyDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) + | QsFragmentKind.AdjointDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) + | QsFragmentKind.ControlledDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) + | QsFragmentKind.ControlledAdjointDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) + | QsFragmentKind.OperationDeclaration (n, signature, _) -> (n, signature) |> SymbolsInCallableDeclaration + | QsFragmentKind.FunctionDeclaration (n, signature, _) -> (n, signature) |> SymbolsInCallableDeclaration + | QsFragmentKind.TypeDefinition (sym, t, _) -> (sym, t) |> SymbolsInArgumentTuple + | QsFragmentKind.DeclarationAttribute (sym, ex) -> [], ([AttributeAsCallExpr (sym, ex)], []) |> collectWith SymbolsFromExpr |> addVariable sym + | QsFragmentKind.NamespaceDeclaration sym -> sym |> SymbolDeclarations, ([], [], []) + | QsFragmentKind.OpenDirective (nsName, alias) -> [alias] |> chooseValues, ([nsName], [], []) + | QsFragmentKind.InvalidFragment _ -> [], ([], [], []) |> SymbolInformation.New let rec private ExpressionsInInitializer item = item.Initializer |> function @@ -284,7 +284,7 @@ let public TypeInfo (symbolTable : NamespaceManager) (currentNS, source) (qsType // TODO: Include modifiers. let name = decl.QualifiedName.Name.Value |> withNewLine let ns = sprintf "Namespace: %s" decl.QualifiedName.Namespace.Value |> withNewLine - let info = sprintf "Underlying type: %s" (TypeName decl.Type.UnderlyingType) + let info = sprintf "Underlying type: %s" (TypeName decl.Type) let doc = PrintSummary decl.Documentation markdown sprintf "User defined type %s%s%s%s" name ns info doc | None, Some sym -> sprintf "Type %s" sym.Value @@ -341,8 +341,14 @@ let public PrintArgumentTuple item = [] let public PrintSignature (header : CallableDeclarationHeader) = let callable = - QsCallable.New header.Kind (header.SourceFile, Null) - (header.QualifiedName, header.Attributes, header.ArgumentTuple, header.Signature, ImmutableArray.Empty, ImmutableArray.Empty, QsComments.Empty); + QsCallable.New header.Kind (header.SourceFile, Null) (header.QualifiedName, + header.Attributes, + header.Modifiers, + header.ArgumentTuple, + header.Signature, + ImmutableArray.Empty, + ImmutableArray.Empty, + QsComments.Empty) let signature = SyntaxTreeToQs.DeclarationSignature (callable, new Func<_,_>(TypeName)) let annotation = CharacteristicsAnnotation (header.Signature.Information.Characteristics, sprintf "%s%s" newLine) sprintf "%s%s" signature annotation @@ -385,7 +391,7 @@ let public DeclarationInfo symbolTable (locals : LocalDeclarations) (currentNS, // TODO: Include modifiers. let name = decl.QualifiedName.Name.Value |> withNewLine let ns = sprintf "Namespace: %s" decl.QualifiedName.Namespace.Value |> withNewLine - let info = sprintf "Underlying type: %s" (decl.Type.UnderlyingType |> TypeName) + let info = sprintf "Underlying type: %s" (decl.Type |> TypeName) let doc = PrintSummary decl.Documentation markdown sprintf "Declaration of user defined type %s%s%s%s" name ns info doc | None, _ -> diff --git a/src/QsCompiler/SyntaxProcessor/TreeVerification.fs b/src/QsCompiler/SyntaxProcessor/TreeVerification.fs index f19150f45f..91286c560f 100644 --- a/src/QsCompiler/SyntaxProcessor/TreeVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/TreeVerification.fs @@ -153,7 +153,6 @@ let CheckDefinedTypesForCycles (definitions : ImmutableArray vtypeList |> Seq.toList | QsTypeKind.UserDefinedType udt -> updateContainedReferences rootIndex (location, QsQualifiedName.New(udt.Namespace, udt.Name)) - |> List.map (fun s -> s.UnderlyingType) | _ -> [] let walk_udts () = // builds up containedTypes and containedIn diff --git a/src/QsCompiler/Tests.Compiler/SerializationTests.fs b/src/QsCompiler/Tests.Compiler/SerializationTests.fs index 57964756c2..13b47a3fbf 100644 --- a/src/QsCompiler/Tests.Compiler/SerializationTests.fs +++ b/src/QsCompiler/Tests.Compiler/SerializationTests.fs @@ -52,7 +52,6 @@ module SerializationTests = ArgumentType = argType |> ResolvedType.New ReturnType = rType |> ResolvedType.New Information = CallableInformation.New(ResolvedCharacteristics.FromProperties props, InferredCallableInformation.NoInformation) - Modifiers = {Access = DefaultAccess} } let varDecl name t (s, e) = { @@ -149,6 +148,7 @@ module SerializationTests = Kind = QsCallableKind.TypeConstructor QualifiedName = qualifiedName "Microsoft.Quantum" "Pair" Attributes = ImmutableArray.Empty + Modifiers = {Access = DefaultAccess} SourceFile = "%%%" |> NonNullable.New Position = (2,4) |> DeclarationHeader.Offset.Defined SymbolRange = ({Line = 1; Column = 9}, {Line = 1; Column = 13}) |> DeclarationHeader.Range.Defined @@ -162,6 +162,7 @@ module SerializationTests = Kind = QsCallableKind.Function QualifiedName = qualifiedName "Microsoft.Quantum" "emptyFunction" Attributes = ImmutableArray.Empty + Modifiers = {Access = DefaultAccess} SourceFile = "%%%" |> NonNullable.New Position = (4,4) |> DeclarationHeader.Offset.Defined SymbolRange = ({Line = 1; Column = 10}, {Line = 1; Column = 23}) |> DeclarationHeader.Range.Defined @@ -175,6 +176,7 @@ module SerializationTests = Kind = QsCallableKind.Operation QualifiedName = qualifiedName "Microsoft.Quantum" "emptyOperation" Attributes = ImmutableArray.Empty + Modifiers = {Access = DefaultAccess} SourceFile = "%%%" |> NonNullable.New Position = (5,4) |> DeclarationHeader.Offset.Defined SymbolRange = ({Line = 1; Column = 11}, {Line = 1; Column = 25}) |> DeclarationHeader.Range.Defined @@ -188,6 +190,7 @@ module SerializationTests = Kind = QsCallableKind.TypeConstructor QualifiedName = qualifiedName "Microsoft.Quantum" "Unused" Attributes = ImmutableArray.Empty + Modifiers = {Access = DefaultAccess} SourceFile = "%%%" |> NonNullable.New Position = (3,4) |> DeclarationHeader.Offset.Defined SymbolRange = ({Line = 1; Column = 9}, {Line = 1; Column = 15}) |> DeclarationHeader.Range.Defined @@ -208,11 +211,11 @@ module SerializationTests = { QualifiedName = qualifiedName "Microsoft.Quantum" "Pair" Attributes = ImmutableArray.Empty + Modifiers = {Access = DefaultAccess} SourceFile = "%%%" |> NonNullable.New Position = (2,4) |> DeclarationHeader.Offset.Defined SymbolRange = ({Line = 1; Column = 9}, {Line = 1; Column = 13}) |> DeclarationHeader.Range.Defined - Type = {UnderlyingType = ResolvedType.New tupleIntIntType - Modifiers = {Access = DefaultAccess}} + Type = ResolvedType.New tupleIntIntType TypeItems = intIntTypeItems Documentation = ImmutableArray.Empty } @@ -221,11 +224,11 @@ module SerializationTests = { QualifiedName = qualifiedName "Microsoft.Quantum" "Unused" Attributes = ImmutableArray.Empty + Modifiers = {Access = DefaultAccess} SourceFile = "%%%" |> NonNullable.New Position = (3,4) |> DeclarationHeader.Offset.Defined SymbolRange = ({Line = 1; Column = 9}, {Line = 1; Column = 15}) |> DeclarationHeader.Range.Defined - Type = {UnderlyingType = ResolvedType.New tupleIntIntType - Modifiers = {Access = DefaultAccess}} + Type = ResolvedType.New tupleIntIntType TypeItems = intIntTypeItems Documentation = ImmutableArray.Empty } diff --git a/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs b/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs index 0e13f98d19..6e1926e01d 100644 --- a/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs +++ b/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs @@ -158,12 +158,10 @@ element indexes the subsystem on which the generator acts on. var generatorIndexType = new QsCustomType(MakeFullName("GeneratorIndex"), ImmutableArray.Empty, + new Modifiers(AccessModifier.DefaultAccess), NonNullable.New("GeneratorRepresentation.qs"), ZeroLocation, - new ResolvedTypeSignature( - baseType, - new Modifiers(AccessModifier.DefaultAccess) - ), + baseType, typeItems, comments.ToImmutableArray(), QsComments.Empty); @@ -327,8 +325,7 @@ of the generator represented by $U$. var typeParams = new QsLocalSymbol[] { }.ToImmutableArray(); var argTypes = new ResolvedType[] { qubitToUnitOp, qubitToUnitOp, qubitToUnitOpAC, phaseEstOp, qubitArrayType }.ToImmutableArray(); var argTupleType = ResolvedType.New(QsType.NewTupleType(argTypes)); - var noModifiers = new Modifiers(AccessModifier.DefaultAccess); - var signature = new ResolvedSignature(typeParams, argTupleType, doubleType, noInfo, noModifiers); + var signature = new ResolvedSignature(typeParams, argTupleType, doubleType, noInfo); var args = new List { BuildArgument("statePrepUnitary", qubitToUnitOp), BuildArgument("adiabaticUnitary", qubitToUnitOp), @@ -343,6 +340,7 @@ of the generator represented by $U$. var qsCallable = new QsCallable(QsCallableKind.Operation, MakeFullName("AdiabaticStateEnergyUnitary"), ImmutableArray.Empty, + new Modifiers(AccessModifier.DefaultAccess), NonNullable.New("Techniques.qs"), ZeroLocation, signature, diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 94f56090bc..09bfb554a6 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -148,14 +148,10 @@ let private signature = let symbolDeclaration = expectedIdentifierDeclaration (lAngle <|> lTuple) let returnTypeAnnotation = expected (typeAnnotation eof) ErrorCode.InvalidReturnTypeAnnotation ErrorCode.MissingReturnTypeAnnotation invalidType eof let characteristicsAnnotation = opt (qsCharacteristics.parse >>. expectedCharacteristics eof) |>> Option.defaultValue ((EmptySet, Null) |> Characteristics.New) + let signature = genericParamList .>>. (argumentTuple .>>. returnTypeAnnotation) .>>. characteristicsAnnotation |>> CallableSignature.New // TODO: Parse modifiers. - let signature = - genericParamList - .>>. (argumentTuple .>>. returnTypeAnnotation) - .>>. characteristicsAnnotation - .>>. (preturn { Access = DefaultAccess }) - |>> CallableSignature.New symbolDeclaration .>>. signature + |>> (fun (symbol, signature) -> (symbol, signature, {Access = DefaultAccess})) /// Parses a Q# functor generator directive. /// For a user defined implementation, expects a tuple argument of items that can either be a localIdentifier or omittedSymbols. @@ -281,18 +277,17 @@ and private controlledAdjointDeclaration = /// Uses buildFragment to parse a Q# OperationDeclaration as QsFragment. and private operationDeclaration = - let invalid = OperationDeclaration (invalidSymbol, CallableSignature.Invalid) + let invalid = OperationDeclaration (invalidSymbol, CallableSignature.Invalid, {Access = DefaultAccess}) buildFragment opDeclHeader.parse signature invalid OperationDeclaration eof /// Uses buildFragment to parse a Q# FunctionDeclaration as QsFragment. and private functionDeclaration = - let invalid = FunctionDeclaration (invalidSymbol, CallableSignature.Invalid) + let invalid = FunctionDeclaration (invalidSymbol, CallableSignature.Invalid, {Access = DefaultAccess}) buildFragment fctDeclHeader.parse signature invalid FunctionDeclaration eof /// Uses buildFragment to parse a Q# TypeDefinition as QsFragment. and private udtDeclaration = - let invalid = TypeDefinition (invalidSymbol, - { Items = invalidArgTupleItem; Modifiers = { Access = DefaultAccess } }) + let invalid = TypeDefinition (invalidSymbol, invalidArgTupleItem, {Access = DefaultAccess}) let udtTuple = // not unified with the argument tuple for callable declarations, since the error handling needs to be different let asAnonymousItem t = QsTupleItem ((MissingSymbol, Null) |> QsSymbol.New, t) let namedItem = @@ -305,10 +300,10 @@ and private udtDeclaration = buildTupleItem tupleItem (fst >> QsTuple) ErrorCode.InvalidUdtItemDeclaration ErrorCode.MissingUdtItemDeclaration invalidArgTupleItem eof let invalidNamedSingle = followedBy namedItem >>. optTupleBrackets namedItem |>> fst invalidNamedSingle <|> udtTupleItem // require parenthesis for a single named item - // TODO: Parse modifiers. let declBody = - expectedIdentifierDeclaration equal .>> equal - .>>. (udtTuple |>> (fun items -> { Items = items; Modifiers = { Access = DefaultAccess } })) + // TODO: Parse modifiers. + expectedIdentifierDeclaration equal .>> equal .>>. udtTuple + |>> (fun (symbol, tuple) -> (symbol, tuple, {Access = DefaultAccess})) buildFragment typeDeclHeader.parse declBody invalid TypeDefinition eof diff --git a/src/QsCompiler/TextProcessor/SyntaxExtensions.fs b/src/QsCompiler/TextProcessor/SyntaxExtensions.fs index 4bd1e818ae..9134864d02 100644 --- a/src/QsCompiler/TextProcessor/SyntaxExtensions.fs +++ b/src/QsCompiler/TextProcessor/SyntaxExtensions.fs @@ -98,22 +98,19 @@ type QsCompilerDiagnostic with type CallableSignature with - static member internal New (((typeParams, (argType, returnType)), characteristics), modifiers) = { + static member internal New ((typeParams, (argType, returnType)), characteristics) = { TypeParameters = typeParams Argument = argType ReturnType = returnType Characteristics = characteristics - Modifiers = modifiers } static member internal Invalid = let invalidType = (InvalidType, Null) |> QsType.New let invalidSymbol = (InvalidSymbol, Null) |> QsSymbol.New let invalidArg = QsTuple ([QsTupleItem (invalidSymbol, invalidType)].ToImmutableArray()) - CallableSignature.New (((ImmutableArray.Empty, - (invalidArg, invalidType)), - (InvalidSetExpr, Null) |> Characteristics.New), - { Access = DefaultAccess }) + CallableSignature.New ((ImmutableArray.Empty, (invalidArg, invalidType)), + (InvalidSetExpr, Null) |> Characteristics.New) type QsFragment with diff --git a/src/QsCompiler/Transformations/Monomorphization.cs b/src/QsCompiler/Transformations/Monomorphization.cs index fbfb0d5a11..f52732b913 100644 --- a/src/QsCompiler/Transformations/Monomorphization.cs +++ b/src/QsCompiler/Transformations/Monomorphization.cs @@ -224,9 +224,8 @@ public override ResolvedSignature onSignature(ResolvedSignature s) ImmutableArray.Empty, s.ArgumentType, s.ReturnType, - s.Information, - s.Modifiers - ); + s.Information + ); return base.onSignature(s); } } From 5b9a9e5fe49f728d1a7968dc96669916169427ce Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 24 Jan 2020 09:22:33 -0800 Subject: [PATCH 007/135] Fix attribute reader test --- src/QsCompiler/Tests.Compiler/SerializationTests.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/SerializationTests.fs b/src/QsCompiler/Tests.Compiler/SerializationTests.fs index 13b47a3fbf..a2a9ebe72b 100644 --- a/src/QsCompiler/Tests.Compiler/SerializationTests.fs +++ b/src/QsCompiler/Tests.Compiler/SerializationTests.fs @@ -235,15 +235,15 @@ module SerializationTests = |> testOne [] - let CALLABLE_1 = "{\"Kind\":{\"Case\":\"TypeConstructor\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":2,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":9},\"Item2\":{\"Line\":1,\"Column\":13}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"__Item1__\"]},\"Type\":{\"Case\":\"Int\"},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":1}}}]},{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"__Item2__\"]},\"Type\":{\"Case\":\"Int\"},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":1}}}]}]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"TupleType\",\"Fields\":[[{\"Case\":\"Int\"},{\"Case\":\"Int\"}]]},\"ReturnType\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":true}},\"Modifiers\":{\"Access\":{\"Case\":\"DefaultAccess\"}}},\"Documentation\":[\"type constructor for user defined type\"]}" + let CALLABLE_1 = "{\"Kind\":{\"Case\":\"TypeConstructor\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\"},\"Attributes\":[],\"Modifiers\":{\"Access\":{\"Case\":\"DefaultAccess\"}},\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":2,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":9},\"Item2\":{\"Line\":1,\"Column\":13}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"__Item1__\"]},\"Type\":{\"Case\":\"Int\"},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":1}}}]},{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"__Item2__\"]},\"Type\":{\"Case\":\"Int\"},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":1}}}]}]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"TupleType\",\"Fields\":[[{\"Case\":\"Int\"},{\"Case\":\"Int\"}]]},\"ReturnType\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":true}}},\"Documentation\":[\"type constructor for user defined type\"]}" [] - let TYPE_1 = "{\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":2,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":9},\"Item2\":{\"Line\":1,\"Column\":13}},\"Type\":{\"Item1\":{\"Case\":\"TupleType\",\"Fields\":[[{\"Case\":\"Int\"},{\"Case\":\"Int\"}]]},\"Item2\":{\"Access\":{\"Case\":\"DefaultAccess\"}}},\"TypeItems\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"Case\":\"Anonymous\",\"Fields\":[{\"Case\":\"Int\"}]}]},{\"Case\":\"QsTupleItem\",\"Fields\":[{\"Case\":\"Anonymous\",\"Fields\":[{\"Case\":\"Int\"}]}]}]]},\"Documentation\":[]}" + let TYPE_1 = "{\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\"},\"Attributes\":[],\"Modifiers\":{\"Access\":{\"Case\":\"DefaultAccess\"}},\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":2,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":9},\"Item2\":{\"Line\":1,\"Column\":13}},\"Type\":{\"Case\":\"TupleType\",\"Fields\":[[{\"Case\":\"Int\"},{\"Case\":\"Int\"}]]},\"TypeItems\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"Case\":\"Anonymous\",\"Fields\":[{\"Case\":\"Int\"}]}]},{\"Case\":\"QsTupleItem\",\"Fields\":[{\"Case\":\"Anonymous\",\"Fields\":[{\"Case\":\"Int\"}]}]}]]},\"Documentation\":[]}" [] - let CALLABLE_2 = "{\"Kind\":{\"Case\":\"Function\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyFunction\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":4,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":10},\"Item2\":{\"Line\":1,\"Column\":23}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"p\"]},\"Type\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":25},\"Item2\":{\"Line\":1,\"Column\":26}}}]}]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"ReturnType\":{\"Case\":\"UnitType\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}},\"Modifiers\":{\"Access\":{\"Case\":\"DefaultAccess\"}}},\"Documentation\":[]}" + let CALLABLE_2 = "{\"Kind\":{\"Case\":\"Function\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyFunction\"},\"Attributes\":[],\"Modifiers\":{\"Access\":{\"Case\":\"DefaultAccess\"}},\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":4,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":10},\"Item2\":{\"Line\":1,\"Column\":23}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[{\"Case\":\"QsTupleItem\",\"Fields\":[{\"VariableName\":{\"Case\":\"ValidName\",\"Fields\":[\"p\"]},\"Type\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"InferredInformation\":{\"IsMutable\":false,\"HasLocalQuantumDependency\":false},\"Position\":{\"Case\":\"Null\"},\"Range\":{\"Item1\":{\"Line\":1,\"Column\":25},\"Item2\":{\"Line\":1,\"Column\":26}}}]}]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"UserDefinedType\",\"Fields\":[{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"Pair\",\"Range\":{\"Case\":\"Null\"}}]},\"ReturnType\":{\"Case\":\"UnitType\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}}},\"Documentation\":[]}" [] let SPECIALIZATION_1 = "{\"Kind\":{\"Case\":\"QsBody\"},\"TypeArguments\":{\"Case\":\"Null\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}},\"Parent\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyFunction\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":4,\"Item2\":43},\"HeaderRange\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":5}},\"Documentation\":[]}" [] - let CALLABLE_3 = "{\"Kind\":{\"Case\":\"Operation\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyOperation\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":5,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":11},\"Item2\":{\"Line\":1,\"Column\":25}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"UnitType\"},\"ReturnType\":{\"Case\":\"UnitType\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}},\"Modifiers\":{\"Access\":{\"Case\":\"DefaultAccess\"}}},\"Documentation\":[]}" + let CALLABLE_3 = "{\"Kind\":{\"Case\":\"Operation\"},\"QualifiedName\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyOperation\"},\"Attributes\":[],\"Modifiers\":{\"Access\":{\"Case\":\"DefaultAccess\"}},\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":5,\"Item2\":4},\"SymbolRange\":{\"Item1\":{\"Line\":1,\"Column\":11},\"Item2\":{\"Line\":1,\"Column\":25}},\"ArgumentTuple\":{\"Case\":\"QsTuple\",\"Fields\":[[]]},\"Signature\":{\"TypeParameters\":[],\"ArgumentType\":{\"Case\":\"UnitType\"},\"ReturnType\":{\"Case\":\"UnitType\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}}},\"Documentation\":[]}" [] let SPECIALIZATION_3 = "{\"Kind\":{\"Case\":\"QsBody\"},\"TypeArguments\":{\"Case\":\"Null\"},\"Information\":{\"Characteristics\":{\"Case\":\"EmptySet\"},\"InferredInformation\":{\"IsSelfAdjoint\":false,\"IsIntrinsic\":false}},\"Parent\":{\"Namespace\":\"Microsoft.Quantum\",\"Name\":\"emptyOperation\"},\"Attributes\":[],\"SourceFile\":\"%%%\",\"Position\":{\"Item1\":5,\"Item2\":39},\"HeaderRange\":{\"Item1\":{\"Line\":1,\"Column\":1},\"Item2\":{\"Line\":1,\"Column\":5}},\"Documentation\":[]}" From d0a4bd8634e116ecd1a46fe35a3456780d9b8d4a Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 24 Jan 2020 09:46:53 -0800 Subject: [PATCH 008/135] Undo superfluous changes --- src/QsCompiler/Core/ConstructorExtensions.fs | 4 ++-- src/QsCompiler/Core/DeclarationHeaders.fs | 3 +-- src/QsCompiler/Core/SymbolResolution.fs | 7 +------ src/QsCompiler/Core/SymbolTable.fs | 7 +------ src/QsCompiler/Core/TreeTransformation.fs | 5 +++-- src/QsCompiler/DataStructures/SyntaxTree.fs | 10 +++++----- src/QsCompiler/Tests.Compiler/SerializationTests.fs | 4 ++-- src/QsCompiler/TextProcessor/SyntaxExtensions.fs | 4 ++-- src/QsCompiler/Transformations/Monomorphization.cs | 2 +- 9 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/QsCompiler/Core/ConstructorExtensions.fs b/src/QsCompiler/Core/ConstructorExtensions.fs index a373186bfe..2c3a8226ec 100644 --- a/src/QsCompiler/Core/ConstructorExtensions.fs +++ b/src/QsCompiler/Core/ConstructorExtensions.fs @@ -213,13 +213,13 @@ type QsCallable with static member NewTypeConstructor = QsCallable.New QsCallableKind.TypeConstructor type QsCustomType with - static member New (source, location) (name, attributes, modifiers, items, typeSignature, documentation, comments) = { + static member New (source, location) (name, attributes, modifiers, items, underlyingType, documentation, comments) = { FullName = name Attributes = attributes Modifiers = modifiers SourceFile = source Location = location - Type = typeSignature + Type = underlyingType TypeItems = items Documentation = documentation Comments = comments diff --git a/src/QsCompiler/Core/DeclarationHeaders.fs b/src/QsCompiler/Core/DeclarationHeaders.fs index 9fd83377a7..5b6f6cb67e 100644 --- a/src/QsCompiler/Core/DeclarationHeaders.fs +++ b/src/QsCompiler/Core/DeclarationHeaders.fs @@ -126,8 +126,7 @@ type TypeDeclarationHeader = { let attributesAreNullOrDefault = Object.ReferenceEquals(header.Attributes, null) || header.Attributes.IsDefault let header = if attributesAreNullOrDefault then {header with Attributes = ImmutableArray.Empty} else header // no reason to raise an error if not (Object.ReferenceEquals(header.TypeItems, null)) then success, header - else false, {header with - TypeItems = header.Type |> Anonymous |> QsTupleItem |> ImmutableArray.Create |> QsTuple} + else false, {header with TypeItems = ImmutableArray.Create (header.Type |> Anonymous |> QsTupleItem) |> QsTuple} member this.ToJson() : string = DeclarationHeader.ToJson this diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index 1768de51a0..e292cf7718 100644 --- a/src/QsCompiler/Core/SymbolResolution.fs +++ b/src/QsCompiler/Core/SymbolResolution.fs @@ -236,12 +236,7 @@ module SymbolResolution = let errs = TypeParameterResolutionWarnings argType (returnType, signature.ReturnType.Range |> orDefault) typeParams (typeParams |> Seq.map fst).ToImmutableArray(), errs let callableInfo = CallableInformation.Common specBundleInfos - let resolvedSig = { - TypeParameters = resolvedParams - ArgumentType = argType - ReturnType = returnType - Information = callableInfo - } + let resolvedSig = { TypeParameters = resolvedParams; ArgumentType = argType; ReturnType = returnType; Information = callableInfo } (resolvedSig, argTuple), [inErr; outErr; resErrs; tpErrs] |> Array.concat /// Give a routine for type resolution, fully resolves the given user defined type as well as its items. diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 36a7caa927..b2b809fa41 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -151,12 +151,7 @@ type private PartialNamespace private | QsTupleItem (n, t) -> ImmutableArray.Create (replaceAnonymous (n, t)) |> QsTuple | QsTuple _ -> buildItem typeTuple let returnType = {Type = UserDefinedType (QualifiedSymbol (this.Name, tName) |> withoutRange); Range = Null} - { - TypeParameters = ImmutableArray.Empty - Argument = constructorArgument - ReturnType = returnType - Characteristics = {Characteristics = EmptySet; Range = Null} - } + {TypeParameters = ImmutableArray.Empty; Argument = constructorArgument; ReturnType = returnType; Characteristics = {Characteristics = EmptySet; Range = Null}} // There are a couple of reasons not just blindly attach all attributes associated with the type to the constructor: // For one, we would need to make sure that the range information for duplications is stripped such that e.g. rename commands are not executed multiple times. diff --git a/src/QsCompiler/Core/TreeTransformation.fs b/src/QsCompiler/Core/TreeTransformation.fs index 3468219968..5dfa121706 100644 --- a/src/QsCompiler/Core/TreeTransformation.fs +++ b/src/QsCompiler/Core/TreeTransformation.fs @@ -162,10 +162,11 @@ type SyntaxTreeTransformation() = let loc = this.onLocation t.Location let attributes = t.Attributes |> Seq.map this.onAttribute |> ImmutableArray.CreateRange let modifiers = this.onModifiers t.Modifiers - let typeTuple = this.Scope.Expression.Type.Transform t.Type + let underlyingType = this.Scope.Expression.Type.Transform t.Type let typeItems = this.onTypeItems t.TypeItems let doc = this.onDocumentation t.Documentation - QsCustomType.New (source, loc) (t.FullName, attributes, modifiers, typeItems, typeTuple, doc, t.Comments) + let comments = t.Comments + QsCustomType.New (source, loc) (t.FullName, attributes, modifiers, typeItems, underlyingType, doc, comments) abstract member onCallableImplementation : QsCallable -> QsCallable diff --git a/src/QsCompiler/DataStructures/SyntaxTree.fs b/src/QsCompiler/DataStructures/SyntaxTree.fs index f6bd4929a0..ebf275acec 100644 --- a/src/QsCompiler/DataStructures/SyntaxTree.fs +++ b/src/QsCompiler/DataStructures/SyntaxTree.fs @@ -713,10 +713,10 @@ type QsCustomType = { /// and the range contains the range occupied by the type name relative to that position. /// The location is Null for auto-generated types defined by the compiler. Location : QsNullable - /// Contains the type and access information for the user defined type. - /// Note that a user defined type is *not* considered to be a subtype of its underlying type, but rather its own, - /// entirely distinct type, and the underlying type is merely the argument type of the (auto-generated) type - /// constructor associated with the user defined type. + /// Contains the underlying Q# type. + /// Note that a user defined type is *not* considered to be a subtype of its underlying type, + /// but rather its own, entirely distinct type, + /// and the underlying type is merely the argument type of the (auto-generated) type constructor associated with the user defined type. Type : ResolvedType /// contains the type tuple defining the named and anonymous items of the type TypeItems : QsTuple @@ -771,4 +771,4 @@ type QsCompilation = { /// Contains the names of all entry points of the compilation. /// In the case of a library the array is empty. EntryPoints : ImmutableArray -} \ No newline at end of file +} diff --git a/src/QsCompiler/Tests.Compiler/SerializationTests.fs b/src/QsCompiler/Tests.Compiler/SerializationTests.fs index a2a9ebe72b..c48e8f233d 100644 --- a/src/QsCompiler/Tests.Compiler/SerializationTests.fs +++ b/src/QsCompiler/Tests.Compiler/SerializationTests.fs @@ -215,7 +215,7 @@ module SerializationTests = SourceFile = "%%%" |> NonNullable.New Position = (2,4) |> DeclarationHeader.Offset.Defined SymbolRange = ({Line = 1; Column = 9}, {Line = 1; Column = 13}) |> DeclarationHeader.Range.Defined - Type = ResolvedType.New tupleIntIntType + Type = tupleIntIntType |> ResolvedType.New TypeItems = intIntTypeItems Documentation = ImmutableArray.Empty } @@ -228,7 +228,7 @@ module SerializationTests = SourceFile = "%%%" |> NonNullable.New Position = (3,4) |> DeclarationHeader.Offset.Defined SymbolRange = ({Line = 1; Column = 9}, {Line = 1; Column = 15}) |> DeclarationHeader.Range.Defined - Type = ResolvedType.New tupleIntIntType + Type = tupleIntIntType |> ResolvedType.New TypeItems = intIntTypeItems Documentation = ImmutableArray.Empty } diff --git a/src/QsCompiler/TextProcessor/SyntaxExtensions.fs b/src/QsCompiler/TextProcessor/SyntaxExtensions.fs index 9134864d02..b90542b2a8 100644 --- a/src/QsCompiler/TextProcessor/SyntaxExtensions.fs +++ b/src/QsCompiler/TextProcessor/SyntaxExtensions.fs @@ -109,8 +109,8 @@ type CallableSignature with let invalidType = (InvalidType, Null) |> QsType.New let invalidSymbol = (InvalidSymbol, Null) |> QsSymbol.New let invalidArg = QsTuple ([QsTupleItem (invalidSymbol, invalidType)].ToImmutableArray()) - CallableSignature.New ((ImmutableArray.Empty, (invalidArg, invalidType)), - (InvalidSetExpr, Null) |> Characteristics.New) + ((ImmutableArray.Empty, (invalidArg, invalidType)), (InvalidSetExpr, Null) |> Characteristics.New) + |> CallableSignature.New type QsFragment with diff --git a/src/QsCompiler/Transformations/Monomorphization.cs b/src/QsCompiler/Transformations/Monomorphization.cs index f52732b913..504df2f1d2 100644 --- a/src/QsCompiler/Transformations/Monomorphization.cs +++ b/src/QsCompiler/Transformations/Monomorphization.cs @@ -225,7 +225,7 @@ public override ResolvedSignature onSignature(ResolvedSignature s) s.ArgumentType, s.ReturnType, s.Information - ); + ); return base.onSignature(s); } } From 88a3e83e39d00dac30bee57a37d4c3c746570283 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 24 Jan 2020 09:47:06 -0800 Subject: [PATCH 009/135] Update documentation for DefaultAccess --- src/QsCompiler/DataStructures/SyntaxTokens.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/DataStructures/SyntaxTokens.fs b/src/QsCompiler/DataStructures/SyntaxTokens.fs index 9029b48b1c..1ad2502e3c 100644 --- a/src/QsCompiler/DataStructures/SyntaxTokens.fs +++ b/src/QsCompiler/DataStructures/SyntaxTokens.fs @@ -195,7 +195,8 @@ type CallableSignature = { /// Defines where a global declaration may be accessed. [] type AccessModifier = - /// The default access modifier is public, which means the type or callable can be used from anywhere. + /// For callables and types, the default access modifier is public, which means the type or callable can be used + /// from anywhere. For specializations, the default access modifier is the same as the parent callable. | DefaultAccess /// Internal access means that a type or callable may only be used from within the compilation unit in which it is /// declared. From 4ec65741647e06fef652d45326a3743d79bbff99 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 27 Jan 2020 11:34:14 -0800 Subject: [PATCH 010/135] Re-enable execution tests --- QsCompiler.sln | 15 +++++++++++++++ .../Libraries/Library1/Library1.csproj | 2 +- .../Simulation/Target/Simulation.csproj | 2 +- src/QsCompiler/Tests.Compiler/ExecutionTests.fs | 4 ++-- .../Tests.Compiler/Tests.Compiler.fsproj | 3 +++ 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/QsCompiler.sln b/QsCompiler.sln index f7ee3f64dc..7a330f37f1 100644 --- a/QsCompiler.sln +++ b/QsCompiler.sln @@ -34,6 +34,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simulation", "Simulation", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Simulation", "src\QsCompiler\TestTargets\Simulation\Target\Simulation.csproj", "{D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "src\QsCompiler\TestTargets\Simulation\Example\Example.csproj", "{2E331781-F7ED-4EF1-8451-896636C6D93A}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{D2E36476-A65F-4310-9C4C-B721BCC47B00}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Library1", "src\QsCompiler\TestTargets\Libraries\Library1\Library1.csproj", "{DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}" @@ -220,6 +222,18 @@ Global {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x64.Build.0 = Release|Any CPU {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x86.ActiveCfg = Release|Any CPU {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1}.Release|x86.Build.0 = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x64.ActiveCfg = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x64.Build.0 = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x86.ActiveCfg = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Debug|x86.Build.0 = Debug|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|Any CPU.Build.0 = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x64.ActiveCfg = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x64.Build.0 = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x86.ActiveCfg = Release|Any CPU + {2E331781-F7ED-4EF1-8451-896636C6D93A}.Release|x86.Build.0 = Release|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -254,6 +268,7 @@ Global {256A6275-FC7F-42E9-9931-BC6EA6D0F31A} = {B4A9484D-31FC-4A27-9E26-4C8DE3E02D77} {76BA96DA-DC1E-4315-A3ED-5F0700A79812} = {6077A717-50BF-4F87-B439-CA549AF6A4AE} {D50583DF-FBEF-45EF-B523-70B2CDBE1DD1} = {76BA96DA-DC1E-4315-A3ED-5F0700A79812} + {2E331781-F7ED-4EF1-8451-896636C6D93A} = {76BA96DA-DC1E-4315-A3ED-5F0700A79812} {D2E36476-A65F-4310-9C4C-B721BCC47B00} = {6077A717-50BF-4F87-B439-CA549AF6A4AE} {DDAA35BF-7BFC-431F-9F7E-182F01DAEB3F} = {D2E36476-A65F-4310-9C4C-B721BCC47B00} EndGlobalSection diff --git a/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj b/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj index 1cc8934561..d75a34e2b7 100644 --- a/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj +++ b/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj b/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj index 499597d0dc..64514b972e 100644 --- a/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj +++ b/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/QsCompiler/Tests.Compiler/ExecutionTests.fs b/src/QsCompiler/Tests.Compiler/ExecutionTests.fs index f1e47bf5b3..a4a4db38cd 100644 --- a/src/QsCompiler/Tests.Compiler/ExecutionTests.fs +++ b/src/QsCompiler/Tests.Compiler/ExecutionTests.fs @@ -40,7 +40,7 @@ type ExecutionTests (output:ITestOutputHelper) = AssertEqual expectedOutput out - // TODO: [] + [] member this.``Specialization Generation for Conjugations`` () = ExecuteAndCompareOutput "ConjugationsInBody" " @@ -139,7 +139,7 @@ type ExecutionTests (output:ITestOutputHelper) = " - // TODO: [] + [] member this.``Referencing Projects and Packages`` () = ExecuteAndCompareOutput "PackageAndProjectReference" " diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index cd7685cc7a..2f3634b115 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -150,6 +150,9 @@ + + false + From eb43e6d00e146ee7c0f62fa9d9595eabce9353f9 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 27 Jan 2020 19:20:51 -0800 Subject: [PATCH 011/135] Add parsing for access modifiers --- .../EditorSupport/CodeActions.cs | 14 ++++++------- .../CompilationManager/TypeChecking.cs | 12 +++++------ .../DataStructures/ReservedKeywords.fs | 5 +++++ src/QsCompiler/DataStructures/SyntaxTokens.fs | 6 +++--- .../DeclarationVerification.fs | 6 +++--- .../SyntaxProcessor/SyntaxExtensions.fs | 6 +++--- .../TextProcessor/QsFragmentParsing.fs | 20 +++++++++---------- src/QsCompiler/TextProcessor/QsKeywords.fs | 5 +++++ 8 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index a6b0946ed4..67bc8bd159 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -233,9 +233,9 @@ string CharacteristicsAnnotation(Characteristics c) IEnumerable GetCharacteristics(QsTuple> argTuple) => SyntaxGenerator.ExtractItems(argTuple).SelectMany(item => item.Item2.ExtractCharacteristics()).Distinct(); var characteristicsInFragment = - fragment?.Kind is QsFragmentKind.FunctionDeclaration function ? GetCharacteristics(function.Item2.Argument) : - fragment?.Kind is QsFragmentKind.OperationDeclaration operation ? GetCharacteristics(operation.Item2.Argument) : - fragment?.Kind is QsFragmentKind.TypeDefinition type ? GetCharacteristics(type.Item2) : + fragment?.Kind is QsFragmentKind.FunctionDeclaration function ? GetCharacteristics(function.Item3.Argument) : + fragment?.Kind is QsFragmentKind.OperationDeclaration operation ? GetCharacteristics(operation.Item3.Argument) : + fragment?.Kind is QsFragmentKind.TypeDefinition type ? GetCharacteristics(type.Item3) : Enumerable.Empty(); //var symbolInfo = file.TryGetQsSymbolInfo(d.Range.Start, false, out var fragment); @@ -437,11 +437,11 @@ bool EmptyOrFirstAttribute(IEnumerable line, out CodeFragment att) var docString = $"{docPrefix}# Summary{endLine}{docPrefix}{endLine}"; var (argTuple, typeParams) = - callableDecl.IsValue ? (callableDecl.Item.Item2.Item2.Argument, - callableDecl.Item.Item2.Item2.TypeParameters) - : typeDecl.IsValue ? (typeDecl.Item.Item2.Item1, ImmutableArray.Empty) + callableDecl.IsValue ? (callableDecl.Item.Item2.Item3.Argument, + callableDecl.Item.Item2.Item3.TypeParameters) + : typeDecl.IsValue ? (typeDecl.Item.Item2.Item2, ImmutableArray.Empty) : (null, ImmutableArray.Empty); - var hasOutput = callableDecl.IsValue && !callableDecl.Item.Item2.Item2.ReturnType.Type.IsUnitType; + var hasOutput = callableDecl.IsValue && !callableDecl.Item.Item2.Item3.ReturnType.Type.IsUnitType; var args = argTuple == null ? ImmutableArray>.Empty : SyntaxGenerator.ExtractItems(argTuple); docString = String.Concat( diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index beba4e80b0..969d0c42a6 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -111,13 +111,13 @@ internal static ImmutableArray DocumentingComments(this FileContentManag /// /// Returns the HeaderItems corresponding to all type declarations with a valid name in the given file, or null if the given file is null. /// - private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry>, Modifiers>>)> GetTypeDeclarationHeaderItems + private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry>>>)> GetTypeDeclarationHeaderItems (this FileContentManager file) => file.GetHeaderItems(file?.TypeDeclarationTokens(), frag => frag.Kind.DeclaredType(), null); /// /// Returns the HeaderItems corresponding to all callable declarations with a valid name in the given file, or null if the given file is null. /// - private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry>)> GetCallableDeclarationHeaderItems + private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry>)> GetCallableDeclarationHeaderItems (this FileContentManager file) => file.GetHeaderItems(file?.CallableDeclarationTokens(), frag => frag.Kind.DeclaredCallable(), null); /// @@ -128,7 +128,7 @@ internal static ImmutableArray DocumentingComments(this FileContentManag /// The function returns Null if the Kind of the given fragment is null. /// private static QsNullable)>> SpecializationDeclaration - (HeaderEntry> parent, CodeFragment fragment) + (HeaderEntry> parent, CodeFragment fragment) { var specDecl = fragment.Kind?.DeclaredSpecialization(); var Null = QsNullable)>>.Null; @@ -226,7 +226,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position // add all type declarations var typesToCompile = AddItems(file.GetTypeDeclarationHeaderItems(), - (pos, name, decl, att, doc) => (ContainingParent(pos, namespaces)).TryAddType(file.FileName, Location(pos, name.Item2), name, decl.Item1, att, decl.Item2, doc), + (pos, name, decl, att, doc) => (ContainingParent(pos, namespaces)).TryAddType(file.FileName, Location(pos, name.Item2), name, decl.Item2, att, decl.Item1, doc), file.FileName.Value, diagnostics); var tokensToCompile = new List<(QsQualifiedName, (QsComments, IEnumerable))>(); @@ -238,7 +238,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position // add all callable declarations var callablesToCompile = AddItems(file.GetCallableDeclarationHeaderItems(), - (pos, name, decl, att, doc) => (ContainingParent(pos, namespaces)).TryAddCallableDeclaration(file.FileName, Location(pos, name.Item2), name, Tuple.Create(decl.Item1, decl.Item2), att, decl.Item3, doc), + (pos, name, decl, att, doc) => (ContainingParent(pos, namespaces)).TryAddCallableDeclaration(file.FileName, Location(pos, name.Item2), name, Tuple.Create(decl.Item1, decl.Item3), att, decl.Item2, doc), file.FileName.Value, diagnostics); // add all callable specilizations -> TOOD: needs to be adapted for specializations outside the declaration body (not yet supported) @@ -271,7 +271,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position /// Throws an ArgumentNullException if either the given namespace or diagnostics are null. /// private static List AddSpecializationsToNamespace(FileContentManager file, Namespace ns, - (CodeFragment.TokenIndex, HeaderEntry>) parent, List diagnostics) + (CodeFragment.TokenIndex, HeaderEntry>) parent, List diagnostics) { if (ns == null) throw new ArgumentNullException(nameof(ns)); if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index c6dfb3462b..c1d3ad47e6 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -155,6 +155,11 @@ module Declarations = /// keyword for a Q# declaration let Namespace = "namespace" + + /// keyword for a Q# declaration modifier + let Private = "private" + /// keyword for a Q# declaration modifier + let Internal = "internal" /// contains keywords for Q# directives diff --git a/src/QsCompiler/DataStructures/SyntaxTokens.fs b/src/QsCompiler/DataStructures/SyntaxTokens.fs index 1ad2502e3c..4529b5e663 100644 --- a/src/QsCompiler/DataStructures/SyntaxTokens.fs +++ b/src/QsCompiler/DataStructures/SyntaxTokens.fs @@ -234,9 +234,9 @@ type QsFragmentKind = | AdjointDeclaration of QsSpecializationGenerator | ControlledDeclaration of QsSpecializationGenerator | ControlledAdjointDeclaration of QsSpecializationGenerator -| OperationDeclaration of QsSymbol * CallableSignature * Modifiers -| FunctionDeclaration of QsSymbol * CallableSignature * Modifiers -| TypeDefinition of QsSymbol * QsTuple * Modifiers +| OperationDeclaration of Modifiers * QsSymbol * CallableSignature +| FunctionDeclaration of Modifiers * QsSymbol * CallableSignature +| TypeDefinition of Modifiers * QsSymbol * QsTuple | DeclarationAttribute of QsSymbol * QsExpression | OpenDirective of QsSymbol * QsNullable | NamespaceDeclaration of QsSymbol diff --git a/src/QsCompiler/SyntaxProcessor/DeclarationVerification.fs b/src/QsCompiler/SyntaxProcessor/DeclarationVerification.fs index 7d167547a4..9b134a4fb4 100644 --- a/src/QsCompiler/SyntaxProcessor/DeclarationVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/DeclarationVerification.fs @@ -71,7 +71,7 @@ let public OpenedNamespaceName this onInvalid = [] let public DeclaredType this = match this with - | TypeDefinition (sym, decl, modifiers) -> (sym, (decl, modifiers)) |> Value + | TypeDefinition (mods, sym, decl) -> (sym, (mods, decl)) |> Value | _ -> Null /// If the given fragment kind is a type declaration, @@ -88,8 +88,8 @@ let public DeclaredTypeName this onInvalid = [] let public DeclaredCallable this = match this with - | FunctionDeclaration (sym, decl, modifiers) -> (sym, (QsCallableKind.Function, decl, modifiers)) |> Value - | OperationDeclaration (sym, decl, modifiers) -> (sym, (QsCallableKind.Operation, decl, modifiers)) |> Value + | FunctionDeclaration (mods, sym, decl) -> (sym, (QsCallableKind.Function, mods, decl)) |> Value + | OperationDeclaration (mods, sym, decl) -> (sym, (QsCallableKind.Operation, mods, decl)) |> Value | _ -> Null /// If the given fragment kind is a callable declaration, diff --git a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs index a11614af44..d2d30660e7 100644 --- a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs +++ b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs @@ -173,9 +173,9 @@ let public SymbolInformation fragmentKind = | QsFragmentKind.AdjointDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) | QsFragmentKind.ControlledDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) | QsFragmentKind.ControlledAdjointDeclaration gen -> gen |> SymbolsInGenerator, ([], [], []) - | QsFragmentKind.OperationDeclaration (n, signature, _) -> (n, signature) |> SymbolsInCallableDeclaration - | QsFragmentKind.FunctionDeclaration (n, signature, _) -> (n, signature) |> SymbolsInCallableDeclaration - | QsFragmentKind.TypeDefinition (sym, t, _) -> (sym, t) |> SymbolsInArgumentTuple + | QsFragmentKind.OperationDeclaration (_, n, signature) -> (n, signature) |> SymbolsInCallableDeclaration + | QsFragmentKind.FunctionDeclaration (_, n, signature) -> (n, signature) |> SymbolsInCallableDeclaration + | QsFragmentKind.TypeDefinition (_, sym, t) -> (sym, t) |> SymbolsInArgumentTuple | QsFragmentKind.DeclarationAttribute (sym, ex) -> [], ([AttributeAsCallExpr (sym, ex)], []) |> collectWith SymbolsFromExpr |> addVariable sym | QsFragmentKind.NamespaceDeclaration sym -> sym |> SymbolDeclarations, ([], [], []) | QsFragmentKind.OpenDirective (nsName, alias) -> [alias] |> chooseValues, ([nsName], [], []) diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 09bfb554a6..5c574399b3 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -113,6 +113,11 @@ let private allocationScope = let initializerTuple = buildTupleItem validInitializer buildInitializerTuple ErrorCode.InvalidInitializerExpression ErrorCode.MissingInitializerExpression invalidInitializer isTupleContinuation optTupleBrackets (initializerTuple |> symbolBinding equal ErrorCode.ExpectingAssignment) |>> fst +/// Parses keywords that modify the visibility or behavior of a declaration. +let private modifiers = + let accessModifier = (qsPrivate.parse >>% Private) <|> (qsInternal.parse >>% Internal) <|>% DefaultAccess + accessModifier |>> fun access -> { Access = access } + /// Parses a Q# operation or function signature. /// Expects type annotations for each symbol in the argument tuple, and raises Missing- or InvalidTypeAnnotation errors otherwise. /// Raises Missing- or InvalidArumentDeclaration error for entirely missing or invalid arguments, @@ -149,9 +154,7 @@ let private signature = let returnTypeAnnotation = expected (typeAnnotation eof) ErrorCode.InvalidReturnTypeAnnotation ErrorCode.MissingReturnTypeAnnotation invalidType eof let characteristicsAnnotation = opt (qsCharacteristics.parse >>. expectedCharacteristics eof) |>> Option.defaultValue ((EmptySet, Null) |> Characteristics.New) let signature = genericParamList .>>. (argumentTuple .>>. returnTypeAnnotation) .>>. characteristicsAnnotation |>> CallableSignature.New - // TODO: Parse modifiers. - symbolDeclaration .>>. signature - |>> (fun (symbol, signature) -> (symbol, signature, {Access = DefaultAccess})) + tuple3 modifiers symbolDeclaration signature /// Parses a Q# functor generator directive. /// For a user defined implementation, expects a tuple argument of items that can either be a localIdentifier or omittedSymbols. @@ -277,17 +280,17 @@ and private controlledAdjointDeclaration = /// Uses buildFragment to parse a Q# OperationDeclaration as QsFragment. and private operationDeclaration = - let invalid = OperationDeclaration (invalidSymbol, CallableSignature.Invalid, {Access = DefaultAccess}) + let invalid = OperationDeclaration ({Access = DefaultAccess}, invalidSymbol, CallableSignature.Invalid) buildFragment opDeclHeader.parse signature invalid OperationDeclaration eof /// Uses buildFragment to parse a Q# FunctionDeclaration as QsFragment. and private functionDeclaration = - let invalid = FunctionDeclaration (invalidSymbol, CallableSignature.Invalid, {Access = DefaultAccess}) + let invalid = FunctionDeclaration ({Access = DefaultAccess}, invalidSymbol, CallableSignature.Invalid) buildFragment fctDeclHeader.parse signature invalid FunctionDeclaration eof /// Uses buildFragment to parse a Q# TypeDefinition as QsFragment. and private udtDeclaration = - let invalid = TypeDefinition (invalidSymbol, invalidArgTupleItem, {Access = DefaultAccess}) + let invalid = TypeDefinition ({Access = DefaultAccess}, invalidSymbol, invalidArgTupleItem) let udtTuple = // not unified with the argument tuple for callable declarations, since the error handling needs to be different let asAnonymousItem t = QsTupleItem ((MissingSymbol, Null) |> QsSymbol.New, t) let namedItem = @@ -300,10 +303,7 @@ and private udtDeclaration = buildTupleItem tupleItem (fst >> QsTuple) ErrorCode.InvalidUdtItemDeclaration ErrorCode.MissingUdtItemDeclaration invalidArgTupleItem eof let invalidNamedSingle = followedBy namedItem >>. optTupleBrackets namedItem |>> fst invalidNamedSingle <|> udtTupleItem // require parenthesis for a single named item - let declBody = - // TODO: Parse modifiers. - expectedIdentifierDeclaration equal .>> equal .>>. udtTuple - |>> (fun (symbol, tuple) -> (symbol, tuple, {Access = DefaultAccess})) + let declBody = tuple3 modifiers (expectedIdentifierDeclaration equal .>> equal) udtTuple buildFragment typeDeclHeader.parse declBody invalid TypeDefinition eof diff --git a/src/QsCompiler/TextProcessor/QsKeywords.fs b/src/QsCompiler/TextProcessor/QsKeywords.fs index ed0dc00bde..dd6b38f113 100644 --- a/src/QsCompiler/TextProcessor/QsKeywords.fs +++ b/src/QsCompiler/TextProcessor/QsKeywords.fs @@ -200,6 +200,11 @@ let typeDeclHeader = addFragmentHeader Declarations.Type /// keyword for a Q# declaration (QsFragmentHeader) let namespaceDeclHeader = addFragmentHeader Declarations.Namespace +/// keyword for a Q# declaration modifier (QsLanguageKeyword) +let qsPrivate = addLanguageKeyword Declarations.Private +/// keyword for a Q# declaration modifier (QsLanguageKeyword) +let qsInternal = addLanguageKeyword Declarations.Internal + // directives /// keyword for a Q# directive (QsFragmentHeader) From 0556439b9452c5c4a8fc428d5c9502e492a9d427 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 28 Jan 2020 17:54:15 -0800 Subject: [PATCH 012/135] Update syntax highlighting for private and internal --- .../syntaxes/qsharp.tmLanguage.json.v.template | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template b/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template index 10a844f2a7..f6090feb4a 100644 --- a/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template +++ b/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template @@ -50,7 +50,7 @@ }, { "name": "keyword.other.qsharp", - "match": "\\b(new|not|and|or|w\/)\\b" + "match": "\\b(new|not|and|or|w\/|private|internal)\\b" }, { "comment": "C# reserved words which can't be used in Q#, A-D", @@ -60,12 +60,12 @@ { "comment": "C# reserved words which can't be used in Q#, E-L", "name": "invalid.illegal.el.qsharp", - "match": "\\b(enum|event|explicit|extern|finally|fixed|float|foreach|goto|implicit|int|interface|internal|lock|long)\\b" + "match": "\\b(enum|event|explicit|extern|finally|fixed|float|foreach|goto|implicit|int|interface|lock|long)\\b" }, { "comment": "C# reserved words which can't be used in Q#, N-S", "name": "invalid.illegal.ns.qsharp", - "match": "\\b(null|object|operator|out|override|params|private|protected|public|readonly|ref|sbyte|sealed|short|sizeof|stackalloc)\\b" + "match": "\\b(null|object|operator|out|override|params|protected|public|readonly|ref|sbyte|sealed|short|sizeof|stackalloc)\\b" }, { "comment": "C# reserved words which can't be used in Q#, S-V", From c5f90db5afab59a9850fed1f8e0f4247fe1f0529 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 29 Jan 2020 14:47:12 -0800 Subject: [PATCH 013/135] Add TODOs for parsing the final syntax --- src/QsCompiler/TextProcessor/QsFragmentParsing.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 5c574399b3..1ecf990dfb 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -154,6 +154,7 @@ let private signature = let returnTypeAnnotation = expected (typeAnnotation eof) ErrorCode.InvalidReturnTypeAnnotation ErrorCode.MissingReturnTypeAnnotation invalidType eof let characteristicsAnnotation = opt (qsCharacteristics.parse >>. expectedCharacteristics eof) |>> Option.defaultValue ((EmptySet, Null) |> Characteristics.New) let signature = genericParamList .>>. (argumentTuple .>>. returnTypeAnnotation) .>>. characteristicsAnnotation |>> CallableSignature.New + // TODO: Parse modifiers before the "operation" and "function" keywords. tuple3 modifiers symbolDeclaration signature /// Parses a Q# functor generator directive. @@ -302,7 +303,8 @@ and private udtDeclaration = let tupleItem = attempt namedItem <|> (typeParser typeTuple |>> asAnonymousItem) // namedItem needs to be first, and we can't be permissive for tuple types! buildTupleItem tupleItem (fst >> QsTuple) ErrorCode.InvalidUdtItemDeclaration ErrorCode.MissingUdtItemDeclaration invalidArgTupleItem eof let invalidNamedSingle = followedBy namedItem >>. optTupleBrackets namedItem |>> fst - invalidNamedSingle <|> udtTupleItem // require parenthesis for a single named item + invalidNamedSingle <|> udtTupleItem // require parenthesis for a single named item + // TODO: Parse modifiers before the "operation" and "function" keywords. let declBody = tuple3 modifiers (expectedIdentifierDeclaration equal .>> equal) udtTuple buildFragment typeDeclHeader.parse declBody invalid TypeDefinition eof From 32f6e9b5161ea0157bc433305c7d3206611cdc58 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 29 Jan 2020 14:49:46 -0800 Subject: [PATCH 014/135] Fix code style for creating modifiers record --- src/QsCompiler/TextProcessor/QsFragmentParsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 1ecf990dfb..d6c1b32fd6 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -116,7 +116,7 @@ let private allocationScope = /// Parses keywords that modify the visibility or behavior of a declaration. let private modifiers = let accessModifier = (qsPrivate.parse >>% Private) <|> (qsInternal.parse >>% Internal) <|>% DefaultAccess - accessModifier |>> fun access -> { Access = access } + accessModifier |>> fun access -> {Access = access} /// Parses a Q# operation or function signature. /// Expects type annotations for each symbol in the argument tuple, and raises Missing- or InvalidTypeAnnotation errors otherwise. From 3ef2013c7f2a013c1dc8cfec92aa499baac595f8 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 29 Jan 2020 18:23:22 -0800 Subject: [PATCH 015/135] Parse modifiers before fragment headers --- .../TextProcessor/QsFragmentParsing.fs | 22 +++++++++++++------ src/QsCompiler/TextProcessor/QsKeywords.fs | 8 +++---- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index d6c1b32fd6..c41a131bae 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -154,8 +154,7 @@ let private signature = let returnTypeAnnotation = expected (typeAnnotation eof) ErrorCode.InvalidReturnTypeAnnotation ErrorCode.MissingReturnTypeAnnotation invalidType eof let characteristicsAnnotation = opt (qsCharacteristics.parse >>. expectedCharacteristics eof) |>> Option.defaultValue ((EmptySet, Null) |> Characteristics.New) let signature = genericParamList .>>. (argumentTuple .>>. returnTypeAnnotation) .>>. characteristicsAnnotation |>> CallableSignature.New - // TODO: Parse modifiers before the "operation" and "function" keywords. - tuple3 modifiers symbolDeclaration signature + symbolDeclaration .>>. signature /// Parses a Q# functor generator directive. /// For a user defined implementation, expects a tuple argument of items that can either be a localIdentifier or omittedSymbols. @@ -218,6 +217,10 @@ let rec private getFragments() = // Note: this needs to be a function! (ctrlDeclHeader , controlledDeclaration ) (bodyDeclHeader , bodyDeclaration ) (importDirectiveHeader, openDirective ) + // Modifiers need to be considered fragment headers for parsing reasons, but the actual parsing is handled by + // the fragment being modified. + (qsPrivate , pzero ) + (qsInternal , pzero ) ] and private headerCheck = // DO NOT REMOVE - the check is executed once at the beginning, just as it should be @@ -282,12 +285,16 @@ and private controlledAdjointDeclaration = /// Uses buildFragment to parse a Q# OperationDeclaration as QsFragment. and private operationDeclaration = let invalid = OperationDeclaration ({Access = DefaultAccess}, invalidSymbol, CallableSignature.Invalid) - buildFragment opDeclHeader.parse signature invalid OperationDeclaration eof + let makeOperation mods (symbol, signature) = OperationDeclaration (mods, symbol, signature) + modifiers .>> followedBy opDeclHeader.parse |> attempt >>= fun mods -> + buildFragment opDeclHeader.parse signature invalid (makeOperation mods) eof /// Uses buildFragment to parse a Q# FunctionDeclaration as QsFragment. and private functionDeclaration = let invalid = FunctionDeclaration ({Access = DefaultAccess}, invalidSymbol, CallableSignature.Invalid) - buildFragment fctDeclHeader.parse signature invalid FunctionDeclaration eof + let makeFunction mods (symbol, signature) = FunctionDeclaration (mods, symbol, signature) + modifiers .>> followedBy fctDeclHeader.parse |> attempt >>= fun mods -> + buildFragment fctDeclHeader.parse signature invalid (makeFunction mods) eof /// Uses buildFragment to parse a Q# TypeDefinition as QsFragment. and private udtDeclaration = @@ -304,9 +311,10 @@ and private udtDeclaration = buildTupleItem tupleItem (fst >> QsTuple) ErrorCode.InvalidUdtItemDeclaration ErrorCode.MissingUdtItemDeclaration invalidArgTupleItem eof let invalidNamedSingle = followedBy namedItem >>. optTupleBrackets namedItem |>> fst invalidNamedSingle <|> udtTupleItem // require parenthesis for a single named item - // TODO: Parse modifiers before the "operation" and "function" keywords. - let declBody = tuple3 modifiers (expectedIdentifierDeclaration equal .>> equal) udtTuple - buildFragment typeDeclHeader.parse declBody invalid TypeDefinition eof + let declBody = expectedIdentifierDeclaration equal .>> equal .>>. udtTuple + let makeType mods (symbol, underlyingType) = TypeDefinition (mods, symbol, underlyingType) + modifiers .>> followedBy typeDeclHeader.parse |> attempt >>= fun mods -> + buildFragment typeDeclHeader.parse declBody invalid (makeType mods) eof // statement parsing diff --git a/src/QsCompiler/TextProcessor/QsKeywords.fs b/src/QsCompiler/TextProcessor/QsKeywords.fs index dd6b38f113..0b88fc6965 100644 --- a/src/QsCompiler/TextProcessor/QsKeywords.fs +++ b/src/QsCompiler/TextProcessor/QsKeywords.fs @@ -200,10 +200,10 @@ let typeDeclHeader = addFragmentHeader Declarations.Type /// keyword for a Q# declaration (QsFragmentHeader) let namespaceDeclHeader = addFragmentHeader Declarations.Namespace -/// keyword for a Q# declaration modifier (QsLanguageKeyword) -let qsPrivate = addLanguageKeyword Declarations.Private -/// keyword for a Q# declaration modifier (QsLanguageKeyword) -let qsInternal = addLanguageKeyword Declarations.Internal +/// keyword for a Q# declaration modifier (QsFragmentHeader) +let qsPrivate = addFragmentHeader Declarations.Private +/// keyword for a Q# declaration modifier (QsFragmentHeader) +let qsInternal = addFragmentHeader Declarations.Internal // directives From 09b5f70872f3324f6c0c056ddaa5e918586a2a00 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 30 Jan 2020 21:02:03 -0800 Subject: [PATCH 016/135] Include modifiers in the fragment header parser --- .../TextProcessor/QsFragmentParsing.fs | 96 +++++++++++-------- src/QsCompiler/TextProcessor/SyntaxBuilder.fs | 32 +++---- 2 files changed, 69 insertions(+), 59 deletions(-) diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index c41a131bae..2825b2cdaf 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -238,12 +238,12 @@ and private openDirective = let nsNameAndAlias = let aliasOption = (importedAs.parse >>. expectedNamespaceName eof |>> Value) <|>% Null expectedNamespaceName importedAs.parse .>>. aliasOption - buildFragment importDirectiveHeader.parse nsNameAndAlias invalid OpenDirective eof + buildFragment importDirectiveHeader.parse nsNameAndAlias invalid (fun _ -> OpenDirective) eof /// Uses buildFragment to parse a Q# NamespaceDeclaration as QsFragment. and private namespaceDeclaration = let invalid = NamespaceDeclaration invalidSymbol - buildFragment namespaceDeclHeader.parse (expectedNamespaceName eof) invalid NamespaceDeclaration eof + buildFragment namespaceDeclHeader.parse (expectedNamespaceName eof) invalid (fun _ -> NamespaceDeclaration) eof /// Uses buildFragment to parse a Q# DeclarationAttribute as QsFragment. and private attributeDeclaration = @@ -257,7 +257,7 @@ and private attributeDeclaration = let invalidAttribute = (invalidSymbol, unknownExpr) let invalidErr, missingErr = ErrorCode.InvalidAttributeIdentifier, ErrorCode.MissingAttributeIdentifier expected (attributeId .>>. expectedArgs) invalidErr missingErr invalidAttribute attributeIntro - buildFragment attributeIntro expectedAttribute invalid DeclarationAttribute attributeIntro + buildFragment attributeIntro expectedAttribute invalid (fun _ -> DeclarationAttribute) attributeIntro // operation and function parsing @@ -265,36 +265,42 @@ and private attributeDeclaration = /// Uses buildFragment to parse a Q# BodyDeclaration as QsFragment. and private bodyDeclaration = let invalid = BodyDeclaration unknownGenerator - buildFragment bodyDeclHeader.parse functorGenDirective invalid BodyDeclaration eof + buildFragment bodyDeclHeader.parse functorGenDirective invalid (fun _ -> BodyDeclaration) eof /// Uses buildFragment to parse a Q# AdjointDeclaration as QsFragment. and private adjointDeclaration = let invalid = AdjointDeclaration unknownGenerator - buildFragment adjDeclHeader.parse functorGenDirective invalid AdjointDeclaration eof + buildFragment adjDeclHeader.parse functorGenDirective invalid (fun _ -> AdjointDeclaration) eof /// Uses buildFragment to parse a Q# ControlledDeclaration as QsFragment. and private controlledDeclaration = let invalid = ControlledDeclaration unknownGenerator - buildFragment ctrlDeclHeader.parse functorGenDirective invalid ControlledDeclaration eof + buildFragment ctrlDeclHeader.parse functorGenDirective invalid (fun _ -> ControlledDeclaration) eof /// Uses buildFragment to parse a Q# ControlledAdjointDeclaration as QsFragment. and private controlledAdjointDeclaration = let invalid = ControlledAdjointDeclaration unknownGenerator - buildFragment (attempt ctrlAdjDeclHeader.parse) functorGenDirective invalid ControlledAdjointDeclaration eof + buildFragment (attempt ctrlAdjDeclHeader.parse) functorGenDirective invalid (fun _ -> ControlledAdjointDeclaration) eof /// Uses buildFragment to parse a Q# OperationDeclaration as QsFragment. and private operationDeclaration = let invalid = OperationDeclaration ({Access = DefaultAccess}, invalidSymbol, CallableSignature.Invalid) - let makeOperation mods (symbol, signature) = OperationDeclaration (mods, symbol, signature) - modifiers .>> followedBy opDeclHeader.parse |> attempt >>= fun mods -> - buildFragment opDeclHeader.parse signature invalid (makeOperation mods) eof - + buildFragment + (modifiers .>> opDeclHeader.parse |> attempt) + signature + invalid + (fun mods (symbol, signature) -> OperationDeclaration (mods, symbol, signature)) + eof + /// Uses buildFragment to parse a Q# FunctionDeclaration as QsFragment. and private functionDeclaration = let invalid = FunctionDeclaration ({Access = DefaultAccess}, invalidSymbol, CallableSignature.Invalid) - let makeFunction mods (symbol, signature) = FunctionDeclaration (mods, symbol, signature) - modifiers .>> followedBy fctDeclHeader.parse |> attempt >>= fun mods -> - buildFragment fctDeclHeader.parse signature invalid (makeFunction mods) eof + buildFragment + (modifiers .>> fctDeclHeader.parse |> attempt) + signature + invalid + (fun mods (symbol, signature) -> FunctionDeclaration (mods, symbol, signature)) + eof /// Uses buildFragment to parse a Q# TypeDefinition as QsFragment. and private udtDeclaration = @@ -312,9 +318,12 @@ and private udtDeclaration = let invalidNamedSingle = followedBy namedItem >>. optTupleBrackets namedItem |>> fst invalidNamedSingle <|> udtTupleItem // require parenthesis for a single named item let declBody = expectedIdentifierDeclaration equal .>> equal .>>. udtTuple - let makeType mods (symbol, underlyingType) = TypeDefinition (mods, symbol, underlyingType) - modifiers .>> followedBy typeDeclHeader.parse |> attempt >>= fun mods -> - buildFragment typeDeclHeader.parse declBody invalid (makeType mods) eof + buildFragment + (modifiers .>> typeDeclHeader.parse |> attempt) + declBody + invalid + (fun mods (symbol, underlyingType) -> TypeDefinition (mods, symbol, underlyingType)) + eof // statement parsing @@ -322,23 +331,33 @@ and private udtDeclaration = /// Uses buildFragment to parse a Q# return-statement as QsFragment. and private returnStatement = let invalid = ReturnStatement unknownExpr - buildFragment qsReturn.parse (expectedExpr eof) invalid ReturnStatement eof + buildFragment qsReturn.parse (expectedExpr eof) invalid (fun _ -> ReturnStatement) eof /// Uses buildFragment to parse a Q# fail-statement as QsFragment. and private failStatement = let invalid = FailStatement unknownExpr - buildFragment qsFail.parse (expectedExpr eof) invalid FailStatement eof + buildFragment qsFail.parse (expectedExpr eof) invalid (fun _ -> FailStatement) eof /// Uses buildFragment to parse a Q# immutable binding (i.e. let-statement) as QsFragment. and private letStatement = let invalid = ImmutableBinding (invalidSymbol, unknownExpr) - buildFragment qsImmutableBinding.parse (expectedExpr eof |> symbolBinding equal ErrorCode.ExpectingAssignment) invalid ImmutableBinding eof + buildFragment + qsImmutableBinding.parse + (expectedExpr eof |> symbolBinding equal ErrorCode.ExpectingAssignment) + invalid + (fun _ -> ImmutableBinding) + eof /// Uses buildFragment to parse a Q# mutable binding (i.e. mutable-statement) as QsFragment. and private mutableStatement = let invalid = MutableBinding (invalidSymbol, unknownExpr) - buildFragment qsMutableBinding.parse (expectedExpr eof |> symbolBinding equal ErrorCode.ExpectingAssignment) invalid MutableBinding eof + buildFragment + qsMutableBinding.parse + (expectedExpr eof |> symbolBinding equal ErrorCode.ExpectingAssignment) + invalid + (fun _ -> MutableBinding) + eof /// Uses buildFragment to parse a Q# value update (i.e. set-statement) as QsFragment. @@ -391,23 +410,22 @@ and private setStatement = let expectedEqual = expected equal ErrorCode.ExpectingAssignment ErrorCode.ExpectingAssignment "" (preturn ()) symbolTuple .>> expectedEqual .>>. expectedExpr eof let invalid = ValueUpdate (unknownExpr, unknownExpr) - buildFragment qsValueUpdate.parse (attempt applyAndReassign <|> symbolUpdate) invalid ValueUpdate eof + buildFragment qsValueUpdate.parse (attempt applyAndReassign <|> symbolUpdate) invalid (fun _ -> ValueUpdate) eof /// Uses buildFragment to parse a Q# if clause as QsFragment. and private ifClause = let invalid = IfClause unknownExpr - buildFragment qsIf.parse (expectedCondition eof) invalid IfClause eof + buildFragment qsIf.parse (expectedCondition eof) invalid (fun _ -> IfClause) eof /// Uses buildFragment to parse a Q# elif clause as QsFragment. and private elifClause = let invalid = ElifClause unknownExpr - buildFragment qsElif.parse (expectedCondition eof) invalid ElifClause eof + buildFragment qsElif.parse (expectedCondition eof) invalid (fun _ -> ElifClause) eof /// Uses buildFragment to parse a Q# else clause as QsFragment. and private elseClause = - let valid = fun _ -> ElseClause - buildFragment qsElse.parse (preturn "") ElseClause valid eof + buildFragment qsElse.parse (preturn "") ElseClause (fun _ _ -> ElseClause) eof /// Uses buildFragment to parse a Q# for-statement intro (for-statement without the body) as QsFragment. @@ -415,48 +433,45 @@ and private forHeader = let invalid = ForLoopIntro (invalidSymbol, unknownExpr) let loopVariableBinding = expectedExpr rTuple |> symbolBinding qsRangeIter.parse ErrorCode.ExpectingIteratorItemAssignment let forBody = optTupleBrackets loopVariableBinding |>> fst - buildFragment qsFor.parse forBody invalid ForLoopIntro eof + buildFragment qsFor.parse forBody invalid (fun _ -> ForLoopIntro) eof /// Uses buildFragment to parse a Q# while-statement intro (while-statement without the body) as QsFragment. and private whileHeader = let invalid = WhileLoopIntro unknownExpr let whileBody = optTupleBrackets (expectedExpr isTupleContinuation) |>> fst - buildFragment qsWhile.parse whileBody invalid WhileLoopIntro eof + buildFragment qsWhile.parse whileBody invalid (fun _ -> WhileLoopIntro) eof /// Uses buildFragment to parse a Q# repeat intro as QsFragment. and private repeatHeader = - let valid = fun _ -> RepeatIntro - buildFragment qsRepeat.parse (preturn "") RepeatIntro valid eof + buildFragment qsRepeat.parse (preturn "") RepeatIntro (fun _ _ -> RepeatIntro) eof /// Uses buildFragment to parse a Q# until success clause as QsFragment. and private untilSuccess = let invalid = UntilSuccess (unknownExpr, false) let optionalFixup = qsRUSfixup.parse >>% true <|> preturn false - buildFragment qsUntil.parse (expectedCondition qsRUSfixup.parse .>>. optionalFixup) invalid UntilSuccess eof + buildFragment qsUntil.parse (expectedCondition qsRUSfixup.parse .>>. optionalFixup) invalid (fun _ -> UntilSuccess) eof /// Uses buildFragment to parse a Q# within-block intro as QsFragment. and private withinHeader = - let valid = fun _ -> WithinBlockIntro - buildFragment qsWithin.parse (preturn "") WithinBlockIntro valid eof + buildFragment qsWithin.parse (preturn "") WithinBlockIntro (fun _ _ -> WithinBlockIntro) eof /// Uses buildFragment to parse a Q# apply block intro as QsFragment. and private applyHeader = - let valid = fun _ -> ApplyBlockIntro - buildFragment qsApply.parse (preturn "") ApplyBlockIntro valid eof + buildFragment qsApply.parse (preturn "") ApplyBlockIntro (fun _ _ -> ApplyBlockIntro) eof /// Uses buildFragment to parse a Q# using block intro as QsFragment. and private usingHeader = let invalid = UsingBlockIntro (invalidSymbol, invalidInitializer) - buildFragment qsUsing.parse allocationScope invalid UsingBlockIntro eof + buildFragment qsUsing.parse allocationScope invalid (fun _ -> UsingBlockIntro) eof /// Uses buildFragment to parse a Q# borrowing block intro as QsFragment. and private borrowingHeader = let invalid = BorrowingBlockIntro (invalidSymbol, invalidInitializer) - buildFragment qsBorrowing.parse allocationScope invalid BorrowingBlockIntro eof + buildFragment qsBorrowing.parse allocationScope invalid (fun _ -> BorrowingBlockIntro) eof /// Uses buildFragment to parse a Q# expression statement as QsFragment. @@ -470,7 +485,7 @@ let private expressionStatement = errRange |> QsCompilerDiagnostic.Error (ErrorCode.NonCallExprAsStatement, []) |> preturn >>= pushDiagnostic let anyExpr = getPosition .>>. expr >>= errOnNonCallLike >>% InvalidFragment // keeping this as unknown fragment so no further type checking is done attempt callLikeExpr |>> ExpressionStatement <|> anyExpr - buildFragment (lookAhead valid) valid invalid id eof // let's limit this to call like expressions + buildFragment (lookAhead valid) valid invalid (fun _ -> id) eof // let's limit this to call like expressions // externally called routines @@ -483,8 +498,5 @@ let internal codeFragment = <|> attributeDeclaration <|> expressionStatement// the expressionStatement needs to be last let invalidFragment = - let valid = fun _ -> InvalidFragment - buildFragment (preturn ()) (fail "invalid syntax") InvalidFragment valid attributeIntro + buildFragment (preturn ()) (fail "invalid syntax") InvalidFragment (fun _ _ -> InvalidFragment) attributeIntro attempt validFragment <|> invalidFragment - - diff --git a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs index 04bf21a1d4..67fcb67e11 100644 --- a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs +++ b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs @@ -424,14 +424,17 @@ let private filterAndAdapt (diagnostics : QsCompilerDiagnostic list) endPos = |> List.map (fun diagnostic -> diagnostic.WithRange(rangeWithinFragment diagnostic.Range)) /// Constructs a QsCodeFragment. -/// If the given header succeeds, attempts the given body parser to obtain the argument to construct the QsFragment using the given -/// fragmentKind constructor, or defaults to the given invalid fragment. -/// Generates a suitable error if the body parser fails. -/// If the body parser succeeds, advances until the next fragment header or until the given continuation succeeds, -/// generating an ExcessContinuation error for any non-whitespace code. -/// Determines the Range and Text for the fragment and attaches all current diagnostics saved in the user state to the QsFragment. -/// Upon fragment construction, clears all diagnostics currently stored in the UserState. -let internal buildFragment header body (invalid : QsFragmentKind) (fragmentKind : 'a -> QsFragmentKind) continuation = +/// +/// If the given header parser succeeds, attempts the given body parser. +/// +/// If the body parser succeeds, gives the results from both the header and body to the given fragmentKind constructor, +/// and advances until the next fragment header or until the given continuation succeeds, generating an +/// ExcessContinuation error for any non-whitespace code. Otherwise, if the body parser fails, defaults to the given +/// invalid fragment and generates a diagnostic. +/// +/// Determines the Range and Text for the fragment and attaches all current diagnostics saved in the user state to the +/// QsFragment. Upon fragment construction, clears all diagnostics currently stored in the UserState. +let internal buildFragment header body (invalid : QsFragmentKind) fragmentKind continuation = let build (kind, (startPos, (text, endPos))) = getUserState .>> clearDiagnostics |>> fun diagnostics -> @@ -449,18 +452,13 @@ let internal buildFragment header body (invalid : QsFragmentKind) (fragmentKind (getPosition .>>. fragmentEnd) |> runOnSubstream state let continuation = (continuation >>% ()) <|> (qsFragmentHeader >>% ()) - let validBody state = + let validBody state headerResult = let processExcessCode = buildError (skipInvalidUntil continuation) ErrorCode.ExcessContinuation >>% () (body .>>? followedBy (continuation <|> eof)) <|> (body .>> processExcessCode) - |>> fragmentKind .>>. delimiters state + |>> fragmentKind headerResult .>>. delimiters state let invalidBody state = getPosition .>> (advanceTo continuation) .>>. delimiters state >>= buildDiagnostic getCharStreamState >>= fun state -> - header >>. (attempt (validBody state) <|> invalidBody state) >>= build - - - - - - + header >>= fun headerResult -> + (attempt (validBody state headerResult) <|> invalidBody state) >>= build From 16bc4486ef2a938b97c641d0c53db96a9bf28077 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 30 Jan 2020 22:16:02 -0800 Subject: [PATCH 017/135] Move private/internal to a separate list --- .../TextProcessor/QsFragmentParsing.fs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 2825b2cdaf..8e6f655311 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -190,7 +190,7 @@ let private functorGenDirective = // parsing all possible directives for all fun // Q# fragments // making this recursive so any new fragment only needs to be added here (defining the necessary keywords in the Language module) -let rec private getFragments() = // Note: this needs to be a function! +let rec private getFragments () = // Note: this needs to be a function! [ (qsImmutableBinding , letStatement ) (qsMutableBinding , mutableStatement ) @@ -217,14 +217,20 @@ let rec private getFragments() = // Note: this needs to be a function! (ctrlDeclHeader , controlledDeclaration ) (bodyDeclHeader , bodyDeclaration ) (importDirectiveHeader, openDirective ) - // Modifiers need to be considered fragment headers for parsing reasons, but the actual parsing is handled by - // the fragment being modified. - (qsPrivate , pzero ) - (qsInternal , pzero ) + ] + +/// Fragment headers which do not have their own fragment kind. Instead, they are only parsed as part of another +/// fragment kind. +and private dependentHeaders = + [ + qsPrivate + qsInternal ] and private headerCheck = // DO NOT REMOVE - the check is executed once at the beginning, just as it should be - let implementedHeaders = (getFragments() |> List.map (fun x -> (fst x).id)).ToImmutableHashSet() + let implementedHeaders = + ((getFragments () |> List.map (fun (keyword, _) -> keyword.id)) @ + (dependentHeaders |> List.map (fun keyword -> keyword.id))).ToImmutableHashSet() let existingHeaders = Keywords.FragmentHeaders.ToImmutableHashSet() if (implementedHeaders.SymmetricExcept existingHeaders).Count <> 0 then System.NotImplementedException "mismatch between existing Q# fragments and implemented Q# fragments" |> raise From 56cbb57bf21bdc6f5a5058b5b5e2021c5962fc44 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 30 Jan 2020 22:36:29 -0800 Subject: [PATCH 018/135] Make getFragments a normal value --- .../TextProcessor/QsFragmentParsing.fs | 99 ++++++++++--------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 8e6f655311..54c0cf9fb0 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -189,57 +189,10 @@ let private functorGenDirective = // parsing all possible directives for all fun // Q# fragments -// making this recursive so any new fragment only needs to be added here (defining the necessary keywords in the Language module) -let rec private getFragments () = // Note: this needs to be a function! - [ - (qsImmutableBinding , letStatement ) - (qsMutableBinding , mutableStatement ) - (qsValueUpdate , setStatement ) - (qsReturn , returnStatement ) - (qsFail , failStatement ) - (qsIf , ifClause ) - (qsElif , elifClause ) - (qsElse , elseClause ) - (qsFor , forHeader ) - (qsWhile , whileHeader ) - (qsRepeat , repeatHeader ) - (qsUntil , untilSuccess ) - (qsWithin , withinHeader ) - (qsApply , applyHeader ) - (qsUsing , usingHeader ) - (qsBorrowing , borrowingHeader ) - (namespaceDeclHeader , namespaceDeclaration ) - (typeDeclHeader , udtDeclaration ) - (opDeclHeader , operationDeclaration ) - (fctDeclHeader , functionDeclaration ) - (ctrlAdjDeclHeader , controlledAdjointDeclaration) // needs to be before adjointDeclaration and controlledDeclaration! - (adjDeclHeader , adjointDeclaration ) - (ctrlDeclHeader , controlledDeclaration ) - (bodyDeclHeader , bodyDeclaration ) - (importDirectiveHeader, openDirective ) - ] - -/// Fragment headers which do not have their own fragment kind. Instead, they are only parsed as part of another -/// fragment kind. -and private dependentHeaders = - [ - qsPrivate - qsInternal - ] - -and private headerCheck = // DO NOT REMOVE - the check is executed once at the beginning, just as it should be - let implementedHeaders = - ((getFragments () |> List.map (fun (keyword, _) -> keyword.id)) @ - (dependentHeaders |> List.map (fun keyword -> keyword.id))).ToImmutableHashSet() - let existingHeaders = Keywords.FragmentHeaders.ToImmutableHashSet() - if (implementedHeaders.SymmetricExcept existingHeaders).Count <> 0 then - System.NotImplementedException "mismatch between existing Q# fragments and implemented Q# fragments" |> raise - - // namespace and attribute parsing /// Uses buildFragment to parse a Q# OpenDirective as QsFragment. -and private openDirective = +let rec private openDirective = let invalid = OpenDirective (invalidSymbol, Null) let nsNameAndAlias = let aliasOption = (importedAs.parse >>. expectedNamespaceName eof |>> Value) <|>% Null @@ -493,6 +446,54 @@ let private expressionStatement = attempt callLikeExpr |>> ExpressionStatement <|> anyExpr buildFragment (lookAhead valid) valid invalid (fun _ -> id) eof // let's limit this to call like expressions +/// Fragment header keywords and their corresponding fragment parsers. +let private fragments = + let fragments = + [ + (qsImmutableBinding, letStatement) + (qsMutableBinding, mutableStatement) + (qsValueUpdate, setStatement) + (qsReturn, returnStatement) + (qsFail, failStatement) + (qsIf, ifClause) + (qsElif, elifClause) + (qsElse, elseClause) + (qsFor, forHeader) + (qsWhile, whileHeader) + (qsRepeat, repeatHeader) + (qsUntil, untilSuccess) + (qsWithin, withinHeader) + (qsApply, applyHeader) + (qsUsing, usingHeader) + (qsBorrowing, borrowingHeader) + (namespaceDeclHeader, namespaceDeclaration) + (typeDeclHeader, udtDeclaration) + (opDeclHeader, operationDeclaration) + (fctDeclHeader, functionDeclaration) + (ctrlAdjDeclHeader, controlledAdjointDeclaration) // needs to be before adjointDeclaration and controlledDeclaration! + (adjDeclHeader, adjointDeclaration) + (ctrlDeclHeader, controlledDeclaration) + (bodyDeclHeader, bodyDeclaration) + (importDirectiveHeader, openDirective) + ] + + // Fragment headers which do not have their own fragment kind. Instead, they are only parsed as part of another + // fragment kind. + let dependentHeaders = + [ + qsPrivate + qsInternal + ] + + // Make sure all of the fragment header keywords are listed here. + let implementedHeaders = + ((fragments |> List.map (fun (keyword, _) -> keyword.id)) @ + (dependentHeaders |> List.map (fun keyword -> keyword.id))).ToImmutableHashSet() + let existingHeaders = Keywords.FragmentHeaders.ToImmutableHashSet() + if (implementedHeaders.SymmetricExcept existingHeaders).Count <> 0 then + System.NotImplementedException "mismatch between existing Q# fragments and implemented Q# fragments" |> raise + + fragments // externally called routines @@ -500,7 +501,7 @@ let private expressionStatement = /// Raises a suitable error and returns UnknownStatement as QsFragment if the parsing fails. let internal codeFragment = let validFragment = - choice (getFragments() |> List.map snd) + choice (fragments |> List.map snd) <|> attributeDeclaration <|> expressionStatement// the expressionStatement needs to be last let invalidFragment = From c757cc4b2eba957c5e26dfdb7721876787a57e25 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 31 Jan 2020 10:10:14 -0800 Subject: [PATCH 019/135] Remove let rec from fragment parsers --- .../TextProcessor/QsFragmentParsing.fs | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 54c0cf9fb0..9840283df5 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -192,7 +192,7 @@ let private functorGenDirective = // parsing all possible directives for all fun // namespace and attribute parsing /// Uses buildFragment to parse a Q# OpenDirective as QsFragment. -let rec private openDirective = +let private openDirective = let invalid = OpenDirective (invalidSymbol, Null) let nsNameAndAlias = let aliasOption = (importedAs.parse >>. expectedNamespaceName eof |>> Value) <|>% Null @@ -200,12 +200,12 @@ let rec private openDirective = buildFragment importDirectiveHeader.parse nsNameAndAlias invalid (fun _ -> OpenDirective) eof /// Uses buildFragment to parse a Q# NamespaceDeclaration as QsFragment. -and private namespaceDeclaration = +let private namespaceDeclaration = let invalid = NamespaceDeclaration invalidSymbol buildFragment namespaceDeclHeader.parse (expectedNamespaceName eof) invalid (fun _ -> NamespaceDeclaration) eof /// Uses buildFragment to parse a Q# DeclarationAttribute as QsFragment. -and private attributeDeclaration = +let private attributeDeclaration = let invalid = DeclarationAttribute (invalidSymbol, unknownExpr) let attributeId = multiSegmentSymbol ErrorCode.InvalidIdentifierName |>> asQualifiedSymbol let expectedArgs = @@ -222,27 +222,27 @@ and private attributeDeclaration = // operation and function parsing /// Uses buildFragment to parse a Q# BodyDeclaration as QsFragment. -and private bodyDeclaration = +let private bodyDeclaration = let invalid = BodyDeclaration unknownGenerator buildFragment bodyDeclHeader.parse functorGenDirective invalid (fun _ -> BodyDeclaration) eof /// Uses buildFragment to parse a Q# AdjointDeclaration as QsFragment. -and private adjointDeclaration = +let private adjointDeclaration = let invalid = AdjointDeclaration unknownGenerator buildFragment adjDeclHeader.parse functorGenDirective invalid (fun _ -> AdjointDeclaration) eof /// Uses buildFragment to parse a Q# ControlledDeclaration as QsFragment. -and private controlledDeclaration = +let private controlledDeclaration = let invalid = ControlledDeclaration unknownGenerator buildFragment ctrlDeclHeader.parse functorGenDirective invalid (fun _ -> ControlledDeclaration) eof /// Uses buildFragment to parse a Q# ControlledAdjointDeclaration as QsFragment. -and private controlledAdjointDeclaration = +let private controlledAdjointDeclaration = let invalid = ControlledAdjointDeclaration unknownGenerator buildFragment (attempt ctrlAdjDeclHeader.parse) functorGenDirective invalid (fun _ -> ControlledAdjointDeclaration) eof /// Uses buildFragment to parse a Q# OperationDeclaration as QsFragment. -and private operationDeclaration = +let private operationDeclaration = let invalid = OperationDeclaration ({Access = DefaultAccess}, invalidSymbol, CallableSignature.Invalid) buildFragment (modifiers .>> opDeclHeader.parse |> attempt) @@ -252,7 +252,7 @@ and private operationDeclaration = eof /// Uses buildFragment to parse a Q# FunctionDeclaration as QsFragment. -and private functionDeclaration = +let private functionDeclaration = let invalid = FunctionDeclaration ({Access = DefaultAccess}, invalidSymbol, CallableSignature.Invalid) buildFragment (modifiers .>> fctDeclHeader.parse |> attempt) @@ -262,7 +262,7 @@ and private functionDeclaration = eof /// Uses buildFragment to parse a Q# TypeDefinition as QsFragment. -and private udtDeclaration = +let private udtDeclaration = let invalid = TypeDefinition ({Access = DefaultAccess}, invalidSymbol, invalidArgTupleItem) let udtTuple = // not unified with the argument tuple for callable declarations, since the error handling needs to be different let asAnonymousItem t = QsTupleItem ((MissingSymbol, Null) |> QsSymbol.New, t) @@ -288,18 +288,18 @@ and private udtDeclaration = // statement parsing /// Uses buildFragment to parse a Q# return-statement as QsFragment. -and private returnStatement = +let private returnStatement = let invalid = ReturnStatement unknownExpr buildFragment qsReturn.parse (expectedExpr eof) invalid (fun _ -> ReturnStatement) eof /// Uses buildFragment to parse a Q# fail-statement as QsFragment. -and private failStatement = +let private failStatement = let invalid = FailStatement unknownExpr buildFragment qsFail.parse (expectedExpr eof) invalid (fun _ -> FailStatement) eof /// Uses buildFragment to parse a Q# immutable binding (i.e. let-statement) as QsFragment. -and private letStatement = +let private letStatement = let invalid = ImmutableBinding (invalidSymbol, unknownExpr) buildFragment qsImmutableBinding.parse @@ -309,7 +309,7 @@ and private letStatement = eof /// Uses buildFragment to parse a Q# mutable binding (i.e. mutable-statement) as QsFragment. -and private mutableStatement = +let private mutableStatement = let invalid = MutableBinding (invalidSymbol, unknownExpr) buildFragment qsMutableBinding.parse @@ -320,7 +320,7 @@ and private mutableStatement = /// Uses buildFragment to parse a Q# value update (i.e. set-statement) as QsFragment. -and private setStatement = +let private setStatement = let applyAndReassignOp = let updateAndReassign id = let update = pstring qsCopyAndUpdateOp.cont |> term @@ -373,22 +373,22 @@ and private setStatement = /// Uses buildFragment to parse a Q# if clause as QsFragment. -and private ifClause = +let private ifClause = let invalid = IfClause unknownExpr buildFragment qsIf.parse (expectedCondition eof) invalid (fun _ -> IfClause) eof /// Uses buildFragment to parse a Q# elif clause as QsFragment. -and private elifClause = +let private elifClause = let invalid = ElifClause unknownExpr buildFragment qsElif.parse (expectedCondition eof) invalid (fun _ -> ElifClause) eof /// Uses buildFragment to parse a Q# else clause as QsFragment. -and private elseClause = +let private elseClause = buildFragment qsElse.parse (preturn "") ElseClause (fun _ _ -> ElseClause) eof /// Uses buildFragment to parse a Q# for-statement intro (for-statement without the body) as QsFragment. -and private forHeader = +let private forHeader = let invalid = ForLoopIntro (invalidSymbol, unknownExpr) let loopVariableBinding = expectedExpr rTuple |> symbolBinding qsRangeIter.parse ErrorCode.ExpectingIteratorItemAssignment let forBody = optTupleBrackets loopVariableBinding |>> fst @@ -396,39 +396,39 @@ and private forHeader = /// Uses buildFragment to parse a Q# while-statement intro (while-statement without the body) as QsFragment. -and private whileHeader = +let private whileHeader = let invalid = WhileLoopIntro unknownExpr let whileBody = optTupleBrackets (expectedExpr isTupleContinuation) |>> fst buildFragment qsWhile.parse whileBody invalid (fun _ -> WhileLoopIntro) eof /// Uses buildFragment to parse a Q# repeat intro as QsFragment. -and private repeatHeader = +let private repeatHeader = buildFragment qsRepeat.parse (preturn "") RepeatIntro (fun _ _ -> RepeatIntro) eof /// Uses buildFragment to parse a Q# until success clause as QsFragment. -and private untilSuccess = +let private untilSuccess = let invalid = UntilSuccess (unknownExpr, false) let optionalFixup = qsRUSfixup.parse >>% true <|> preturn false buildFragment qsUntil.parse (expectedCondition qsRUSfixup.parse .>>. optionalFixup) invalid (fun _ -> UntilSuccess) eof /// Uses buildFragment to parse a Q# within-block intro as QsFragment. -and private withinHeader = +let private withinHeader = buildFragment qsWithin.parse (preturn "") WithinBlockIntro (fun _ _ -> WithinBlockIntro) eof /// Uses buildFragment to parse a Q# apply block intro as QsFragment. -and private applyHeader = +let private applyHeader = buildFragment qsApply.parse (preturn "") ApplyBlockIntro (fun _ _ -> ApplyBlockIntro) eof /// Uses buildFragment to parse a Q# using block intro as QsFragment. -and private usingHeader = +let private usingHeader = let invalid = UsingBlockIntro (invalidSymbol, invalidInitializer) buildFragment qsUsing.parse allocationScope invalid (fun _ -> UsingBlockIntro) eof /// Uses buildFragment to parse a Q# borrowing block intro as QsFragment. -and private borrowingHeader = +let private borrowingHeader = let invalid = BorrowingBlockIntro (invalidSymbol, invalidInitializer) buildFragment qsBorrowing.parse allocationScope invalid (fun _ -> BorrowingBlockIntro) eof From 5514bb11e6043a08d2fe5c7b14a8b3af80a11955 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 31 Jan 2020 15:06:48 -0800 Subject: [PATCH 020/135] Move fragment header consistency check to do-block --- .../TextProcessor/QsFragmentParsing.fs | 94 +++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 9840283df5..41d271db21 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -432,51 +432,37 @@ let private borrowingHeader = let invalid = BorrowingBlockIntro (invalidSymbol, invalidInitializer) buildFragment qsBorrowing.parse allocationScope invalid (fun _ -> BorrowingBlockIntro) eof - -/// Uses buildFragment to parse a Q# expression statement as QsFragment. -/// Raises a NonCallExprAsStatement error if the expression is not a call-like expression, and returns UnknownStatement as QsFragment instead. -/// Raising an error for partial applications (that can't possibly be of type unit) is up to the type checking. -let private expressionStatement = - let invalid = ExpressionStatement unknownExpr - let valid = - let errOnNonCallLike (pos, ex : QsExpression) = - let errRange = match ex.Range with | Value range -> range | Null -> (QsPositionInfo.New pos, QsPositionInfo.New pos) - errRange |> QsCompilerDiagnostic.Error (ErrorCode.NonCallExprAsStatement, []) |> preturn >>= pushDiagnostic - let anyExpr = getPosition .>>. expr >>= errOnNonCallLike >>% InvalidFragment // keeping this as unknown fragment so no further type checking is done - attempt callLikeExpr |>> ExpressionStatement <|> anyExpr - buildFragment (lookAhead valid) valid invalid (fun _ -> id) eof // let's limit this to call like expressions - /// Fragment header keywords and their corresponding fragment parsers. let private fragments = - let fragments = - [ - (qsImmutableBinding, letStatement) - (qsMutableBinding, mutableStatement) - (qsValueUpdate, setStatement) - (qsReturn, returnStatement) - (qsFail, failStatement) - (qsIf, ifClause) - (qsElif, elifClause) - (qsElse, elseClause) - (qsFor, forHeader) - (qsWhile, whileHeader) - (qsRepeat, repeatHeader) - (qsUntil, untilSuccess) - (qsWithin, withinHeader) - (qsApply, applyHeader) - (qsUsing, usingHeader) - (qsBorrowing, borrowingHeader) - (namespaceDeclHeader, namespaceDeclaration) - (typeDeclHeader, udtDeclaration) - (opDeclHeader, operationDeclaration) - (fctDeclHeader, functionDeclaration) - (ctrlAdjDeclHeader, controlledAdjointDeclaration) // needs to be before adjointDeclaration and controlledDeclaration! - (adjDeclHeader, adjointDeclaration) - (ctrlDeclHeader, controlledDeclaration) - (bodyDeclHeader, bodyDeclaration) - (importDirectiveHeader, openDirective) - ] + [ + (qsImmutableBinding, letStatement) + (qsMutableBinding, mutableStatement) + (qsValueUpdate, setStatement) + (qsReturn, returnStatement) + (qsFail, failStatement) + (qsIf, ifClause) + (qsElif, elifClause) + (qsElse, elseClause) + (qsFor, forHeader) + (qsWhile, whileHeader) + (qsRepeat, repeatHeader) + (qsUntil, untilSuccess) + (qsWithin, withinHeader) + (qsApply, applyHeader) + (qsUsing, usingHeader) + (qsBorrowing, borrowingHeader) + (namespaceDeclHeader, namespaceDeclaration) + (typeDeclHeader, udtDeclaration) + (opDeclHeader, operationDeclaration) + (fctDeclHeader, functionDeclaration) + (ctrlAdjDeclHeader, controlledAdjointDeclaration) // needs to be before adjointDeclaration and controlledDeclaration! + (adjDeclHeader, adjointDeclaration) + (ctrlDeclHeader, controlledDeclaration) + (bodyDeclHeader, bodyDeclaration) + (importDirectiveHeader, openDirective) + ] +do // Fragment headers which do not have their own fragment kind. Instead, they are only parsed as part of another // fragment kind. let dependentHeaders = @@ -484,16 +470,30 @@ let private fragments = qsPrivate qsInternal ] - - // Make sure all of the fragment header keywords are listed here. + + // Make sure all of the fragment header keywords are listed above. let implementedHeaders = - ((fragments |> List.map (fun (keyword, _) -> keyword.id)) @ - (dependentHeaders |> List.map (fun keyword -> keyword.id))).ToImmutableHashSet() + List.append + (List.map (fun (keyword, _) -> keyword.id) fragments) + (List.map (fun keyword -> keyword.id) dependentHeaders) + |> fun h -> h.ToImmutableHashSet() let existingHeaders = Keywords.FragmentHeaders.ToImmutableHashSet() if (implementedHeaders.SymmetricExcept existingHeaders).Count <> 0 then System.NotImplementedException "mismatch between existing Q# fragments and implemented Q# fragments" |> raise - fragments + +/// Uses buildFragment to parse a Q# expression statement as QsFragment. +/// Raises a NonCallExprAsStatement error if the expression is not a call-like expression, and returns UnknownStatement as QsFragment instead. +/// Raising an error for partial applications (that can't possibly be of type unit) is up to the type checking. +let private expressionStatement = + let invalid = ExpressionStatement unknownExpr + let valid = + let errOnNonCallLike (pos, ex : QsExpression) = + let errRange = match ex.Range with | Value range -> range | Null -> (QsPositionInfo.New pos, QsPositionInfo.New pos) + errRange |> QsCompilerDiagnostic.Error (ErrorCode.NonCallExprAsStatement, []) |> preturn >>= pushDiagnostic + let anyExpr = getPosition .>>. expr >>= errOnNonCallLike >>% InvalidFragment // keeping this as unknown fragment so no further type checking is done + attempt callLikeExpr |>> ExpressionStatement <|> anyExpr + buildFragment (lookAhead valid) valid invalid (fun _ -> id) eof // let's limit this to call like expressions // externally called routines From 4374c1338bbca575dc1f43aff050ada5cbb104ce Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 3 Feb 2020 11:55:20 -0800 Subject: [PATCH 021/135] Add access modifiers to code completion --- .../Tests.Compiler/CompletionParsingTests.fs | 6 ++++++ .../TextProcessor/CodeCompletion/FragmentParsing.fs | 10 +++++++--- .../TextProcessor/CodeCompletion/ParsingPrimitives.fs | 5 +++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs b/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs index beba2f499a..7e8edfd2ad 100644 --- a/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs +++ b/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs @@ -128,6 +128,8 @@ let ``Namespace top-level parser tests`` () = Keyword "function" Keyword "operation" Keyword "newtype" + Keyword "private" + Keyword "internal" Keyword "open" ] List.iter (matches NamespaceTopLevel Null) [ @@ -143,6 +145,10 @@ let ``Namespace top-level parser tests`` () = ("newt", keywords) ("newtype", keywords) ("open", keywords) + ("pri", keywords) + ("private", keywords) + ("int", keywords) + ("internal", keywords) ] [] diff --git a/src/QsCompiler/TextProcessor/CodeCompletion/FragmentParsing.fs b/src/QsCompiler/TextProcessor/CodeCompletion/FragmentParsing.fs index f58fc1e20f..95c6f6ae53 100644 --- a/src/QsCompiler/TextProcessor/CodeCompletion/FragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/CodeCompletion/FragmentParsing.fs @@ -22,6 +22,10 @@ open Microsoft.Quantum.QsCompiler.TextProcessing.CodeCompletion.ParsingPrimitive #nowarn "40" +/// Parses a declaration modifier list. +let private modifiers = + expectedKeyword qsPrivate <|>@ expectedKeyword qsInternal + /// Parses a callable signature. let private callableSignature = let name = expectedId Declaration (term symbol) @@ -33,11 +37,11 @@ let private callableSignature = /// Parses a function declaration. let private functionDeclaration = - expectedKeyword fctDeclHeader ?>> callableSignature + optR modifiers @>> expectedKeyword fctDeclHeader ?>> callableSignature /// Parses an operation declaration. let private operationDeclaration = - expectedKeyword opDeclHeader ?>> callableSignature ?>> characteristicsAnnotation + optR modifiers @>> expectedKeyword opDeclHeader ?>> callableSignature ?>> characteristicsAnnotation /// Parses a user-defined type declaration. let private udtDeclaration = @@ -46,7 +50,7 @@ let private udtDeclaration = let namedItem = name ?>> expected colon ?>> qsType return! qsType <|>@ tuple1 (namedItem <|>@ udt) } - expectedKeyword typeDeclHeader ?>> name ?>> expected equal ?>> udt + optR modifiers @>> expectedKeyword typeDeclHeader ?>> name ?>> expected equal ?>> udt /// Parses an open directive. let private openDirective = diff --git a/src/QsCompiler/TextProcessor/CodeCompletion/ParsingPrimitives.fs b/src/QsCompiler/TextProcessor/CodeCompletion/ParsingPrimitives.fs index 17690806e3..eb77be0c10 100644 --- a/src/QsCompiler/TextProcessor/CodeCompletion/ParsingPrimitives.fs +++ b/src/QsCompiler/TextProcessor/CodeCompletion/ParsingPrimitives.fs @@ -110,6 +110,11 @@ let expectedQualifiedSymbol kind = attempt (term qualifiedSymbol |>> fst .>> previousCharSatisfiesNot Char.IsWhiteSpace .>> optional eot) <|> (term qualifiedSymbol >>% []) +/// Optionally parses `p`, backtracking if it consumes EOT so another parser can try, too. Best if used with `@>>`, +/// e.g., `optR foo @>> bar`. +let optR p = + (p .>> previousCharSatisfies ((<>) '\u0004') |> opt |>> Option.defaultValue [] |> attempt) <|> lookAhead p + /// `manyR p shouldBacktrack` is like `many p` but is reentrant on the last item if /// 1. The last item is followed by EOT; and /// 2. `shouldBacktrack` succeeds at the beginning of the last item, or the last item consists of only EOT. From c26c93febc212ad6f5cb5a0673c36f732ff22620 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 3 Feb 2020 14:30:54 -0800 Subject: [PATCH 022/135] Include modifiers in code completion tests --- .../Tests.Compiler/CompletionParsingTests.fs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs b/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs index 7e8edfd2ad..dadb2e21c5 100644 --- a/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs +++ b/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs @@ -94,7 +94,7 @@ let private operationTopLevelStatement = Keyword "controlled" ] -let testElifElse scope previous = +let private testElifElse scope previous = let statement = match scope with | Operation | OperationTopLevel -> operationStatement @@ -109,6 +109,13 @@ let testElifElse scope previous = ("else ", []) ] +let private testWithModifiers tests = + List.concat [ + tests + List.map (fun (input, result) -> ("private " + input, result)) tests + List.map (fun (input, result) -> ("internal " + input, result)) tests + ] |> List.iter (matches NamespaceTopLevel Null) + [] let ``Top-level parser tests`` () = List.iter (matches TopLevel Null) [ @@ -147,13 +154,15 @@ let ``Namespace top-level parser tests`` () = ("open", keywords) ("pri", keywords) ("private", keywords) + ("private ", [Keyword "function"; Keyword "operation"; Keyword "newtype"]) ("int", keywords) ("internal", keywords) + ("internal ", [Keyword "function"; Keyword "operation"; Keyword "newtype"]) ] [] let ``Function declaration parser tests`` () = - List.iter (matches NamespaceTopLevel Null) [ + testWithModifiers [ ("function ", [Declaration]) ("function Foo", [Declaration]) ("function Foo ", []) @@ -206,7 +215,7 @@ let ``Operation declaration parser tests`` () = Keyword "Adj" Keyword "Ctl" ] - List.iter (matches NamespaceTopLevel Null) [ + testWithModifiers [ ("operation ", [Declaration]) ("operation Foo", [Declaration]) ("operation Foo ", []) @@ -287,7 +296,7 @@ let ``Operation declaration parser tests`` () = [] let ``Type declaration parser tests`` () = - List.iter (matches NamespaceTopLevel Null) [ + testWithModifiers [ ("newtype ", [Declaration]) ("newtype MyType", [Declaration]) ("newtype MyType ", []) From cef7da927475c2eb31244228acc9e152b3989503 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 3 Feb 2020 14:43:27 -0800 Subject: [PATCH 023/135] optR makes more sense this way --- .../TextProcessor/CodeCompletion/ParsingPrimitives.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/TextProcessor/CodeCompletion/ParsingPrimitives.fs b/src/QsCompiler/TextProcessor/CodeCompletion/ParsingPrimitives.fs index eb77be0c10..fc6e6b8851 100644 --- a/src/QsCompiler/TextProcessor/CodeCompletion/ParsingPrimitives.fs +++ b/src/QsCompiler/TextProcessor/CodeCompletion/ParsingPrimitives.fs @@ -113,7 +113,7 @@ let expectedQualifiedSymbol kind = /// Optionally parses `p`, backtracking if it consumes EOT so another parser can try, too. Best if used with `@>>`, /// e.g., `optR foo @>> bar`. let optR p = - (p .>> previousCharSatisfies ((<>) '\u0004') |> opt |>> Option.defaultValue [] |> attempt) <|> lookAhead p + attempt (p .>> previousCharSatisfies ((<>) '\u0004')) <|> lookAhead p <|>% [] /// `manyR p shouldBacktrack` is like `many p` but is reentrant on the last item if /// 1. The last item is followed by EOT; and From 9e48013d2952af416645303da2ae915f88452b9e Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 4 Feb 2020 14:18:16 -0800 Subject: [PATCH 024/135] Fix fragment being created without consuming input --- .../TextProcessor/QsFragmentParsing.fs | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 41d271db21..78cb5fc73b 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -432,6 +432,10 @@ let private borrowingHeader = let invalid = BorrowingBlockIntro (invalidSymbol, invalidInitializer) buildFragment qsBorrowing.parse allocationScope invalid (fun _ -> BorrowingBlockIntro) eof +/// Always builds an invalid fragment after parsing the given fragment header. +let private buildInvalidFragment header = + buildFragment header (fail "invalid syntax") InvalidFragment (fun _ _ -> InvalidFragment) attributeIntro + /// Fragment header keywords and their corresponding fragment parsers. let private fragments = [ @@ -460,23 +464,18 @@ let private fragments = (ctrlDeclHeader, controlledDeclaration) (bodyDeclHeader, bodyDeclaration) (importDirectiveHeader, openDirective) + + // These fragment headers do not have their own fragment kind. Instead, they are only parsed as part of another + // fragment kind. If one of these headers occurs by itself, without the other header it's a part of, an invalid + // fragment should be created. Since they're at the end of the list, we know that all of the other fragment + // kinds have been tried first. + (qsPrivate, buildInvalidFragment qsPrivate.parse) + (qsInternal, buildInvalidFragment qsInternal.parse) ] do - // Fragment headers which do not have their own fragment kind. Instead, they are only parsed as part of another - // fragment kind. - let dependentHeaders = - [ - qsPrivate - qsInternal - ] - // Make sure all of the fragment header keywords are listed above. - let implementedHeaders = - List.append - (List.map (fun (keyword, _) -> keyword.id) fragments) - (List.map (fun keyword -> keyword.id) dependentHeaders) - |> fun h -> h.ToImmutableHashSet() + let implementedHeaders = (List.map (fun (keyword, _) -> keyword.id) fragments).ToImmutableHashSet() let existingHeaders = Keywords.FragmentHeaders.ToImmutableHashSet() if (implementedHeaders.SymmetricExcept existingHeaders).Count <> 0 then System.NotImplementedException "mismatch between existing Q# fragments and implemented Q# fragments" |> raise @@ -503,7 +502,5 @@ let internal codeFragment = let validFragment = choice (fragments |> List.map snd) <|> attributeDeclaration - <|> expressionStatement// the expressionStatement needs to be last - let invalidFragment = - buildFragment (preturn ()) (fail "invalid syntax") InvalidFragment (fun _ _ -> InvalidFragment) attributeIntro - attempt validFragment <|> invalidFragment + <|> expressionStatement // the expressionStatement needs to be last + attempt validFragment <|> buildInvalidFragment (preturn ()) From 01a5bd6e336d40f415782dfdcbf254b66db24035 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 7 Feb 2020 09:31:01 -0800 Subject: [PATCH 025/135] Check for access when resolving callables --- src/QsCompiler/Core/SymbolTable.fs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index b2b809fa41..497f7472a7 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -756,6 +756,14 @@ and NamespaceManager | Value (declSource, deprecation) -> buildAndReturn (ns.Name, declSource, deprecation, [||]) | Null -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeInNamespace, [symName.Value; qualifier.Value]) |] + /// Returns whether a declaration is visible from the calling location, given whether the calling location is in the + /// same assembly and/or namespace as the declaration, and the declaration's modifiers. + let IsDeclarationVisible isSameAssembly isSameNs modifiers = + match modifiers.Access with + | DefaultAccess -> true + | Internal -> isSameAssembly + | Private -> isSameAssembly && isSameNs + /// Given the name of the namespace as well as the source file in which the attribute occurs, resolves the given attribute. /// Generates suitable diagnostics if a suitable attribute cannot be determined, /// or if the attribute argument contains expressions that are not supported, @@ -1247,14 +1255,22 @@ and NamespaceManager try match (nsName, source) |> TryResolveQualifier callableName.Namespace with | None -> Null | Some ns -> ns.CallablesInReferencedAssemblies.TryGetValue callableName.Name |> function - | true, cDecl -> Value cDecl + | true, cDecl -> + if IsDeclarationVisible false (nsName = cDecl.QualifiedName.Namespace) cDecl.Modifiers then + Value cDecl + else Null | false, _ -> declSource |> function | Some source -> let kind, decl = ns.CallableInSource source callableName.Name // ok only because/if we have covered that the callable is not in a reference! - BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) + if IsDeclarationVisible true (nsName = ns.Name) decl.Modifiers then + BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) + else Null | None -> ns.CallablesDefinedInAllSources().TryGetValue callableName.Name |> function - | true, (source, (kind, decl)) -> BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) + | true, (source, (kind, decl)) -> + if IsDeclarationVisible true (nsName = ns.Name) decl.Modifiers then + BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) + else Null | false, _ -> Null finally syncRoot.ExitReadLock() @@ -1270,7 +1286,7 @@ and NamespaceManager syncRoot.EnterReadLock() try match (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsCallable cName) with | [(declNS, (declSource, _))] -> this.TryGetCallableHeader ({Namespace = declNS; Name = cName}, Some declSource) (nsName, source) |> function - | Null -> QsCompilerError.Raise "failed to get the callable information about a resolved callable"; Null, Seq.empty + | Null -> Null, Seq.empty | info -> info, seq {yield declNS} | resolutions -> Null, resolutions.Select fst finally syncRoot.ExitReadLock() From 03ec339ad9d2ce005357b3dd5d845839e73d1d3f Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 7 Feb 2020 17:55:58 -0800 Subject: [PATCH 026/135] Check access when resolving types --- src/QsCompiler/Core/SymbolTable.fs | 77 +++++++++++++++++++----------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 497f7472a7..bf9d16cd14 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -419,10 +419,17 @@ and Namespace private partial.GetSpecializations cName |> Seq.map (fun (kind, decl) -> kind, (partial.Source, decl)))).ToImmutableArray() | Null -> ArgumentException "no callable with the given name exist within the namespace" |> raise - /// If this namespace contains a declaration for the given type name, - /// returns a Value with the name of the source file or the name of the file within a referenced assembly - /// in which it is declared as well as a string option indicating the redirection for the type if it has been deprecated. + /// If this namespace contains a declaration for the given type name, returns a Value with: + /// + /// (1) the name of the source file or the name of the file within a referenced assembly in which it is + /// declared; + /// (2) a string option indicating the redirection for the type if it has been deprecated; + /// (3) true if the declaration is declared in the assembly being compiled, or false if it is declared in a + /// referenced assembly; + /// (4) the modifiers for the declaration. + /// /// Returns Null otherwise. + /// /// Whether the type has been deprecated is determined by checking the associated attributes for an attribute with the corresponding name. /// Note that if the type is declared in a source files, the *unresolved* attributes will be checked. /// In that case checkDeprecation is used to validate the namespace qualification of the attribute. @@ -430,9 +437,9 @@ and Namespace private member this.ContainsType (tName, ?checkDeprecation : (string -> bool)) = let checkDeprecation = defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) match TypesInReferences.TryGetValue tName with - | true, tDecl -> Value (tDecl.SourceFile, tDecl.Attributes |> SymbolResolution.TryFindRedirect) + | true, tDecl -> Value (tDecl.SourceFile, tDecl.Attributes |> SymbolResolution.TryFindRedirect, false, tDecl.Modifiers) | false, _ -> FromSingleSource (fun partialNS -> partialNS.TryGetType tName |> function - | true, tDecl -> Some (partialNS.Source, tDecl.DefinedAttributes |> SymbolResolution.TryFindRedirectInUnresolved checkDeprecation) + | true, tDecl -> Some (partialNS.Source, tDecl.DefinedAttributes |> SymbolResolution.TryFindRedirectInUnresolved checkDeprecation, true, tDecl.Modifiers) | false, _ -> None) /// If this namespace contains the declaration for the given callable name, @@ -726,6 +733,14 @@ and NamespaceManager | true, _ -> [builtIn.Namespace.Value]; | false, _ -> ArgumentException "no namespace with the given name exists" |> raise + /// Returns whether a declaration is visible from the calling location, given whether the calling location is in the + /// same assembly and/or namespace as the declaration, and the declaration's modifiers. + let IsDeclarationVisible isSameAssembly isSameNs modifiers = + match modifiers.Access with + | DefaultAccess -> true + | Internal -> isSameAssembly + | Private -> isSameAssembly && isSameNs + /// Given the qualified or unqualfied name of a type used within the given parent namespace and source file, determines if such a type exists /// and returns its full name and the source file or referenced assembly in which it is defined as Some if it does. /// Returns None if no such type exist, or if the type name is unqualified and ambiguous. @@ -740,11 +755,14 @@ and NamespaceManager Some ({Namespace = ns; Name = symName; Range = symRange}, declSource), deprecatedWarnings |> Array.append errs let tryFind (parentNS, source) (tName, tRange) = match (parentNS, source) |> PossibleResolutions (fun ns -> ns.ContainsType (tName, checkQualificationForDeprecation)) with - | [] -> Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownType, [tName.Value]) |] - | [(nsName, (declSource, deprecated))] -> Value (nsName, declSource, deprecated), [||] - | resolutions -> + | [(nsName, (declSource, deprecated, isSameAssembly, modifiers))] + when IsDeclarationVisible isSameAssembly (parentNS = nsName) modifiers -> + Value (nsName, declSource, deprecated), [||] + | resolutions when resolutions.Length > 1 -> let diagArg = String.Join(", ", resolutions.Select (fun (ns,_) -> ns.Value)) Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AmbiguousType, [tName.Value; diagArg]) |] + | _ -> Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownType, [tName.Value]) |] + match nsName with | None -> tryFind (parentNS, source) (symName, symRange) |> function | Value (ns, declSource, deprecation), errs -> buildAndReturn (ns, declSource, deprecation, errs) @@ -753,16 +771,11 @@ and NamespaceManager | None -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownNamespace, [qualifier.Value]) |] | Some ns -> ns.ContainsType (symName, checkQualificationForDeprecation) |> function - | Value (declSource, deprecation) -> buildAndReturn (ns.Name, declSource, deprecation, [||]) - | Null -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeInNamespace, [symName.Value; qualifier.Value]) |] + | Value (declSource, deprecation, isSameAssembly, modifiers) + when IsDeclarationVisible isSameAssembly (parentNS = ns.Name) modifiers -> + buildAndReturn (ns.Name, declSource, deprecation, [||]) + | _ -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeInNamespace, [symName.Value; qualifier.Value]) |] - /// Returns whether a declaration is visible from the calling location, given whether the calling location is in the - /// same assembly and/or namespace as the declaration, and the declaration's modifiers. - let IsDeclarationVisible isSameAssembly isSameNs modifiers = - match modifiers.Access with - | DefaultAccess -> true - | Internal -> isSameAssembly - | Private -> isSameAssembly && isSameNs /// Given the name of the namespace as well as the source file in which the attribute occurs, resolves the given attribute. /// Generates suitable diagnostics if a suitable attribute cannot be determined, @@ -1239,7 +1252,7 @@ and NamespaceManager let BuildHeader fullName (source, kind, declaration : Resolution<_,_>) = let fallback () = (declaration.Defined, [CallableInformation.Invalid]) |> this.ResolveCallableSignature (kind, callableName, source) |> fst let resolvedSignature, argTuple = declaration.Resolved.ValueOrApply fallback - Value { + { Kind = kind QualifiedName = fullName Attributes = declaration.ResolvedAttributes @@ -1263,13 +1276,13 @@ and NamespaceManager | Some source -> let kind, decl = ns.CallableInSource source callableName.Name // ok only because/if we have covered that the callable is not in a reference! if IsDeclarationVisible true (nsName = ns.Name) decl.Modifiers then - BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) + BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Value else Null | None -> ns.CallablesDefinedInAllSources().TryGetValue callableName.Name |> function | true, (source, (kind, decl)) -> if IsDeclarationVisible true (nsName = ns.Name) decl.Modifiers then - BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) + BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Value else Null | false, _ -> Null finally syncRoot.ExitReadLock() @@ -1300,7 +1313,7 @@ and NamespaceManager let BuildHeader fullName (source, declaration) = let fallback () = declaration.Defined |> this.ResolveTypeDeclaration (typeName, source) |> fst let underlyingType, items = declaration.Resolved.ValueOrApply fallback - Value { + { QualifiedName = fullName Attributes = declaration.ResolvedAttributes Modifiers = declaration.Modifiers @@ -1312,17 +1325,26 @@ and NamespaceManager Documentation = declaration.Documentation } syncRoot.EnterReadLock() - try match (nsName, source) |> TryResolveQualifier typeName.Namespace with + try + match (nsName, source) |> TryResolveQualifier typeName.Namespace with | None -> Null | Some ns -> ns.TypesInReferencedAssemblies.TryGetValue typeName.Name |> function - | true, tDecl -> Value tDecl + | true, tDecl -> + if IsDeclarationVisible false (nsName = tDecl.QualifiedName.Namespace) tDecl.Modifiers then + Value tDecl + else Null | false, _ -> declSource |> function | Some source -> let decl = ns.TypeInSource source typeName.Name // ok only because/if we have covered that the type is not in a reference! - BuildHeader {typeName with Namespace = ns.Name} (source, decl) + if IsDeclarationVisible true (nsName = ns.Name) decl.Modifiers then + BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Value + else Null | None -> ns.TypesDefinedInAllSources().TryGetValue typeName.Name |> function - | true, (source, decl) -> BuildHeader {typeName with Namespace = ns.Name} (source, decl) + | true, (source, decl) -> + if IsDeclarationVisible true (nsName = ns.Name) decl.Modifiers then + BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Value + else Null | false, _ -> Null finally syncRoot.ExitReadLock() @@ -1337,8 +1359,9 @@ and NamespaceManager member this.TryResolveAndGetType tName (nsName, source) = syncRoot.EnterReadLock() try match (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsType tName) with - | [(declNS, (declSource, _))] -> this.TryGetTypeHeader ({Namespace = declNS; Name = tName}, Some declSource) (nsName, source) |> function - | Null -> QsCompilerError.Raise "failed to get the type information about a resolved type"; Null, Seq.empty + | [(declNS, (declSource, _, _, _))] -> + this.TryGetTypeHeader ({Namespace = declNS; Name = tName}, Some declSource) (nsName, source) |> function + | Null -> Null, Seq.empty | info -> info, seq {yield declNS} | resolutions -> Null, resolutions.Select fst finally syncRoot.ExitReadLock() From 82a18b191bf50413461a3c39f9a0ad8f89757135 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 10 Feb 2020 15:58:42 -0800 Subject: [PATCH 027/135] Add error code for inaccessible type in a qualified symbol --- src/QsCompiler/Core/SymbolTable.fs | 45 +++++++++++--------- src/QsCompiler/DataStructures/Diagnostics.fs | 2 + 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index bf9d16cd14..0fb2be45e2 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -733,13 +733,13 @@ and NamespaceManager | true, _ -> [builtIn.Namespace.Value]; | false, _ -> ArgumentException "no namespace with the given name exists" |> raise - /// Returns whether a declaration is visible from the calling location, given whether the calling location is in the - /// same assembly and/or namespace as the declaration, and the declaration's modifiers. - let IsDeclarationVisible isSameAssembly isSameNs modifiers = + /// Returns whether a declaration is accessible from the calling location, given whether the calling location is in + /// the same assembly and/or namespace as the declaration, and the declaration's modifiers. + let IsDeclarationAccessible sameAssembly sameNs modifiers = match modifiers.Access with | DefaultAccess -> true - | Internal -> isSameAssembly - | Private -> isSameAssembly && isSameNs + | Internal -> sameAssembly + | Private -> sameAssembly && sameNs /// Given the qualified or unqualfied name of a type used within the given parent namespace and source file, determines if such a type exists /// and returns its full name and the source file or referenced assembly in which it is defined as Some if it does. @@ -754,14 +754,17 @@ and NamespaceManager let deprecatedWarnings = deprecation |> SymbolResolution.GenerateDeprecationWarning ({Namespace = ns; Name = symName}, symRange |> orDefault) Some ({Namespace = ns; Name = symName; Range = symRange}, declSource), deprecatedWarnings |> Array.append errs let tryFind (parentNS, source) (tName, tRange) = - match (parentNS, source) |> PossibleResolutions (fun ns -> ns.ContainsType (tName, checkQualificationForDeprecation)) with - | [(nsName, (declSource, deprecated, isSameAssembly, modifiers))] - when IsDeclarationVisible isSameAssembly (parentNS = nsName) modifiers -> - Value (nsName, declSource, deprecated), [||] - | resolutions when resolutions.Length > 1 -> + let resolutions = + (parentNS, source) + |> PossibleResolutions (fun ns -> ns.ContainsType (tName, checkQualificationForDeprecation)) + |> List.filter (fun (nsName, (_, _, sameAssembly, modifiers)) -> + IsDeclarationAccessible sameAssembly (parentNS = nsName) modifiers) + match resolutions with + | [] -> Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownType, [tName.Value]) |] + | [(nsName, (declSource, deprecated, _, _))] -> Value (nsName, declSource, deprecated), [||] + | resolutions -> let diagArg = String.Join(", ", resolutions.Select (fun (ns,_) -> ns.Value)) Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AmbiguousType, [tName.Value; diagArg]) |] - | _ -> Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownType, [tName.Value]) |] match nsName with | None -> tryFind (parentNS, source) (symName, symRange) |> function @@ -771,9 +774,11 @@ and NamespaceManager | None -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownNamespace, [qualifier.Value]) |] | Some ns -> ns.ContainsType (symName, checkQualificationForDeprecation) |> function - | Value (declSource, deprecation, isSameAssembly, modifiers) - when IsDeclarationVisible isSameAssembly (parentNS = ns.Name) modifiers -> - buildAndReturn (ns.Name, declSource, deprecation, [||]) + | Value (declSource, deprecation, isSameAssembly, modifiers) -> + if IsDeclarationAccessible isSameAssembly (parentNS = ns.Name) modifiers then + buildAndReturn (ns.Name, declSource, deprecation, [||]) + else + None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InaccessibleTypeInNamespace, [symName.Value; qualifier.Value]) |] | _ -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeInNamespace, [symName.Value; qualifier.Value]) |] @@ -1269,19 +1274,19 @@ and NamespaceManager | None -> Null | Some ns -> ns.CallablesInReferencedAssemblies.TryGetValue callableName.Name |> function | true, cDecl -> - if IsDeclarationVisible false (nsName = cDecl.QualifiedName.Namespace) cDecl.Modifiers then + if IsDeclarationAccessible false (nsName = cDecl.QualifiedName.Namespace) cDecl.Modifiers then Value cDecl else Null | false, _ -> declSource |> function | Some source -> let kind, decl = ns.CallableInSource source callableName.Name // ok only because/if we have covered that the callable is not in a reference! - if IsDeclarationVisible true (nsName = ns.Name) decl.Modifiers then + if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers then BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Value else Null | None -> ns.CallablesDefinedInAllSources().TryGetValue callableName.Name |> function | true, (source, (kind, decl)) -> - if IsDeclarationVisible true (nsName = ns.Name) decl.Modifiers then + if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers then BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Value else Null | false, _ -> Null @@ -1330,19 +1335,19 @@ and NamespaceManager | None -> Null | Some ns -> ns.TypesInReferencedAssemblies.TryGetValue typeName.Name |> function | true, tDecl -> - if IsDeclarationVisible false (nsName = tDecl.QualifiedName.Namespace) tDecl.Modifiers then + if IsDeclarationAccessible false (nsName = tDecl.QualifiedName.Namespace) tDecl.Modifiers then Value tDecl else Null | false, _ -> declSource |> function | Some source -> let decl = ns.TypeInSource source typeName.Name // ok only because/if we have covered that the type is not in a reference! - if IsDeclarationVisible true (nsName = ns.Name) decl.Modifiers then + if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers then BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Value else Null | None -> ns.TypesDefinedInAllSources().TryGetValue typeName.Name |> function | true, (source, decl) -> - if IsDeclarationVisible true (nsName = ns.Name) decl.Modifiers then + if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers then BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Value else Null | false, _ -> Null diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 947a535a03..c1324c7763 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -197,6 +197,7 @@ type ErrorCode = | UnknownTypeParameterName = 6107 | UnknownItemName = 6108 | NotMarkedAsAttribute = 6109 + | InaccessibleTypeInNamespace = 6110 | ArgumentTupleShapeMismatch = 6201 | ArgumentTupleMismatch = 6202 @@ -540,6 +541,7 @@ type DiagnosticItem = | ErrorCode.UnknownTypeParameterName -> "No type parameter with the name \"{0}\" exists." | ErrorCode.UnknownItemName -> "The type {0} does not define an item with name \"{1}\"." | ErrorCode.NotMarkedAsAttribute -> "The type {0} is not marked as an attribute. Add \"@Attribute()\" above its declaration to indicate that it may be used as attribute." + | ErrorCode.InaccessibleTypeInNamespace -> "The type {0} in namespace {1} is not accessible from here." | ErrorCode.ArgumentTupleShapeMismatch -> "The shape of the given tuple does not match the expected type. Got an argument of type {0}, expecting one of type {1} instead." | ErrorCode.ArgumentTupleMismatch -> "The type of the given tuple does not match the expected type. Got an argument of type {0}, expecting one of type {1} instead." From 96d6161f0ad311ba1952116765e1058e1bcd25db Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 10 Feb 2020 16:20:59 -0800 Subject: [PATCH 028/135] Show specific error message for inaccessible type in open NS --- src/QsCompiler/Core/SymbolTable.fs | 17 +++++++++++------ src/QsCompiler/DataStructures/Diagnostics.fs | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 0fb2be45e2..396d10cf03 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -754,16 +754,21 @@ and NamespaceManager let deprecatedWarnings = deprecation |> SymbolResolution.GenerateDeprecationWarning ({Namespace = ns; Name = symName}, symRange |> orDefault) Some ({Namespace = ns; Name = symName; Range = symRange}, declSource), deprecatedWarnings |> Array.append errs let tryFind (parentNS, source) (tName, tRange) = - let resolutions = + let allResolutions = (parentNS, source) |> PossibleResolutions (fun ns -> ns.ContainsType (tName, checkQualificationForDeprecation)) - |> List.filter (fun (nsName, (_, _, sameAssembly, modifiers)) -> + let accessibleResolutions = + allResolutions |> List.filter (fun (nsName, (_, _, sameAssembly, modifiers)) -> IsDeclarationAccessible sameAssembly (parentNS = nsName) modifiers) - match resolutions with - | [] -> Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownType, [tName.Value]) |] + match accessibleResolutions with + | [] -> + if List.isEmpty allResolutions then + Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownType, [tName.Value]) |] + else + Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InaccessibleType, [tName.Value]) |] | [(nsName, (declSource, deprecated, _, _))] -> Value (nsName, declSource, deprecated), [||] - | resolutions -> - let diagArg = String.Join(", ", resolutions.Select (fun (ns,_) -> ns.Value)) + | _ -> + let diagArg = String.Join(", ", accessibleResolutions.Select (fun (ns,_) -> ns.Value)) Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AmbiguousType, [tName.Value; diagArg]) |] match nsName with diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index c1324c7763..867d0b7249 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -173,6 +173,7 @@ type ErrorCode = | TypeConstructorOverlapWithCallable = 6004 | UnknownType = 6005 | AmbiguousType = 6006 + | InaccessibleType = 6007 | AmbiguousCallable = 6008 | TypeSpecializationMismatch = 6009 | SpecializationForUnknownCallable = 6010 @@ -517,6 +518,7 @@ type DiagnosticItem = | ErrorCode.TypeConstructorOverlapWithCallable -> "Invalid type declaration. A function or operation with the name \"{0}\" already exists." | ErrorCode.UnknownType -> "No type with the name \"{0}\" exists in any of the open namespaces." | ErrorCode.AmbiguousType -> "Multiple open namespaces contain a type with the name \"{0}\". Use a fully qualified name instead. Open namespaces containing a type with that name are {1}." + | ErrorCode.InaccessibleType -> "The type {0} exists in an open namespace, but is not accessible from here." | ErrorCode.AmbiguousCallable -> "Multiple open namespaces contain a callable with the name \"{0}\". Use a fully qualified name instead. Open namespaces containing a callable with that name are {1}." | ErrorCode.TypeSpecializationMismatch -> "Invalid specialization declaration. The type specializations do not match the expected number of type parameters. Expecting {0} type argument(s)." | ErrorCode.SpecializationForUnknownCallable -> "No callable with the name \"{0}\" exists in the current namespace. Specializations need to be declared in the same namespace as the callable they extend." From e1794ec2b747cb43c6857cc1910c665e779dc2d5 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 12 Feb 2020 11:02:23 -0800 Subject: [PATCH 029/135] Add custom error message for inaccessible callable (unqualified) - needs cleanup --- src/QsCompiler/Core/SymbolTable.fs | 43 ++++++++++++------- src/QsCompiler/DataStructures/Diagnostics.fs | 4 +- .../SyntaxProcessor/SymbolTracker.fs | 12 +++--- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 396d10cf03..fba84acfe5 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -424,8 +424,8 @@ and Namespace private /// (1) the name of the source file or the name of the file within a referenced assembly in which it is /// declared; /// (2) a string option indicating the redirection for the type if it has been deprecated; - /// (3) true if the declaration is declared in the assembly being compiled, or false if it is declared in a - /// referenced assembly; + /// (3) true if the declaration is declared in the assembly currently being compiled, or false if it is declared + /// in a referenced assembly; /// (4) the modifiers for the declaration. /// /// Returns Null otherwise. @@ -442,10 +442,17 @@ and Namespace private | true, tDecl -> Some (partialNS.Source, tDecl.DefinedAttributes |> SymbolResolution.TryFindRedirectInUnresolved checkDeprecation, true, tDecl.Modifiers) | false, _ -> None) - /// If this namespace contains the declaration for the given callable name, - /// returns a Value with the name of the source file or the name of the file within a referenced assembly - /// in which it is declared as well as a string option indicating the redirection for the callable if it has been deprecated. + /// If this namespace contains a declaration for the given callable name, returns a Value with: + /// + /// (1) the name of the source file or the name of the file within a referenced assembly in which it is + /// declared; + /// (2) a string option indicating the redirection for the type if it has been deprecated; + /// (3) true if the declaration is declared in the assembly currently being compiled, or false if it is declared + /// in a referenced assembly; + /// (4) the modifiers for the declaration. + /// /// Returns Null otherwise. + /// /// If the given callable corresponds to the (auto-generated) type constructor for a user defined type, /// returns the file in which that type is declared as source. /// Whether the callable has been deprecated is determined by checking the associated attributes for an attribute with the corresponding name. @@ -455,9 +462,9 @@ and Namespace private member this.ContainsCallable (cName, ?checkDeprecation : (string -> bool)) = let checkDeprecation = defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) match CallablesInReferences.TryGetValue cName with - | true, cDecl -> Value (cDecl.SourceFile, cDecl.Attributes |> SymbolResolution.TryFindRedirect) + | true, cDecl -> Value (cDecl.SourceFile, cDecl.Attributes |> SymbolResolution.TryFindRedirect, false, cDecl.Modifiers) | false, _ -> FromSingleSource (fun partialNS -> partialNS.TryGetCallable cName |> function - | true, (_, cDecl) -> Some (partialNS.Source, cDecl.DefinedAttributes |> SymbolResolution.TryFindRedirectInUnresolved checkDeprecation) + | true, (_, cDecl) -> Some (partialNS.Source, cDecl.DefinedAttributes |> SymbolResolution.TryFindRedirectInUnresolved checkDeprecation, true, cDecl.Modifiers) | false, _ -> None) /// Sets the resolution for the type with the given name in the given source file to the given type, @@ -597,7 +604,7 @@ and Namespace private match Parts.TryGetValue source with | true, partial -> match this.ContainsCallable cName with - | Value (declSource, _) -> + | Value (declSource, _, _, _) -> let AddAndClearCache () = CallablesDefinedInAllSourcesCache <- null partial.AddCallableSpecialization location kind (cName, generator, attributes, documentation) @@ -757,9 +764,8 @@ and NamespaceManager let allResolutions = (parentNS, source) |> PossibleResolutions (fun ns -> ns.ContainsType (tName, checkQualificationForDeprecation)) - let accessibleResolutions = - allResolutions |> List.filter (fun (nsName, (_, _, sameAssembly, modifiers)) -> - IsDeclarationAccessible sameAssembly (parentNS = nsName) modifiers) + let accessibleResolutions = allResolutions |> List.filter (fun (nsName, (_, _, sameAssembly, modifiers)) -> + IsDeclarationAccessible sameAssembly (parentNS = nsName) modifiers) match accessibleResolutions with | [] -> if List.isEmpty allResolutions then @@ -1307,11 +1313,16 @@ and NamespaceManager /// Returns Null as well as a list with namespaces containing a callable with that name if this is not the case. member this.TryResolveAndGetCallable cName (nsName, source) = syncRoot.EnterReadLock() - try match (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsCallable cName) with - | [(declNS, (declSource, _))] -> this.TryGetCallableHeader ({Namespace = declNS; Name = cName}, Some declSource) (nsName, source) |> function - | Null -> Null, Seq.empty - | info -> info, seq {yield declNS} - | resolutions -> Null, resolutions.Select fst + let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsCallable cName) + let accessibleResolutions, inaccessibleResolutions = + allResolutions |> List.partition (fun (declaredNS, (_, _, sameAssembly, modifiers)) -> + IsDeclarationAccessible sameAssembly (declaredNS = nsName) modifiers) + try match accessibleResolutions with + | [(declNS, (declSource, _, _, _))] -> + this.TryGetCallableHeader ({Namespace = declNS; Name = cName}, Some declSource) (nsName, source) |> function + | Null -> Null, (Seq.empty, Seq.empty) + | info -> info, (seq {yield declNS}, Seq.empty) + | _ -> Null, (accessibleResolutions.Select fst, inaccessibleResolutions.Select fst) finally syncRoot.ExitReadLock() /// Given a qualified type name, returns the corresponding TypeDeclarationHeader as Value, diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 867d0b7249..2fe34c783d 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -173,7 +173,6 @@ type ErrorCode = | TypeConstructorOverlapWithCallable = 6004 | UnknownType = 6005 | AmbiguousType = 6006 - | InaccessibleType = 6007 | AmbiguousCallable = 6008 | TypeSpecializationMismatch = 6009 | SpecializationForUnknownCallable = 6010 @@ -188,6 +187,8 @@ type ErrorCode = | AliasForOpenedNamespace = 6019 | InvalidNamespaceAliasName = 6020 // i.e. the chosen alias already exists | ConflictInReferences = 6021 + | InaccessibleType = 6022 + | InaccessibleCallable = 6023 | ExpectingUnqualifiedSymbol = 6101 | ExpectingItemName = 6102 @@ -519,6 +520,7 @@ type DiagnosticItem = | ErrorCode.UnknownType -> "No type with the name \"{0}\" exists in any of the open namespaces." | ErrorCode.AmbiguousType -> "Multiple open namespaces contain a type with the name \"{0}\". Use a fully qualified name instead. Open namespaces containing a type with that name are {1}." | ErrorCode.InaccessibleType -> "The type {0} exists in an open namespace, but is not accessible from here." + | ErrorCode.InaccessibleCallable -> "The callable {0} exists in an open namespace, but is not accessible from here." | ErrorCode.AmbiguousCallable -> "Multiple open namespaces contain a callable with the name \"{0}\". Use a fully qualified name instead. Open namespaces containing a callable with that name are {1}." | ErrorCode.TypeSpecializationMismatch -> "Invalid specialization declaration. The type specializations do not match the expected number of type parameters. Expecting {0} type argument(s)." | ErrorCode.SpecializationForUnknownCallable -> "No callable with the name \"{0}\" exists in the current namespace. Specializations need to be declared in the same namespace as the callable they extend." diff --git a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs index ad1333f429..7777c3ee2e 100644 --- a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs +++ b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs @@ -101,7 +101,7 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif /// source file, namespace, and callable associated with this symbol tracker instance. let globalCallableWithName (ns, name : NonNullable) = ns |> function | None -> GlobalSymbols().TryResolveAndGetCallable name (parent.Namespace, sourceFile) - | Some nsName -> GlobalSymbols().TryGetCallable (QsQualifiedName.New (nsName, name)) (parent.Namespace, sourceFile), Seq.empty + | Some nsName -> GlobalSymbols().TryGetCallable (QsQualifiedName.New (nsName, name)) (parent.Namespace, sourceFile), (Seq.empty, Seq.empty) /// the namespace and callable declaration within which the symbols tracked by this SymbolTracker instance are used member this.Parent = parent @@ -216,11 +216,13 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif | QsCallableKind.Operation -> buildCallable QsTypeKind.Operation decl.QualifiedName decl.Signature decl.Attributes | QsCallableKind.TypeConstructor | QsCallableKind.Function -> buildCallable (fst >> QsTypeKind.Function) decl.QualifiedName decl.Signature decl.Attributes - | Null, (possibleResolutions : NonNullable seq) -> + | Null, ((possibleResolutions : NonNullable seq), (inaccessibleResolutions : NonNullable seq)) -> let resolutionStrings = String.Join(", ", possibleResolutions |> Seq.map (fun nsName -> nsName.Value)) - let ambiguousCallableErr = (ErrorCode.AmbiguousCallable, [sym.Value; resolutionStrings]) - let errCode = if possibleResolutions.Count() > 1 then ambiguousCallableErr else (ErrorCode.UnknownIdentifier, [sym.Value]) - qsSym.RangeOrDefault |> QsCompilerDiagnostic.Error errCode |> addDiagnostic; + let errCode = + if possibleResolutions.Any() then (ErrorCode.AmbiguousCallable, [sym.Value; resolutionStrings]) + else if inaccessibleResolutions.Any() then (ErrorCode.InaccessibleCallable, [sym.Value]) + else (ErrorCode.UnknownIdentifier, [sym.Value]) + qsSym.RangeOrDefault |> QsCompilerDiagnostic.Error errCode |> addDiagnostic invalid let resolveNative sym = From f9f9565bb49db85aeebf586327809839471af09a Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 12 Feb 2020 14:30:18 -0800 Subject: [PATCH 030/135] Show specific error message for inaccessible qualified callables --- .../EditorSupport/CodeCompletion.cs | 9 +- .../EditorSupport/EditorCommands.cs | 29 ++++-- .../EditorSupport/SymbolInformation.cs | 16 ++-- .../CompilationManager/TypeChecking.cs | 20 ++-- src/QsCompiler/Core/SymbolTable.fs | 93 ++++++++++++------- src/QsCompiler/DataStructures/Diagnostics.fs | 2 + .../SyntaxProcessor/SymbolTracker.fs | 52 ++++++----- .../SyntaxProcessor/SyntaxExtensions.fs | 8 +- 8 files changed, 142 insertions(+), 87 deletions(-) diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs index d94372d85c..1b8f87fffc 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs @@ -3,6 +3,7 @@ using Microsoft.Quantum.QsCompiler.CompilationBuilder.DataStructures; using Microsoft.Quantum.QsCompiler.DataTypes; +using Microsoft.Quantum.QsCompiler.SymbolManagement; using Microsoft.Quantum.QsCompiler.SyntaxProcessing; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; @@ -499,9 +500,9 @@ private static string TryGetDocumentation( { case CompletionItemKind.Function: case CompletionItemKind.Constructor: - var callable = compilation.GlobalSymbols.TryGetCallable( + var result = compilation.GlobalSymbols.TryGetCallable( data.QualifiedName, data.QualifiedName.Namespace, NonNullable.New(data.SourceFile)); - if (callable.IsNull) + if (!(result is ResolutionResult.Found callable)) return null; var signature = callable.Item.PrintSignature(); var documentation = callable.Item.Documentation.PrintSummary(useMarkdown); @@ -510,8 +511,8 @@ private static string TryGetDocumentation( var type = compilation.GlobalSymbols.TryGetType( data.QualifiedName, data.QualifiedName.Namespace, NonNullable.New(data.SourceFile)) - .Item; - return type?.Documentation.PrintSummary(useMarkdown).Trim(); + as ResolutionResult.Found; + return type?.Item.Documentation.PrintSummary(useMarkdown).Trim(); default: return null; } diff --git a/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs b/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs index 0a0750f4a7..7a5fe606ba 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.Quantum.QsCompiler.CompilationBuilder.DataStructures; using Microsoft.Quantum.QsCompiler.DataTypes; +using Microsoft.Quantum.QsCompiler.SymbolManagement; using Microsoft.Quantum.QsCompiler.SyntaxProcessing; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; @@ -248,13 +249,27 @@ List FunctorApplications(ref QsExpression ex) // extracting and adapting the relevant information for the called callable - var ns = NonNullable.New(nsName); - var methodDecl = id.Item1.Symbol is QsSymbolKind.Symbol sym - ? compilation.GlobalSymbols.TryResolveAndGetCallable(sym.Item, ns, file.FileName).Item1 - : id.Item1.Symbol is QsSymbolKind.QualifiedSymbol qualSym - ? compilation.GlobalSymbols.TryGetCallable(new QsQualifiedName(qualSym.Item1, qualSym.Item2), ns, file.FileName) - : QsNullable.Null; - if (methodDecl.IsNull) return null; + ResolutionResult.Found methodDecl; + if (id.Item1.Symbol is QsSymbolKind.Symbol sym) + { + methodDecl = + compilation.GlobalSymbols.TryResolveAndGetCallable(sym.Item, + NonNullable.New(nsName), + file.FileName) + as ResolutionResult.Found; + } + else if (id.Item1.Symbol is QsSymbolKind.QualifiedSymbol qualSym) + { + methodDecl = + compilation.GlobalSymbols.TryGetCallable(new QsQualifiedName(qualSym.Item1, qualSym.Item2), + NonNullable.New(nsName), + file.FileName) + as ResolutionResult.Found; + } + else + { + return null; + } var (documentation, argTuple) = (methodDecl.Item.Documentation, methodDecl.Item.ArgumentTuple); var nrCtlApplications = functors.Where(f => f.Equals(QsFunctor.Controlled)).Count(); diff --git a/src/QsCompiler/CompilationManager/EditorSupport/SymbolInformation.cs b/src/QsCompiler/CompilationManager/EditorSupport/SymbolInformation.cs index ecb0387970..dcfd583ce5 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/SymbolInformation.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/SymbolInformation.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.Quantum.QsCompiler.CompilationBuilder.DataStructures; using Microsoft.Quantum.QsCompiler.DataTypes; +using Microsoft.Quantum.QsCompiler.SymbolManagement; using Microsoft.Quantum.QsCompiler.SyntaxProcessing; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; @@ -188,17 +189,20 @@ internal static bool TryGetReferences( if (nsName == null) return false; var ns = NonNullable.New(nsName); - QsQualifiedName fullName = null; + var result = ResolutionResult.NotFound; if (sym.Symbol is QsSymbolKind.Symbol name) { - var header = compilation.GlobalSymbols.TryResolveAndGetCallable(name.Item, ns, file.FileName).Item1; - if (header.IsValue) fullName = header.Item.QualifiedName; + result = compilation.GlobalSymbols.TryResolveAndGetCallable(name.Item, ns, file.FileName); } - if (sym.Symbol is QsSymbolKind.QualifiedSymbol qualName) + else if (sym.Symbol is QsSymbolKind.QualifiedSymbol qualifiedName) { - var header = compilation.GlobalSymbols.TryGetCallable(new QsQualifiedName(qualName.Item1, qualName.Item2), ns, file.FileName); - if (header.IsValue) fullName = header.Item.QualifiedName; + result = compilation.GlobalSymbols.TryGetCallable( + new QsQualifiedName(qualifiedName.Item1, qualifiedName.Item2), + ns, + file.FileName); } + var fullName = result is ResolutionResult.Found header ? header.Item.QualifiedName : null; + return compilation.TryGetReferences(fullName, out declarationLocation, out referenceLocations, limitToSourceFiles); } diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index 969d0c42a6..3ec4c066e9 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -1346,19 +1346,23 @@ internal static List RunTypeChecking (CompilationUnit compilation, // check that the declarations for the types and callables to be built from the given FragmentTrees exist in the given CompilationUnit var typeRoots = roots.Where(root => root.Value.Item2.Specializations == null); var typeDeclarations = typeRoots.ToImmutableDictionary( - root => root.Key, root => + root => root.Key, + root => { - var info = compilation.GlobalSymbols.TryGetType(root.Key, root.Value.Item2.Namespace, root.Value.Item2.Source); - if (info.IsNull) throw new ArgumentException("type to build is no longer present in the given NamespaceManager"); - return info.Item; + var result = compilation.GlobalSymbols.TryGetType(root.Key, root.Value.Item2.Namespace, root.Value.Item2.Source); + return result is ResolutionResult.Found type + ? type.Item + : throw new ArgumentException("type to build is no longer present in the given NamespaceManager"); }); var callableRoots = roots.Where(root => root.Value.Item2.Specializations != null); var callableDeclarations = callableRoots.ToImmutableDictionary( - root => root.Key, root => + root => root.Key, + root => { - var info = compilation.GlobalSymbols.TryGetCallable(root.Key, root.Value.Item2.Namespace, root.Value.Item2.Source); - if (info.IsNull) throw new ArgumentException("callable to build is no longer present in the given NamespaceManager"); - return info.Item; + var result = compilation.GlobalSymbols.TryGetCallable(root.Key, root.Value.Item2.Namespace, root.Value.Item2.Source); + return result is ResolutionResult.Found callable + ? callable.Item + : throw new ArgumentException("callable to build is no longer present in the given NamespaceManager"); }); (QsQualifiedName, ImmutableArray) GetSpecializations diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index fba84acfe5..d186dd98dd 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -18,6 +18,19 @@ open Microsoft.Quantum.QsCompiler.SyntaxTree open Newtonsoft.Json +/// Represents the outcome of resolving a symbol in the symbol table. +type ResolutionResult<'T> = + /// The symbol resolved successfully. + | Found of 'T + /// The symbol is ambiguous, and more than one resolution is possible. Contains the list of possible namespaces in + /// which the symbol could be resolved to. + | Ambiguous of NonNullable seq + /// The symbol resolved to a declaration which is not accessible from the location referencing it. + | Inaccessible + /// No declaration with that name was found. + | NotFound + + /// Note that this class is *not* threadsafe! type private PartialNamespace private (name : NonNullable, @@ -1282,25 +1295,25 @@ and NamespaceManager } syncRoot.EnterReadLock() try match (nsName, source) |> TryResolveQualifier callableName.Namespace with - | None -> Null + | None -> NotFound | Some ns -> ns.CallablesInReferencedAssemblies.TryGetValue callableName.Name |> function | true, cDecl -> - if IsDeclarationAccessible false (nsName = cDecl.QualifiedName.Namespace) cDecl.Modifiers then - Value cDecl - else Null + if IsDeclarationAccessible false (nsName = cDecl.QualifiedName.Namespace) cDecl.Modifiers + then Found cDecl + else Inaccessible | false, _ -> declSource |> function | Some source -> let kind, decl = ns.CallableInSource source callableName.Name // ok only because/if we have covered that the callable is not in a reference! - if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers then - BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Value - else Null + if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers + then BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Found + else Inaccessible | None -> ns.CallablesDefinedInAllSources().TryGetValue callableName.Name |> function | true, (source, (kind, decl)) -> - if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers then - BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Value - else Null - | false, _ -> Null + if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers + then BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Found + else Inaccessible + | false, _ -> NotFound finally syncRoot.ExitReadLock() /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader as Value, @@ -1313,16 +1326,20 @@ and NamespaceManager /// Returns Null as well as a list with namespaces containing a callable with that name if this is not the case. member this.TryResolveAndGetCallable cName (nsName, source) = syncRoot.EnterReadLock() - let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsCallable cName) - let accessibleResolutions, inaccessibleResolutions = - allResolutions |> List.partition (fun (declaredNS, (_, _, sameAssembly, modifiers)) -> - IsDeclarationAccessible sameAssembly (declaredNS = nsName) modifiers) - try match accessibleResolutions with + try + let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsCallable cName) + let accessibleResolutions = + allResolutions |> List.filter (fun (declaredNS, (_, _, sameAssembly, modifiers)) -> + IsDeclarationAccessible sameAssembly (declaredNS = nsName) modifiers) + match accessibleResolutions with + | [] -> if not <| List.isEmpty allResolutions then Inaccessible else NotFound | [(declNS, (declSource, _, _, _))] -> this.TryGetCallableHeader ({Namespace = declNS; Name = cName}, Some declSource) (nsName, source) |> function - | Null -> Null, (Seq.empty, Seq.empty) - | info -> info, (seq {yield declNS}, Seq.empty) - | _ -> Null, (accessibleResolutions.Select fst, inaccessibleResolutions.Select fst) + | Found decl -> Found decl + | _ -> + QsCompilerError.Raise "Expected to find the header corresponding to a possible resolution" + NotFound + | _ -> accessibleResolutions.Select fst |> Ambiguous finally syncRoot.ExitReadLock() /// Given a qualified type name, returns the corresponding TypeDeclarationHeader as Value, @@ -1348,25 +1365,25 @@ and NamespaceManager syncRoot.EnterReadLock() try match (nsName, source) |> TryResolveQualifier typeName.Namespace with - | None -> Null + | None -> NotFound | Some ns -> ns.TypesInReferencedAssemblies.TryGetValue typeName.Name |> function | true, tDecl -> - if IsDeclarationAccessible false (nsName = tDecl.QualifiedName.Namespace) tDecl.Modifiers then - Value tDecl - else Null + if IsDeclarationAccessible false (nsName = tDecl.QualifiedName.Namespace) tDecl.Modifiers + then Found tDecl + else Inaccessible | false, _ -> declSource |> function | Some source -> let decl = ns.TypeInSource source typeName.Name // ok only because/if we have covered that the type is not in a reference! - if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers then - BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Value - else Null + if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers + then BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Found + else Inaccessible | None -> ns.TypesDefinedInAllSources().TryGetValue typeName.Name |> function | true, (source, decl) -> - if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers then - BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Value - else Null - | false, _ -> Null + if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers + then BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Found + else Inaccessible + | false, _ -> NotFound finally syncRoot.ExitReadLock() /// Given a qualified type name, returns the corresponding TypeDeclarationHeader as Value, @@ -1379,12 +1396,20 @@ and NamespaceManager /// Returns Null as well as a list with namespaces containing a type with that name if this is not the case. member this.TryResolveAndGetType tName (nsName, source) = syncRoot.EnterReadLock() - try match (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsType tName) with + try + let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsType tName) + let accessibleResolutions = + allResolutions |> List.filter (fun (declaredNS, (_, _, sameAssembly, modifiers)) -> + IsDeclarationAccessible sameAssembly (declaredNS = nsName) modifiers) + match accessibleResolutions with + | [] -> if not <| List.isEmpty allResolutions then Inaccessible else NotFound | [(declNS, (declSource, _, _, _))] -> this.TryGetTypeHeader ({Namespace = declNS; Name = tName}, Some declSource) (nsName, source) |> function - | Null -> Null, Seq.empty - | info -> info, seq {yield declNS} - | resolutions -> Null, resolutions.Select fst + | Found decl -> Found decl + | _ -> + QsCompilerError.Raise "Expected to find the header corresponding to a possible resolution" + NotFound + | _ -> accessibleResolutions.Select fst |> Ambiguous finally syncRoot.ExitReadLock() diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 2fe34c783d..66cfc24962 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -200,6 +200,7 @@ type ErrorCode = | UnknownItemName = 6108 | NotMarkedAsAttribute = 6109 | InaccessibleTypeInNamespace = 6110 + | InaccessibleCallableInNamespace = 6111 | ArgumentTupleShapeMismatch = 6201 | ArgumentTupleMismatch = 6202 @@ -546,6 +547,7 @@ type DiagnosticItem = | ErrorCode.UnknownItemName -> "The type {0} does not define an item with name \"{1}\"." | ErrorCode.NotMarkedAsAttribute -> "The type {0} is not marked as an attribute. Add \"@Attribute()\" above its declaration to indicate that it may be used as attribute." | ErrorCode.InaccessibleTypeInNamespace -> "The type {0} in namespace {1} is not accessible from here." + | ErrorCode.InaccessibleCallableInNamespace -> "The callable {0} in namespace {1} is not accessible from here." | ErrorCode.ArgumentTupleShapeMismatch -> "The shape of the given tuple does not match the expected type. Got an argument of type {0}, expecting one of type {1} instead." | ErrorCode.ArgumentTupleMismatch -> "The type of the given tuple does not match the expected type. Got an argument of type {0}, expecting one of type {1} instead." diff --git a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs index 7777c3ee2e..3b3aec8f50 100644 --- a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs +++ b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs @@ -6,7 +6,6 @@ namespace Microsoft.Quantum.QsCompiler.SymbolTracker open System open System.Collections.Generic open System.Collections.Immutable -open System.Linq open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics @@ -71,11 +70,11 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif /// IMPORTANT: these need to be adapted if we want to support type specializations and/or external specializations! let parentIsOperation, typeParameters, expectedReturnType = match GlobalSymbols().TryGetCallable parent (parent.Namespace, sourceFile) with - | Null -> ArgumentException "the given NamespaceManager does not contain a callable with the given parent name" |> raise - | Value decl -> + | Found decl -> let isOperation = decl.Kind |> function | QsCallableKind.Operation -> true | _ -> false let validTypeParams = decl.Signature.TypeParameters |> Seq.choose (function | ValidName name -> Some name | InvalidName -> None) isOperation, validTypeParams.ToImmutableArray(), decl.Signature.ReturnType |> StripPositionInfo.Apply // NOTE: valid only since we do not yet support external and/or type specializations + | _ -> ArgumentException "the given NamespaceManager does not contain a callable with the given parent name" |> raise /// If a local variable with the given name is visible on the current scope, /// returns the dictionary that contains its declaration as Value. @@ -93,7 +92,7 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif /// source file, namespace, and callable associated with this symbol tracker instance. let globalTypeWithName (ns, name : NonNullable) = ns |> function | None -> GlobalSymbols().TryResolveAndGetType name (parent.Namespace, sourceFile) - | Some nsName -> GlobalSymbols().TryGetType (QsQualifiedName.New (nsName, name)) (parent.Namespace, sourceFile), Seq.empty + | Some nsName -> GlobalSymbols().TryGetType (QsQualifiedName.New (nsName, name)) (parent.Namespace, sourceFile) /// If a callable declaration (including type constructors!) for a callable with the given name exists in GlobalSymbols, /// returns a its header information as Value. Returns Null otherwise. @@ -101,7 +100,7 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif /// source file, namespace, and callable associated with this symbol tracker instance. let globalCallableWithName (ns, name : NonNullable) = ns |> function | None -> GlobalSymbols().TryResolveAndGetCallable name (parent.Namespace, sourceFile) - | Some nsName -> GlobalSymbols().TryGetCallable (QsQualifiedName.New (nsName, name)) (parent.Namespace, sourceFile), (Seq.empty, Seq.empty) + | Some nsName -> GlobalSymbols().TryGetCallable (QsQualifiedName.New (nsName, name)) (parent.Namespace, sourceFile) /// the namespace and callable declaration within which the symbols tracked by this SymbolTracker instance are used member this.Parent = parent @@ -150,8 +149,8 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif /// Throws an InvalidOperationException if no scope is currently open. member this.TryAddVariableDeclartion (decl : LocalVariableDeclaration>) = if pushedScopes.Length = 0 then InvalidOperationException "no scope is currently open" |> raise - if (globalTypeWithName (None, decl.VariableName)) |> fst <> Null then false, [| decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.GlobalTypeAlreadyExists, [decl.VariableName.Value]) |] - elif (globalCallableWithName (None, decl.VariableName)) |> fst <> Null then false, [| decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.GlobalCallableAlreadyExists, [decl.VariableName.Value]) |] + if (globalTypeWithName (None, decl.VariableName)) <> NotFound then false, [| decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.GlobalTypeAlreadyExists, [decl.VariableName.Value]) |] + elif (globalCallableWithName (None, decl.VariableName)) <> NotFound then false, [| decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.GlobalCallableAlreadyExists, [decl.VariableName.Value]) |] elif (localVariableWithName decl.VariableName) <> Null then false, [| decl.Range |> QsCompilerDiagnostic.Error (ErrorCode.LocalVariableAlreadyExists, [decl.VariableName.Value]) |] else pushedScopes.Head.LocalVariables.Add(decl.VariableName, decl); true, [||] @@ -210,20 +209,24 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif let idType = kind ((argType, returnType), decl.Information) |> ResolvedType.New LocalVariableDeclaration.New false (defaultLoc, GlobalCallable fullName, idType, false), decl.TypeParameters - let resolveGlobal (sym : NonNullable) input = + let addDiagnosticForSymbol code args = + qsSym.RangeOrDefault |> QsCompilerDiagnostic.Error (code, args) |> addDiagnostic + invalid + + let resolveGlobal (ns : NonNullable option, sym : NonNullable) input = match input with - | Value (decl : CallableDeclarationHeader), _ -> decl.Kind |> function + | Found (decl : CallableDeclarationHeader) -> decl.Kind |> function | QsCallableKind.Operation -> buildCallable QsTypeKind.Operation decl.QualifiedName decl.Signature decl.Attributes | QsCallableKind.TypeConstructor | QsCallableKind.Function -> buildCallable (fst >> QsTypeKind.Function) decl.QualifiedName decl.Signature decl.Attributes - | Null, ((possibleResolutions : NonNullable seq), (inaccessibleResolutions : NonNullable seq)) -> - let resolutionStrings = String.Join(", ", possibleResolutions |> Seq.map (fun nsName -> nsName.Value)) - let errCode = - if possibleResolutions.Any() then (ErrorCode.AmbiguousCallable, [sym.Value; resolutionStrings]) - else if inaccessibleResolutions.Any() then (ErrorCode.InaccessibleCallable, [sym.Value]) - else (ErrorCode.UnknownIdentifier, [sym.Value]) - qsSym.RangeOrDefault |> QsCompilerDiagnostic.Error errCode |> addDiagnostic - invalid + | Ambiguous possibilities -> + let possibleNames = String.Join(", ", possibilities |> Seq.map (fun nsName -> nsName.Value)) + addDiagnosticForSymbol ErrorCode.AmbiguousCallable [sym.Value; possibleNames] + | Inaccessible -> + match ns with + | None -> addDiagnosticForSymbol ErrorCode.InaccessibleCallable [sym.Value] + | Some ns -> addDiagnosticForSymbol ErrorCode.InaccessibleCallableInNamespace [sym.Value; ns.Value] + | NotFound -> addDiagnosticForSymbol ErrorCode.UnknownIdentifier [sym.Value] let resolveNative sym = match localVariableWithName sym with @@ -231,14 +234,13 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif let decl = dict.[sym] let properties = (defaultLoc, LocalVariable sym, decl.Type |> StripPositionInfo.Apply, decl.InferredInformation.HasLocalQuantumDependency) properties |> LocalVariableDeclaration.New decl.InferredInformation.IsMutable, ImmutableArray<_>.Empty - | Null -> globalCallableWithName (None, sym) |> resolveGlobal sym + | Null -> globalCallableWithName (None, sym) |> resolveGlobal (None, sym) match qsSym.Symbol with | InvalidSymbol -> invalid | Symbol sym -> resolveNative sym - | QualifiedSymbol (ns, sym) -> globalCallableWithName (Some ns, sym) |> resolveGlobal sym - | _ -> qsSym.RangeOrDefault |> QsCompilerDiagnostic.Error (ErrorCode.ExpectingIdentifier, []) |> addDiagnostic; - invalid + | QualifiedSymbol (ns, sym) -> globalCallableWithName (Some ns, sym) |> resolveGlobal (Some ns, sym) + | _ -> addDiagnosticForSymbol ErrorCode.ExpectingIdentifier [] /// Given a Q# type, resolves it calling the NamespaceManager associated with this symbol tracker. /// For each diagnostic generated during the resolution, calls the given addDiagnostics function on it. @@ -252,9 +254,11 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif /// Adds a suitable error using the given function and returns None if no declaration can be found. member private this.TryGetTypeDeclaration addError (udt : UserDefinedType) = match globalTypeWithName (Some udt.Namespace, udt.Name) with - | Value decl, _ -> Value decl - | Null, _ -> // may occur when the return type of a referenced callable is defined in an assembly that is not referenced - addError (ErrorCode.IndirectlyReferencedExpressionType, [sprintf "%s.%s" udt.Namespace.Value udt.Name.Value]); Null + | Found decl -> Value decl + | _ -> + // may occur when the return type of a referenced callable is defined in an assembly that is not referenced + addError (ErrorCode.IndirectlyReferencedExpressionType, [sprintf "%s.%s" udt.Namespace.Value udt.Name.Value]) + Null /// Given the fully qualified name of a user defined type, returns its underlying type where all range information is stripped. /// Adds a suitable diagnostic and returns an invalid type if the underlying type could not be determined. diff --git a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs index d2d30660e7..33e016e322 100644 --- a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs +++ b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs @@ -211,13 +211,13 @@ let public CallExpressions fragmentKind = let private tryResolveWith resolve extract (currentNS, source) = function | QsSymbolKind.Symbol sym -> try resolve sym (currentNS, source) |> function - | Value decl, _ -> Some decl, Some sym - | Null, _ -> None, Some sym + | Found decl -> Some decl, Some sym + | _ -> None, Some sym with | :? ArgumentException -> None, Some sym | QsSymbolKind.QualifiedSymbol (ns, sym) -> try extract {Namespace = ns; Name = sym} (currentNS, source) |> function - | Value decl -> Some decl, Some sym - | Null -> None, None + | Found decl -> Some decl, Some sym + | _ -> None, None with | :? ArgumentException -> None, None | _ -> None, None From 0ea94f93bb1874f2e8cdcc44421f3569eb4f7893 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 12 Feb 2020 14:43:48 -0800 Subject: [PATCH 031/135] Rename ContainsCallable/Type to TryFindCallable/Type --- src/QsCompiler/Core/SymbolTable.fs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index d186dd98dd..22a31358ec 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -427,7 +427,7 @@ and Namespace private /// This excludes specializations that are defined in files contained in referenced assemblies. /// Throws an ArgumentException if no callable with the given name is defined in this namespace. member internal this.SpecializationsDefinedInAllSources cName = - match this.ContainsCallable cName with + match this.TryFindCallable cName with | Value _ -> (Parts.Values.SelectMany (fun partial -> partial.GetSpecializations cName |> Seq.map (fun (kind, decl) -> kind, (partial.Source, decl)))).ToImmutableArray() | Null -> ArgumentException "no callable with the given name exist within the namespace" |> raise @@ -447,7 +447,7 @@ and Namespace private /// Note that if the type is declared in a source files, the *unresolved* attributes will be checked. /// In that case checkDeprecation is used to validate the namespace qualification of the attribute. /// If checkDeprecation is not specified, it is assumed that no qualification is needed in the relevant namespace and source file. - member this.ContainsType (tName, ?checkDeprecation : (string -> bool)) = + member this.TryFindType (tName, ?checkDeprecation : (string -> bool)) = let checkDeprecation = defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) match TypesInReferences.TryGetValue tName with | true, tDecl -> Value (tDecl.SourceFile, tDecl.Attributes |> SymbolResolution.TryFindRedirect, false, tDecl.Modifiers) @@ -472,7 +472,7 @@ and Namespace private /// Note that if the type is declared in a source files, the *unresolved* attributes will be checked. /// In that case checkDeprecation is used to validate the namespace qualification of the attribute. /// If checkDeprecation is not specified, it is assumed that no qualification is needed in the relevant namespace and source file. - member this.ContainsCallable (cName, ?checkDeprecation : (string -> bool)) = + member this.TryFindCallable (cName, ?checkDeprecation : (string -> bool)) = let checkDeprecation = defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) match CallablesInReferences.TryGetValue cName with | true, cDecl -> Value (cDecl.SourceFile, cDecl.Attributes |> SymbolResolution.TryFindRedirect, false, cDecl.Modifiers) @@ -569,7 +569,7 @@ and Namespace private TypesDefinedInAllSourcesCache <- null CallablesDefinedInAllSourcesCache <- null partial.AddType location (tName, typeTuple, attributes, modifiers, documentation); [||] - | true, _ -> this.ContainsType tName |> function + | true, _ -> this.TryFindType tName |> function | Value _ -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeRedefinition, [tName.Value]) |] | Null -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeConstructorOverlapWithCallable, [tName.Value]) |] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise @@ -585,7 +585,7 @@ and Namespace private | true, partial when not (IsDefined cName) -> CallablesDefinedInAllSourcesCache <- null partial.AddCallableDeclaration location (cName, (kind, signature), attributes, modifiers, documentation); [||] - | true, _ -> this.ContainsType cName |> function + | true, _ -> this.TryFindType cName |> function | Value _ -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableOverlapWithTypeConstructor, [cName.Value]) |] | Null -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableRedefinition, [cName.Value]) |] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise @@ -616,7 +616,7 @@ and Namespace private match Parts.TryGetValue source with | true, partial -> - match this.ContainsCallable cName with + match this.TryFindCallable cName with | Value (declSource, _, _, _) -> let AddAndClearCache () = CallablesDefinedInAllSourcesCache <- null @@ -746,7 +746,7 @@ and NamespaceManager let PossibleQualifications (nsName, source) (builtIn : BuiltIn) = match Namespaces.TryGetValue nsName with | true, ns when ns.Sources.Contains source -> (ns.ImportedNamespaces source).TryGetValue builtIn.Namespace |> function - | true, null when ns.ContainsType builtIn.Name = Null || nsName.Value = builtIn.Namespace.Value -> [""; builtIn.Namespace.Value] + | true, null when ns.TryFindType builtIn.Name = Null || nsName.Value = builtIn.Namespace.Value -> [""; builtIn.Namespace.Value] | true, null -> [builtIn.Namespace.Value] // the built-in type or callable is shadowed | true, alias -> [alias; builtIn.Namespace.Value] | false, _ -> [builtIn.Namespace.Value] @@ -776,7 +776,7 @@ and NamespaceManager let tryFind (parentNS, source) (tName, tRange) = let allResolutions = (parentNS, source) - |> PossibleResolutions (fun ns -> ns.ContainsType (tName, checkQualificationForDeprecation)) + |> PossibleResolutions (fun ns -> ns.TryFindType (tName, checkQualificationForDeprecation)) let accessibleResolutions = allResolutions |> List.filter (fun (nsName, (_, _, sameAssembly, modifiers)) -> IsDeclarationAccessible sameAssembly (parentNS = nsName) modifiers) match accessibleResolutions with @@ -797,7 +797,7 @@ and NamespaceManager | Some qualifier -> (parentNS, source) |> TryResolveQualifier qualifier |> function | None -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownNamespace, [qualifier.Value]) |] | Some ns -> - ns.ContainsType (symName, checkQualificationForDeprecation) |> function + ns.TryFindType (symName, checkQualificationForDeprecation) |> function | Value (declSource, deprecation, isSameAssembly, modifiers) -> if IsDeclarationAccessible isSameAssembly (parentNS = ns.Name) modifiers then buildAndReturn (ns.Name, declSource, deprecation, [||]) @@ -1327,7 +1327,7 @@ and NamespaceManager member this.TryResolveAndGetCallable cName (nsName, source) = syncRoot.EnterReadLock() try - let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsCallable cName) + let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.TryFindCallable cName) let accessibleResolutions = allResolutions |> List.filter (fun (declaredNS, (_, _, sameAssembly, modifiers)) -> IsDeclarationAccessible sameAssembly (declaredNS = nsName) modifiers) @@ -1397,7 +1397,7 @@ and NamespaceManager member this.TryResolveAndGetType tName (nsName, source) = syncRoot.EnterReadLock() try - let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsType tName) + let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.TryFindType tName) let accessibleResolutions = allResolutions |> List.filter (fun (declaredNS, (_, _, sameAssembly, modifiers)) -> IsDeclarationAccessible sameAssembly (declaredNS = nsName) modifiers) @@ -1427,16 +1427,16 @@ and NamespaceManager member this.NamespacesContainingCallable cName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! syncRoot.EnterReadLock() - try let containsCallable (ns : Namespace) = ns.ContainsCallable cName |> QsNullable<_>.Map (fun _ -> ns.Name) - (Namespaces.Values |> QsNullable<_>.Choose containsCallable).ToImmutableArray() + try let tryFindCallable (ns : Namespace) = ns.TryFindCallable cName |> QsNullable<_>.Map (fun _ -> ns.Name) + (Namespaces.Values |> QsNullable<_>.Choose tryFindCallable).ToImmutableArray() finally syncRoot.ExitReadLock() /// Returns the names of all namespaces in which a type with the given name is declared. member this.NamespacesContainingType tName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! syncRoot.EnterReadLock() - try let containsType (ns : Namespace) = ns.ContainsType tName |> QsNullable<_>.Map (fun _ -> ns.Name) - (Namespaces.Values |> QsNullable<_>.Choose containsType).ToImmutableArray() + try let tryFindType (ns : Namespace) = ns.TryFindType tName |> QsNullable<_>.Map (fun _ -> ns.Name) + (Namespaces.Values |> QsNullable<_>.Choose tryFindType).ToImmutableArray() finally syncRoot.ExitReadLock() /// Returns the name of all namespaces declared in source files or referenced assemblies. From d9485293c4411d2a5fddf4b294de1b9df6f22742 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 13 Feb 2020 18:33:00 -0800 Subject: [PATCH 032/135] Make errors for using less accessible types in more accessible declarations --- src/QsCompiler/Core/SymbolTable.fs | 160 +++++++++++++------ src/QsCompiler/DataStructures/Diagnostics.fs | 4 + 2 files changed, 115 insertions(+), 49 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 22a31358ec..acbc5a9087 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -770,41 +770,93 @@ and NamespaceManager let TryResolveTypeName (parentNS, source) ((nsName, symName), symRange) = let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange let checkQualificationForDeprecation qual = BuiltIn.Deprecated |> PossibleQualifications (parentNS, source) |> Seq.contains qual - let buildAndReturn (ns, declSource, deprecation, errs) = - let deprecatedWarnings = deprecation |> SymbolResolution.GenerateDeprecationWarning ({Namespace = ns; Name = symName}, symRange |> orDefault) - Some ({Namespace = ns; Name = symName; Range = symRange}, declSource), deprecatedWarnings |> Array.append errs - let tryFind (parentNS, source) (tName, tRange) = + let buildAndReturn (ns, declSource, deprecation, modifiers, errs) = + let deprecatedWarnings = + deprecation + |> SymbolResolution.GenerateDeprecationWarning ({Namespace = ns; Name = symName}, symRange |> orDefault) + (Some ({Namespace = ns; Name = symName; Range = symRange}, declSource, modifiers), + Array.append errs deprecatedWarnings) + let tryFind (parentNS, source) (tName, tRange) = let allResolutions = - (parentNS, source) - |> PossibleResolutions (fun ns -> ns.TryFindType (tName, checkQualificationForDeprecation)) + PossibleResolutions + (fun ns -> ns.TryFindType (tName, checkQualificationForDeprecation)) + (parentNS, source) let accessibleResolutions = allResolutions |> List.filter (fun (nsName, (_, _, sameAssembly, modifiers)) -> IsDeclarationAccessible sameAssembly (parentNS = nsName) modifiers) match accessibleResolutions with | [] -> - if List.isEmpty allResolutions then - Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownType, [tName.Value]) |] - else - Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InaccessibleType, [tName.Value]) |] - | [(nsName, (declSource, deprecated, _, _))] -> Value (nsName, declSource, deprecated), [||] + if List.isEmpty allResolutions + then Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownType, [tName.Value]) |] + else Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InaccessibleType, [tName.Value]) |] + | [(nsName, (declSource, deprecated, _, modifiers))] -> + Value (nsName, declSource, deprecated, modifiers), [||] | _ -> let diagArg = String.Join(", ", accessibleResolutions.Select (fun (ns,_) -> ns.Value)) Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AmbiguousType, [tName.Value; diagArg]) |] - match nsName with + match nsName with | None -> tryFind (parentNS, source) (symName, symRange) |> function - | Value (ns, declSource, deprecation), errs -> buildAndReturn (ns, declSource, deprecation, errs) + | Value (ns, declSource, deprecation, modifiers), errs -> + buildAndReturn (ns, declSource, deprecation, modifiers, errs) | Null, errs -> None, errs | Some qualifier -> (parentNS, source) |> TryResolveQualifier qualifier |> function - | None -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownNamespace, [qualifier.Value]) |] - | Some ns -> + | None -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownNamespace, [qualifier.Value]) |] + | Some ns -> ns.TryFindType (symName, checkQualificationForDeprecation) |> function - | Value (declSource, deprecation, isSameAssembly, modifiers) -> - if IsDeclarationAccessible isSameAssembly (parentNS = ns.Name) modifiers then - buildAndReturn (ns.Name, declSource, deprecation, [||]) - else - None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InaccessibleTypeInNamespace, [symName.Value; qualifier.Value]) |] + | Value (declSource, deprecation, sameAssembly, modifiers) -> + if IsDeclarationAccessible sameAssembly (parentNS = ns.Name) modifiers + then buildAndReturn (ns.Name, declSource, deprecation, modifiers, [||]) + else None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InaccessibleTypeInNamespace, [symName.Value; qualifier.Value]) |] | _ -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeInNamespace, [symName.Value; qualifier.Value]) |] + /// Fully (i.e. recursively) resolves the given Q# type used within the given parent in the given source file. The + /// resolution consists of replacing all unqualified names for user defined types by their qualified name. + /// + /// Generates an array of diagnostics for the cases where no user defined type of the specified name (qualified or + /// unqualified) can be found. In that case, resolves the user defined type by replacing it with the Q# type + /// denoting an invalid type. + /// + /// Diagnostics can be generated in additional cases by returning an array of diagnostics from the given checkUdt + /// function. + /// + /// Verifies that all used type parameters are defined in the given list of type parameters, and generates suitable + /// diagnostics if they are not, replacing them by the Q# type denoting an invalid type. Returns the resolved type + /// as well as an array with diagnostics. + /// + /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is + /// consistent with the defined callables. + /// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations. + /// Throws a NonSupportedException if the QsType to resolve contains a MissingType. + let resolveType (parent : QsQualifiedName, tpNames, source) qsType checkUdt = + let processUDT = TryResolveTypeName (parent.Namespace, source) >> function + | Some (udt, _, modifiers), errs -> UserDefinedType udt, Array.append errs (checkUdt (udt, modifiers)) + | None, errs -> InvalidType, errs + let processTP (symName, symRange) = + if tpNames |> Seq.contains symName + then TypeParameter {Origin = parent; TypeName = symName; Range = symRange}, [||] + else InvalidType, [| symRange.ValueOr QsCompilerDiagnostic.DefaultRange + |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeParameterName, [symName.Value]) |] + syncRoot.EnterReadLock() + try SymbolResolution.ResolveType (processUDT, processTP) qsType + finally syncRoot.ExitReadLock() + + /// Compares the accessibility of the parent declaration with the accessibility of the UDT being referenced. If the + /// accessibility of a referenced type is less than the accessibility of the parent, returns a diagnostic using the + /// given error code. Otherwise, returns an empty array. + let checkUdtAccessibility code + (parent : NonNullable, parentModifiers) + (udt : UserDefinedType, udtModifiers) = + match (parentModifiers.Access, udtModifiers.Access) with + | (DefaultAccess, DefaultAccess) + | (Internal, DefaultAccess) + | (Internal, Internal) + | (Private, _) -> [||] // OK + | _ -> + // Error + [| QsCompilerDiagnostic.Error + (code, [udt.Name.Value; parent.Value]) + (udt.Range.ValueOr QsCompilerDiagnostic.DefaultRange) |] + /// Given the name of the namespace as well as the source file in which the attribute occurs, resolves the given attribute. /// Generates suitable diagnostics if a suitable attribute cannot be determined, @@ -818,7 +870,7 @@ and NamespaceManager member private this.ResolveAttribute (parentNS, source) attribute = let getAttribute ((nsName, symName), symRange) = match TryResolveTypeName (parentNS, source) ((nsName, symName), symRange) with - | Some (udt, declSource), errs -> // declSource may be the name of an assembly! + | Some (udt, declSource, _), errs -> // declSource may be the name of an assembly! let fullName = sprintf "%s.%s" udt.Namespace.Value udt.Name.Value let validQualifications = BuiltIn.Attribute |> PossibleQualifications (udt.Namespace, declSource) match Namespaces.TryGetValue udt.Namespace with @@ -909,33 +961,37 @@ and NamespaceManager let resAttr = attr |> List.fold validateAttributes ([], []) |> snd resAttr.Reverse() |> ImmutableArray.CreateRange, errs.ToArray() - /// Fully (i.e. recursively) resolves the given Q# type used within the given parent in the given source file. - /// The resolution consists of replacing all unqualified names for user defined types by their qualified name. - /// Generates an array of diagnostics for the cases where no user defined type of the specified name (qualified or unqualified) can be found. - /// In that case, resolves the user defined type by replacing it with the Q# type denoting an invalid type. - /// Verifies that all used type parameters are defined in the given list of type parameters, - /// and generates suitable diagnostics if they are not, replacing them by the Q# type denoting an invalid type. - /// Returns the resolved type as well as an array with diagnostics. - /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is consistent with the defined callables. + /// Fully (i.e. recursively) resolves the given Q# type used within the given parent in the given source file. The + /// resolution consists of replacing all unqualified names for user defined types by their qualified name. + /// + /// Generates an array of diagnostics for the cases where no user defined type of the specified name (qualified or + /// unqualified) can be found. In that case, resolves the user defined type by replacing it with the Q# type + /// denoting an invalid type. + /// + /// Verifies that all used type parameters are defined in the given list of type parameters, and generates suitable + /// diagnostics if they are not, replacing them by the Q# type denoting an invalid type. Returns the resolved type + /// as well as an array with diagnostics. + /// + /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is + /// consistent with the defined callables. /// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations. - /// Throws a NonSupportedException if the QsType to resolve contains a MissingType. - member this.ResolveType (parent : QsQualifiedName, tpNames : ImmutableArray<_>, source : NonNullable) (qsType : QsType) : ResolvedType * QsCompilerDiagnostic[] = - let processUDT = TryResolveTypeName (parent.Namespace, source) >> function - | Some (udt, _), errs -> UserDefinedType udt, errs - | None, errs -> InvalidType, errs - let processTP (symName, symRange) = - if tpNames |> Seq.contains symName then TypeParameter {Origin = parent; TypeName = symName; Range = symRange}, [||] - else InvalidType, [| symRange.ValueOr QsCompilerDiagnostic.DefaultRange |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeParameterName, [symName.Value]) |] - syncRoot.EnterReadLock() - try SymbolResolution.ResolveType (processUDT, processTP) qsType - finally syncRoot.ExitReadLock() + /// Throws a NonSupportedException if the QsType to resolve contains a MissingType. + member this.ResolveType (parent : QsQualifiedName, tpNames : ImmutableArray<_>, source : NonNullable) + (qsType : QsType) + : ResolvedType * QsCompilerDiagnostic[] = + resolveType (parent, tpNames, source) qsType (fun _ -> [||]) /// Resolves the underlying type as well as all named and unnamed items for the given type declaration in the specified source file. /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is consistent with the defined types. /// May throw an exception if the given parent and/or source file is inconsistent with the defined types. /// Throws an ArgumentException if the given type tuple is an empty QsTuple. - member private this.ResolveTypeDeclaration (fullName : QsQualifiedName, source) typeTuple = - let resolveType = this.ResolveType (fullName, ImmutableArray<_>.Empty, source) // currently type parameters for udts are not supported + member private this.ResolveTypeDeclaration (fullName, source, modifiers) typeTuple = + // Currently, type parameters for UDTs are not supported. + let resolveType qsType = + resolveType + (fullName, ImmutableArray<_>.Empty, source) + qsType + (checkUdtAccessibility ErrorCode.TypeLessAccessibleThanParentType (fullName.Name, modifiers)) SymbolResolution.ResolveTypeDeclaration resolveType typeTuple /// Given the namespace and the name of the callable that the given signature belongs to, as well as its kind and the source file it is declared in, @@ -946,9 +1002,14 @@ and NamespaceManager /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is consistent with the defined callables. /// May throw an exception if the given parent and/or source file is inconsistent with the defined callables. /// Throws an ArgumentException if the given list of characteristics is empty. - member private this.ResolveCallableSignature (parentKind, parentName : QsQualifiedName, source) (signature : CallableSignature, specBundleCharacteristics) = - let resolveType tpNames t = - let res, errs = this.ResolveType (parentName, tpNames, source) t + member private this.ResolveCallableSignature (parentKind, parentName, source, modifiers) + (signature, specBundleCharacteristics) = + let resolveType tpNames qsType = + let res, errs = + resolveType + (parentName, tpNames, source) + qsType + (checkUdtAccessibility ErrorCode.TypeLessAccessibleThanParentCallable (parentName.Name, modifiers)) if parentKind <> TypeConstructor then res, errs else res.WithoutRangeInfo, errs // strip positional info for auto-generated type constructors SymbolResolution.ResolveCallableSignature (resolveType, specBundleCharacteristics) signature @@ -976,7 +1037,7 @@ and NamespaceManager ns.TypesDefinedInAllSources() |> Seq.collect (fun kvPair -> let tName, (source, qsType) = kvPair.Key, kvPair.Value let fullName = {Namespace = ns.Name; Name = tName} - let resolved, msgs = qsType.Defined |> this.ResolveTypeDeclaration (fullName, source) + let resolved, msgs = qsType.Defined |> this.ResolveTypeDeclaration (fullName, source, qsType.Modifiers) ns.SetTypeResolution source (tName, resolved |> Value, ImmutableArray.Empty) msgs |> Array.map (fun msg -> source, (qsType.Position, msg)))) // ... before we can resolve the corresponding attributes. @@ -1031,7 +1092,7 @@ and NamespaceManager // and finally we resolve the overall signature (whose characteristics are the intersection of the one of all bundles) let characteristics = props.Values |> Seq.map (fun bundle -> bundle.BundleInfo) |> Seq.toList - let resolved, msgs = (signature.Defined, characteristics) |> this.ResolveCallableSignature (kind, parent, source) // no positional info for type constructors + let resolved, msgs = (signature.Defined, characteristics) |> this.ResolveCallableSignature (kind, parent, source, signature.Modifiers) // no positional info for type constructors ns.SetCallableResolution source (parent.Name, resolved |> Value, callableAttributes) errs <- (attrErrs |> Array.map (fun m -> source, m)) :: (msgs |> Array.map (fun m -> source, (signature.Position, m))) :: errs @@ -1279,7 +1340,7 @@ and NamespaceManager /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. member private this.TryGetCallableHeader (callableName : QsQualifiedName, declSource) (nsName, source) = let BuildHeader fullName (source, kind, declaration : Resolution<_,_>) = - let fallback () = (declaration.Defined, [CallableInformation.Invalid]) |> this.ResolveCallableSignature (kind, callableName, source) |> fst + let fallback () = (declaration.Defined, [CallableInformation.Invalid]) |> this.ResolveCallableSignature (kind, callableName, source, declaration.Modifiers) |> fst let resolvedSignature, argTuple = declaration.Resolved.ValueOrApply fallback { Kind = kind @@ -1349,7 +1410,8 @@ and NamespaceManager /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. member private this.TryGetTypeHeader (typeName : QsQualifiedName, declSource) (nsName, source) = let BuildHeader fullName (source, declaration) = - let fallback () = declaration.Defined |> this.ResolveTypeDeclaration (typeName, source) |> fst + let fallback () = + declaration.Defined |> this.ResolveTypeDeclaration (typeName, source, declaration.Modifiers) |> fst let underlyingType, items = declaration.Resolved.ValueOrApply fallback { QualifiedName = fullName diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 66cfc24962..52f78c0ce5 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -255,6 +255,8 @@ type ErrorCode = | RUSloopWithinAutoInversion = 6315 | QuantumDependencyOutsideExprStatement = 6316 | InvalidReassignmentInApplyBlock = 6317 + | TypeLessAccessibleThanParentType = 6318 + | TypeLessAccessibleThanParentCallable = 6319 | UnexpectedCommandLineCompilerException = 7001 | MissingInputFileOrSnippet = 7002 @@ -602,6 +604,8 @@ type DiagnosticItem = | ErrorCode.RUSloopWithinAutoInversion -> "Auto-generation of inversions is not supported for operations that contain repeat-until-success-loops." | ErrorCode.QuantumDependencyOutsideExprStatement -> "Auto-generation of inversions is not supported for operations that contain operation calls outside expression statements." | ErrorCode.InvalidReassignmentInApplyBlock -> "Variables that are used in the within-block (specifying the outer transformation) cannot be reassigned in the apply-block (specifying the inner transformation)." + | ErrorCode.TypeLessAccessibleThanParentType -> "The type {0} is less accessible than the parent type {1}." + | ErrorCode.TypeLessAccessibleThanParentCallable -> "The type {0} is less accessible than the callable {1}." | ErrorCode.UnexpectedCommandLineCompilerException -> "The command line compiler threw an exception." | ErrorCode.MissingInputFileOrSnippet -> "The command line compiler needs a list of files or a code snippet to process." From 39625386e6a952079a4d88b2f35ff65e32470b2a Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 14 Feb 2020 10:23:53 -0800 Subject: [PATCH 033/135] Reword description of Ambiguous --- src/QsCompiler/Core/SymbolTable.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 22a31358ec..411f7fab63 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -22,8 +22,8 @@ open Newtonsoft.Json type ResolutionResult<'T> = /// The symbol resolved successfully. | Found of 'T - /// The symbol is ambiguous, and more than one resolution is possible. Contains the list of possible namespaces in - /// which the symbol could be resolved to. + /// An unqualified symbol is ambiguous, and it is possible to resolve it to more than one namespace. Includes the + /// list of possible namespaces. | Ambiguous of NonNullable seq /// The symbol resolved to a declaration which is not accessible from the location referencing it. | Inaccessible From 990c76e596c2010ea05f59a63148f66e1c61a7e5 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 14 Feb 2020 10:35:56 -0800 Subject: [PATCH 034/135] Update comments for NamespacesContaining{Callable, Type} --- src/QsCompiler/Core/SymbolTable.fs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 411f7fab63..84636a6ad7 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -1423,17 +1423,25 @@ and NamespaceManager | Some ns -> ns.Name.Value finally syncRoot.ExitReadLock() - /// Returns the names of all namespaces in which a callable with the given name is declared. - member this.NamespacesContainingCallable cName = + /// Returns the names of all namespaces in which a callable with the given name is declared, including private and + /// internal declarations. + member this.NamespacesContainingCallable cName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! + // + // TODO: It may be useful to limit the results to only declarations which are accessible from a given location + // (e.g., for code actions in the language server). syncRoot.EnterReadLock() try let tryFindCallable (ns : Namespace) = ns.TryFindCallable cName |> QsNullable<_>.Map (fun _ -> ns.Name) (Namespaces.Values |> QsNullable<_>.Choose tryFindCallable).ToImmutableArray() finally syncRoot.ExitReadLock() - /// Returns the names of all namespaces in which a type with the given name is declared. - member this.NamespacesContainingType tName = + /// Returns the names of all namespaces in which a type with the given name is declared, including private and + /// internal declarations. + member this.NamespacesContainingType tName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! + // + // TODO: It may be useful to limit the results to only declarations which are accessible from a given location + // (e.g., for code actions in the language server). syncRoot.EnterReadLock() try let tryFindType (ns : Namespace) = ns.TryFindType tName |> QsNullable<_>.Map (fun _ -> ns.Name) (Namespaces.Values |> QsNullable<_>.Choose tryFindType).ToImmutableArray() From df315a45bc13a4b8794888bb3111e20adc5ee0a1 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 14 Feb 2020 11:16:27 -0800 Subject: [PATCH 035/135] Clarify checkUdt parameter in doc comment --- src/QsCompiler/Core/SymbolTable.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 774729d4ec..bab63147a4 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -816,8 +816,8 @@ and NamespaceManager /// unqualified) can be found. In that case, resolves the user defined type by replacing it with the Q# type /// denoting an invalid type. /// - /// Diagnostics can be generated in additional cases by returning an array of diagnostics from the given checkUdt - /// function. + /// Diagnostics can be generated in additional cases when UDTs are referenced by returning an array of diagnostics + /// from the given checkUdt function. /// /// Verifies that all used type parameters are defined in the given list of type parameters, and generates suitable /// diagnostics if they are not, replacing them by the Q# type denoting an invalid type. Returns the resolved type From 1544c387ed58370b8b8f0001cc1c6084f2dbb1b1 Mon Sep 17 00:00:00 2001 From: Sarah Marshall <33814365+marshallsa@users.noreply.github.com> Date: Fri, 14 Feb 2020 16:54:45 -0800 Subject: [PATCH 036/135] Update src/QsCompiler/Core/SymbolTable.fs Co-Authored-By: bettinaheim <34236215+bettinaheim@users.noreply.github.com> --- src/QsCompiler/Core/SymbolTable.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index bab63147a4..2d905a4ea8 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -975,7 +975,7 @@ and NamespaceManager /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is /// consistent with the defined callables. /// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations. - /// Throws a NonSupportedException if the QsType to resolve contains a MissingType. + /// Throws a NotSupportedException if the QsType to resolve contains a MissingType. member this.ResolveType (parent : QsQualifiedName, tpNames : ImmutableArray<_>, source : NonNullable) (qsType : QsType) : ResolvedType * QsCompilerDiagnostic[] = @@ -1599,4 +1599,3 @@ and NamespaceManager let importsHash = imports |> Seq.toList |> hash hash (callablesHash, typesHash), importsHash finally syncRoot.ExitReadLock() - From e23493790c9447b88802a98ff147d2f3c87972a9 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 14 Feb 2020 17:06:07 -0800 Subject: [PATCH 037/135] NonSupportedException -> NotSupportedException --- src/QsCompiler/Core/SymbolResolution.fs | 2 +- src/QsCompiler/Core/SymbolTable.fs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index e292cf7718..a2710b8f54 100644 --- a/src/QsCompiler/Core/SymbolResolution.fs +++ b/src/QsCompiler/Core/SymbolResolution.fs @@ -273,7 +273,7 @@ module SymbolResolution = /// Returns the resolved type as well as an array with diagnostics. /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is inconsistent with the defined callables. /// May throw an ArgumentException if no namespace with the given name exists, or the given source file is not listed as source of that namespace. - /// Throws a NonSupportedException if the QsType to resolve contains a MissingType. + /// Throws a NotSupportedException if the QsType to resolve contains a MissingType. let rec internal ResolveType (processUDT, processTypeParameter) (qsType : QsType) = let resolve = ResolveType (processUDT, processTypeParameter) let asResolvedType t = ResolvedType.New (true, t) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 2d905a4ea8..bab087b90a 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -826,7 +826,7 @@ and NamespaceManager /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is /// consistent with the defined callables. /// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations. - /// Throws a NonSupportedException if the QsType to resolve contains a MissingType. + /// Throws a NotSupportedException if the QsType to resolve contains a MissingType. let resolveType (parent : QsQualifiedName, tpNames, source) qsType checkUdt = let processUDT = TryResolveTypeName (parent.Namespace, source) >> function | Some (udt, _, modifiers), errs -> UserDefinedType udt, Array.append errs (checkUdt (udt, modifiers)) From 1507a5ed23147f2375e0c571e156af5b160b0e5a Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 18 Feb 2020 15:25:37 -0800 Subject: [PATCH 038/135] Add tests for access modifiers --- .../Tests.Compiler/AccessModifierTests.fs | 53 ++++++++ .../TestCases/AccessModifiers.qs | 120 ++++++++++++++++++ .../Tests.Compiler/Tests.Compiler.fsproj | 4 + 3 files changed, 177 insertions(+) create mode 100644 src/QsCompiler/Tests.Compiler/AccessModifierTests.fs create mode 100644 src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs new file mode 100644 index 0000000000..67ae86bcb0 --- /dev/null +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.QsCompiler.Testing + +open System.Collections.Generic +open Microsoft.Quantum.QsCompiler.DataTypes +open Microsoft.Quantum.QsCompiler.Diagnostics +open Microsoft.Quantum.QsCompiler.SyntaxExtensions +open Microsoft.Quantum.QsCompiler.SyntaxTree +open Xunit + + +type AccessModifierTests (output) = + inherit CompilerTests (CompilerTests.Compile "TestCases" ["AccessModifiers.qs"], output) + + member private this.Expect name (diagnostics : IEnumerable) = + let ns = "Microsoft.Quantum.Testing.TypeChecking" |> NonNullable<_>.New + let name = name |> NonNullable<_>.New + this.Verify (QsQualifiedName.New (ns, name), diagnostics) + + [] + member this.``Callables with access modifiers`` () = + this.Expect "CallableUseOK" [] + this.Expect "CallableUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] + this.Expect "CallableQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallableInNamespace] + + [] + member this.``Types with access modifiers`` () = + this.Expect "TypeUseOK" [] + this.Expect "TypeUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleType] + this.Expect "TypeQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleTypeInNamespace] + + [] + member this.``Callable signatures`` () = + this.Expect "PublicCallableLeaksPrivateTypeIn" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "PublicCallableLeaksPrivateTypeOut" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "InternalCallableLeaksPrivateTypeIn" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "InternalCallableLeaksPrivateTypeOut" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "CallablePrivateTypeOK" [] + this.Expect "CallableLeaksInternalTypeIn" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "CallableLeaksInternalTypeOut" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "InternalCallableInternalTypeOK" [] + this.Expect "PrivateCallableInternalTypeOK" [] + + [] + member this.``Underlying types`` () = + this.Expect "PublicTypeLeaksPrivateType" [Error ErrorCode.TypeLessAccessibleThanParentType] + this.Expect "InternalTypeLeaksPrivateType" [Error ErrorCode.TypeLessAccessibleThanParentType] + this.Expect "PrivateTypePrivateTypeOK" [] + this.Expect "PublicTypeLeaksInternalType" [Error ErrorCode.TypeLessAccessibleThanParentType] + this.Expect "InternalTypeInternalTypeOK" [] + this.Expect "PrivateTypeInternalTypeOK" [] \ No newline at end of file diff --git a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs new file mode 100644 index 0000000000..df3b16ce18 --- /dev/null +++ b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/// This namespace contains test cases for access modifiers. +namespace Microsoft.Quantum.Testing.TypeChecking { + open Microsoft.Quantum.Testing.TypeChecking.A; + open Microsoft.Quantum.Testing.TypeChecking.B as B; + + private newtype T1 = Unit; + + internal newtype T2 = Unit; + + private function F1 () : Unit {} + + internal function F2 () : Unit {} + + // Callables with access modifiers + + function CallableUseOK () : Unit { + F1(); + F2(); + AF2(); + B.BF2(); + } + + function CallableUnqualifiedUsePrivateInaccessible () : Unit { + AF1(); + } + + function CallableQualifiedUsePrivateInaccessible () : Unit { + B.BF1(); + } + + // Types with access modifiers + + function TypeUseOK () : Unit { + let t1 = new T1[1]; + let t2 = new T2[1]; + let at2 = new AT2[1]; + let bt2 = new B.BT2[1]; + } + + function TypeUnqualifiedUsePrivateInaccessible () : Unit { + let at1 = new AT1[1]; + } + + function TypeQualifiedUsePrivateInaccessible () : Unit { + let bt1 = new B.BT1[1]; + } + + // Callable signatures + + function PublicCallableLeaksPrivateTypeIn (x : T1) : Unit {} + + function PublicCallableLeaksPrivateTypeOut () : T1 { + return T1(); + } + + internal function InternalCallableLeaksPrivateTypeIn (x : T1) : Unit {} + + internal function InternalCallableLeaksPrivateTypeOut () : T1 { + return T1(); + } + + private function CallablePrivateTypeOK (x : T1) : T1 { + return T1(); + } + + function CallableLeaksInternalTypeIn (x : T2) : Unit {} + + function CallableLeaksInternalTypeOut () : T2 { + return T2(); + } + + internal function InternalCallableInternalTypeOK (x : T2) : T2 { + return T2(); + } + + private function PrivateCallableInternalTypeOK (x : T1) : T1 { + return T1(); + } + + // Underlying types + + newtype PublicTypeLeaksPrivateType = T1; + + internal newtype InternalTypeLeaksPrivateType = T1; + + private newtype PrivateTypePrivateTypeOK = T1; + + newtype PublicTypeLeaksInternalType = T2; + + internal newtype InternalTypeInternalTypeOK = T2; + + private newtype PrivateTypeInternalTypeOK = T2; +} + +/// This namespace contains additional definitions of types and callables meant to be used by the +/// Microsoft.Quantum.Testing.TypeChecking namespace. +namespace Microsoft.Quantum.Testing.TypeChecking.A { + private function AF1 () : Unit {} + + internal function AF2 () : Unit {} + + private newtype AT1 = Unit; + + internal newtype AT2 = Unit; +} + +/// This namespace contains additional definitions of types and callables meant to be used by the +/// Microsoft.Quantum.Testing.TypeChecking namespace. +namespace Microsoft.Quantum.Testing.TypeChecking.B { + private function BF1 () : Unit {} + + internal function BF2 () : Unit {} + + private newtype BT1 = Unit; + + internal newtype BT2 = Unit; +} \ No newline at end of file diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 2f3634b115..52ecb2dc33 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -120,6 +120,9 @@ PreserveNewest + + PreserveNewest + @@ -136,6 +139,7 @@ + From d985c9ba944f8e9d99251b83457c40f6850fe1f4 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 18 Feb 2020 20:01:01 -0800 Subject: [PATCH 039/135] Add a few more tests --- .../Tests.Compiler/AccessModifierTests.fs | 10 +++++++--- .../Tests.Compiler/TestCases/AccessModifiers.qs | 16 +++++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index 67ae86bcb0..11622eeec2 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -33,8 +33,10 @@ type AccessModifierTests (output) = [] member this.``Callable signatures`` () = - this.Expect "PublicCallableLeaksPrivateTypeIn" [Error ErrorCode.TypeLessAccessibleThanParentCallable] - this.Expect "PublicCallableLeaksPrivateTypeOut" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "PublicCallableLeaksPrivateTypeIn1" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "PublicCallableLeaksPrivateTypeIn2" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "PublicCallableLeaksPrivateTypeOut1" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "PublicCallableLeaksPrivateTypeOut2" [Error ErrorCode.TypeLessAccessibleThanParentCallable] this.Expect "InternalCallableLeaksPrivateTypeIn" [Error ErrorCode.TypeLessAccessibleThanParentCallable] this.Expect "InternalCallableLeaksPrivateTypeOut" [Error ErrorCode.TypeLessAccessibleThanParentCallable] this.Expect "CallablePrivateTypeOK" [] @@ -45,7 +47,9 @@ type AccessModifierTests (output) = [] member this.``Underlying types`` () = - this.Expect "PublicTypeLeaksPrivateType" [Error ErrorCode.TypeLessAccessibleThanParentType] + this.Expect "PublicTypeLeaksPrivateType1" [Error ErrorCode.TypeLessAccessibleThanParentType] + this.Expect "PublicTypeLeaksPrivateType2" [Error ErrorCode.TypeLessAccessibleThanParentType] + this.Expect "PublicTypeLeaksPrivateType3" [Error ErrorCode.TypeLessAccessibleThanParentType] this.Expect "InternalTypeLeaksPrivateType" [Error ErrorCode.TypeLessAccessibleThanParentType] this.Expect "PrivateTypePrivateTypeOK" [] this.Expect "PublicTypeLeaksInternalType" [Error ErrorCode.TypeLessAccessibleThanParentType] diff --git a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs index df3b16ce18..543f4a2ae0 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs @@ -50,12 +50,18 @@ namespace Microsoft.Quantum.Testing.TypeChecking { // Callable signatures - function PublicCallableLeaksPrivateTypeIn (x : T1) : Unit {} + function PublicCallableLeaksPrivateTypeIn1 (x : T1) : Unit {} + + function PublicCallableLeaksPrivateTypeIn2 (x : (Int, (T1, Bool))) : Unit {} - function PublicCallableLeaksPrivateTypeOut () : T1 { + function PublicCallableLeaksPrivateTypeOut1 () : T1 { return T1(); } + function PublicCallableLeaksPrivateTypeOut2 () : (Double, ((Result, T1), Bool)) { + return (1.0, ((Zero, T1()), true)); + } + internal function InternalCallableLeaksPrivateTypeIn (x : T1) : Unit {} internal function InternalCallableLeaksPrivateTypeOut () : T1 { @@ -82,7 +88,11 @@ namespace Microsoft.Quantum.Testing.TypeChecking { // Underlying types - newtype PublicTypeLeaksPrivateType = T1; + newtype PublicTypeLeaksPrivateType1 = T1; + + newtype PublicTypeLeaksPrivateType2 = (Int, T1); + + newtype PublicTypeLeaksPrivateType3 = (Int, (T1, Bool)); internal newtype InternalTypeLeaksPrivateType = T1; From 214e6e8af7fc52db2af43bee3ea3e9ba2d401bba Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 19 Feb 2020 15:04:32 -0800 Subject: [PATCH 040/135] Add tests for access modifiers in a referenced assembly --- .../Libraries/Library1/AccessModifiers.qs | 22 +++++++++++++++++++ .../Tests.Compiler/AccessModifierTests.fs | 20 +++++++++++++++-- .../Tests.Compiler/AutoGenerationTests.fs | 2 +- .../Tests.Compiler/ExecutionTests.fs | 4 ++-- .../Tests.Compiler/GlobalVerificationTests.fs | 2 +- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 2 +- .../Tests.Compiler/LocalVerificationTests.fs | 2 +- .../TestCases/AccessModifiers.qs | 21 +++++++++++++++++- .../TestUtils/SetupVerificationTests.fs | 12 +++++----- .../Tests.Compiler/Tests.Compiler.fsproj | 7 +++--- .../Tests.Compiler/TypeCheckingTests.fs | 2 +- 11 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs diff --git a/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs new file mode 100644 index 0000000000..2ebfc4e8e2 --- /dev/null +++ b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs @@ -0,0 +1,22 @@ +/// This namespace contains additional definitions used by the test cases for access modifiers. +namespace Microsoft.Quantum.Testing.TypeChecking { + private newtype T1 = Unit; + + internal newtype T2 = Unit; + + private function F1 () : Unit {} + + internal function F2 () : Unit {} +} + +/// This namespace contains additional definitions of types and callables meant to be used by the +/// Microsoft.Quantum.Testing.TypeChecking namespace. +namespace Microsoft.Quantum.Testing.TypeChecking.C { + private function CF1 () : Unit {} + + internal function CF2 () : Unit {} + + private newtype CT1 = Unit; + + internal newtype CT2 = Unit; +} diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index 11622eeec2..e811c3d3ac 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -9,27 +9,43 @@ open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTree open Xunit +open System.IO +open System.Linq type AccessModifierTests (output) = - inherit CompilerTests (CompilerTests.Compile "TestCases" ["AccessModifiers.qs"], output) + inherit CompilerTests (CompilerTests.Compile "TestCases" + ["AccessModifiers.qs"] + [File.ReadAllLines("ReferenceTargets.txt").ElementAt(1)], + output) member private this.Expect name (diagnostics : IEnumerable) = let ns = "Microsoft.Quantum.Testing.TypeChecking" |> NonNullable<_>.New let name = name |> NonNullable<_>.New this.Verify (QsQualifiedName.New (ns, name), diagnostics) + [] + member this.``Redefine inaccessible symbols in reference`` () = + this.Expect "T1" [] + this.Expect "T2" [] + this.Expect "F1" [] + this.Expect "F2" [] + [] member this.``Callables with access modifiers`` () = this.Expect "CallableUseOK" [] this.Expect "CallableUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] this.Expect "CallableQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallableInNamespace] + this.Expect "CallableReferencePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] + this.Expect "CallableReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] [] member this.``Types with access modifiers`` () = this.Expect "TypeUseOK" [] this.Expect "TypeUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleType] this.Expect "TypeQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleTypeInNamespace] + this.Expect "TypeReferencePrivateInaccessible" [Error ErrorCode.InaccessibleType] + this.Expect "TypeReferenceInternalInaccessible" [Error ErrorCode.InaccessibleType] [] member this.``Callable signatures`` () = @@ -54,4 +70,4 @@ type AccessModifierTests (output) = this.Expect "PrivateTypePrivateTypeOK" [] this.Expect "PublicTypeLeaksInternalType" [Error ErrorCode.TypeLessAccessibleThanParentType] this.Expect "InternalTypeInternalTypeOK" [] - this.Expect "PrivateTypeInternalTypeOK" [] \ No newline at end of file + this.Expect "PrivateTypeInternalTypeOK" [] diff --git a/src/QsCompiler/Tests.Compiler/AutoGenerationTests.fs b/src/QsCompiler/Tests.Compiler/AutoGenerationTests.fs index f3a1806ecc..2ae27951e9 100644 --- a/src/QsCompiler/Tests.Compiler/AutoGenerationTests.fs +++ b/src/QsCompiler/Tests.Compiler/AutoGenerationTests.fs @@ -13,7 +13,7 @@ open Xunit.Abstractions type FunctorAutoGenTests (output:ITestOutputHelper) = - inherit CompilerTests(CompilerTests.Compile "TestCases" ["General.qs"; "FunctorGeneration.qs"], output) + inherit CompilerTests(CompilerTests.Compile "TestCases" ["General.qs"; "FunctorGeneration.qs"] [], output) member private this.Expect name (diag : IEnumerable) = let ns = "Microsoft.Quantum.Testing.FunctorGeneration" |> NonNullable<_>.New diff --git a/src/QsCompiler/Tests.Compiler/ExecutionTests.fs b/src/QsCompiler/Tests.Compiler/ExecutionTests.fs index a4a4db38cd..3e222a6ffc 100644 --- a/src/QsCompiler/Tests.Compiler/ExecutionTests.fs +++ b/src/QsCompiler/Tests.Compiler/ExecutionTests.fs @@ -25,8 +25,8 @@ type ExecutionTests (output:ITestOutputHelper) = let ExecuteOnQuantumSimulator cName = let exitCode, ex = ref -101, ref null let out, err = ref (new StringBuilder()), ref (new StringBuilder()) - let exe = File.ReadAllLines("ExecutionTarget.txt").Single() - let args = sprintf "%s %s.%s" exe "Microsoft.Quantum.Testing.ExecutionTests" cName + let exe = File.ReadAllLines("ReferenceTargets.txt").First() + let args = sprintf "\"%s\" %s.%s" exe "Microsoft.Quantum.Testing.ExecutionTests" cName let ranToEnd = ProcessRunner.Run ("dotnet", args, out, err, exitCode, ex, timeout = 10000) Assert.False(String.IsNullOrWhiteSpace exe) Assert.True(ranToEnd) diff --git a/src/QsCompiler/Tests.Compiler/GlobalVerificationTests.fs b/src/QsCompiler/Tests.Compiler/GlobalVerificationTests.fs index 73b1c26f7a..384b25b378 100644 --- a/src/QsCompiler/Tests.Compiler/GlobalVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/GlobalVerificationTests.fs @@ -13,7 +13,7 @@ open Xunit.Abstractions type GlobalVerificationTests (output:ITestOutputHelper) = - inherit CompilerTests(CompilerTests.Compile "TestCases" ["General.qs";"GlobalVerification.qs";"Types.qs";System.IO.Path.Join("LinkingTests", "Core.qs")], output) + inherit CompilerTests(CompilerTests.Compile "TestCases" ["General.qs"; "GlobalVerification.qs"; "Types.qs"; System.IO.Path.Join("LinkingTests", "Core.qs")] [], output) member private this.Expect name (diag : IEnumerable) = let ns = "Microsoft.Quantum.Testing.GlobalVerification" |> NonNullable<_>.New diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 39206b7e61..7b6c8f0958 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -20,7 +20,7 @@ open Xunit.Abstractions type LinkingTests (output:ITestOutputHelper) = - inherit CompilerTests(CompilerTests.Compile (Path.Combine ("TestCases", "LinkingTests" )) ["Core.qs"; "InvalidEntryPoints.qs"], output) + inherit CompilerTests(CompilerTests.Compile (Path.Combine ("TestCases", "LinkingTests" )) ["Core.qs"; "InvalidEntryPoints.qs"] [], output) let compilationManager = new CompilationUnitManager(new Action (fun ex -> failwith ex.Message)) diff --git a/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs b/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs index 8ce7b57234..1d086165b8 100644 --- a/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs @@ -14,7 +14,7 @@ open Xunit.Abstractions type LocalVerificationTests (output:ITestOutputHelper) = - inherit CompilerTests(CompilerTests.Compile "TestCases" ["General.qs"; "LocalVerification.qs"; "Types.qs"; Path.Combine ("LinkingTests", "Core.qs")], output) + inherit CompilerTests(CompilerTests.Compile "TestCases" ["General.qs"; "LocalVerification.qs"; "Types.qs"; Path.Combine ("LinkingTests", "Core.qs")] [], output) member private this.Expect name (diag : IEnumerable) = let ns = "Microsoft.Quantum.Testing.LocalVerification" |> NonNullable<_>.New diff --git a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs index 543f4a2ae0..a0f87342f7 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs @@ -5,7 +5,10 @@ namespace Microsoft.Quantum.Testing.TypeChecking { open Microsoft.Quantum.Testing.TypeChecking.A; open Microsoft.Quantum.Testing.TypeChecking.B as B; + open Microsoft.Quantum.Testing.TypeChecking.C; + // Redefine inaccessible symbols in reference + private newtype T1 = Unit; internal newtype T2 = Unit; @@ -31,6 +34,14 @@ namespace Microsoft.Quantum.Testing.TypeChecking { B.BF1(); } + function CallableReferencePrivateInaccessible () : Unit { + CF1(); + } + + function CallableReferenceInternalInaccessible () : Unit { + CF2(); + } + // Types with access modifiers function TypeUseOK () : Unit { @@ -48,6 +59,14 @@ namespace Microsoft.Quantum.Testing.TypeChecking { let bt1 = new B.BT1[1]; } + function TypeReferencePrivateInaccessible () : Unit { + let ct1 = new CT1[1]; + } + + function TypeReferenceInternalInaccessible () : Unit { + let ct2 = new CT2[1]; + } + // Callable signatures function PublicCallableLeaksPrivateTypeIn1 (x : T1) : Unit {} @@ -127,4 +146,4 @@ namespace Microsoft.Quantum.Testing.TypeChecking.B { private newtype BT1 = Unit; internal newtype BT2 = Unit; -} \ No newline at end of file +} diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs index 764104c323..eb03d7079a 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs @@ -90,13 +90,13 @@ type CompilerTests (compilation : CompilationUnitManager.Compilation, output:ITe if other.Any() then NotImplementedException "unknown diagnostics item to verify" |> raise - static member Compile srcFolder files = + static member Compile srcFolder files references = let compileFiles (files : IEnumerable<_>) = let mgr = new CompilationUnitManager(fun ex -> failwith ex.Message) - files.ToImmutableDictionary(Path.GetFullPath >> Uri, File.ReadAllText) + files.ToImmutableDictionary(Path.GetFullPath >> Uri, File.ReadAllText) |> CompilationUnitManager.InitializeFileManagers - |> mgr.AddOrUpdateSourceFilesAsync + |> mgr.AddOrUpdateSourceFilesAsync |> ignore - mgr.Build() - files |> Seq.map (fun file -> Path.Combine (srcFolder, file)) |> compileFiles - + mgr.UpdateReferencesAsync(new References(ProjectManager.LoadReferencedAssemblies(references))) |> ignore + mgr.Build() + files |> Seq.map (fun file -> Path.Combine (srcFolder, file)) |> compileFiles diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 52ecb2dc33..bd49916d67 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -164,11 +164,12 @@ - + - "$(MSBuildThisFileDirectory)..\TestTargets\Simulation\Example\bin\$(Configuration)\netcoreapp3.0\Example.dll" + $(MSBuildThisFileDirectory)..\TestTargets\Simulation\Example\bin\$(Configuration)\netcoreapp3.0\Example.dll + $(MSBuildThisFileDirectory)..\TestTargets\Libraries\Library1\bin\$(Configuration)\netcoreapp3.0\Library1.dll - + diff --git a/src/QsCompiler/Tests.Compiler/TypeCheckingTests.fs b/src/QsCompiler/Tests.Compiler/TypeCheckingTests.fs index 824f3bc14f..d116eeb6f4 100644 --- a/src/QsCompiler/Tests.Compiler/TypeCheckingTests.fs +++ b/src/QsCompiler/Tests.Compiler/TypeCheckingTests.fs @@ -13,7 +13,7 @@ open Xunit.Abstractions type TypeCheckingTests (output:ITestOutputHelper) = - inherit CompilerTests(CompilerTests.Compile "TestCases" ["General.qs"; "TypeChecking.qs"; "Types.qs"], output) + inherit CompilerTests(CompilerTests.Compile "TestCases" ["General.qs"; "TypeChecking.qs"; "Types.qs"] [], output) member private this.Expect name (diag : IEnumerable) = let ns = "Microsoft.Quantum.Testing.TypeChecking" |> NonNullable<_>.New From 74f456a5ea8f993443c5d52b99970a9a91159514 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 19 Feb 2020 15:10:40 -0800 Subject: [PATCH 041/135] Update access modifiers test namespace --- .../Libraries/Library1/AccessModifiers.qs | 6 +++--- .../Tests.Compiler/AccessModifierTests.fs | 2 +- .../Tests.Compiler/TestCases/AccessModifiers.qs | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs index 2ebfc4e8e2..c8e3534701 100644 --- a/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs +++ b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs @@ -1,5 +1,5 @@ /// This namespace contains additional definitions used by the test cases for access modifiers. -namespace Microsoft.Quantum.Testing.TypeChecking { +namespace Microsoft.Quantum.Testing.AccessModifiers { private newtype T1 = Unit; internal newtype T2 = Unit; @@ -10,8 +10,8 @@ namespace Microsoft.Quantum.Testing.TypeChecking { } /// This namespace contains additional definitions of types and callables meant to be used by the -/// Microsoft.Quantum.Testing.TypeChecking namespace. -namespace Microsoft.Quantum.Testing.TypeChecking.C { +/// Microsoft.Quantum.Testing.AccessModifiers namespace. +namespace Microsoft.Quantum.Testing.AccessModifiers.C { private function CF1 () : Unit {} internal function CF2 () : Unit {} diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index e811c3d3ac..7aa29a3e6e 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -20,7 +20,7 @@ type AccessModifierTests (output) = output) member private this.Expect name (diagnostics : IEnumerable) = - let ns = "Microsoft.Quantum.Testing.TypeChecking" |> NonNullable<_>.New + let ns = "Microsoft.Quantum.Testing.AccessModifiers" |> NonNullable<_>.New let name = name |> NonNullable<_>.New this.Verify (QsQualifiedName.New (ns, name), diagnostics) diff --git a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs index a0f87342f7..819250c67a 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs @@ -2,10 +2,10 @@ // Licensed under the MIT License. /// This namespace contains test cases for access modifiers. -namespace Microsoft.Quantum.Testing.TypeChecking { - open Microsoft.Quantum.Testing.TypeChecking.A; - open Microsoft.Quantum.Testing.TypeChecking.B as B; - open Microsoft.Quantum.Testing.TypeChecking.C; +namespace Microsoft.Quantum.Testing.AccessModifiers { + open Microsoft.Quantum.Testing.AccessModifiers.A; + open Microsoft.Quantum.Testing.AccessModifiers.B as B; + open Microsoft.Quantum.Testing.AccessModifiers.C; // Redefine inaccessible symbols in reference @@ -125,8 +125,8 @@ namespace Microsoft.Quantum.Testing.TypeChecking { } /// This namespace contains additional definitions of types and callables meant to be used by the -/// Microsoft.Quantum.Testing.TypeChecking namespace. -namespace Microsoft.Quantum.Testing.TypeChecking.A { +/// Microsoft.Quantum.Testing.AccessModifiers namespace. +namespace Microsoft.Quantum.Testing.AccessModifiers.A { private function AF1 () : Unit {} internal function AF2 () : Unit {} @@ -137,8 +137,8 @@ namespace Microsoft.Quantum.Testing.TypeChecking.A { } /// This namespace contains additional definitions of types and callables meant to be used by the -/// Microsoft.Quantum.Testing.TypeChecking namespace. -namespace Microsoft.Quantum.Testing.TypeChecking.B { +/// Microsoft.Quantum.Testing.AccessModifiers namespace. +namespace Microsoft.Quantum.Testing.AccessModifiers.B { private function BF1 () : Unit {} internal function BF2 () : Unit {} From fdd0bffbb4abda9a2a277386bf3f0a3c1d6996d4 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 19 Feb 2020 15:33:13 -0800 Subject: [PATCH 042/135] Skip access modifier tests for now --- src/QsCompiler/Tests.Compiler/AccessModifierTests.fs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index 7aa29a3e6e..90f1365172 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -24,14 +24,14 @@ type AccessModifierTests (output) = let name = name |> NonNullable<_>.New this.Verify (QsQualifiedName.New (ns, name), diagnostics) - [] + [] member this.``Redefine inaccessible symbols in reference`` () = this.Expect "T1" [] this.Expect "T2" [] this.Expect "F1" [] this.Expect "F2" [] - [] + [] member this.``Callables with access modifiers`` () = this.Expect "CallableUseOK" [] this.Expect "CallableUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] @@ -39,7 +39,7 @@ type AccessModifierTests (output) = this.Expect "CallableReferencePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] this.Expect "CallableReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] - [] + [] member this.``Types with access modifiers`` () = this.Expect "TypeUseOK" [] this.Expect "TypeUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleType] @@ -47,7 +47,7 @@ type AccessModifierTests (output) = this.Expect "TypeReferencePrivateInaccessible" [Error ErrorCode.InaccessibleType] this.Expect "TypeReferenceInternalInaccessible" [Error ErrorCode.InaccessibleType] - [] + [] member this.``Callable signatures`` () = this.Expect "PublicCallableLeaksPrivateTypeIn1" [Error ErrorCode.TypeLessAccessibleThanParentCallable] this.Expect "PublicCallableLeaksPrivateTypeIn2" [Error ErrorCode.TypeLessAccessibleThanParentCallable] @@ -61,7 +61,7 @@ type AccessModifierTests (output) = this.Expect "InternalCallableInternalTypeOK" [] this.Expect "PrivateCallableInternalTypeOK" [] - [] + [] member this.``Underlying types`` () = this.Expect "PublicTypeLeaksPrivateType1" [Error ErrorCode.TypeLessAccessibleThanParentType] this.Expect "PublicTypeLeaksPrivateType2" [Error ErrorCode.TypeLessAccessibleThanParentType] From 1dae0058d1395a3bd8a2133ebef8da36155ec572 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 19 Feb 2020 15:47:07 -0800 Subject: [PATCH 043/135] Add tests for type constructor accessibility --- .../Tests.Compiler/AccessModifierTests.fs | 4 +++ .../TestCases/AccessModifiers.qs | 36 ++++++++++++++----- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index 90f1365172..9f6f85c6e1 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -43,9 +43,13 @@ type AccessModifierTests (output) = member this.``Types with access modifiers`` () = this.Expect "TypeUseOK" [] this.Expect "TypeUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleType] + this.Expect "TypeConstructorUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] this.Expect "TypeQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleTypeInNamespace] + this.Expect "TypeConstructorQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallableInNamespace] this.Expect "TypeReferencePrivateInaccessible" [Error ErrorCode.InaccessibleType] + this.Expect "TypeConstructorReferencePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] this.Expect "TypeReferenceInternalInaccessible" [Error ErrorCode.InaccessibleType] + this.Expect "TypeConstructorReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] [] member this.``Callable signatures`` () = diff --git a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs index 819250c67a..60c8fe8bee 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs @@ -45,26 +45,46 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { // Types with access modifiers function TypeUseOK () : Unit { - let t1 = new T1[1]; - let t2 = new T2[1]; - let at2 = new AT2[1]; - let bt2 = new B.BT2[1]; + let t1 = T1(); + let t1s = new T1[1]; + let t2 = T2(); + let t2s = new T2[1]; + let at2 = AT2(); + let at2s = new AT2[1]; + let bt2 = BT2(); + let bt2s = new B.BT2[1]; } function TypeUnqualifiedUsePrivateInaccessible () : Unit { - let at1 = new AT1[1]; + let at1s = new AT1[1]; + } + + function TypeConstructorUnqualifiedUsePrivateInaccessible () : Unit { + let at1 = AT1(); } function TypeQualifiedUsePrivateInaccessible () : Unit { - let bt1 = new B.BT1[1]; + let bt1s = new B.BT1[1]; + } + + function TypeConstructorQualifiedUsePrivateInaccessible () : Unit { + let bt1 = B.BT1(); } function TypeReferencePrivateInaccessible () : Unit { - let ct1 = new CT1[1]; + let ct1s = new CT1[1]; + } + + function TypeConstructorReferencePrivateInaccessible () : Unit { + let ct1 = CT1(); } function TypeReferenceInternalInaccessible () : Unit { - let ct2 = new CT2[1]; + let ct2s = new CT2[1]; + } + + function TypeConstructorReferenceInternalInaccessible () : Unit { + let ct2 = CT2(); } // Callable signatures From 1dc0f72de719391aeb3d928cdba4f514945dd695 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 19 Feb 2020 16:14:34 -0800 Subject: [PATCH 044/135] Re-enable tests except for redeclarations of references --- .../Libraries/Library1/AccessModifiers.qs | 13 +++-- .../Tests.Compiler/AccessModifierTests.fs | 34 +++++------ .../TestCases/AccessModifiers.qs | 58 ++++++++++--------- 3 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs index c8e3534701..e4eef94633 100644 --- a/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs +++ b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs @@ -1,12 +1,15 @@ -/// This namespace contains additional definitions used by the test cases for access modifiers. +/// This file contains redefinitions of types and callables declared in Tests.Compiler\TestCases\AccessModifiers.qs. It +/// is used as an assembly reference to test support for re-using names of inaccessible declarations in references. namespace Microsoft.Quantum.Testing.AccessModifiers { - private newtype T1 = Unit; + // TODO: Uncomment these definitions when re-using names of inaccessible declarations in references is supported. - internal newtype T2 = Unit; + // private newtype T1 = Unit; - private function F1 () : Unit {} + // internal newtype T2 = Unit; - internal function F2 () : Unit {} + // private function F1 () : Unit {} + + // internal function F2 () : Unit {} } /// This namespace contains additional definitions of types and callables meant to be used by the diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index 9f6f85c6e1..f9a35640c2 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -24,34 +24,21 @@ type AccessModifierTests (output) = let name = name |> NonNullable<_>.New this.Verify (QsQualifiedName.New (ns, name), diagnostics) - [] - member this.``Redefine inaccessible symbols in reference`` () = - this.Expect "T1" [] - this.Expect "T2" [] - this.Expect "F1" [] - this.Expect "F2" [] - - [] - member this.``Callables with access modifiers`` () = + [] + member this.``Callables`` () = this.Expect "CallableUseOK" [] this.Expect "CallableUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] this.Expect "CallableQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallableInNamespace] - this.Expect "CallableReferencePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] - this.Expect "CallableReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] - [] - member this.``Types with access modifiers`` () = + [] + member this.``Types`` () = this.Expect "TypeUseOK" [] this.Expect "TypeUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleType] this.Expect "TypeConstructorUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] this.Expect "TypeQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleTypeInNamespace] this.Expect "TypeConstructorQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallableInNamespace] - this.Expect "TypeReferencePrivateInaccessible" [Error ErrorCode.InaccessibleType] - this.Expect "TypeConstructorReferencePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] - this.Expect "TypeReferenceInternalInaccessible" [Error ErrorCode.InaccessibleType] - this.Expect "TypeConstructorReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] - [] + [] member this.``Callable signatures`` () = this.Expect "PublicCallableLeaksPrivateTypeIn1" [Error ErrorCode.TypeLessAccessibleThanParentCallable] this.Expect "PublicCallableLeaksPrivateTypeIn2" [Error ErrorCode.TypeLessAccessibleThanParentCallable] @@ -65,7 +52,7 @@ type AccessModifierTests (output) = this.Expect "InternalCallableInternalTypeOK" [] this.Expect "PrivateCallableInternalTypeOK" [] - [] + [] member this.``Underlying types`` () = this.Expect "PublicTypeLeaksPrivateType1" [Error ErrorCode.TypeLessAccessibleThanParentType] this.Expect "PublicTypeLeaksPrivateType2" [Error ErrorCode.TypeLessAccessibleThanParentType] @@ -75,3 +62,12 @@ type AccessModifierTests (output) = this.Expect "PublicTypeLeaksInternalType" [Error ErrorCode.TypeLessAccessibleThanParentType] this.Expect "InternalTypeInternalTypeOK" [] this.Expect "PrivateTypeInternalTypeOK" [] + + [] + member this.``References`` () = + this.Expect "CallableReferencePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] + this.Expect "CallableReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] + this.Expect "TypeReferencePrivateInaccessible" [Error ErrorCode.InaccessibleType] + this.Expect "TypeConstructorReferencePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] + this.Expect "TypeReferenceInternalInaccessible" [Error ErrorCode.InaccessibleType] + this.Expect "TypeConstructorReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] diff --git a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs index 60c8fe8bee..994c301433 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs @@ -7,7 +7,7 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { open Microsoft.Quantum.Testing.AccessModifiers.B as B; open Microsoft.Quantum.Testing.AccessModifiers.C; - // Redefine inaccessible symbols in reference + // Redefine inaccessible references (see TestTargets\Libraries\Library1\AccessModifiers.qs) private newtype T1 = Unit; @@ -17,7 +17,7 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { internal function F2 () : Unit {} - // Callables with access modifiers + // Callables function CallableUseOK () : Unit { F1(); @@ -34,15 +34,7 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { B.BF1(); } - function CallableReferencePrivateInaccessible () : Unit { - CF1(); - } - - function CallableReferenceInternalInaccessible () : Unit { - CF2(); - } - - // Types with access modifiers + // Types function TypeUseOK () : Unit { let t1 = T1(); @@ -51,7 +43,7 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { let t2s = new T2[1]; let at2 = AT2(); let at2s = new AT2[1]; - let bt2 = BT2(); + let bt2 = B.BT2(); let bt2s = new B.BT2[1]; } @@ -71,22 +63,6 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { let bt1 = B.BT1(); } - function TypeReferencePrivateInaccessible () : Unit { - let ct1s = new CT1[1]; - } - - function TypeConstructorReferencePrivateInaccessible () : Unit { - let ct1 = CT1(); - } - - function TypeReferenceInternalInaccessible () : Unit { - let ct2s = new CT2[1]; - } - - function TypeConstructorReferenceInternalInaccessible () : Unit { - let ct2 = CT2(); - } - // Callable signatures function PublicCallableLeaksPrivateTypeIn1 (x : T1) : Unit {} @@ -142,6 +118,32 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { internal newtype InternalTypeInternalTypeOK = T2; private newtype PrivateTypeInternalTypeOK = T2; + + // References + + function CallableReferencePrivateInaccessible () : Unit { + CF1(); + } + + function CallableReferenceInternalInaccessible () : Unit { + CF2(); + } + + function TypeReferencePrivateInaccessible () : Unit { + let ct1s = new CT1[1]; + } + + function TypeConstructorReferencePrivateInaccessible () : Unit { + let ct1 = CT1(); + } + + function TypeReferenceInternalInaccessible () : Unit { + let ct2s = new CT2[1]; + } + + function TypeConstructorReferenceInternalInaccessible () : Unit { + let ct2 = CT2(); + } } /// This namespace contains additional definitions of types and callables meant to be used by the From 43539b667be20038b2861ff01bbdcb95ba9c73fe Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 19 Feb 2020 16:19:22 -0800 Subject: [PATCH 045/135] Replace tabs with spaces --- .../Tests.Compiler/TestCases/AccessModifiers.qs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs index 994c301433..d26ab0bc00 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs @@ -123,27 +123,27 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { function CallableReferencePrivateInaccessible () : Unit { CF1(); - } + } function CallableReferenceInternalInaccessible () : Unit { CF2(); - } + } function TypeReferencePrivateInaccessible () : Unit { let ct1s = new CT1[1]; - } + } function TypeConstructorReferencePrivateInaccessible () : Unit { let ct1 = CT1(); - } + } function TypeReferenceInternalInaccessible () : Unit { let ct2s = new CT2[1]; - } + } function TypeConstructorReferenceInternalInaccessible () : Unit { let ct2 = CT2(); - } + } } /// This namespace contains additional definitions of types and callables meant to be used by the From bf7057c878803abf845005eaf19436366adc96d6 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 19 Feb 2020 18:50:08 -0800 Subject: [PATCH 046/135] Use more descriptive names --- .../Libraries/Library1/AccessModifiers.qs | 16 +-- .../TestCases/AccessModifiers.qs | 124 +++++++++--------- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs index e4eef94633..8a38dafb1f 100644 --- a/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs +++ b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs @@ -3,23 +3,23 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { // TODO: Uncomment these definitions when re-using names of inaccessible declarations in references is supported. - // private newtype T1 = Unit; + // private newtype PrivateType = Unit; - // internal newtype T2 = Unit; + // internal newtype InternalType = Unit; - // private function F1 () : Unit {} + // private function PrivateFunction () : Unit {} - // internal function F2 () : Unit {} + // internal function InternalFunction () : Unit {} } /// This namespace contains additional definitions of types and callables meant to be used by the /// Microsoft.Quantum.Testing.AccessModifiers namespace. namespace Microsoft.Quantum.Testing.AccessModifiers.C { - private function CF1 () : Unit {} + private newtype PrivateTypeC = Unit; - internal function CF2 () : Unit {} + internal newtype InternalTypeC = Unit; - private newtype CT1 = Unit; + private function PrivateFunctionC () : Unit {} - internal newtype CT2 = Unit; + internal function InternalFunctionC () : Unit {} } diff --git a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs index d26ab0bc00..05d677bff3 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs @@ -9,163 +9,163 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { // Redefine inaccessible references (see TestTargets\Libraries\Library1\AccessModifiers.qs) - private newtype T1 = Unit; + private newtype PrivateType = Unit; - internal newtype T2 = Unit; + internal newtype InternalType = Unit; - private function F1 () : Unit {} + private function PrivateFunction () : Unit {} - internal function F2 () : Unit {} + internal function InternalFunction () : Unit {} // Callables function CallableUseOK () : Unit { - F1(); - F2(); - AF2(); - B.BF2(); + PrivateFunction(); + InternalFunction(); + InternalFunctionA(); + B.InternalFunctionB(); } function CallableUnqualifiedUsePrivateInaccessible () : Unit { - AF1(); + PrivateFunctionA(); } function CallableQualifiedUsePrivateInaccessible () : Unit { - B.BF1(); + B.PrivateFunctionB(); } // Types function TypeUseOK () : Unit { - let t1 = T1(); - let t1s = new T1[1]; - let t2 = T2(); - let t2s = new T2[1]; - let at2 = AT2(); - let at2s = new AT2[1]; - let bt2 = B.BT2(); - let bt2s = new B.BT2[1]; + let pt = PrivateType(); + let pts = new PrivateType[1]; + let it = InternalType(); + let its = new InternalType[1]; + let ita = InternalTypeA(); + let itas = new InternalTypeA[1]; + let itb = B.InternalTypeB(); + let itbs = new B.InternalTypeB[1]; } function TypeUnqualifiedUsePrivateInaccessible () : Unit { - let at1s = new AT1[1]; + let ptas = new PrivateTypeA[1]; } function TypeConstructorUnqualifiedUsePrivateInaccessible () : Unit { - let at1 = AT1(); + let pta = PrivateTypeA(); } function TypeQualifiedUsePrivateInaccessible () : Unit { - let bt1s = new B.BT1[1]; + let ptbs = new B.PrivateTypeB[1]; } function TypeConstructorQualifiedUsePrivateInaccessible () : Unit { - let bt1 = B.BT1(); + let ptb = B.PrivateTypeB(); } // Callable signatures - function PublicCallableLeaksPrivateTypeIn1 (x : T1) : Unit {} + function PublicCallableLeaksPrivateTypeIn1 (x : PrivateType) : Unit {} - function PublicCallableLeaksPrivateTypeIn2 (x : (Int, (T1, Bool))) : Unit {} + function PublicCallableLeaksPrivateTypeIn2 (x : (Int, (PrivateType, Bool))) : Unit {} - function PublicCallableLeaksPrivateTypeOut1 () : T1 { - return T1(); + function PublicCallableLeaksPrivateTypeOut1 () : PrivateType { + return PrivateType(); } - function PublicCallableLeaksPrivateTypeOut2 () : (Double, ((Result, T1), Bool)) { - return (1.0, ((Zero, T1()), true)); + function PublicCallableLeaksPrivateTypeOut2 () : (Double, ((Result, PrivateType), Bool)) { + return (1.0, ((Zero, PrivateType()), true)); } - internal function InternalCallableLeaksPrivateTypeIn (x : T1) : Unit {} + internal function InternalCallableLeaksPrivateTypeIn (x : PrivateType) : Unit {} - internal function InternalCallableLeaksPrivateTypeOut () : T1 { - return T1(); + internal function InternalCallableLeaksPrivateTypeOut () : PrivateType { + return PrivateType(); } - private function CallablePrivateTypeOK (x : T1) : T1 { - return T1(); + private function CallablePrivateTypeOK (x : PrivateType) : PrivateType { + return PrivateType(); } - function CallableLeaksInternalTypeIn (x : T2) : Unit {} + function CallableLeaksInternalTypeIn (x : InternalType) : Unit {} - function CallableLeaksInternalTypeOut () : T2 { - return T2(); + function CallableLeaksInternalTypeOut () : InternalType { + return InternalType(); } - internal function InternalCallableInternalTypeOK (x : T2) : T2 { - return T2(); + internal function InternalCallableInternalTypeOK (x : InternalType) : InternalType { + return InternalType(); } - private function PrivateCallableInternalTypeOK (x : T1) : T1 { - return T1(); + private function PrivateCallableInternalTypeOK (x : PrivateType) : PrivateType { + return PrivateType(); } // Underlying types - newtype PublicTypeLeaksPrivateType1 = T1; + newtype PublicTypeLeaksPrivateType1 = PrivateType; - newtype PublicTypeLeaksPrivateType2 = (Int, T1); + newtype PublicTypeLeaksPrivateType2 = (Int, PrivateType); - newtype PublicTypeLeaksPrivateType3 = (Int, (T1, Bool)); + newtype PublicTypeLeaksPrivateType3 = (Int, (PrivateType, Bool)); - internal newtype InternalTypeLeaksPrivateType = T1; + internal newtype InternalTypeLeaksPrivateType = PrivateType; - private newtype PrivateTypePrivateTypeOK = T1; + private newtype PrivateTypePrivateTypeOK = PrivateType; - newtype PublicTypeLeaksInternalType = T2; + newtype PublicTypeLeaksInternalType = InternalType; - internal newtype InternalTypeInternalTypeOK = T2; + internal newtype InternalTypeInternalTypeOK = InternalType; - private newtype PrivateTypeInternalTypeOK = T2; + private newtype PrivateTypeInternalTypeOK = InternalType; // References function CallableReferencePrivateInaccessible () : Unit { - CF1(); + PrivateFunctionC(); } function CallableReferenceInternalInaccessible () : Unit { - CF2(); + InternalFunctionC(); } function TypeReferencePrivateInaccessible () : Unit { - let ct1s = new CT1[1]; + let ptcs = new PrivateTypeC[1]; } function TypeConstructorReferencePrivateInaccessible () : Unit { - let ct1 = CT1(); + let ptc = PrivateTypeC(); } function TypeReferenceInternalInaccessible () : Unit { - let ct2s = new CT2[1]; + let itcs = new InternalTypeC[1]; } function TypeConstructorReferenceInternalInaccessible () : Unit { - let ct2 = CT2(); + let itc = InternalTypeC(); } } /// This namespace contains additional definitions of types and callables meant to be used by the /// Microsoft.Quantum.Testing.AccessModifiers namespace. namespace Microsoft.Quantum.Testing.AccessModifiers.A { - private function AF1 () : Unit {} + private function PrivateFunctionA () : Unit {} - internal function AF2 () : Unit {} + internal function InternalFunctionA () : Unit {} - private newtype AT1 = Unit; + private newtype PrivateTypeA = Unit; - internal newtype AT2 = Unit; + internal newtype InternalTypeA = Unit; } /// This namespace contains additional definitions of types and callables meant to be used by the /// Microsoft.Quantum.Testing.AccessModifiers namespace. namespace Microsoft.Quantum.Testing.AccessModifiers.B { - private function BF1 () : Unit {} + private function PrivateFunctionB () : Unit {} - internal function BF2 () : Unit {} + internal function InternalFunctionB () : Unit {} - private newtype BT1 = Unit; + private newtype PrivateTypeB = Unit; - internal newtype BT2 = Unit; + internal newtype InternalTypeB = Unit; } From 9d544b99197d940b67928b6179d4287db15b7158 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 21 Feb 2020 10:56:23 -0800 Subject: [PATCH 047/135] Add access modifiers to hover information --- .../SyntaxProcessor/SyntaxExtensions.fs | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs index 33e016e322..611b719cf8 100644 --- a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs +++ b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs @@ -230,6 +230,10 @@ let private globalCallableResolution (symbolTable : NamespaceManager) (currentNS let private newLine = " \n" // spaces first here so it will work with markdown as well let private withNewLine line = sprintf "%s%s" line newLine +/// Converts the first character of the string to uppercase. +let private toUpperFirst (s : string) = + s.[0..0].ToUpper() + s.[1..] + let private AsDocComment (doc : string seq) = if doc = null then null elif doc.Any() then @@ -259,6 +263,13 @@ let private namespaceDocumentation (docs : ILookup, Immutabl let allDoc = docs.SelectMany(fun entry -> entry.SelectMany(fun d -> d.AsEnumerable())) // the key is the source file PrintSummary allDoc markdown +/// Adds a string describing the modifiers in front of the string describing a kind of declaration. +let private showModifiers kind modifiers = + match modifiers.Access with + | DefaultAccess -> kind + | Internal -> "internal " + kind + | Private -> "private " + kind + type private TName () = inherit ExpressionTypeToQs(new ExpressionToQs()) override this.onCharacteristicsExpression characteristics = @@ -281,12 +292,12 @@ let public TypeInfo (symbolTable : NamespaceManager) (currentNS, source) (qsType let udtInfo udt = match udt |> globalTypeResolution symbolTable (currentNS, source) with | Some decl, _ -> - // TODO: Include modifiers. + let kind = showModifiers "user-defined type" decl.Modifiers |> toUpperFirst let name = decl.QualifiedName.Name.Value |> withNewLine let ns = sprintf "Namespace: %s" decl.QualifiedName.Namespace.Value |> withNewLine let info = sprintf "Underlying type: %s" (TypeName decl.Type) let doc = PrintSummary decl.Documentation markdown - sprintf "User defined type %s%s%s%s" name ns info doc + sprintf "%s %s%s%s%s" kind name ns info doc | None, Some sym -> sprintf "Type %s" sym.Value | _ -> "?" let typeParamName onUnknown (sym : QsSymbol) = @@ -329,10 +340,10 @@ let public TypeInfo (symbolTable : NamespaceManager) (currentNS, source) (qsType | _ -> sprintf "Built-in type %s%s" (typeName qsType.Type) doc |> NonNullable.New -let private printCallableKind capitalize = function - | QsCallableKind.Function -> if capitalize then "Function" else "function" - | QsCallableKind.Operation -> if capitalize then "Operation" else "operation" - | QsCallableKind.TypeConstructor -> if capitalize then "Type constructor" else "type constructor" +let private printCallableKind = function + | QsCallableKind.Function -> "function" + | QsCallableKind.Operation -> "operation" + | QsCallableKind.TypeConstructor -> "type constructor" [] let public PrintArgumentTuple item = @@ -356,11 +367,12 @@ let public PrintSignature (header : CallableDeclarationHeader) = [] let public VariableInfo (symbolTable : NamespaceManager) (locals : LocalDeclarations) (currentNS, source) (qsSym : QsSymbol) markdown = match qsSym |> globalCallableResolution symbolTable (currentNS, source) with - | Some decl, _ -> - let name = sprintf "%s %s" (printCallableKind true decl.Kind) (PrintSignature decl) |> withNewLine + | Some decl, _ -> + let kind = showModifiers (printCallableKind decl.Kind) decl.Modifiers |> toUpperFirst + let nameAndSignature = PrintSignature decl |> withNewLine let ns = sprintf "Namespace: %s" decl.QualifiedName.Namespace.Value let doc = PrintSummary decl.Documentation markdown - sprintf "%s%s%s" name ns doc + sprintf "%s %s%s%s" kind nameAndSignature ns doc | None, Some sym -> let localVars = locals.AsVariableLookup() if localVars.ContainsKey sym then @@ -388,25 +400,26 @@ let public DeclarationInfo symbolTable (locals : LocalDeclarations) (currentNS, | false, _ -> match qsSym |> globalTypeResolution symbolTable (currentNS, source) with // needs to be before querying callables | Some decl, _ -> - // TODO: Include modifiers. + let kind = showModifiers "user-defined type" decl.Modifiers let name = decl.QualifiedName.Name.Value |> withNewLine let ns = sprintf "Namespace: %s" decl.QualifiedName.Namespace.Value |> withNewLine let info = sprintf "Underlying type: %s" (decl.Type |> TypeName) let doc = PrintSummary decl.Documentation markdown - sprintf "Declaration of user defined type %s%s%s%s" name ns info doc + sprintf "Declaration of %s %s%s%s%s" kind name ns info doc | None, _ -> match qsSym |> globalCallableResolution symbolTable (currentNS, source) with | Some decl, _ -> - let functorSupport characteristics = - TypeString.onCharacteristicsExpression characteristics |> ignore - if String.IsNullOrWhiteSpace TypeString.Output then "(None)" else TypeString.Output - let name = sprintf "%s %s" (printCallableKind false decl.Kind) decl.QualifiedName.Name.Value |> withNewLine + let kind = showModifiers (printCallableKind decl.Kind) decl.Modifiers + let name = decl.QualifiedName.Name.Value |> withNewLine let ns = sprintf "Namespace: %s" decl.QualifiedName.Namespace.Value |> withNewLine let input = sprintf "Input type: %s" (decl.Signature.ArgumentType |> TypeName) |> withNewLine let output = sprintf "Output type: %s" (decl.Signature.ReturnType |> TypeName) |> withNewLine + let functorSupport characteristics = + TypeString.onCharacteristicsExpression characteristics |> ignore + if String.IsNullOrWhiteSpace TypeString.Output then "(None)" else TypeString.Output let fs = sprintf "Supported functors: %s" (decl.Signature.Information.Characteristics |> functorSupport) let doc = PrintSummary decl.Documentation markdown - sprintf "Declaration of %s%s%s%s%s%s" name ns input output fs doc + sprintf "Declaration of %s %s%s%s%s%s%s" kind name ns input output fs doc | None, _ -> match symbolTable.Documentation().TryGetValue name with | true, docs -> sprintf "Declaration of a partial namespace %s%s" name.Value (namespaceDocumentation (docs, markdown)) @@ -482,5 +495,3 @@ let public SymbolDeclaration (symbolTable : NamespaceManager) (locals : LocalDec | Some decl, _ -> decl.Location |> QsNullable<_>.Map (fun loc -> decl.SourceFile, loc.Offset, loc.Range) | _ -> LocalVariable locals qsSym |> QsNullable<_>.Map (fun (_, pos, range) -> source, pos, range) | _ -> Null - - From 8fd37f22da5a29a04cf6499db749e17b9dcad348 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 21 Feb 2020 12:23:11 -0800 Subject: [PATCH 048/135] Add access modifiers to Q# code output --- src/QsCompiler/Transformations/CodeOutput.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/Transformations/CodeOutput.cs b/src/QsCompiler/Transformations/CodeOutput.cs index 8108a6fc63..3d9e990898 100644 --- a/src/QsCompiler/Transformations/CodeOutput.cs +++ b/src/QsCompiler/Transformations/CodeOutput.cs @@ -1204,6 +1204,19 @@ public override QsSpecialization onControlledAdjointSpecialization(QsSpecializat return base.onControlledAdjointSpecialization(spec); } + /// + /// Converts the modifiers into a string containing keywords for all the applied modifiers, or an empty string + /// if all modifiers have default values. The string ends in a blank space if it is not empty. + /// + /// The modifiers to convert into a keyword string. + /// A string containing keywords for all of the applied modifiers. + private static string GetModifierKeywords(Modifiers modifiers) => modifiers.Access.Tag switch + { + AccessModifier.Tags.Internal => Keywords.qsInternal.id + " ", + AccessModifier.Tags.Private => Keywords.qsPrivate.id + " ", + _ => "" + }; + private QsCallable onCallable(QsCallable c, string declHeader) { if (!c.Kind.IsTypeConstructor) @@ -1245,7 +1258,7 @@ bool NeedsToBeExplicit (QsSpecialization s) c = c.WithSpecializations(specs => specs.Where(NeedsToBeExplicit).ToImmutableArray()); this.nrSpecialzations = c.Specializations.Length; - this.AddToOutput($"{declHeader} {signature}"); + this.AddToOutput($"{GetModifierKeywords(c.Modifiers)}{declHeader} {signature}"); if (!String.IsNullOrWhiteSpace(characteristics)) this.AddToOutput($"{Keywords.qsCharacteristics.id} {characteristics}"); this.AddBlock(() => c.Specializations.Select(dispatchSpecialization).ToImmutableArray()); this.AddToOutput(""); @@ -1275,8 +1288,9 @@ public override QsCustomType onType(QsCustomType t) else if (item is QsTypeItem.Anonymous type) return (null, type.Item); else throw new NotImplementedException("unknown case for type item"); } - var udtTuple = ArgumentTuple(t.TypeItems, GetItemNameAndType, this.Type); - this.AddDirective($"{Keywords.typeDeclHeader.id} {t.FullName.Name.Value} = {udtTuple}"); + var modifiers = GetModifierKeywords(t.Modifiers); + var udtTuple = ArgumentTuple(t.TypeItems, GetItemNameAndType, this.Type); + this.AddDirective($"{modifiers}{Keywords.typeDeclHeader.id} {t.FullName.Name.Value} = {udtTuple}"); return t; } From 8b09d3c14dde7dcecc9fb62e2598adc9bd322d1f Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 21 Feb 2020 17:43:41 -0800 Subject: [PATCH 049/135] Add test for skipping inaccessible items in documentation --- .../DocumentationParser/DocNamespace.cs | 32 ++++--- .../Tests.DocGenerator/DocParsingTests.cs | 13 +-- .../Tests.DocGenerator/DocWritingTests.cs | 84 +++++++++++++++++++ src/QsCompiler/Tests.DocGenerator/Utils.cs | 22 +++++ 4 files changed, 127 insertions(+), 24 deletions(-) create mode 100644 src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs create mode 100644 src/QsCompiler/Tests.DocGenerator/Utils.cs diff --git a/src/QsCompiler/DocumentationParser/DocNamespace.cs b/src/QsCompiler/DocumentationParser/DocNamespace.cs index 36b2fbaf06..5e2c32c582 100644 --- a/src/QsCompiler/DocumentationParser/DocNamespace.cs +++ b/src/QsCompiler/DocumentationParser/DocNamespace.cs @@ -185,10 +185,11 @@ void WriteItem(DocItem i) } /// - /// Writes the YAML file for this namespace. + /// Writes the YAML file for this namespace to a stream. /// - /// The directory to write the file to - internal void WriteToFile(string directoryPath) + /// The stream to write to. + /// The preexisting YAML mapping node. + internal void WriteToStream(Stream stream, YamlMappingNode? rootNode = null) { string ToSequenceKey(string itemTypeName) { @@ -211,8 +212,7 @@ string ToSequenceKey(string itemTypeName) return ""; } - var rootNode = Utils.ReadYamlFile(directoryPath, name) as YamlMappingNode ?? new YamlMappingNode(); - + rootNode ??= new YamlMappingNode(); rootNode.AddStringMapping(Utils.UidKey, uid); rootNode.AddStringMapping(Utils.NameKey, name); if (!String.IsNullOrEmpty(summary)) @@ -223,7 +223,7 @@ string ToSequenceKey(string itemTypeName) var itemTypeNodes = new Dictionary>(); // Collect existing items - foreach (var itemType in new []{Utils.FunctionKind, Utils.OperationKind, Utils.UdtKind}) + foreach (var itemType in new[] { Utils.FunctionKind, Utils.OperationKind, Utils.UdtKind }) { var seqKey = ToSequenceKey(itemType); var thisList = new SortedDictionary(); @@ -280,14 +280,22 @@ string ToSequenceKey(string itemTypeName) } var doc = new YamlDocument(rootNode); - var stream = new YamlStream(doc); + var yamlStream = new YamlStream(doc); + using var output = new StreamWriter(stream); + output.WriteLine("### " + Utils.QsNamespaceYamlMime); + yamlStream.Save(output, false); + } + + /// + /// Writes the YAML file for this namespace. + /// + /// The directory to write the file to + internal void WriteToFile(string directoryPath) + { + var rootNode = Utils.ReadYamlFile(directoryPath, name) as YamlMappingNode; var tocFileName = Path.Combine(directoryPath, name + Utils.YamlExtension); - using (var text = new StreamWriter(File.Open(tocFileName, FileMode.Create))) - { - text.WriteLine("### " + Utils.QsNamespaceYamlMime); - stream.Save(text, false); - } + WriteToStream(File.Open(tocFileName, FileMode.Create), rootNode); } } } diff --git a/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs b/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs index 6e1926e01d..dad33df6a6 100644 --- a/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs +++ b/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs @@ -10,7 +10,7 @@ using System.IO; using System.Linq; using Xunit; - +using static Microsoft.Quantum.QsCompiler.Documentation.Testing.Utils; namespace Microsoft.Quantum.QsCompiler.Documentation.Testing { @@ -20,17 +20,6 @@ namespace Microsoft.Quantum.QsCompiler.Documentation.Testing public class DocParsingTests { - private readonly Tuple EmptyRange = - new Tuple(QsPositionInfo.Zero, QsPositionInfo.Zero); - private readonly QsNullable ZeroLocation = - QsNullable.NewValue(new QsLocation(new Tuple(0, 0), new Tuple(QsPositionInfo.Zero, QsPositionInfo.Zero))); - private readonly NonNullable CanonName = NonNullable.New("Microsoft.Quantum.Canon"); - - private QsQualifiedName MakeFullName(string name) - { - return new QsQualifiedName(CanonName, NonNullable.New(name)); - } - [Fact] public void ParseDocComments() { diff --git a/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs b/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs new file mode 100644 index 0000000000..c22476152b --- /dev/null +++ b/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs @@ -0,0 +1,84 @@ +using Microsoft.Quantum.QsCompiler.DataTypes; +using Microsoft.Quantum.QsCompiler.SyntaxTokens; +using Microsoft.Quantum.QsCompiler.SyntaxTree; +using System; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Text; +using Xunit; +using static Microsoft.Quantum.QsCompiler.Documentation.Testing.Utils; + +namespace Microsoft.Quantum.QsCompiler.Documentation.Testing +{ + using ArgDeclType = LocalVariableDeclaration; + using QsType = QsTypeKind; + + public class DocWritingTests + { + [Fact] + public void ExcludeInaccessible() + { + var elements = + new[] { AccessModifier.DefaultAccess, AccessModifier.Internal, AccessModifier.Private } + .SelectMany(access => + { + var source = NonNullable.New("Tests.qs"); + var unit = ResolvedType.New(QsType.UnitType); + + var signature = new ResolvedSignature(Array.Empty().ToImmutableArray(), + unit, + unit, + CallableInformation.NoInformation); + var argumentTuple = QsTuple.NewQsTuple(ImmutableArray.Create>()); + var callable = new QsCallable(kind: QsCallableKind.Operation, + fullName: MakeFullName(access + "Operation"), + attributes: ImmutableArray.Empty, + modifiers: new Modifiers(access), + sourceFile: source, + location: ZeroLocation, + signature: signature, + argumentTuple: argumentTuple, + specializations: ImmutableArray.Create(), + documentation: ImmutableArray.Create(), + comments: QsComments.Empty); + + var typeItems = QsTuple.NewQsTuple( + ImmutableArray.Create(QsTuple.NewQsTupleItem(QsTypeItem.NewAnonymous(unit)))); + var type = new QsCustomType(fullName: MakeFullName(access + "Type"), + attributes: ImmutableArray.Empty, + modifiers: new Modifiers(access), + sourceFile: source, + location: ZeroLocation, + type: unit, + typeItems: typeItems, + documentation: ImmutableArray.Create(), + comments: QsComments.Empty); + return new[] + { + QsNamespaceElement.NewQsCallable(callable), + QsNamespaceElement.NewQsCustomType(type) + }; + }); + var emptyLookup = Array.Empty>().ToLookup(x => NonNullable.New("")); + var ns = new QsNamespace(CanonName, elements.ToImmutableArray(), emptyLookup); + var docNs = new DocNamespace(ns); + var stream = new MemoryStream(); + docNs.WriteToStream(stream, null); + + var expected = @"### YamlMime:QSharpNamespace +uid: microsoft.quantum.canon +name: Microsoft.Quantum.Canon +operations: +- uid: microsoft.quantum.canon.defaultaccessoperation + summary: '' +newtypes: +- uid: microsoft.quantum.canon.defaultaccesstype + summary: '' +... +"; + var actual = Encoding.UTF8.GetString(stream.ToArray()); + Assert.Equal(expected, actual); + } + } +} diff --git a/src/QsCompiler/Tests.DocGenerator/Utils.cs b/src/QsCompiler/Tests.DocGenerator/Utils.cs new file mode 100644 index 0000000000..905ad03f74 --- /dev/null +++ b/src/QsCompiler/Tests.DocGenerator/Utils.cs @@ -0,0 +1,22 @@ +using Microsoft.Quantum.QsCompiler.DataTypes; +using Microsoft.Quantum.QsCompiler.SyntaxTree; +using System; + +namespace Microsoft.Quantum.QsCompiler.Documentation.Testing +{ + internal static class Utils + { + internal static readonly Tuple EmptyRange = + new Tuple(QsPositionInfo.Zero, QsPositionInfo.Zero); + internal static readonly QsNullable ZeroLocation = + QsNullable.NewValue( + new QsLocation(new Tuple(0, 0), + new Tuple(QsPositionInfo.Zero, QsPositionInfo.Zero))); + internal static readonly NonNullable CanonName = NonNullable.New("Microsoft.Quantum.Canon"); + + internal static QsQualifiedName MakeFullName(string name) + { + return new QsQualifiedName(CanonName, NonNullable.New(name)); + } + } +} From d4b8949a5ece0cbab1dd5e49077c760adfb8d2a3 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 21 Feb 2020 18:03:35 -0800 Subject: [PATCH 050/135] Skip private and internal items when generating documentation --- src/QsCompiler/DocumentationParser/DocNamespace.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/QsCompiler/DocumentationParser/DocNamespace.cs b/src/QsCompiler/DocumentationParser/DocNamespace.cs index 5e2c32c582..6fcc440e92 100644 --- a/src/QsCompiler/DocumentationParser/DocNamespace.cs +++ b/src/QsCompiler/DocumentationParser/DocNamespace.cs @@ -3,11 +3,11 @@ #nullable enable using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.Quantum.QsCompiler.DataTypes; +using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; using YamlDotNet.RepresentationModel; @@ -34,11 +34,11 @@ internal class DocNamespace internal DocNamespace(QsNamespace ns, IEnumerable? sourceFiles = null) { var sourceFileSet = sourceFiles == null ? null : new HashSet(sourceFiles); - bool IsVisible(NonNullable qualifiedName, NonNullable source) + bool IsVisible(NonNullable source, AccessModifier access, NonNullable qualifiedName) { var name = qualifiedName.Value; var includeInDocs = sourceFileSet == null || sourceFileSet.Contains(source.Value); - return includeInDocs && !(name.StartsWith("_") || name.EndsWith("_") + return includeInDocs && access.IsDefaultAccess && !(name.StartsWith("_") || name.EndsWith("_") || name.EndsWith("Impl", StringComparison.InvariantCultureIgnoreCase) || name.EndsWith("ImplA", StringComparison.InvariantCultureIgnoreCase) || name.EndsWith("ImplC", StringComparison.InvariantCultureIgnoreCase) @@ -65,7 +65,7 @@ bool IsVisible(NonNullable qualifiedName, NonNullable source) if (item is QsNamespaceElement.QsCallable c) { var callable = c.Item; - if (IsVisible(callable.FullName.Name, callable.SourceFile) && + if (IsVisible(callable.SourceFile, callable.Modifiers.Access, callable.FullName.Name) && (callable.Kind != QsCallableKind.TypeConstructor)) { items.Add(new DocCallable(name, callable)); @@ -74,7 +74,7 @@ bool IsVisible(NonNullable qualifiedName, NonNullable source) else if (item is QsNamespaceElement.QsCustomType u) { var udt = u.Item; - if (IsVisible(udt.FullName.Name, udt.SourceFile)) + if (IsVisible(udt.SourceFile, udt.Modifiers.Access, udt.FullName.Name)) { items.Add(new DocUdt(name, udt)); } From 7481a69148c52287cd9b66e04dd495d50b2a5744 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 21 Feb 2020 18:07:09 -0800 Subject: [PATCH 051/135] Add copyright headers --- src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs | 5 ++++- src/QsCompiler/Tests.DocGenerator/Utils.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs b/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs index c22476152b..ebcaf71125 100644 --- a/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs +++ b/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs @@ -1,4 +1,7 @@ -using Microsoft.Quantum.QsCompiler.DataTypes; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; using System; diff --git a/src/QsCompiler/Tests.DocGenerator/Utils.cs b/src/QsCompiler/Tests.DocGenerator/Utils.cs index 905ad03f74..c9badf778a 100644 --- a/src/QsCompiler/Tests.DocGenerator/Utils.cs +++ b/src/QsCompiler/Tests.DocGenerator/Utils.cs @@ -1,4 +1,7 @@ -using Microsoft.Quantum.QsCompiler.DataTypes; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.SyntaxTree; using System; From e13d62c199e7bf5f3d3776373939eaa7b58696a9 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 21 Feb 2020 18:12:52 -0800 Subject: [PATCH 052/135] Add doc comment for new test --- src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs b/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs index ebcaf71125..ef870117b3 100644 --- a/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs +++ b/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs @@ -19,6 +19,9 @@ namespace Microsoft.Quantum.QsCompiler.Documentation.Testing public class DocWritingTests { + /// + /// Tests that internal and private items are skipped when generating documentation for a namespace. + /// [Fact] public void ExcludeInaccessible() { From 9d02420a433ef8f36da6ba4270e67eba4273e8a3 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 21 Feb 2020 18:52:43 -0800 Subject: [PATCH 053/135] Clarify doc comment for WriteToStream --- src/QsCompiler/DocumentationParser/DocNamespace.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/DocumentationParser/DocNamespace.cs b/src/QsCompiler/DocumentationParser/DocNamespace.cs index 6fcc440e92..5a3240f5a2 100644 --- a/src/QsCompiler/DocumentationParser/DocNamespace.cs +++ b/src/QsCompiler/DocumentationParser/DocNamespace.cs @@ -188,7 +188,9 @@ void WriteItem(DocItem i) /// Writes the YAML file for this namespace to a stream. /// /// The stream to write to. - /// The preexisting YAML mapping node. + /// + /// The mapping node representing the preexisting contents of this namespace's YAML file. + /// internal void WriteToStream(Stream stream, YamlMappingNode? rootNode = null) { string ToSequenceKey(string itemTypeName) From c7b512f6ce9e9af0ed91352f8580d875028989fd Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 24 Feb 2020 15:46:38 -0800 Subject: [PATCH 054/135] Hide inaccessible callables and types from code completion --- .../EditorSupport/CodeCompletion.cs | 57 ++++++++++--------- src/QsCompiler/Core/SymbolTable.fs | 24 ++++++++ 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs index 1b8f87fffc..56b004ab13 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs @@ -216,7 +216,7 @@ private static IEnumerable GetCompletionsForKind( .Concat(GetGlobalNamespaceCompletions(compilation, namespacePrefix)) .Concat(GetNamespaceAliasCompletions(file, compilation, position, namespacePrefix)); case CompletionKind.Tags.NamedItem: - return GetNamedItemCompletions(compilation); + return GetNamedItemCompletions(compilation, currentNamespace); case CompletionKind.Tags.Namespace: return GetGlobalNamespaceCompletions(compilation, namespacePrefix) @@ -311,7 +311,8 @@ private static IEnumerable GetLocalCompletions( } /// - /// Returns completions for all callables visible given the current namespace and the list of open namespaces. + /// Returns completions for all accessible callables given the current namespace and the list of open + /// namespaces, or an empty enumerable if the current namespace is null or symbols haven't been resolved yet. /// /// /// Thrown when any argument except is null. @@ -329,12 +330,12 @@ private static IEnumerable GetCallableCompletions( if (openNamespaces == null) throw new ArgumentNullException(nameof(openNamespaces)); - if (!compilation.GlobalSymbols.ContainsResolutions) + if (currentNamespace == null || !compilation.GlobalSymbols.ContainsResolutions) return Array.Empty(); return - compilation.GlobalSymbols.DefinedCallables() - .Concat(compilation.GlobalSymbols.ImportedCallables()) - .Where(callable => IsVisible(callable.QualifiedName, currentNamespace, openNamespaces)) + compilation.GlobalSymbols.AccessibleCallables(NonNullable.New(currentNamespace)) + .Where(callable => + IsAccessibleAsUnqualifiedName(callable.QualifiedName, currentNamespace, openNamespaces)) .Select(callable => new CompletionItem() { Label = callable.QualifiedName.Name.Value, @@ -349,7 +350,8 @@ private static IEnumerable GetCallableCompletions( } /// - /// Returns completions for all types visible given the current namespace and the list of open namespaces. + /// Returns completions for all accessible types given the current namespace and the list of open namespaces, or + /// an empty enumerable if the current namespace is null or symbols haven't been resolved yet. /// /// /// Thrown when any argument except is null. @@ -367,12 +369,11 @@ private static IEnumerable GetTypeCompletions( if (openNamespaces == null) throw new ArgumentNullException(nameof(openNamespaces)); - if (!compilation.GlobalSymbols.ContainsResolutions) + if (currentNamespace == null || !compilation.GlobalSymbols.ContainsResolutions) return Array.Empty(); return - compilation.GlobalSymbols.DefinedTypes() - .Concat(compilation.GlobalSymbols.ImportedTypes()) - .Where(type => IsVisible(type.QualifiedName, currentNamespace, openNamespaces)) + compilation.GlobalSymbols.AccessibleTypes(NonNullable.New(currentNamespace)) + .Where(type => IsAccessibleAsUnqualifiedName(type.QualifiedName, currentNamespace, openNamespaces)) .Select(type => new CompletionItem() { Label = type.QualifiedName.Name.Value, @@ -386,18 +387,21 @@ private static IEnumerable GetTypeCompletions( } /// - /// Returns completions for all named items in any type. + /// Returns completions for all named items in any type that are accessible from the given namespace, or an + /// empty enumerable if the current namespace is null or symbols haven't been resolved yet. /// - /// Thrown when the argument is null. - private static IEnumerable GetNamedItemCompletions(CompilationUnit compilation) + /// + /// Thrown when any argument except is null. + /// + private static IEnumerable GetNamedItemCompletions( + CompilationUnit compilation, string currentNamespace) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); - if (!compilation.GlobalSymbols.ContainsResolutions) + if (currentNamespace == null || !compilation.GlobalSymbols.ContainsResolutions) return Array.Empty(); - return compilation.GlobalSymbols.DefinedTypes() - .Concat(compilation.GlobalSymbols.ImportedTypes()) + return compilation.GlobalSymbols.AccessibleTypes(NonNullable.New(currentNamespace)) .SelectMany(type => ExtractItems(type.TypeItems)) .Where(item => item.IsNamed) .Select(item => new CompletionItem() @@ -439,8 +443,8 @@ private static IEnumerable GetGlobalNamespaceCompletions( } /// - /// Returns completions for namespace aliases with the given prefix that are visible at the given position in - /// the file. + /// Returns completions for namespace aliases with the given prefix that are accessible from the given position + /// in the file. /// /// Note: a dot will be added after the given prefix if it is not the empty string, and doesn't already end with /// a dot. @@ -519,7 +523,7 @@ private static string TryGetDocumentation( } /// - /// Returns the names of all namespaces that have been opened without an alias and are visible from the given + /// Returns the names of all namespaces that have been opened without an alias and are accessible from the given /// position in the file. Returns an empty enumerator if the position is invalid. /// /// Thrown when any argument is null. @@ -715,14 +719,15 @@ private static CompletionList ToCompletionList(this IEnumerable }; /// - /// Returns true if the qualified name is visible given the current namespace and a list of open namespaces. + /// Returns true if the declaration with the given qualified name would be accessible if it was referenced using + /// its unqualified name, given the current namespace and a list of open namespaces. /// - /// Names that start with "_" are treated as "private"; they are only visible from the namespace in which they - /// are declared. + /// Note: Names that start with "_" are treated as "private;" they are only accessible from the namespace in + /// which they are declared. /// - private static bool IsVisible(QsQualifiedName qualifiedName, - string currentNamespace, - IEnumerable openNamespaces) => + private static bool IsAccessibleAsUnqualifiedName(QsQualifiedName qualifiedName, + string currentNamespace, + IEnumerable openNamespaces) => openNamespaces.Contains(qualifiedName.Namespace.Value) && (!qualifiedName.Name.Value.StartsWith("_") || qualifiedName.Namespace.Value == currentNamespace); } diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index bab087b90a..da0e240cc0 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -1229,6 +1229,18 @@ and NamespaceManager defined.ToImmutableArray() finally syncRoot.ExitReadLock() + /// Returns the declaration headers for all callables (either defined in source files or imported from referenced + /// assemblies) that are accessible from the given namespace. + /// + /// Throws an InvalidOperationException if the symbols are not currently resolved. + member this.AccessibleCallables nsName = + Seq.append + (Seq.map (fun callable -> callable, true) (this.DefinedCallables())) + (Seq.map (fun callable -> callable, false) (this.ImportedCallables())) + |> Seq.filter (fun (callable, sameAssembly) -> + IsDeclarationAccessible sameAssembly (nsName = callable.QualifiedName.Namespace) callable.Modifiers) + |> Seq.map fst + /// Returns the source file and TypeDeclarationHeader of all types imported from referenced assemblies. member this.ImportedTypes() = syncRoot.EnterReadLock() @@ -1261,6 +1273,18 @@ and NamespaceManager defined.ToImmutableArray() finally syncRoot.ExitReadLock() + /// Returns the declaration headers for all types (either defined in source files or imported from referenced + /// assemblies) that are accessible from the given namespace. + /// + /// Throws an InvalidOperationException if the symbols are not currently resolved. + member this.AccessibleTypes nsName = + Seq.append + (Seq.map (fun qsType -> qsType, true) (this.DefinedTypes())) + (Seq.map (fun qsType -> qsType, false) (this.ImportedTypes())) + |> Seq.filter (fun (qsType, sameAssembly) -> + IsDeclarationAccessible sameAssembly (nsName = qsType.QualifiedName.Namespace) qsType.Modifiers) + |> Seq.map fst + /// removes the given source file and all its content from all namespaces member this.RemoveSource source = syncRoot.EnterWriteLock() From 267413bfa548a0ecec4fc3d8091c8883339a968b Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 24 Feb 2020 21:00:46 -0800 Subject: [PATCH 055/135] Update namespace suggestion code actions for access modifiers --- .../EditorSupport/CodeActions.cs | 35 +++++++++++----- src/QsCompiler/Core/SymbolTable.fs | 40 ++++++++++++------- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index 67bc8bd159..3b654c9aa9 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -10,6 +10,7 @@ using Microsoft.Quantum.QsCompiler.Diagnostics; using Microsoft.Quantum.QsCompiler.SyntaxProcessing; using Microsoft.Quantum.QsCompiler.SyntaxTokens; +using Microsoft.Quantum.QsCompiler.SyntaxTree; using Microsoft.Quantum.QsCompiler.TextProcessing; using Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -39,7 +40,7 @@ private static WorkspaceEdit GetWorkspaceEdit(this FileContentManager file, para /// /// Returns all namespaces in which a callable with the name of the symbol at the given position in the given file belongs to. - /// Returns an empty collection if any of the arguments is null or if no unqualified symbol exists at that location. + /// Returns an empty collection if any of the arguments is null or if no valid unqualified symbol exists at that location. /// Returns the name of the identifier as out parameter if an unqualified symbol exists at that location. /// private static IEnumerable> NamespaceSuggestionsForIdAtPosition @@ -47,14 +48,16 @@ private static IEnumerable> NamespaceSuggestionsForIdAtPosit { var variables = file?.TryGetQsSymbolInfo(pos, true, out CodeFragment _)?.UsedVariables; idName = variables != null && variables.Any() ? variables.Single().Symbol.AsDeclarationName(null) : null; - return idName != null && compilation != null - ? compilation.GlobalSymbols.NamespacesContainingCallable(NonNullable.New(idName)) + var nsName = file.TryGetNamespaceAt(pos); + return idName != null && compilation != null && nsName != null + ? compilation.GlobalSymbols.NamespacesContainingCallable(NonNullable.New(idName), + NonNullable.New(nsName)) : ImmutableArray>.Empty; } /// /// Returns all namespaces in which a type with the name of the symbol at the given position in the given file belongs to. - /// Returns an empty collection if any of the arguments is null or if no unqualified symbol exists at that location. + /// Returns an empty collection if any of the arguments is null or if no valid unqualified symbol exists at that location. /// Returns the name of the type as out parameter if an unqualified symbol exists at that location. /// private static IEnumerable> NamespaceSuggestionsForTypeAtPosition @@ -64,8 +67,10 @@ private static IEnumerable> NamespaceSuggestionsForTypeAtPos typeName = types != null && types.Any() && types.Single().Type is QsTypeKind.UserDefinedType udt ? udt.Item.Symbol.AsDeclarationName(null) : null; - return typeName != null && compilation != null - ? compilation.GlobalSymbols.NamespacesContainingType(NonNullable.New(typeName)) + var nsName = file.TryGetNamespaceAt(pos); + return typeName != null && compilation != null && nsName != null + ? compilation.GlobalSymbols.NamespacesContainingType(NonNullable.New(typeName), + NonNullable.New(nsName)) : ImmutableArray>.Empty; } @@ -294,9 +299,21 @@ IEnumerable GetCharacteristics(QsTuple> internal static IEnumerable<(string, WorkspaceEdit)> SuggestionsForIndexRange (this FileContentManager file, CompilationUnit compilation, LSP.Range range) { - if (file == null || compilation == null || range?.Start == null) return Enumerable.Empty<(string, WorkspaceEdit)>(); - var indexRangeNamespaces = compilation.GlobalSymbols.NamespacesContainingCallable(BuiltIn.IndexRange.Name); - if (!indexRangeNamespaces.Contains(BuiltIn.IndexRange.Namespace)) return Enumerable.Empty<(string, WorkspaceEdit)>(); + if (file == null || compilation == null || range?.Start == null) + { + return Enumerable.Empty<(string, WorkspaceEdit)>(); + } + + // Ensure that the IndexRange library function exists in this compilation unit. + var indexRange = new QsQualifiedName(BuiltIn.IndexRange.Namespace, BuiltIn.IndexRange.Name); + var nsName = file.TryGetNamespaceAt(range.Start); + if (nsName == null || !compilation.GlobalSymbols.TryGetCallable(indexRange, + NonNullable.New(nsName), + file.FileName).IsFound) + { + return Enumerable.Empty<(string, WorkspaceEdit)>(); + } + var suggestedOpenDir = file.OpenDirectiveSuggestions(range.Start.Line, BuiltIn.IndexRange.Namespace); /// Returns true the given expression is of the form "0 .. Length(args) - 1", diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index bab087b90a..204fbe3c2e 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -1485,27 +1485,39 @@ and NamespaceManager | Some ns -> ns.Name.Value finally syncRoot.ExitReadLock() - /// Returns the names of all namespaces in which a callable with the given name is declared, including private and - /// internal declarations. - member this.NamespacesContainingCallable cName = + /// Returns the names of all namespaces in which a callable is declared that has the given name and is accessible + /// from the given parent namespace. + member this.NamespacesContainingCallable cName nsName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! - // - // TODO: It may be useful to limit the results to only declarations which are accessible from a given location - // (e.g., for code actions in the language server). syncRoot.EnterReadLock() - try let tryFindCallable (ns : Namespace) = ns.TryFindCallable cName |> QsNullable<_>.Map (fun _ -> ns.Name) + try + let tryFindCallable (ns : Namespace) = + // TODO: Add QsNullable<_>.Bind and refactor this. + ns.TryFindCallable cName + |> QsNullable<_>.Fold + (fun _ (_, _, sameAssembly, modifiers) -> + if IsDeclarationAccessible sameAssembly (nsName = ns.Name) modifiers + then Value ns.Name + else Null) + Null (Namespaces.Values |> QsNullable<_>.Choose tryFindCallable).ToImmutableArray() finally syncRoot.ExitReadLock() - /// Returns the names of all namespaces in which a type with the given name is declared, including private and - /// internal declarations. - member this.NamespacesContainingType tName = + /// Returns the names of all namespaces in which a type is declared that has the given name and is accessible from + /// the given parent namespace. + member this.NamespacesContainingType tName nsName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! - // - // TODO: It may be useful to limit the results to only declarations which are accessible from a given location - // (e.g., for code actions in the language server). syncRoot.EnterReadLock() - try let tryFindType (ns : Namespace) = ns.TryFindType tName |> QsNullable<_>.Map (fun _ -> ns.Name) + try + let tryFindType (ns : Namespace) = + // TODO: Add QsNullable<_>.Bind and refactor this. + ns.TryFindType tName + |> QsNullable<_>.Fold + (fun _ (_, _, sameAssembly, modifiers) -> + if IsDeclarationAccessible sameAssembly (nsName = ns.Name) modifiers + then Value ns.Name + else Null) + Null (Namespaces.Values |> QsNullable<_>.Choose tryFindType).ToImmutableArray() finally syncRoot.ExitReadLock() From 3c0cdb163b27f7621ad00a58c3d4917975b3bd62 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 25 Feb 2020 08:56:21 -0800 Subject: [PATCH 056/135] Add QsNullable.Bind --- src/QsCompiler/Core/SymbolTable.fs | 24 ++++++++-------------- src/QsCompiler/DataStructures/DataTypes.fs | 11 +++++++--- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 204fbe3c2e..9eee14d022 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -1492,14 +1492,10 @@ and NamespaceManager syncRoot.EnterReadLock() try let tryFindCallable (ns : Namespace) = - // TODO: Add QsNullable<_>.Bind and refactor this. - ns.TryFindCallable cName - |> QsNullable<_>.Fold - (fun _ (_, _, sameAssembly, modifiers) -> - if IsDeclarationAccessible sameAssembly (nsName = ns.Name) modifiers - then Value ns.Name - else Null) - Null + ns.TryFindCallable cName |> QsNullable<_>.Bind (fun (_, _, sameAssembly, modifiers) -> + if IsDeclarationAccessible sameAssembly (nsName = ns.Name) modifiers + then Value ns.Name + else Null) (Namespaces.Values |> QsNullable<_>.Choose tryFindCallable).ToImmutableArray() finally syncRoot.ExitReadLock() @@ -1510,14 +1506,10 @@ and NamespaceManager syncRoot.EnterReadLock() try let tryFindType (ns : Namespace) = - // TODO: Add QsNullable<_>.Bind and refactor this. - ns.TryFindType tName - |> QsNullable<_>.Fold - (fun _ (_, _, sameAssembly, modifiers) -> - if IsDeclarationAccessible sameAssembly (nsName = ns.Name) modifiers - then Value ns.Name - else Null) - Null + ns.TryFindType tName |> QsNullable<_>.Bind (fun (_, _, sameAssembly, modifiers) -> + if IsDeclarationAccessible sameAssembly (nsName = ns.Name) modifiers + then Value ns.Name + else Null) (Namespaces.Values |> QsNullable<_>.Choose tryFindType).ToImmutableArray() finally syncRoot.ExitReadLock() diff --git a/src/QsCompiler/DataStructures/DataTypes.fs b/src/QsCompiler/DataStructures/DataTypes.fs index 92b86673ed..1bbe527321 100644 --- a/src/QsCompiler/DataStructures/DataTypes.fs +++ b/src/QsCompiler/DataStructures/DataTypes.fs @@ -13,11 +13,16 @@ type QsNullable<'T> = // to avoid having to include the F# core in the C# part o | Null | Value of 'T + /// If the given nullable has a value, applies the given function to it and returns the result, which must be + /// another nullable. Returns Null otherwise. + static member Bind fct = function + | Null -> Null + | Value v -> fct v + /// If the given nullable has a value, applies the given function to it and returns the result as Value, /// and returns Null otherwise. - static member Map fct = function - | Null -> Null - | Value v -> Value (fct v) + static member Map fct = + QsNullable<_>.Bind (fct >> Value) /// If the given nullable has a value, applies the given function to it. static member Iter fct = function From 53f6ca43c045c0c5f248a3a915d96f8d633bbfe2 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 25 Feb 2020 09:08:35 -0800 Subject: [PATCH 057/135] Refactor this if statement a little --- .../CompilationManager/EditorSupport/CodeActions.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index 3b654c9aa9..09effa6596 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -305,11 +305,12 @@ IEnumerable GetCharacteristics(QsTuple> } // Ensure that the IndexRange library function exists in this compilation unit. - var indexRange = new QsQualifiedName(BuiltIn.IndexRange.Namespace, BuiltIn.IndexRange.Name); var nsName = file.TryGetNamespaceAt(range.Start); - if (nsName == null || !compilation.GlobalSymbols.TryGetCallable(indexRange, - NonNullable.New(nsName), - file.FileName).IsFound) + var indexRange = compilation.GlobalSymbols.TryGetCallable( + new QsQualifiedName(BuiltIn.IndexRange.Namespace, BuiltIn.IndexRange.Name), + NonNullable.New(nsName), + file.FileName); + if (nsName == null || !indexRange.IsFound) { return Enumerable.Empty<(string, WorkspaceEdit)>(); } From 9c721d4db21718eeb97be722984ef6d5f6c5e55d Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 25 Feb 2020 12:52:09 -0800 Subject: [PATCH 058/135] Move nsName == null check earlier --- .../CompilationManager/EditorSupport/CodeActions.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index 09effa6596..54fd54d2b9 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -306,17 +306,19 @@ IEnumerable GetCharacteristics(QsTuple> // Ensure that the IndexRange library function exists in this compilation unit. var nsName = file.TryGetNamespaceAt(range.Start); + if (nsName == null) + { + return Enumerable.Empty<(string, WorkspaceEdit)>(); + } var indexRange = compilation.GlobalSymbols.TryGetCallable( new QsQualifiedName(BuiltIn.IndexRange.Namespace, BuiltIn.IndexRange.Name), NonNullable.New(nsName), file.FileName); - if (nsName == null || !indexRange.IsFound) + if (!indexRange.IsFound) { return Enumerable.Empty<(string, WorkspaceEdit)>(); } - var suggestedOpenDir = file.OpenDirectiveSuggestions(range.Start.Line, BuiltIn.IndexRange.Namespace); - /// Returns true the given expression is of the form "0 .. Length(args) - 1", /// as well as the range of the entire expression and the argument tuple "(args)" as out parameters. bool IsIndexRange(QsExpression iterExpr, Position offset, out LSP.Range exprRange, out LSP.Range argRange) @@ -361,6 +363,7 @@ IEnumerable IndexRangeEdits(CodeFragment fragment) var fragments = file.FragmentsOverlappingWithRange(range); var edits = fragments.SelectMany(IndexRangeEdits); + var suggestedOpenDir = file.OpenDirectiveSuggestions(range.Start.Line, BuiltIn.IndexRange.Namespace); return edits.Any() ? new[] { ("Use IndexRange to iterate over indices.", file.GetWorkspaceEdit(suggestedOpenDir.Concat(edits).ToArray())) } : Enumerable.Empty<(string, WorkspaceEdit)>(); From aaf1018f2f968d44e453a891dbcf97cd130ec712 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 25 Feb 2020 18:17:20 -0800 Subject: [PATCH 059/135] Update doc comments for namespace suggestions --- .../EditorSupport/CodeActions.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index 54fd54d2b9..63e87b8a2f 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -39,9 +39,13 @@ private static WorkspaceEdit GetWorkspaceEdit(this FileContentManager file, para } /// - /// Returns all namespaces in which a callable with the name of the symbol at the given position in the given file belongs to. - /// Returns an empty collection if any of the arguments is null or if no valid unqualified symbol exists at that location. - /// Returns the name of the identifier as out parameter if an unqualified symbol exists at that location. + /// Returns all namespaces in which a callable with the name of the symbol at the given position in the given + /// file belongs to. + /// + /// Returns an empty collection if any of the arguments is null, if no unqualified symbol exists at that + /// location, or if the position is not part of a namespace. + /// + /// Returns the name of the identifier as an out parameter if an unqualified symbol exists at that location. /// private static IEnumerable> NamespaceSuggestionsForIdAtPosition (this FileContentManager file, Position pos, CompilationUnit compilation, out string idName) @@ -56,9 +60,13 @@ private static IEnumerable> NamespaceSuggestionsForIdAtPosit } /// - /// Returns all namespaces in which a type with the name of the symbol at the given position in the given file belongs to. - /// Returns an empty collection if any of the arguments is null or if no valid unqualified symbol exists at that location. - /// Returns the name of the type as out parameter if an unqualified symbol exists at that location. + /// Returns all namespaces in which a type with the name of the symbol at the given position in the given file + /// belongs to. + /// + /// Returns an empty collection if any of the arguments is null, if no unqualified symbol exists at that + /// location, or if the position is not part of a namespace. + /// + /// Returns the name of the type as an out parameter if an unqualified symbol exists at that location. /// private static IEnumerable> NamespaceSuggestionsForTypeAtPosition (this FileContentManager file, Position pos, CompilationUnit compilation, out string typeName) From 84fadb9f6716cf03edbed5c4e926bf2a1970f943 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 26 Feb 2020 15:20:46 -0800 Subject: [PATCH 060/135] Update symbol table documentation for access modifiers --- src/QsCompiler/Core/SymbolTable.fs | 198 +++++++++++++++++++---------- 1 file changed, 128 insertions(+), 70 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 3b7b8af48c..56a4704839 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -31,7 +31,9 @@ type ResolutionResult<'T> = | NotFound -/// Note that this class is *not* threadsafe! +/// Represents the partial declaration of a namespace in a single file. +/// +/// Note that this class is *not* thread-safe, and access modifiers are always ignored when looking up declarations. type private PartialNamespace private (name : NonNullable, source : NonNullable, @@ -244,7 +246,9 @@ type private PartialNamespace private | false, _ -> [||] -/// Note that this class is *not* threadsafe! +/// Represents a namespace and all of its declarations. +/// +/// Note that this class is *not* thread-safe, and access modifiers are always ignored when looking up declarations. and Namespace private (name, parts : IEnumerable,PartialNamespace>>, CallablesInReferences : ImmutableDictionary, CallableDeclarationHeader>, @@ -657,14 +661,19 @@ and Namespace private /// Threadsafe class for global symbol management. -/// Takes a lookup for all callables and for all types declared within one of the assemblies -/// referenced by the compilation unit this namespace manager belongs to. -/// The key for the given lookups is the name of the namespace the declarations belongs to. -and NamespaceManager +/// +/// Takes a lookup for all callables and for all types declared within one of the assemblies referenced by the +/// compilation unit this namespace manager belongs to. The key for the given lookups is the name of the namespace the +/// declarations belongs to. +/// +/// The namespace manager takes access modifiers into consideration when resolving symbols. Some methods bypass this +/// (e.g., when returning a list of all declarations). Individual methods document whether they follow or ignore access +/// modifiers. +and NamespaceManager (syncRoot : IReaderWriterLock, - callablesInRefs : IEnumerable, + callablesInRefs : IEnumerable, specializationsInRefs : IEnumerable, - typesInRefs : IEnumerable) = + typesInRefs : IEnumerable) = // 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 @@ -761,12 +770,16 @@ and NamespaceManager | Internal -> sameAssembly | Private -> sameAssembly && sameNs - /// Given the qualified or unqualfied name of a type used within the given parent namespace and source file, determines if such a type exists - /// and returns its full name and the source file or referenced assembly in which it is defined as Some if it does. - /// Returns None if no such type exist, or if the type name is unqualified and ambiguous. + /// Given the qualified or unqualified name of a type used within the given parent namespace and source file, + /// determines if such a type is accessible, and returns its full name and the source file or referenced assembly in + /// which it is defined as Some if it is. + /// + /// Returns None if no such type exists, the type is inaccessible, or if the type name is unqualified and ambiguous. + /// /// Generates and returns an array with suitable diagnostics. - /// Throws an ArgumentException if the given parent namespace does not exist, - /// or if no source file with the given name is listed as source of that namespace. + /// + /// Throws an ArgumentException if the given parent namespace does not exist, or if no source file with the given + /// name is listed as source of that namespace. let TryResolveTypeName (parentNS, source) ((nsName, symName), symRange) = let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange let checkQualificationForDeprecation qual = BuiltIn.Deprecated |> PossibleQualifications (parentNS, source) |> Seq.contains qual @@ -813,8 +826,8 @@ and NamespaceManager /// resolution consists of replacing all unqualified names for user defined types by their qualified name. /// /// Generates an array of diagnostics for the cases where no user defined type of the specified name (qualified or - /// unqualified) can be found. In that case, resolves the user defined type by replacing it with the Q# type - /// denoting an invalid type. + /// unqualified) can be found, or if the type is inaccessible. In that case, resolves the user defined type by + /// replacing it with the Q# type denoting an invalid type. /// /// Diagnostics can be generated in additional cases when UDTs are referenced by returning an array of diagnostics /// from the given checkUdt function. @@ -825,7 +838,8 @@ and NamespaceManager /// /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is /// consistent with the defined callables. - /// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations. + /// + /// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations. /// Throws a NotSupportedException if the QsType to resolve contains a MissingType. let resolveType (parent : QsQualifiedName, tpNames, source) qsType checkUdt = let processUDT = TryResolveTypeName (parent.Namespace, source) >> function @@ -858,15 +872,20 @@ and NamespaceManager (udt.Range.ValueOr QsCompilerDiagnostic.DefaultRange) |] - /// Given the name of the namespace as well as the source file in which the attribute occurs, resolves the given attribute. - /// Generates suitable diagnostics if a suitable attribute cannot be determined, - /// or if the attribute argument contains expressions that are not supported, - /// or if the resolved argument type does not match the expected argument type. + /// Given the name of the namespace as well as the source file in which the attribute occurs, resolves the given + /// attribute. + /// + /// Generates suitable diagnostics if a suitable attribute cannot be found or is not accessible, if the attribute + /// argument contains expressions that are not supported, or if the resolved argument type does not match the + /// expected argument type. + /// /// Returns the resolved attribute as well as the generated diagnostics. - /// The TypeId in the resolved attribute is set to Null if the unresolved Id is not a valid identifier - /// or if the correct attribute cannot be determined, and is set to the corresponding type identifier otherwise. - /// May throw an ArgumentException if the given parent namespace does not exist, - /// or if no source file with the given name is listed as source of that namespace. + /// + /// The TypeId in the resolved attribute is set to Null if the unresolved Id is not a valid identifier or if the + /// correct attribute cannot be determined, and is set to the corresponding type identifier otherwise. + /// + /// May throw an ArgumentException if the given parent namespace does not exist or if no source file with the given + /// name is listed as source of that namespace. member private this.ResolveAttribute (parentNS, source) attribute = let getAttribute ((nsName, symName), symRange) = match TryResolveTypeName (parentNS, source) ((nsName, symName), symRange) with @@ -965,8 +984,8 @@ and NamespaceManager /// resolution consists of replacing all unqualified names for user defined types by their qualified name. /// /// Generates an array of diagnostics for the cases where no user defined type of the specified name (qualified or - /// unqualified) can be found. In that case, resolves the user defined type by replacing it with the Q# type - /// denoting an invalid type. + /// unqualified) can be found or the type is inaccessible. In that case, resolves the user defined type by replacing + /// it with the Q# type denoting an invalid type. /// /// Verifies that all used type parameters are defined in the given list of type parameters, and generates suitable /// diagnostics if they are not, replacing them by the Q# type denoting an invalid type. Returns the resolved type @@ -974,17 +993,25 @@ and NamespaceManager /// /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is /// consistent with the defined callables. - /// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations. + /// + /// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations. /// Throws a NotSupportedException if the QsType to resolve contains a MissingType. member this.ResolveType (parent : QsQualifiedName, tpNames : ImmutableArray<_>, source : NonNullable) (qsType : QsType) : ResolvedType * QsCompilerDiagnostic[] = resolveType (parent, tpNames, source) qsType (fun _ -> [||]) - /// Resolves the underlying type as well as all named and unnamed items for the given type declaration in the specified source file. - /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is consistent with the defined types. - /// May throw an exception if the given parent and/or source file is inconsistent with the defined types. - /// Throws an ArgumentException if the given type tuple is an empty QsTuple. + /// Resolves the underlying type as well as all named and unnamed items for the given type declaration in the + /// specified source file using ResolveType. + /// + /// Generates the same diagnostics as ResolveType, as well as additional diagnostics when the accessibility of the + /// type declaration is greater than the accessibility of any part of its underlying type. + /// + /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is + /// consistent with the defined types. + /// + /// May throw an exception if the given parent and/or source file is inconsistent with the defined types. Throws an + /// ArgumentException if the given type tuple is an empty QsTuple. member private this.ResolveTypeDeclaration (fullName, source, modifiers) typeTuple = // Currently, type parameters for UDTs are not supported. let resolveType qsType = @@ -994,14 +1021,23 @@ and NamespaceManager (checkUdtAccessibility ErrorCode.TypeLessAccessibleThanParentType (fullName.Name, modifiers)) SymbolResolution.ResolveTypeDeclaration resolveType typeTuple - /// Given the namespace and the name of the callable that the given signature belongs to, as well as its kind and the source file it is declared in, - /// fully resolves all Q# types in the signature using ResolveType. - /// Returns a new signature with the resolved types, the resolved argument tuple, as well as the array of diagnostics created during type resolution. - /// The position offset information for the variables declared in the argument tuple will be set to Null. - /// Positional information within types is set to Null if the parent callable is a type constructor. - /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is consistent with the defined callables. - /// May throw an exception if the given parent and/or source file is inconsistent with the defined callables. - /// Throws an ArgumentException if the given list of characteristics is empty. + /// Given the namespace and the name of the callable that the given signature belongs to, as well as its kind and + /// the source file it is declared in, fully resolves all Q# types in the signature using ResolveType. + /// + /// Generates the same diagnostics as ResolveType, as well as additional diagnostics when the accessibility of the + /// callable declaration is greater than the accessibility of any type in its signature. + /// + /// Returns a new signature with the resolved types, the resolved argument tuple, as well as the array of + /// diagnostics created during type resolution. + /// + /// The position offset information for the variables declared in the argument tuple will be set to Null. Positional + /// information within types is set to Null if the parent callable is a type constructor. + /// + /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is + /// consistent with the defined callables. + /// + /// May throw an exception if the given parent and/or source file is inconsistent with the defined callables. Throws + /// an ArgumentException if the given list of characteristics is empty. member private this.ResolveCallableSignature (parentKind, parentName, source, modifiers) (signature, specBundleCharacteristics) = let resolveType tpNames qsType = @@ -1195,7 +1231,8 @@ and NamespaceManager defined.ToImmutableArray() finally syncRoot.ExitReadLock() - /// Returns the source file and CallableDeclarationHeader of all callables imported from referenced assemblies. + /// Returns the source file and CallableDeclarationHeader of all callables imported from referenced assemblies, + /// regardless of accessibility. member this.ImportedCallables () = // TODO: this needs to be adapted if we support external specializations syncRoot.EnterReadLock() @@ -1203,8 +1240,9 @@ and NamespaceManager imported.ToImmutableArray() finally syncRoot.ExitReadLock() - /// Returns the declaration headers for all callables defined in source files. - /// Throws an InvalidOperationException if the symbols are not currently resolved. + /// Returns the declaration headers for all callables defined in source files, regardless of accessibility. + /// + /// Throws an InvalidOperationException if the symbols are not currently resolved. member this.DefinedCallables () = let notResolvedException = InvalidOperationException "callables are not resolved" syncRoot.EnterReadLock() @@ -1241,15 +1279,17 @@ and NamespaceManager IsDeclarationAccessible sameAssembly (nsName = callable.QualifiedName.Namespace) callable.Modifiers) |> Seq.map fst - /// Returns the source file and TypeDeclarationHeader of all types imported from referenced assemblies. - member this.ImportedTypes() = + /// Returns the source file and TypeDeclarationHeader of all types imported from referenced assemblies, regardless + /// of accessibility. + member this.ImportedTypes() = syncRoot.EnterReadLock() try let imported = Namespaces.Values |> Seq.collect (fun ns -> ns.TypesInReferencedAssemblies.Values) imported.ToImmutableArray() finally syncRoot.ExitReadLock() - /// Returns the declaration headers for all types defined in source files. - /// Throws an InvalidOperationException if the symbols are not currently resolved. + /// Returns the declaration headers for all types defined in source files, regardless of accessibility. + /// + /// Throws an InvalidOperationException if the symbols are not currently resolved. member this.DefinedTypes () = let notResolvedException = InvalidOperationException "types are not resolved" syncRoot.EnterReadLock() @@ -1357,11 +1397,15 @@ and NamespaceManager | false, _ -> ArgumentException "no such namespace exists" |> raise finally syncRoot.ExitWriteLock() - /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader as Value, - /// if the qualifier can be resolved within the given parent namespace and source file, and such a callable indeed exists. - /// If the callable is not defined an any of the references and the source file containing the callable declaration is specified (i.e. declSource is Some), - /// throws the corresponding exception if no such callable exists in that file. - /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. + /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader in a ResolutionResult if + /// the qualifier can be resolved within the given parent namespace and source file, and the callable is accessible. + /// + /// If the callable is not defined an any of the references and the source file containing the callable declaration + /// is specified (i.e. declSource is Some), throws the corresponding exception if no such callable exists in that + /// file. + /// + /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent + /// namespace does not exist. member private this.TryGetCallableHeader (callableName : QsQualifiedName, declSource) (nsName, source) = let BuildHeader fullName (source, kind, declaration : Resolution<_,_>) = let fallback () = (declaration.Defined, [CallableInformation.Invalid]) |> this.ResolveCallableSignature (kind, callableName, source, declaration.Modifiers) |> fst @@ -1401,14 +1445,20 @@ and NamespaceManager | false, _ -> NotFound finally syncRoot.ExitReadLock() - /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader as Value, - /// if the qualifier can be resolved within the given parent namespace and source file, and such a callable indeed exists. - /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. - member this.TryGetCallable (callableName : QsQualifiedName) (nsName, source) = this.TryGetCallableHeader (callableName, None) (nsName, source) - - /// If the given callable name can be uniquely resolved within the given namespace and source file, - /// returns the CallableDeclarationHeader with the information on that callable as Value. - /// Returns Null as well as a list with namespaces containing a callable with that name if this is not the case. + /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader in a ResolutionResult if + /// the qualifier can be resolved within the given parent namespace and source file, and the callable is accessible. + /// + /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent + /// namespace does not exist. + member this.TryGetCallable (callableName : QsQualifiedName) (nsName, source) = + this.TryGetCallableHeader (callableName, None) (nsName, source) + + /// Given an unqualified callable name, returns the corresponding CallableDeclarationHeader in a ResolutionResult if + /// the qualifier can be uniquely resolved within the given parent namespace and source file, and the callable is + /// accessible. + /// + /// Returns an Ambiguous result with a list with namespaces containing a type with that name if the name cannot be + /// uniquely resolved. member this.TryResolveAndGetCallable cName (nsName, source) = syncRoot.EnterReadLock() try @@ -1427,11 +1477,14 @@ and NamespaceManager | _ -> accessibleResolutions.Select fst |> Ambiguous finally syncRoot.ExitReadLock() - /// Given a qualified type name, returns the corresponding TypeDeclarationHeader as Value, - /// if the qualifier can be resolved within the given parent namespace and source file, and such a type indeed exists. - /// If the type is not defined an any of the references and the source file containing the type declaration is specified (i.e. declSource is Some), - /// throws the corresponding exception if no such type exists in that file. - /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. + /// Given a qualified type name, returns the corresponding TypeDeclarationHeader in a ResolutionResult if the + /// qualifier can be resolved within the given parent namespace and source file, and the type is accessible. + /// + /// If the type is not defined an any of the references and the source file containing the type declaration is + /// specified (i.e. declSource is Some), throws the corresponding exception if no such type exists in that file. + /// + /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent + /// namespace does not exist. member private this.TryGetTypeHeader (typeName : QsQualifiedName, declSource) (nsName, source) = let BuildHeader fullName (source, declaration) = let fallback () = @@ -1472,14 +1525,19 @@ and NamespaceManager | false, _ -> NotFound finally syncRoot.ExitReadLock() - /// Given a qualified type name, returns the corresponding TypeDeclarationHeader as Value, - /// if the qualifier can be resolved within the given parent namespace and source file, and such a type indeed exists. - /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. + /// Given a qualified type name, returns the corresponding TypeDeclarationHeader in a ResolutionResult if the + /// qualifier can be resolved within the given parent namespace and source file, and the type is accessible. + /// + /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent + /// namespace does not exist. member this.TryGetType (typeName : QsQualifiedName) (nsName, source) = this.TryGetTypeHeader (typeName, None) (nsName, source) - /// If the given type name can be uniquely resolved within the given namespace and source file, - /// returns the TypeDeclarationHeader with the information on that type as Value. - /// Returns Null as well as a list with namespaces containing a type with that name if this is not the case. + /// Given an unqualified type name, returns the corresponding TypeDeclarationHeader in a ResolutionResult if the + /// qualifier can be uniquely resolved within the given parent namespace and source file, and the type is + /// accessible. + /// + /// Returns an Ambiguous result with a list with namespaces containing a type with that name if the name cannot be + /// uniquely resolved. member this.TryResolveAndGetType tName (nsName, source) = syncRoot.EnterReadLock() try From 651c836d8e36cb6b4b4df8cd85551a917ba43e9b Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 2 Mar 2020 20:37:47 -0800 Subject: [PATCH 061/135] first draft for some handles for a package containing target specific information --- .../CommandLineTool/Commands/Build.cs | 22 +++---- .../CommandLineTool/Commands/Diagnose.cs | 15 ++--- src/QsCompiler/CommandLineTool/Options.cs | 59 ++++++++++++++++--- .../CompilationManager/AssemblyLoader.cs | 59 +++++++++++++++---- .../CompilationManager/ProjectManager.cs | 10 ++-- src/QsCompiler/Compiler/CompilationLoader.cs | 44 +++++++++++++- src/QsCompiler/DataStructures/Diagnostics.fs | 12 +++- 7 files changed, 170 insertions(+), 51 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index ba1a7965fd..b1f7d0bc9b 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -16,7 +16,7 @@ namespace Microsoft.Quantum.QsCompiler.CommandLineCompiler public static class BuildCompilation { [Verb("build", HelpText = "Builds a compilation unit to run on the Q# quantum simulation framework.")] - public class BuildOptions : Options + public class BuildOptions : CompilationOptions { [Usage(ApplicationAlias = "qsCompiler")] public static IEnumerable UsageExamples @@ -50,14 +50,6 @@ public static IEnumerable UsageExamples HelpText = "Name of the project (needs to be usable as file name).")] public string ProjectName { get; set; } - [Option("load", Required = false, SetName = CODE_MODE, - HelpText = "[Experimental feature] Path to the .NET Core dll(s) defining additional transformations to include in the compilation process.")] - public IEnumerable Plugins { get; set; } - - [Option("trim", Required = false, Default = 1, - HelpText = "[Experimental feature] Integer indicating how much to simplify the syntax tree by eliminating selective abstractions.")] - public int TrimLevel { get; set; } - [Option("emit-dll", Required = false, Default = false, SetName = CODE_MODE, HelpText = "Specifies whether the compiler should emit a .NET Core dll containing the compiled Q# code.")] public bool EmitDll { get; set; } @@ -121,14 +113,18 @@ public static int Run(BuildOptions options, ConsoleLogger logger) if (options == null) throw new ArgumentNullException(nameof(options)); if (logger == null) throw new ArgumentNullException(nameof(logger)); - if (options?.ResponseFiles != null && options.ResponseFiles.Any()) - { options = FromResponseFiles(options.ResponseFiles); } - if (options == null) return ReturnCode.INVALID_ARGUMENTS; + if (options.ResponseFiles != null && options.ResponseFiles.Any()) + { + var fromResponseFiles = FromResponseFiles(options.ResponseFiles); + options = fromResponseFiles; // FIXME: PROPERLY MERGE OPTIONS INSTEAD + if (options == null) return ReturnCode.INVALID_ARGUMENTS; + } var usesPlugins = options.Plugins != null && options.Plugins.Any(); var loadOptions = new CompilationLoader.Configuration { ProjectName = options.ProjectName, + TargetPackageAssembly = options.GetTargetPackageAssemblyPath(logger), GenerateFunctorSupport = true, SkipSyntaxTreeTrimming = options.TrimLevel == 0, ConvertClassicalControl = options.TrimLevel >= 2, @@ -154,7 +150,7 @@ public static int Run(BuildOptions options, ConsoleLogger logger) } catch (Exception ex) { - logger.Log(ErrorCode.PublishingPerfResultsFailed, new string[]{options.PerfFolder}); + logger.Log(ErrorCode.PublishingPerfResultsFailed, new string[]{ options.PerfFolder }); logger.Log(ex); } } diff --git a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs index 8692affa79..20d58eede8 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs @@ -23,7 +23,7 @@ namespace Microsoft.Quantum.QsCompiler.CommandLineCompiler public static class DiagnoseCompilation { [Verb("diagnose", HelpText = "Generates intermediate representations of the code to help diagnose issues.")] - public class DiagnoseOptions : Options + public class DiagnoseOptions : CompilationOptions { [Usage(ApplicationAlias = "qsCompiler")] public static IEnumerable UsageExamples @@ -56,14 +56,6 @@ public static IEnumerable UsageExamples [Option("code", Required = false, Default = false, HelpText = "Specifies whether to print the Q# code generated based on the built syntax tree.")] public bool PrintCompiledCode { get; set; } - - [Option("trim", Required = false, Default = 1, - HelpText = "[Experimental feature] Integer indicating how much to simplify the syntax tree by eliminating selective abstractions.")] - public int TrimLevel { get; set; } - - [Option("load", Required = false, SetName = CODE_MODE, - HelpText = "[Experimental feature] Path to the .NET Core dll(s) defining additional transformations to include in the compilation process.")] - public IEnumerable Plugins { get; set; } } /// @@ -136,7 +128,7 @@ private static void PrintContentTokenization(Compilation compilation, ILogger lo private static void PrintSyntaxTree(IEnumerable evaluatedTree, Compilation compilation, ILogger logger) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); - evaluatedTree = evaluatedTree ?? compilation.SyntaxTree.Values; + evaluatedTree ??= compilation.SyntaxTree.Values; foreach (var file in compilation.SourceFiles) { @@ -166,7 +158,7 @@ void PrintTree(string serialization) => logger.Log( private static void PrintGeneratedQs(IEnumerable evaluatedTree, Compilation compilation, ILogger logger) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); - evaluatedTree = evaluatedTree ?? compilation.SyntaxTree.Values; + evaluatedTree ??= compilation.SyntaxTree.Values; foreach (var file in compilation.SourceFiles) { @@ -219,6 +211,7 @@ public static int Run(DiagnoseOptions options, ConsoleLogger logger) var loadOptions = new CompilationLoader.Configuration { + TargetPackageAssembly = options.GetTargetPackageAssemblyPath(logger), GenerateFunctorSupport = true, SkipSyntaxTreeTrimming = options.TrimLevel == 0, ConvertClassicalControl = options.TrimLevel >= 2, diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs index ddc0afe413..55a6a9124c 100644 --- a/src/QsCompiler/CommandLineTool/Options.cs +++ b/src/QsCompiler/CommandLineTool/Options.cs @@ -17,6 +17,49 @@ namespace Microsoft.Quantum.QsCompiler.CommandLineCompiler { + public class CompilationOptions : Options + { + [Option("trim", Required = false, Default = 1, + HelpText = "[Experimental feature] Integer indicating how much to simplify the syntax tree by eliminating selective abstractions.")] + public int TrimLevel { get; set; } + + [Option("load", Required = false, SetName = CODE_MODE, + HelpText = "[Experimental feature] Path to the .NET Core dll(s) defining additional transformations to include in the compilation process.")] + public IEnumerable Plugins { get; set; } + + [Option("target-package", Required = false, + HelpText = "Path to the NuGet package containing target specific information and implementations.")] + public string TargetPackage { get; set; } + + /// + /// Returns null if TargetPackage is not null or empty, and + /// returns the path to the assembly containing target specific implementations otherwise. + /// If a logger is specified, logs suitable diagnostics if a TargetPackages is not null or empty, + /// but no path to the target package assembly could be determined. + /// This may be the case if no directory at the TargetPackage location exists, or if its files can't be accessed, + /// or more than one dll matches the pattern by which the target package assembly is identified. + /// + public string GetTargetPackageAssemblyPath(ILogger logger = null) + { + if (String.IsNullOrEmpty(this.TargetPackage)) return null; + try + { + // Disclaimer: we may revise that in the future. + var targetPackageAssembly = Directory.GetFiles(this.TargetPackage, "*Intrinsics.dll", SearchOption.AllDirectories).SingleOrDefault(); + if (targetPackageAssembly != null) return targetPackageAssembly; + } + catch (Exception ex) + { + if (Directory.Exists(this.TargetPackage)) logger?.Log(ex); + else logger?.Log(ErrorCode.CouldNotFineTargetPackage, new[] { this.TargetPackage }); + } + + logger?.Log(ErrorCode.CouldNotFindTargetPackageAssembly, new[] { this.TargetPackage }); + return null; + } + } + + public class Options { public enum LogFormat @@ -59,7 +102,7 @@ public enum LogFormat public LogFormat OutputFormat { get; set; } [Option("package-load-fallback-folders", Required = false, SetName = CODE_MODE, - HelpText = "Specifies the directories the compiler will search when a rewrite step dependency could not be found.")] + HelpText = "Specifies the directories the compiler will search when a compiler dependency could not be found.")] public IEnumerable PackageLoadFallbackFolders { get; set; } @@ -87,15 +130,13 @@ string value(PropertyInfo p) /// /// Given a LogFormat, returns a suitable routing for formatting diagnostics. /// - internal static Func LoggingFormat(LogFormat format) - { - switch (format) + internal static Func LoggingFormat(LogFormat format) => + format switch { - case LogFormat.MsBuild: return Formatting.MsBuildFormat; - case LogFormat.Default: return Formatting.HumanReadableFormat; - default: throw new NotImplementedException("unknown output format for logger"); - } - } + LogFormat.MsBuild => Formatting.MsBuildFormat, + LogFormat.Default => Formatting.HumanReadableFormat, + _ => throw new NotImplementedException("unknown output format for logger"), + }; /// /// Creates a suitable logger for the given command line options, diff --git a/src/QsCompiler/CompilationManager/AssemblyLoader.cs b/src/QsCompiler/CompilationManager/AssemblyLoader.cs index d3b41243a2..7c4937112d 100644 --- a/src/QsCompiler/CompilationManager/AssemblyLoader.cs +++ b/src/QsCompiler/CompilationManager/AssemblyLoader.cs @@ -30,44 +30,82 @@ public static class AssemblyLoader /// and returns the loaded content as out parameter. /// Returns false if some of the content could not be loaded successfully, /// possibly because the referenced assembly has been compiled with an older compiler version. + /// If onDeserializationException is specified, invokes the given action on any exception thrown during deserialization. /// Throws an ArgumentNullException if the given uri is null. /// Throws a FileNotFoundException if no file with the given name exists. /// Throws the corresponding exceptions if the information cannot be extracted. /// - public static bool LoadReferencedAssembly(Uri asm, out References.Headers headers, bool ignoreDllResources = false) + public static bool LoadReferencedAssembly(Uri asm, out References.Headers headers, bool ignoreDllResources = false, Action onDeserializationException = null) { if (asm == null) throw new ArgumentNullException(nameof(asm)); if (!CompilationUnitManager.TryGetFileId(asm, out var id) || !File.Exists(asm.LocalPath)) - { throw new FileNotFoundException($"the uri '{asm}' given to the assembly loader is invalid or the file does not exist"); } + { throw new FileNotFoundException($"The uri '{asm}' given to the assembly loader is invalid or the file does not exist."); } using var stream = File.OpenRead(asm.LocalPath); using var assemblyFile = new PEReader(stream); - if (ignoreDllResources || !FromResource(assemblyFile, out var syntaxTree)) + if (ignoreDllResources || !FromResource(assemblyFile, out var compilation, onDeserializationException)) { var attributes = LoadHeaderAttributes(assemblyFile); headers = new References.Headers(id, attributes); return ignoreDllResources || !attributes.Any(); // just means we have no references } - headers = new References.Headers(id, syntaxTree?.Namespaces ?? ImmutableArray.Empty); + headers = new References.Headers(id, compilation?.Namespaces ?? ImmutableArray.Empty); return true; } + /// + /// Loads the Q# data structures in a referenced assembly given the Uri to that assembly, + /// and returns the loaded content as out parameter. + /// Returns false if some of the content could not be loaded successfully, + /// possibly because the referenced assembly has been compiled with an older compiler version. + /// Catches any exception throw upon loading the compilation, and invokes onException with it if such an action has been specified. + /// Sets the out parameter to null if an exception occurred during loading. + /// Throws an ArgumentNullException if the given uri is null. + /// Throws a FileNotFoundException if no file with the given name exists. + /// + public static bool LoadReferencedAssembly(string asmPath, out QsCompilation compilation, Action onException = null) + { + if (asmPath == null) throw new ArgumentNullException(nameof(asmPath)); + if (!File.Exists(asmPath)) throw new FileNotFoundException($"The file '{asmPath}' does not exist."); + + using var stream = File.OpenRead(asmPath); + using var assemblyFile = new PEReader(stream); + try + { + return FromResource(assemblyFile, out compilation, onException); + } + catch (Exception ex) + { + onException?.Invoke(ex); + compilation = null; + return false; + } + } + // tools for loading the compiled syntax tree from the dll resource (later setup for shipping Q# libraries) /// /// Given a stream containing the binary representation of compiled Q# code, returns the corresponding Q# compilation. /// Returns true if the compilation could be deserialized without throwing an exception, and false otherwise. + /// If onDeserializationException is specified, invokes the given action on any exception thrown during deserialization. /// Throws an ArgumentNullException if the given stream is null, but ignores exceptions thrown during deserialization. /// - public static bool LoadSyntaxTree(Stream stream, out QsCompilation compilation) + public static bool LoadSyntaxTree(Stream stream, out QsCompilation compilation, Action onDeserializationException = null) { if (stream == null) throw new ArgumentNullException(nameof(stream)); using var reader = new BsonDataReader(stream); (compilation, reader.ReadRootValueAsArray) = (null, false); - try { compilation = Json.Serializer.Deserialize(reader); } - catch { return false; } - return compilation != null && !compilation.Namespaces.IsDefault && !compilation.EntryPoints.IsDefault; + try + { + compilation = Json.Serializer.Deserialize(reader); + return compilation != null && !compilation.Namespaces.IsDefault && !compilation.EntryPoints.IsDefault; + } + catch (Exception ex) + { + onDeserializationException?.Invoke(ex); + return false; + } } /// @@ -85,10 +123,11 @@ private static ImmutableDictionary Resources(this Meta /// /// Given a reader for the byte stream of a dotnet dll, loads any Q# compilation included as a resource. /// Returns true as well as the loaded compilation if the given dll includes a suitable resource, and returns false otherwise. + /// If onDeserializationException is specified, invokes the given action on any exception thrown during deserialization. /// Throws an ArgumentNullException if any of the given readers is null. /// May throw an exception if the given binary file has been compiled with a different compiler version. /// - private static bool FromResource(PEReader assemblyFile, out QsCompilation compilation) + private static bool FromResource(PEReader assemblyFile, out QsCompilation compilation, Action onDeserializationException = null) { if (assemblyFile == null) throw new ArgumentNullException(nameof(assemblyFile)); var metadataReader = assemblyFile.GetMetadataReader(); @@ -112,7 +151,7 @@ private static bool FromResource(PEReader assemblyFile, out QsCompilation compil // the first four bytes of the resource denote how long the resource is, and are followed by the actual resource data var resourceLength = BitConverter.ToInt32(image.GetContent(absResourceOffset, sizeof(Int32)).ToArray(), 0); var resourceData = image.GetContent(absResourceOffset + sizeof(Int32), resourceLength).ToArray(); - return LoadSyntaxTree(new MemoryStream(resourceData), out compilation); + return LoadSyntaxTree(new MemoryStream(resourceData), out compilation, onDeserializationException); } diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index 339c48ed51..f73fed31d1 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -470,7 +470,7 @@ public Task ReloadProjectReferenceAsync(IDictionary projectOutputPaths public Task ReloadSourceFileAsync(Uri sourceFile, Func openInEditor = null) { if (sourceFile == null) throw new ArgumentNullException(nameof(sourceFile)); - openInEditor = openInEditor ?? (_ => null); + openInEditor ??= (_ => null); return this.Processing.QueueForExecutionAsync(() => { @@ -589,7 +589,7 @@ private Func, Uri, IEnumerable> Migrat if (openInEditor == null) throw new ArgumentNullException(nameof(openInEditor)); return (filesAddedToProject, projFile) => { - filesAddedToProject = filesAddedToProject ?? ImmutableHashSet.Empty; + filesAddedToProject ??= ImmutableHashSet.Empty; var openFiles = filesAddedToProject.Select(openInEditor).Where(m => m != null).ToImmutableArray(); var removals = openFiles.Select(file => { @@ -618,7 +618,7 @@ private Action, Task> MigrateToDefaultManager(Func { if (removal.IsCanceled) return; - filesRemovedFromProject = filesRemovedFromProject ?? ImmutableHashSet.Empty; + filesRemovedFromProject ??= ImmutableHashSet.Empty; Task.WaitAll(removal); // we *need* to wait here in order to make sure that change notifications are processed in order!! var openFiles = filesRemovedFromProject.Select(openInEditor).Where(m => m != null).ToImmutableHashSet(); foreach (var file in openFiles) @@ -642,7 +642,7 @@ public Task LoadProjectsAsync(IEnumerable projectFiles, ProjectInformation. { if (projectFiles == null || projectFiles.Contains(null)) throw new ArgumentNullException(nameof(projectFiles)); if (projectLoader == null) throw new ArgumentNullException(nameof(projectLoader)); - openInEditor = openInEditor ?? (_ => null); + openInEditor ??= (_ => null); return this.Load.QueueForExecutionAsync(() => { @@ -672,7 +672,7 @@ public Task ProjectChangedOnDiskAsync(Uri projectFile, ProjectInformation.Loader { if (projectFile == null) throw new ArgumentNullException(nameof(projectFile)); if (projectLoader == null) throw new ArgumentNullException(nameof(projectLoader)); - openInEditor = openInEditor ?? (_ => null); + openInEditor ??= (_ => null); // TODO: allow to cancel this task via cancellation token? return this.Load.QueueForExecutionAsync(() => diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 5bd3c0e3fa..0584750f03 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -151,7 +151,13 @@ public struct Configuration /// However, the compiler may overwrite the assembly constants defined for the Q# compilation unit in the dictionary of the loaded step. /// The given dictionary in this configuration is left unchanged in any case. /// - public IReadOnlyDictionary AssemblyConstants; + public IReadOnlyDictionary AssemblyConstants; + /// + /// Path to the assembly that contains a syntax tree with target specific implementations for certain functions and operations. + /// The functions and operations defined in that assembly replace the ones declarated within the compilation unit. + /// If no path is specified here or the specified path is null then this compilation step is omitted. + /// + public string TargetPackageAssembly; /// /// Indicates whether a serialization of the syntax tree needs to be generated. @@ -190,6 +196,7 @@ private class ExecutionStatus internal Status ReferenceLoading = Status.NotRun; internal Status PluginLoading = Status.NotRun; internal Status Validation = Status.NotRun; + internal Status TargetSpecificReplacements = Status.NotRun; internal Status FunctorSupport = Status.NotRun; internal Status PreEvaluation = Status.NotRun; internal Status TreeTrimming = Status.NotRun; @@ -212,6 +219,7 @@ internal bool Success(Configuration options, bool isExe) => this.ReferenceLoading <= 0 && WasSuccessful(true, this.Validation) && WasSuccessful(true, this.PluginLoading) && + WasSuccessful(!String.IsNullOrWhiteSpace(options.TargetPackageAssembly), this.TargetSpecificReplacements) && WasSuccessful(options.GenerateFunctorSupport, this.FunctorSupport) && WasSuccessful(options.AttemptFullPreEvaluation, this.PreEvaluation) && WasSuccessful(!options.SkipSyntaxTreeTrimming, this.TreeTrimming) && @@ -247,6 +255,12 @@ internal bool Success(Configuration options, bool isExe) => /// public Status Validation => this.CompilationStatus.Validation; /// + /// Indicates whether target specific implementations for functions and operations + /// have been used to replace the ones declarated within the compilation unit. + /// This step is only executed if the specified configuration contains the path to the target package. + /// + public Status TargetSpecificReplacements => this.CompilationStatus.TargetSpecificReplacements; + /// /// Indicates whether all specializations were generated successfully. /// This rewrite step is only executed if the corresponding configuration is specified. /// @@ -420,6 +434,34 @@ QsCompilation ExecuteAsAtomicTransformation(RewriteSteps.LoadedStep rewriteStep, return status == Status.Succeeded ? transformed : this.CompilationOutput; } + if (!String.IsNullOrWhiteSpace(this.Config.TargetPackageAssembly)) + { + try + { + var targetDll = Path.GetFullPath(this.Config.TargetPackageAssembly); + var loaded = AssemblyLoader.LoadReferencedAssembly( + targetDll, + out var targetIntrinsics, + ex => this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ex)); + + if (loaded) + { + var rewriteStep = new RewriteSteps.LoadedStep(new IntrinsicResolution(targetIntrinsics), typeof(IRewriteStep), thisDllUri); + this.CompilationOutput = ExecuteAsAtomicTransformation(rewriteStep, ref this.CompilationStatus.TargetSpecificReplacements); + } + else + { + this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ErrorCode.FailedToLoadTargetPackageAssembly, new[] { targetDll }); + targetIntrinsics = null; + } + } + catch (Exception ex) + { + this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ErrorCode.InvalidTargetPackageAssemblyPath, new[] { this.Config.TargetPackageAssembly }); + this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ex); + } + } + if (this.Config.ConvertClassicalControl) { var rewriteStep = new RewriteSteps.LoadedStep(new ClassicallyControlled(), typeof(IRewriteStep), thisDllUri); diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index e960a53ee0..a09653b630 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -267,7 +267,11 @@ type ErrorCode = | UnknownCompilerPlugin = 7015 | CouldNotLoadCompilerPlugin = 7016 | CouldNotInstantiateRewriteStep = 7017 - | UnexpectedCompilerException = 7018 + | CouldNotFineTargetPackage = 7018 + | CouldNotFindTargetPackageAssembly = 7019 + | InvalidTargetPackageAssemblyPath = 7020 + | FailedToLoadTargetPackageAssembly = 7021 + | UnexpectedCompilerException = 7022 | FunctorGenerationFailed = 7101 | TreeTrimmingFailed = 7102 @@ -619,7 +623,11 @@ type DiagnosticItem = | ErrorCode.SourceFilesMissing -> "No source files have been specified." | ErrorCode.UnknownCompilerPlugin -> "Could not find the .NET Core library \"{0}\" specifying transformations to perform as part of the compilation process." | ErrorCode.CouldNotLoadCompilerPlugin -> "Unable to load the file \"{0}\" specifying transformations to perform as part of the compilation process. The file needs to be a suitable .NET Core library." - | ErrorCode.CouldNotInstantiateRewriteStep -> "Could not instantiate the type {0} in \"{1}\" specifying a rewrite step. The type may not have a parameterless constructor. " + | ErrorCode.CouldNotInstantiateRewriteStep -> "Could not instantiate the type {0} in \"{1}\" specifying a rewrite step. The type may not have a parameterless constructor." + | ErrorCode.CouldNotFineTargetPackage -> "Could not find the directory \"{0}\" containing target specific information." + | ErrorCode.CouldNotFindTargetPackageAssembly -> "Could not find the assembly specifying target specific implementations within the target package \"{0}\"." + | ErrorCode.InvalidTargetPackageAssemblyPath -> "Could not find the file \"{0}\" that specifies target specific implementations." + | ErrorCode.FailedToLoadTargetPackageAssembly -> "Unable to load target specific implementations from \"{0}\"." | ErrorCode.UnexpectedCompilerException -> "The compiler threw an exception." | ErrorCode.FunctorGenerationFailed -> "Auto-generation of functor specialization(s) failed." From 390908cffcb79d242be04b2c4ea0d72d107dc684 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 3 Mar 2020 13:16:50 -0800 Subject: [PATCH 062/135] That should properly merge options --- .../CommandLineTool/Commands/Build.cs | 25 ++++++++++++++++--- src/QsCompiler/CommandLineTool/Options.cs | 17 ++++++++++--- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index b1f7d0bc9b..c2dd49c0c2 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -35,7 +35,7 @@ public static IEnumerable UsageExamples } [Option("response-files", Required = true, SetName = RESPONSE_FILES, - HelpText = "Response file(s) providing the command arguments. Required only if no other arguments are specified. This option replaces all other arguments.")] + HelpText = "Response file(s) providing command arguments. Required only if no other arguments are specified. Non-default values for options specified via command line take precedence.")] public IEnumerable ResponseFiles { get; set; } [Option('o', "output", Required = false, SetName = CODE_MODE, @@ -113,11 +113,28 @@ public static int Run(BuildOptions options, ConsoleLogger logger) if (options == null) throw new ArgumentNullException(nameof(options)); if (logger == null) throw new ArgumentNullException(nameof(logger)); - if (options.ResponseFiles != null && options.ResponseFiles.Any()) + while (options.ResponseFiles != null && options.ResponseFiles.Any()) { var fromResponseFiles = FromResponseFiles(options.ResponseFiles); - options = fromResponseFiles; // FIXME: PROPERLY MERGE OPTIONS INSTEAD - if (options == null) return ReturnCode.INVALID_ARGUMENTS; + if (fromResponseFiles == null) return ReturnCode.INVALID_ARGUMENTS; + + options.CodeSnippet ??= fromResponseFiles.CodeSnippet; + options.DocFolder ??= fromResponseFiles.DocFolder; + options.EmitDll = options.EmitDll || fromResponseFiles.EmitDll; + options.Input = (options.Input ?? new string[0]).Concat(fromResponseFiles.Input ?? new string[0]); + options.NoWarn = (options.NoWarn ?? new int[0]).Concat(fromResponseFiles.NoWarn ?? new int[0]); + options.OutputFolder ??= fromResponseFiles.OutputFolder; + options.OutputFormat = options.OutputFormat != DefaultOptions.OutputFormat ? options.OutputFormat : fromResponseFiles.OutputFormat; + options.PackageLoadFallbackFolders = (options.PackageLoadFallbackFolders ?? new string[0]).Concat(fromResponseFiles.PackageLoadFallbackFolders ?? new string[0]); + options.PerfFolder ??= fromResponseFiles.PerfFolder; + options.Plugins = (options.Plugins ?? new string[0]).Concat(fromResponseFiles.Plugins ?? new string[0]); + options.ProjectName ??= fromResponseFiles.ProjectName; + options.References = (options.References ?? new string[0]).Concat(options.References ?? new string[0]); + options.ResponseFiles = fromResponseFiles.ResponseFiles; + options.TargetPackage ??= fromResponseFiles.TargetPackage; + options.TrimLevel = options.TrimLevel != DefaultOptions.TrimLevel ? options.TrimLevel : fromResponseFiles.TrimLevel; + options.Verbosity = options.Verbosity != DefaultOptions.Verbosity ? options.Verbosity : fromResponseFiles.Verbosity; + options.WithinFunction = options.WithinFunction || fromResponseFiles.WithinFunction; } var usesPlugins = options.Plugins != null && options.Plugins.Any(); diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs index 55a6a9124c..82b4ed88e6 100644 --- a/src/QsCompiler/CommandLineTool/Options.cs +++ b/src/QsCompiler/CommandLineTool/Options.cs @@ -17,9 +17,20 @@ namespace Microsoft.Quantum.QsCompiler.CommandLineCompiler { + /// + /// Default values for command line options if nothing is specified. + /// + internal static class DefaultOptions + { + public const string Verbosity = "normal"; + public const Options.LogFormat OutputFormat = Options.LogFormat.Default; + public const int TrimLevel = 1; + } + + public class CompilationOptions : Options { - [Option("trim", Required = false, Default = 1, + [Option("trim", Required = false, Default = DefaultOptions.TrimLevel, HelpText = "[Experimental feature] Integer indicating how much to simplify the syntax tree by eliminating selective abstractions.")] public int TrimLevel { get; set; } @@ -73,7 +84,7 @@ public enum LogFormat protected const string SNIPPET_MODE = "snippetMode"; protected const string RESPONSE_FILES = "responseFiles"; - [Option('v', "verbosity", Required = false, Default = "normal", + [Option('v', "verbosity", Required = false, Default = DefaultOptions.Verbosity, HelpText = "Specifies the verbosity of the logged output. Valid values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic].")] public string Verbosity { get; set; } @@ -97,7 +108,7 @@ public enum LogFormat HelpText = "Warnings with the given code(s) will be ignored.")] public IEnumerable NoWarn { get; set; } - [Option("format", Required = false, Default = LogFormat.Default, + [Option("format", Required = false, Default = DefaultOptions.OutputFormat, HelpText = "Specifies the output format of the command line compiler.")] public LogFormat OutputFormat { get; set; } From 190a8f27cd6900e928de03791dd983ad50bd89d0 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 9 Mar 2020 16:40:29 -0700 Subject: [PATCH 063/135] Remove the private access modifier --- .../EditorSupport/CodeActions.cs | 6 +- .../EditorSupport/CodeCompletion.cs | 6 +- src/QsCompiler/Core/SymbolTable.fs | 84 ++++++++-------- .../DataStructures/ReservedKeywords.fs | 2 - src/QsCompiler/DataStructures/SyntaxTokens.fs | 3 - .../SyntaxProcessor/SyntaxExtensions.fs | 1 - .../Libraries/Library1/AccessModifiers.qs | 8 -- .../Tests.Compiler/AccessModifierTests.fs | 37 ++----- .../Tests.Compiler/CompletionParsingTests.fs | 4 - .../TestCases/AccessModifiers.qs | 99 +++---------------- .../Tests.DocGenerator/DocWritingTests.cs | 2 +- .../CodeCompletion/FragmentParsing.fs | 2 +- .../TextProcessor/QsFragmentParsing.fs | 11 +-- src/QsCompiler/TextProcessor/QsKeywords.fs | 2 - .../Transformations/ClassicallyControlled.cs | 2 +- .../qsharp.tmLanguage.json.v.template | 4 +- 16 files changed, 77 insertions(+), 196 deletions(-) diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index ab1a550853..e1dd3a4c61 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -54,8 +54,7 @@ private static IEnumerable> NamespaceSuggestionsForIdAtPosit idName = variables != null && variables.Any() ? variables.Single().Symbol.AsDeclarationName(null) : null; var nsName = file.TryGetNamespaceAt(pos); return idName != null && compilation != null && nsName != null - ? compilation.GlobalSymbols.NamespacesContainingCallable(NonNullable.New(idName), - NonNullable.New(nsName)) + ? compilation.GlobalSymbols.NamespacesContainingCallable(NonNullable.New(idName)) : ImmutableArray>.Empty; } @@ -77,8 +76,7 @@ private static IEnumerable> NamespaceSuggestionsForTypeAtPos ? udt.Item.Symbol.AsDeclarationName(null) : null; var nsName = file.TryGetNamespaceAt(pos); return typeName != null && compilation != null && nsName != null - ? compilation.GlobalSymbols.NamespacesContainingType(NonNullable.New(typeName), - NonNullable.New(nsName)) + ? compilation.GlobalSymbols.NamespacesContainingType(NonNullable.New(typeName)) : ImmutableArray>.Empty; } diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs index 56b004ab13..5729fc8548 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs @@ -333,7 +333,7 @@ private static IEnumerable GetCallableCompletions( if (currentNamespace == null || !compilation.GlobalSymbols.ContainsResolutions) return Array.Empty(); return - compilation.GlobalSymbols.AccessibleCallables(NonNullable.New(currentNamespace)) + compilation.GlobalSymbols.AccessibleCallables() .Where(callable => IsAccessibleAsUnqualifiedName(callable.QualifiedName, currentNamespace, openNamespaces)) .Select(callable => new CompletionItem() @@ -372,7 +372,7 @@ private static IEnumerable GetTypeCompletions( if (currentNamespace == null || !compilation.GlobalSymbols.ContainsResolutions) return Array.Empty(); return - compilation.GlobalSymbols.AccessibleTypes(NonNullable.New(currentNamespace)) + compilation.GlobalSymbols.AccessibleTypes() .Where(type => IsAccessibleAsUnqualifiedName(type.QualifiedName, currentNamespace, openNamespaces)) .Select(type => new CompletionItem() { @@ -401,7 +401,7 @@ private static IEnumerable GetNamedItemCompletions( if (currentNamespace == null || !compilation.GlobalSymbols.ContainsResolutions) return Array.Empty(); - return compilation.GlobalSymbols.AccessibleTypes(NonNullable.New(currentNamespace)) + return compilation.GlobalSymbols.AccessibleTypes() .SelectMany(type => ExtractItems(type.TypeItems)) .Where(item => item.IsNamed) .Select(item => new CompletionItem() diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 31b63875fd..19a5744b8e 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -763,12 +763,11 @@ and NamespaceManager | false, _ -> ArgumentException "no namespace with the given name exists" |> raise /// Returns whether a declaration is accessible from the calling location, given whether the calling location is in - /// the same assembly and/or namespace as the declaration, and the declaration's modifiers. - let IsDeclarationAccessible sameAssembly sameNs modifiers = - match modifiers.Access with + /// the same assembly as the declaration, and the declaration's access modifier. + let IsDeclarationAccessible sameAssembly access = + match access with | DefaultAccess -> true | Internal -> sameAssembly - | Private -> sameAssembly && sameNs /// Given the qualified or unqualified name of a type used within the given parent namespace and source file, /// determines if such a type is accessible, and returns its full name and the source file or referenced assembly in @@ -794,8 +793,8 @@ and NamespaceManager PossibleResolutions (fun ns -> ns.TryFindType (tName, checkQualificationForDeprecation)) (parentNS, source) - let accessibleResolutions = allResolutions |> List.filter (fun (nsName, (_, _, sameAssembly, modifiers)) -> - IsDeclarationAccessible sameAssembly (parentNS = nsName) modifiers) + let accessibleResolutions = allResolutions |> List.filter (fun (_, (_, _, sameAssembly, modifiers)) -> + IsDeclarationAccessible sameAssembly modifiers.Access) match accessibleResolutions with | [] -> if List.isEmpty allResolutions @@ -817,7 +816,7 @@ and NamespaceManager | Some ns -> ns.TryFindType (symName, checkQualificationForDeprecation) |> function | Value (declSource, deprecation, sameAssembly, modifiers) -> - if IsDeclarationAccessible sameAssembly (parentNS = ns.Name) modifiers + if IsDeclarationAccessible sameAssembly modifiers.Access then buildAndReturn (ns.Name, declSource, deprecation, modifiers, [||]) else None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InaccessibleTypeInNamespace, [symName.Value; qualifier.Value]) |] | _ -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeInNamespace, [symName.Value; qualifier.Value]) |] @@ -843,7 +842,8 @@ and NamespaceManager /// Throws a NotSupportedException if the QsType to resolve contains a MissingType. let resolveType (parent : QsQualifiedName, tpNames, source) qsType checkUdt = let processUDT = TryResolveTypeName (parent.Namespace, source) >> function - | Some (udt, _, modifiers), errs -> UserDefinedType udt, Array.append errs (checkUdt (udt, modifiers)) + | Some (udt, _, modifiers), errs -> + UserDefinedType udt, Array.append errs (checkUdt (udt, modifiers.Access)) | None, errs -> InvalidType, errs let processTP (symName, symRange) = if tpNames |> Seq.contains symName @@ -858,18 +858,12 @@ and NamespaceManager /// accessibility of a referenced type is less than the accessibility of the parent, returns a diagnostic using the /// given error code. Otherwise, returns an empty array. let checkUdtAccessibility code - (parent : NonNullable, parentModifiers) - (udt : UserDefinedType, udtModifiers) = - match (parentModifiers.Access, udtModifiers.Access) with - | (DefaultAccess, DefaultAccess) - | (Internal, DefaultAccess) - | (Internal, Internal) - | (Private, _) -> [||] // OK - | _ -> - // Error - [| QsCompilerDiagnostic.Error - (code, [udt.Name.Value; parent.Value]) - (udt.Range.ValueOr QsCompilerDiagnostic.DefaultRange) |] + (parent : NonNullable, parentAccess) + (udt : UserDefinedType, udtAccess) = + if parentAccess = DefaultAccess && udtAccess = Internal + then [| QsCompilerDiagnostic.Error (code, [udt.Name.Value; parent.Value]) + (udt.Range.ValueOr QsCompilerDiagnostic.DefaultRange) |] + else [||] /// Given the name of the namespace as well as the source file in which the attribute occurs, resolves the given @@ -1018,7 +1012,7 @@ and NamespaceManager resolveType (fullName, ImmutableArray<_>.Empty, source) qsType - (checkUdtAccessibility ErrorCode.TypeLessAccessibleThanParentType (fullName.Name, modifiers)) + (checkUdtAccessibility ErrorCode.TypeLessAccessibleThanParentType (fullName.Name, modifiers.Access)) SymbolResolution.ResolveTypeDeclaration resolveType typeTuple /// Given the namespace and the name of the callable that the given signature belongs to, as well as its kind and @@ -1038,14 +1032,14 @@ and NamespaceManager /// /// May throw an exception if the given parent and/or source file is inconsistent with the defined callables. Throws /// an ArgumentException if the given list of characteristics is empty. - member private this.ResolveCallableSignature (parentKind, parentName, source, modifiers) + member private this.ResolveCallableSignature (parentKind, parentName, source, access) (signature, specBundleCharacteristics) = let resolveType tpNames qsType = let res, errs = resolveType (parentName, tpNames, source) qsType - (checkUdtAccessibility ErrorCode.TypeLessAccessibleThanParentCallable (parentName.Name, modifiers)) + (checkUdtAccessibility ErrorCode.TypeLessAccessibleThanParentCallable (parentName.Name, access)) if parentKind <> TypeConstructor then res, errs else res.WithoutRangeInfo, errs // strip positional info for auto-generated type constructors SymbolResolution.ResolveCallableSignature (resolveType, specBundleCharacteristics) signature @@ -1128,7 +1122,7 @@ and NamespaceManager // and finally we resolve the overall signature (whose characteristics are the intersection of the one of all bundles) let characteristics = props.Values |> Seq.map (fun bundle -> bundle.BundleInfo) |> Seq.toList - let resolved, msgs = (signature.Defined, characteristics) |> this.ResolveCallableSignature (kind, parent, source, signature.Modifiers) // no positional info for type constructors + let resolved, msgs = (signature.Defined, characteristics) |> this.ResolveCallableSignature (kind, parent, source, signature.Modifiers.Access) // no positional info for type constructors ns.SetCallableResolution source (parent.Name, resolved |> Value, callableAttributes) errs <- (attrErrs |> Array.map (fun m -> source, m)) :: (msgs |> Array.map (fun m -> source, (signature.Position, m))) :: errs @@ -1268,15 +1262,15 @@ and NamespaceManager finally syncRoot.ExitReadLock() /// Returns the declaration headers for all callables (either defined in source files or imported from referenced - /// assemblies) that are accessible from the given namespace. + /// assemblies) that are accessible from source files in the compilation unit. /// /// Throws an InvalidOperationException if the symbols are not currently resolved. - member this.AccessibleCallables nsName = + member this.AccessibleCallables () = Seq.append (Seq.map (fun callable -> callable, true) (this.DefinedCallables())) (Seq.map (fun callable -> callable, false) (this.ImportedCallables())) |> Seq.filter (fun (callable, sameAssembly) -> - IsDeclarationAccessible sameAssembly (nsName = callable.QualifiedName.Namespace) callable.Modifiers) + IsDeclarationAccessible sameAssembly callable.Modifiers.Access) |> Seq.map fst /// Returns the source file and TypeDeclarationHeader of all types imported from referenced assemblies, regardless @@ -1314,15 +1308,15 @@ and NamespaceManager finally syncRoot.ExitReadLock() /// Returns the declaration headers for all types (either defined in source files or imported from referenced - /// assemblies) that are accessible from the given namespace. + /// assemblies) that are accessible from source files in the compilation unit. /// /// Throws an InvalidOperationException if the symbols are not currently resolved. - member this.AccessibleTypes nsName = + member this.AccessibleTypes () = Seq.append (Seq.map (fun qsType -> qsType, true) (this.DefinedTypes())) (Seq.map (fun qsType -> qsType, false) (this.ImportedTypes())) |> Seq.filter (fun (qsType, sameAssembly) -> - IsDeclarationAccessible sameAssembly (nsName = qsType.QualifiedName.Namespace) qsType.Modifiers) + IsDeclarationAccessible sameAssembly qsType.Modifiers.Access) |> Seq.map fst /// removes the given source file and all its content from all namespaces @@ -1408,7 +1402,7 @@ and NamespaceManager /// namespace does not exist. member private this.TryGetCallableHeader (callableName : QsQualifiedName, declSource) (nsName, source) = let BuildHeader fullName (source, kind, declaration : Resolution<_,_>) = - let fallback () = (declaration.Defined, [CallableInformation.Invalid]) |> this.ResolveCallableSignature (kind, callableName, source, declaration.Modifiers) |> fst + let fallback () = (declaration.Defined, [CallableInformation.Invalid]) |> this.ResolveCallableSignature (kind, callableName, source, declaration.Modifiers.Access) |> fst let resolvedSignature, argTuple = declaration.Resolved.ValueOrApply fallback { Kind = kind @@ -1427,19 +1421,19 @@ and NamespaceManager | None -> NotFound | Some ns -> ns.CallablesInReferencedAssemblies.TryGetValue callableName.Name |> function | true, cDecl -> - if IsDeclarationAccessible false (nsName = cDecl.QualifiedName.Namespace) cDecl.Modifiers + if IsDeclarationAccessible false cDecl.Modifiers.Access then Found cDecl else Inaccessible | false, _ -> declSource |> function | Some source -> let kind, decl = ns.CallableInSource source callableName.Name // ok only because/if we have covered that the callable is not in a reference! - if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers + if IsDeclarationAccessible true decl.Modifiers.Access then BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Found else Inaccessible | None -> ns.CallablesDefinedInAllSources().TryGetValue callableName.Name |> function | true, (source, (kind, decl)) -> - if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers + if IsDeclarationAccessible true decl.Modifiers.Access then BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Found else Inaccessible | false, _ -> NotFound @@ -1465,7 +1459,7 @@ and NamespaceManager let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.TryFindCallable cName) let accessibleResolutions = allResolutions |> List.filter (fun (declaredNS, (_, _, sameAssembly, modifiers)) -> - IsDeclarationAccessible sameAssembly (declaredNS = nsName) modifiers) + IsDeclarationAccessible sameAssembly modifiers.Access) match accessibleResolutions with | [] -> if not <| List.isEmpty allResolutions then Inaccessible else NotFound | [(declNS, (declSource, _, _, _))] -> @@ -1507,19 +1501,19 @@ and NamespaceManager | None -> NotFound | Some ns -> ns.TypesInReferencedAssemblies.TryGetValue typeName.Name |> function | true, tDecl -> - if IsDeclarationAccessible false (nsName = tDecl.QualifiedName.Namespace) tDecl.Modifiers + if IsDeclarationAccessible false tDecl.Modifiers.Access then Found tDecl else Inaccessible | false, _ -> declSource |> function | Some source -> let decl = ns.TypeInSource source typeName.Name // ok only because/if we have covered that the type is not in a reference! - if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers + if IsDeclarationAccessible true decl.Modifiers.Access then BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Found else Inaccessible | None -> ns.TypesDefinedInAllSources().TryGetValue typeName.Name |> function | true, (source, decl) -> - if IsDeclarationAccessible true (nsName = ns.Name) decl.Modifiers + if IsDeclarationAccessible true decl.Modifiers.Access then BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Found else Inaccessible | false, _ -> NotFound @@ -1544,7 +1538,7 @@ and NamespaceManager let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.TryFindType tName) let accessibleResolutions = allResolutions |> List.filter (fun (declaredNS, (_, _, sameAssembly, modifiers)) -> - IsDeclarationAccessible sameAssembly (declaredNS = nsName) modifiers) + IsDeclarationAccessible sameAssembly modifiers.Access) match accessibleResolutions with | [] -> if not <| List.isEmpty allResolutions then Inaccessible else NotFound | [(declNS, (declSource, _, _, _))] -> @@ -1568,28 +1562,28 @@ and NamespaceManager finally syncRoot.ExitReadLock() /// Returns the names of all namespaces in which a callable is declared that has the given name and is accessible - /// from the given parent namespace. - member this.NamespacesContainingCallable cName nsName = + /// from source files in the compilation unit. + member this.NamespacesContainingCallable cName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! syncRoot.EnterReadLock() try let tryFindCallable (ns : Namespace) = ns.TryFindCallable cName |> QsNullable<_>.Bind (fun (_, _, sameAssembly, modifiers) -> - if IsDeclarationAccessible sameAssembly (nsName = ns.Name) modifiers + if IsDeclarationAccessible sameAssembly modifiers.Access then Value ns.Name else Null) (Namespaces.Values |> QsNullable<_>.Choose tryFindCallable).ToImmutableArray() finally syncRoot.ExitReadLock() /// Returns the names of all namespaces in which a type is declared that has the given name and is accessible from - /// the given parent namespace. - member this.NamespacesContainingType tName nsName = + /// source files in the compilation unit. + member this.NamespacesContainingType tName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! syncRoot.EnterReadLock() try let tryFindType (ns : Namespace) = ns.TryFindType tName |> QsNullable<_>.Bind (fun (_, _, sameAssembly, modifiers) -> - if IsDeclarationAccessible sameAssembly (nsName = ns.Name) modifiers + if IsDeclarationAccessible sameAssembly modifiers.Access then Value ns.Name else Null) (Namespaces.Values |> QsNullable<_>.Choose tryFindType).ToImmutableArray() diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index c1d3ad47e6..5afde67b98 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -156,8 +156,6 @@ module Declarations = /// keyword for a Q# declaration let Namespace = "namespace" - /// keyword for a Q# declaration modifier - let Private = "private" /// keyword for a Q# declaration modifier let Internal = "internal" diff --git a/src/QsCompiler/DataStructures/SyntaxTokens.fs b/src/QsCompiler/DataStructures/SyntaxTokens.fs index 4529b5e663..d1d85df8fd 100644 --- a/src/QsCompiler/DataStructures/SyntaxTokens.fs +++ b/src/QsCompiler/DataStructures/SyntaxTokens.fs @@ -201,9 +201,6 @@ type AccessModifier = /// Internal access means that a type or callable may only be used from within the compilation unit in which it is /// declared. | Internal - /// Private access means that a type or callable may only be used from within the compilation unit and namespace in - /// which it is declared. - | Private /// Used to represent Q# keywords that may be attached to a declaration to modify its visibility or behavior. [] diff --git a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs index a1f57b2f8d..3158e6fda2 100644 --- a/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs +++ b/src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs @@ -268,7 +268,6 @@ let private showModifiers kind modifiers = match modifiers.Access with | DefaultAccess -> kind | Internal -> "internal " + kind - | Private -> "private " + kind type private TName () = inherit SyntaxTreeToQsharp.TypeTransformation() diff --git a/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs index 8a38dafb1f..8b47abeeb7 100644 --- a/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs +++ b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs @@ -3,23 +3,15 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { // TODO: Uncomment these definitions when re-using names of inaccessible declarations in references is supported. - // private newtype PrivateType = Unit; - // internal newtype InternalType = Unit; - // private function PrivateFunction () : Unit {} - // internal function InternalFunction () : Unit {} } /// This namespace contains additional definitions of types and callables meant to be used by the /// Microsoft.Quantum.Testing.AccessModifiers namespace. namespace Microsoft.Quantum.Testing.AccessModifiers.C { - private newtype PrivateTypeC = Unit; - internal newtype InternalTypeC = Unit; - private function PrivateFunctionC () : Unit {} - internal function InternalFunctionC () : Unit {} } diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index f9a35640c2..a952958e57 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -19,7 +19,7 @@ type AccessModifierTests (output) = [File.ReadAllLines("ReferenceTargets.txt").ElementAt(1)], output) - member private this.Expect name (diagnostics : IEnumerable) = + member private this.Expect name (diagnostics : IEnumerable) = let ns = "Microsoft.Quantum.Testing.AccessModifiers" |> NonNullable<_>.New let name = name |> NonNullable<_>.New this.Verify (QsQualifiedName.New (ns, name), diagnostics) @@ -27,47 +27,30 @@ type AccessModifierTests (output) = [] member this.``Callables`` () = this.Expect "CallableUseOK" [] - this.Expect "CallableUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] - this.Expect "CallableQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallableInNamespace] [] member this.``Types`` () = this.Expect "TypeUseOK" [] - this.Expect "TypeUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleType] - this.Expect "TypeConstructorUnqualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] - this.Expect "TypeQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleTypeInNamespace] - this.Expect "TypeConstructorQualifiedUsePrivateInaccessible" [Error ErrorCode.InaccessibleCallableInNamespace] [] member this.``Callable signatures`` () = - this.Expect "PublicCallableLeaksPrivateTypeIn1" [Error ErrorCode.TypeLessAccessibleThanParentCallable] - this.Expect "PublicCallableLeaksPrivateTypeIn2" [Error ErrorCode.TypeLessAccessibleThanParentCallable] - this.Expect "PublicCallableLeaksPrivateTypeOut1" [Error ErrorCode.TypeLessAccessibleThanParentCallable] - this.Expect "PublicCallableLeaksPrivateTypeOut2" [Error ErrorCode.TypeLessAccessibleThanParentCallable] - this.Expect "InternalCallableLeaksPrivateTypeIn" [Error ErrorCode.TypeLessAccessibleThanParentCallable] - this.Expect "InternalCallableLeaksPrivateTypeOut" [Error ErrorCode.TypeLessAccessibleThanParentCallable] - this.Expect "CallablePrivateTypeOK" [] - this.Expect "CallableLeaksInternalTypeIn" [Error ErrorCode.TypeLessAccessibleThanParentCallable] - this.Expect "CallableLeaksInternalTypeOut" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "CallableLeaksInternalTypeIn1" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "CallableLeaksInternalTypeIn2" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "CallableLeaksInternalTypeIn3" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "CallableLeaksInternalTypeOut1" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "CallableLeaksInternalTypeOut2" [Error ErrorCode.TypeLessAccessibleThanParentCallable] + this.Expect "CallableLeaksInternalTypeOut3" [Error ErrorCode.TypeLessAccessibleThanParentCallable] this.Expect "InternalCallableInternalTypeOK" [] - this.Expect "PrivateCallableInternalTypeOK" [] [] member this.``Underlying types`` () = - this.Expect "PublicTypeLeaksPrivateType1" [Error ErrorCode.TypeLessAccessibleThanParentType] - this.Expect "PublicTypeLeaksPrivateType2" [Error ErrorCode.TypeLessAccessibleThanParentType] - this.Expect "PublicTypeLeaksPrivateType3" [Error ErrorCode.TypeLessAccessibleThanParentType] - this.Expect "InternalTypeLeaksPrivateType" [Error ErrorCode.TypeLessAccessibleThanParentType] - this.Expect "PrivateTypePrivateTypeOK" [] - this.Expect "PublicTypeLeaksInternalType" [Error ErrorCode.TypeLessAccessibleThanParentType] + this.Expect "PublicTypeLeaksInternalType1" [Error ErrorCode.TypeLessAccessibleThanParentType] + this.Expect "PublicTypeLeaksInternalType2" [Error ErrorCode.TypeLessAccessibleThanParentType] + this.Expect "PublicTypeLeaksInternalType3" [Error ErrorCode.TypeLessAccessibleThanParentType] this.Expect "InternalTypeInternalTypeOK" [] - this.Expect "PrivateTypeInternalTypeOK" [] [] member this.``References`` () = - this.Expect "CallableReferencePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] this.Expect "CallableReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] - this.Expect "TypeReferencePrivateInaccessible" [Error ErrorCode.InaccessibleType] - this.Expect "TypeConstructorReferencePrivateInaccessible" [Error ErrorCode.InaccessibleCallable] this.Expect "TypeReferenceInternalInaccessible" [Error ErrorCode.InaccessibleType] this.Expect "TypeConstructorReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] diff --git a/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs b/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs index dadb2e21c5..d8c181fc6e 100644 --- a/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs +++ b/src/QsCompiler/Tests.Compiler/CompletionParsingTests.fs @@ -112,7 +112,6 @@ let private testElifElse scope previous = let private testWithModifiers tests = List.concat [ tests - List.map (fun (input, result) -> ("private " + input, result)) tests List.map (fun (input, result) -> ("internal " + input, result)) tests ] |> List.iter (matches NamespaceTopLevel Null) @@ -135,7 +134,6 @@ let ``Namespace top-level parser tests`` () = Keyword "function" Keyword "operation" Keyword "newtype" - Keyword "private" Keyword "internal" Keyword "open" ] @@ -153,8 +151,6 @@ let ``Namespace top-level parser tests`` () = ("newtype", keywords) ("open", keywords) ("pri", keywords) - ("private", keywords) - ("private ", [Keyword "function"; Keyword "operation"; Keyword "newtype"]) ("int", keywords) ("internal", keywords) ("internal ", [Keyword "function"; Keyword "operation"; Keyword "newtype"]) diff --git a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs index 05d677bff3..fda930c5ca 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs @@ -6,39 +6,24 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { open Microsoft.Quantum.Testing.AccessModifiers.A; open Microsoft.Quantum.Testing.AccessModifiers.B as B; open Microsoft.Quantum.Testing.AccessModifiers.C; - - // Redefine inaccessible references (see TestTargets\Libraries\Library1\AccessModifiers.qs) - private newtype PrivateType = Unit; + // Redefine inaccessible references (see TestTargets\Libraries\Library1\AccessModifiers.qs) internal newtype InternalType = Unit; - private function PrivateFunction () : Unit {} - internal function InternalFunction () : Unit {} // Callables function CallableUseOK () : Unit { - PrivateFunction(); InternalFunction(); InternalFunctionA(); B.InternalFunctionB(); } - function CallableUnqualifiedUsePrivateInaccessible () : Unit { - PrivateFunctionA(); - } - - function CallableQualifiedUsePrivateInaccessible () : Unit { - B.PrivateFunctionB(); - } - // Types function TypeUseOK () : Unit { - let pt = PrivateType(); - let pts = new PrivateType[1]; let it = InternalType(); let its = new InternalType[1]; let ita = InternalTypeA(); @@ -47,96 +32,46 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { let itbs = new B.InternalTypeB[1]; } - function TypeUnqualifiedUsePrivateInaccessible () : Unit { - let ptas = new PrivateTypeA[1]; - } - - function TypeConstructorUnqualifiedUsePrivateInaccessible () : Unit { - let pta = PrivateTypeA(); - } - - function TypeQualifiedUsePrivateInaccessible () : Unit { - let ptbs = new B.PrivateTypeB[1]; - } - - function TypeConstructorQualifiedUsePrivateInaccessible () : Unit { - let ptb = B.PrivateTypeB(); - } - // Callable signatures - function PublicCallableLeaksPrivateTypeIn1 (x : PrivateType) : Unit {} - - function PublicCallableLeaksPrivateTypeIn2 (x : (Int, (PrivateType, Bool))) : Unit {} + function CallableLeaksInternalTypeIn1 (x : InternalType) : Unit {} - function PublicCallableLeaksPrivateTypeOut1 () : PrivateType { - return PrivateType(); - } - - function PublicCallableLeaksPrivateTypeOut2 () : (Double, ((Result, PrivateType), Bool)) { - return (1.0, ((Zero, PrivateType()), true)); - } + function CallableLeaksInternalTypeIn2 (x : (Int, InternalType)) : Unit {} - internal function InternalCallableLeaksPrivateTypeIn (x : PrivateType) : Unit {} + function CallableLeaksInternalTypeIn3 (x : (Int, (InternalType, Bool))) : Unit {} - internal function InternalCallableLeaksPrivateTypeOut () : PrivateType { - return PrivateType(); + function CallableLeaksInternalTypeOut1 () : InternalType { + return InternalType(); } - private function CallablePrivateTypeOK (x : PrivateType) : PrivateType { - return PrivateType(); + function CallableLeaksInternalTypeOut2 () : (Int, InternalType) { + return (0, InternalType()); } - function CallableLeaksInternalTypeIn (x : InternalType) : Unit {} - - function CallableLeaksInternalTypeOut () : InternalType { - return InternalType(); + function CallableLeaksInternalTypeOut3 () : (Int, (InternalType, Bool)) { + return (0, (InternalType(), false)); } internal function InternalCallableInternalTypeOK (x : InternalType) : InternalType { return InternalType(); } - private function PrivateCallableInternalTypeOK (x : PrivateType) : PrivateType { - return PrivateType(); - } - // Underlying types - newtype PublicTypeLeaksPrivateType1 = PrivateType; - - newtype PublicTypeLeaksPrivateType2 = (Int, PrivateType); + newtype PublicTypeLeaksInternalType1 = InternalType; - newtype PublicTypeLeaksPrivateType3 = (Int, (PrivateType, Bool)); + newtype PublicTypeLeaksInternalType2 = (Int, InternalType); - internal newtype InternalTypeLeaksPrivateType = PrivateType; - - private newtype PrivateTypePrivateTypeOK = PrivateType; - - newtype PublicTypeLeaksInternalType = InternalType; + newtype PublicTypeLeaksInternalType3 = (Int, (InternalType, Bool)); internal newtype InternalTypeInternalTypeOK = InternalType; - private newtype PrivateTypeInternalTypeOK = InternalType; - // References - function CallableReferencePrivateInaccessible () : Unit { - PrivateFunctionC(); - } - function CallableReferenceInternalInaccessible () : Unit { InternalFunctionC(); } - function TypeReferencePrivateInaccessible () : Unit { - let ptcs = new PrivateTypeC[1]; - } - - function TypeConstructorReferencePrivateInaccessible () : Unit { - let ptc = PrivateTypeC(); - } - function TypeReferenceInternalInaccessible () : Unit { let itcs = new InternalTypeC[1]; } @@ -149,23 +84,15 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { /// This namespace contains additional definitions of types and callables meant to be used by the /// Microsoft.Quantum.Testing.AccessModifiers namespace. namespace Microsoft.Quantum.Testing.AccessModifiers.A { - private function PrivateFunctionA () : Unit {} - internal function InternalFunctionA () : Unit {} - private newtype PrivateTypeA = Unit; - internal newtype InternalTypeA = Unit; } /// This namespace contains additional definitions of types and callables meant to be used by the /// Microsoft.Quantum.Testing.AccessModifiers namespace. namespace Microsoft.Quantum.Testing.AccessModifiers.B { - private function PrivateFunctionB () : Unit {} - internal function InternalFunctionB () : Unit {} - private newtype PrivateTypeB = Unit; - internal newtype InternalTypeB = Unit; } diff --git a/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs b/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs index ef870117b3..8cb04e860b 100644 --- a/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs +++ b/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs @@ -26,7 +26,7 @@ public class DocWritingTests public void ExcludeInaccessible() { var elements = - new[] { AccessModifier.DefaultAccess, AccessModifier.Internal, AccessModifier.Private } + new[] { AccessModifier.DefaultAccess, AccessModifier.Internal } .SelectMany(access => { var source = NonNullable.New("Tests.qs"); diff --git a/src/QsCompiler/TextProcessor/CodeCompletion/FragmentParsing.fs b/src/QsCompiler/TextProcessor/CodeCompletion/FragmentParsing.fs index 95c6f6ae53..051dd94c48 100644 --- a/src/QsCompiler/TextProcessor/CodeCompletion/FragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/CodeCompletion/FragmentParsing.fs @@ -24,7 +24,7 @@ open Microsoft.Quantum.QsCompiler.TextProcessing.CodeCompletion.ParsingPrimitive /// Parses a declaration modifier list. let private modifiers = - expectedKeyword qsPrivate <|>@ expectedKeyword qsInternal + expectedKeyword qsInternal /// Parses a callable signature. let private callableSignature = diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 78cb5fc73b..c4241ab761 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -115,7 +115,7 @@ let private allocationScope = /// Parses keywords that modify the visibility or behavior of a declaration. let private modifiers = - let accessModifier = (qsPrivate.parse >>% Private) <|> (qsInternal.parse >>% Internal) <|>% DefaultAccess + let accessModifier = (qsInternal.parse >>% Internal) <|>% DefaultAccess accessModifier |>> fun access -> {Access = access} /// Parses a Q# operation or function signature. @@ -465,11 +465,10 @@ let private fragments = (bodyDeclHeader, bodyDeclaration) (importDirectiveHeader, openDirective) - // These fragment headers do not have their own fragment kind. Instead, they are only parsed as part of another - // fragment kind. If one of these headers occurs by itself, without the other header it's a part of, an invalid - // fragment should be created. Since they're at the end of the list, we know that all of the other fragment - // kinds have been tried first. - (qsPrivate, buildInvalidFragment qsPrivate.parse) + // This fragment header does not have its own fragment kind. Instead, it is only parsed as part of another + // fragment kind. If this header occurs by itself, without the other header it's a part of, an invalid fragment + // should be created. Since it's at the end of the list, we know that all of the other fragment kinds have been + // tried first. (qsInternal, buildInvalidFragment qsInternal.parse) ] diff --git a/src/QsCompiler/TextProcessor/QsKeywords.fs b/src/QsCompiler/TextProcessor/QsKeywords.fs index 0b88fc6965..b873770526 100644 --- a/src/QsCompiler/TextProcessor/QsKeywords.fs +++ b/src/QsCompiler/TextProcessor/QsKeywords.fs @@ -200,8 +200,6 @@ let typeDeclHeader = addFragmentHeader Declarations.Type /// keyword for a Q# declaration (QsFragmentHeader) let namespaceDeclHeader = addFragmentHeader Declarations.Namespace -/// keyword for a Q# declaration modifier (QsFragmentHeader) -let qsPrivate = addFragmentHeader Declarations.Private /// keyword for a Q# declaration modifier (QsFragmentHeader) let qsInternal = addFragmentHeader Declarations.Internal diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index b82701d4a9..16313d6bce 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -644,7 +644,7 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature QsCallableKind.Operation, newName, ImmutableArray.Empty, - new Modifiers(AccessModifier.Private), + new Modifiers(AccessModifier.Internal), CurrentCallable.Callable.SourceFile, QsNullable.Null, signature, diff --git a/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template b/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template index f6090feb4a..581b1a7833 100644 --- a/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template +++ b/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template @@ -50,7 +50,7 @@ }, { "name": "keyword.other.qsharp", - "match": "\\b(new|not|and|or|w\/|private|internal)\\b" + "match": "\\b(internal|new|not|and|or|w\/)\\b" }, { "comment": "C# reserved words which can't be used in Q#, A-D", @@ -65,7 +65,7 @@ { "comment": "C# reserved words which can't be used in Q#, N-S", "name": "invalid.illegal.ns.qsharp", - "match": "\\b(null|object|operator|out|override|params|protected|public|readonly|ref|sbyte|sealed|short|sizeof|stackalloc)\\b" + "match": "\\b(null|object|operator|out|override|params|private|protected|public|readonly|ref|sbyte|sealed|short|sizeof|stackalloc)\\b" }, { "comment": "C# reserved words which can't be used in Q#, S-V", From ca8f58ee2deb2fc58607654e43bcbe4f47b698ff Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 3 Mar 2020 17:25:53 -0800 Subject: [PATCH 064/135] WIP --- .../Transformations/ClassicallyControlled.cs | 123 +++- .../Transformations/HoistContent.cs | 544 ++++++++++++++++++ 2 files changed, 666 insertions(+), 1 deletion(-) create mode 100644 src/QsCompiler/Transformations/HoistContent.cs diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index b8d47bc3d1..b39708f190 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -10,7 +10,7 @@ using Microsoft.Quantum.QsCompiler.SyntaxTree; using Microsoft.Quantum.QsCompiler.Transformations.Core; using Microsoft.Quantum.QsCompiler.Transformations.SearchAndReplace; - +using Transformations; namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlled { @@ -1283,4 +1283,125 @@ public override ResolvedTypeKind OnTypeParameter(QsTypeParameter tp) } } } + + internal static class HoistTransformation2 + { + private class MyLift : LiftContent + { + private MyLift() : base() + { + this.StatementKinds = new StatementKindTransformation(this); + } + + private new class StatementKindTransformation : LiftContent.StatementKindTransformation + { + public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + private bool IsScopeSingleCall(QsScope contents) + { + if (contents.Statements.Length != 1) return false; + + return contents.Statements[0].Statement is QsStatementKind.QsExpressionStatement expr + && expr.Item.Expression is ExpressionKind.CallLikeExpression call + && !TypedExpression.IsPartialApplication(expr.Item.Expression) + && call.Item1.Expression is ExpressionKind.Identifier; + } + + public override QsStatementKind OnConditionalStatement(QsConditionalStatement stm) + { + var contextValidScope = SharedState.IsValidScope; + var contextHoistParams = SharedState.CurrentHoistParams; + + var isHoistValid = true; + + var newConditionBlocks = new List>(); + var generatedOperations = new List(); + foreach (var conditionBlock in stm.ConditionalBlocks) + { + SharedState.IsValidScope = true; + SharedState.CurrentHoistParams = conditionBlock.Item2.Body.KnownSymbols.IsEmpty + ? ImmutableArray>>.Empty + : conditionBlock.Item2.Body.KnownSymbols.Variables; + + var (expr, block) = this.OnPositionedBlock(QsNullable.NewValue(conditionBlock.Item1), conditionBlock.Item2); + + // ToDo: Reduce the number of unnecessary generated operations by generalizing + // the condition logic for the conversion and using that condition here + //var (isExprCondition, _, _) = IsConditionedOnResultLiteralExpression(expr.Item); + + if (SharedState.IsValidScope) // if sub-scope is valid, hoist content + { + if (/*isExprCondition &&*/ !IsScopeSingleCall(block.Body)) + { + // Hoist the scope to its own operation + var (callable, call) = SharedState.HoistBody(block.Body); + block = new QsPositionedBlock( + new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), + block.Location, + block.Comments); + newConditionBlocks.Add(Tuple.Create(expr.Item, block)); + generatedOperations.Add(callable); + } + else if (block.Body.Statements.Length > 0) + { + newConditionBlocks.Add(Tuple.Create(expr.Item, block)); + } + } + else + { + isHoistValid = false; + break; + } + } + + var newDefault = QsNullable.Null; + if (isHoistValid && stm.Default.IsValue) + { + SharedState.IsValidScope = true; + SharedState.CurrentHoistParams = stm.Default.Item.Body.KnownSymbols.IsEmpty + ? ImmutableArray>>.Empty + : stm.Default.Item.Body.KnownSymbols.Variables; + + var (_, block) = this.OnPositionedBlock(QsNullable.Null, stm.Default.Item); + if (SharedState.IsValidScope) + { + if (!IsScopeSingleCall(block.Body)) // if sub-scope is valid, hoist content + { + // Hoist the scope to its own operation + var (callable, call) = SharedState.HoistBody(block.Body); + block = new QsPositionedBlock( + new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), + block.Location, + block.Comments); + newDefault = QsNullable.NewValue(block); + generatedOperations.Add(callable); + } + else if (block.Body.Statements.Length > 0) + { + newDefault = QsNullable.NewValue(block); + } + } + else + { + isHoistValid = false; + } + } + + if (isHoistValid) + { + SharedState.GeneratedOperations.AddRange(generatedOperations); + } + + SharedState.CurrentHoistParams = contextHoistParams; + SharedState.IsValidScope = contextValidScope; + + return isHoistValid + ? QsStatementKind.NewQsConditionalStatement( + new QsConditionalStatement(newConditionBlocks.ToImmutableArray(), newDefault)) + : QsStatementKind.NewQsConditionalStatement( + new QsConditionalStatement(stm.ConditionalBlocks, stm.Default)); + } + } + } + } } diff --git a/src/QsCompiler/Transformations/HoistContent.cs b/src/QsCompiler/Transformations/HoistContent.cs new file mode 100644 index 0000000000..67f301b28d --- /dev/null +++ b/src/QsCompiler/Transformations/HoistContent.cs @@ -0,0 +1,544 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.Quantum.QsCompiler.DataTypes; +using Microsoft.Quantum.QsCompiler.SyntaxTokens; +using Microsoft.Quantum.QsCompiler.SyntaxTree; +using Microsoft.Quantum.QsCompiler.Transformations.Core; +using Microsoft.Quantum.QsCompiler.Transformations.SearchAndReplace; + +namespace Transformations +{ + using ExpressionKind = QsExpressionKind; + using ResolvedTypeKind = QsTypeKind; + using TypeArgsResolution = ImmutableArray, ResolvedType>>; + + /// + /// Transformation handling the first pass task of hoisting of the contents of conditional statements. + /// If blocks are first validated to see if they can safely be hoisted into their own operation. + /// Validation requirements are that there are no return statements and that there are no set statements + /// on mutables declared outside the block. Setting mutables declared inside the block is valid. + /// If the block is valid, and there is more than one statement in the block, a new operation with the + /// block's contents is generated, having all the same type parameters as the calling context + /// and all known variables at the start of the block become parameters to the new operation. + /// The contents of the conditional block are then replaced with a call to the new operation with all + /// the type parameters and known variables being forwarded to the new operation as arguments. + /// + //internal static class HoistContent + //{ + // internal static QsCompilation Apply(QsCompilation compilation) => LiftContent.Apply(compilation); + + public class LiftContent : SyntaxTreeTransformation + { + public static QsCompilation Apply(QsCompilation compilation) + { + var filter = new LiftContent(); + + return new QsCompilation(compilation.Namespaces.Select(ns => filter.Namespaces.OnNamespace(ns)).ToImmutableArray(), compilation.EntryPoints); + } + + public class CallableDetails + { + public QsCallable Callable; + public QsSpecialization Adjoint; + public QsSpecialization Controlled; + public QsSpecialization ControlledAdjoint; + public QsNullable> TypeArguments; + + public CallableDetails(QsCallable callable) + { + Callable = callable; + Adjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsAdjoint); + Controlled = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlled); + ControlledAdjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlledAdjoint); + TypeArguments = callable.Signature.TypeParameters.Any(param => param.IsValidName) + ? QsNullable>.NewValue(callable.Signature.TypeParameters + .Where(param => param.IsValidName) + .Select(param => + ResolvedType.New(ResolvedTypeKind.NewTypeParameter(new QsTypeParameter( + callable.FullName, + ((QsLocalSymbol.ValidName)param).Item, + QsNullable>.Null + )))) + .ToImmutableArray()) + : QsNullable>.Null; + } + } + + public class TransformationState + { + public bool IsValidScope = true; + public List GeneratedOperations = null; + public ImmutableArray>> CurrentHoistParams = + ImmutableArray>>.Empty; + public bool ContainsHoistParamRef = false; + + public CallableDetails CurrentCallable = null; + public bool InBody = false; + public bool InAdjoint = false; + public bool InControlled = false; + public bool InWithinBlock = false; + + private (ResolvedSignature, IEnumerable) MakeSpecializations( + QsQualifiedName callableName, ResolvedType argsType, SpecializationImplementation bodyImplementation) + { + QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature, SpecializationImplementation impl) => + new QsSpecialization( + kind, + callableName, + ImmutableArray.Empty, + CurrentCallable.Callable.SourceFile, + QsNullable.Null, + QsNullable>.Null, + signature, + impl, + ImmutableArray.Empty, + QsComments.Empty); + + var adj = CurrentCallable.Adjoint; + var ctl = CurrentCallable.Controlled; + var ctlAdj = CurrentCallable.ControlledAdjoint; + + bool addAdjoint = false; + bool addControlled = false; + + if (InWithinBlock) + { + addAdjoint = true; + addControlled = false; + } + else if (InBody) + { + if (adj != null && adj.Implementation is SpecializationImplementation.Generated adjGen) addAdjoint = adjGen.Item.IsInvert; + if (ctl != null && ctl.Implementation is SpecializationImplementation.Generated ctlGen) addControlled = ctlGen.Item.IsDistribute; + if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated ctlAdjGen) + { + addAdjoint = addAdjoint || ctlAdjGen.Item.IsInvert && ctl.Implementation.IsGenerated; + addControlled = addControlled || ctlAdjGen.Item.IsDistribute && adj.Implementation.IsGenerated; + } + } + else if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated gen) + { + addControlled = InAdjoint && gen.Item.IsDistribute; + addAdjoint = InControlled && gen.Item.IsInvert; + } + + var props = new List(); + if (addAdjoint) props.Add(OpProperty.Adjointable); + if (addControlled) props.Add(OpProperty.Controllable); + var newSig = new ResolvedSignature( + CurrentCallable.Callable.Signature.TypeParameters, + argsType, + ResolvedType.New(ResolvedTypeKind.UnitType), + new CallableInformation(ResolvedCharacteristics.FromProperties(props), InferredCallableInformation.NoInformation)); + + var controlledSig = new ResolvedSignature( + newSig.TypeParameters, + ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Create( + ResolvedType.New(ResolvedTypeKind.NewArrayType(ResolvedType.New(ResolvedTypeKind.Qubit))), + newSig.ArgumentType))), + newSig.ReturnType, + newSig.Information); + + var specializations = new List() { MakeSpec(QsSpecializationKind.QsBody, newSig, bodyImplementation) }; + + if (addAdjoint) + { + specializations.Add(MakeSpec( + QsSpecializationKind.QsAdjoint, + newSig, + SpecializationImplementation.NewGenerated(QsGeneratorDirective.Invert))); + } + + if (addControlled) + { + specializations.Add(MakeSpec( + QsSpecializationKind.QsControlled, + controlledSig, + SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); + } + + if (addAdjoint && addControlled) + { + specializations.Add(MakeSpec( + QsSpecializationKind.QsControlledAdjoint, + controlledSig, + SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); + } + + return (newSig, specializations); + } + + private (QsCallable, ResolvedType) GenerateOperation(QsScope contents) + { + var newName = UniqueVariableNames.PrependGuid(CurrentCallable.Callable.FullName); + + var knownVariables = contents.KnownSymbols.IsEmpty + ? ImmutableArray>>.Empty + : contents.KnownSymbols.Variables; + + var parameters = QsTuple>.NewQsTuple(knownVariables + .Select(var => QsTuple>.NewQsTupleItem(new LocalVariableDeclaration( + QsLocalSymbol.NewValidName(var.VariableName), + var.Type, + var.InferredInformation, + var.Position, + var.Range))) + .ToImmutableArray()); + + var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); + if (knownVariables.Length == 1) + { + paramTypes = knownVariables.First().Type; + } + else if (knownVariables.Length > 1) + { + paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables + .Select(var => var.Type) + .ToImmutableArray())); + } + + var (signature, specializations) = MakeSpecializations(newName, paramTypes, SpecializationImplementation.NewProvided(parameters, contents)); + + var controlCallable = new QsCallable( + QsCallableKind.Operation, + newName, + ImmutableArray.Empty, + CurrentCallable.Callable.SourceFile, + QsNullable.Null, + signature, + parameters, + specializations.ToImmutableArray(), + ImmutableArray.Empty, + QsComments.Empty); + + var updatedCallable = UpdateGeneratedOp.Apply(controlCallable, knownVariables, CurrentCallable.Callable.FullName, newName); + + return (updatedCallable, signature.ArgumentType); + } + + // ToDo: Doc comment + public (QsCallable, QsStatement) HoistBody(QsScope body) + { + var (targetOp, originalArgumentType) = GenerateOperation(body); + var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( + Tuple.Create( + originalArgumentType, + ResolvedType.New(ResolvedTypeKind.UnitType)), + targetOp.Signature.Information)); + + var targetTypeArgTypes = CurrentCallable.TypeArguments; + var targetOpId = new TypedExpression + ( + ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetOp.FullName), targetTypeArgTypes), + targetTypeArgTypes.IsNull + ? TypeArgsResolution.Empty + : targetTypeArgTypes.Item + .Select(type => Tuple.Create(targetOp.FullName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + .ToImmutableArray(), + targetOpType, + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + var knownSymbols = body.KnownSymbols.Variables; + + TypedExpression targetArgs = null; + if (knownSymbols.Any()) + { + targetArgs = Utils.CreateValueTupleExpression(knownSymbols.Select(var => Utils.CreateIdentifierExpression( + Identifier.NewLocalVariable(var.VariableName), + TypeArgsResolution.Empty, + var.Type)) + .ToArray()); + } + else + { + targetArgs = new TypedExpression + ( + ExpressionKind.UnitValue, + TypeArgsResolution.Empty, + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + } + + var call = new TypedExpression + ( + ExpressionKind.NewCallLikeExpression(targetOpId, targetArgs), + targetTypeArgTypes.IsNull + ? TypeArgsResolution.Empty + : targetTypeArgTypes.Item + .Select(type => Tuple.Create(CurrentCallable.Callable.FullName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + .ToImmutableArray(), + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, true), + QsNullable>.Null + ); + + return (targetOp, new QsStatement( + QsStatementKind.NewQsExpressionStatement(call), + LocalDeclarations.Empty, + QsNullable.Null, + QsComments.Empty)); + } + } + + public LiftContent() : base(new TransformationState()) + { + this.Namespaces = new NamespaceTransformation(this); + this.StatementKinds = new StatementKindTransformation(this); + this.Expressions = new ExpressionTransformation(this); + this.ExpressionKinds = new ExpressionKindTransformation(this); + this.Types = new TypeTransformation(this, TransformationOptions.Disabled); + } + + public class NamespaceTransformation : NamespaceTransformation + { + public NamespaceTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override QsCallable OnCallableDeclaration(QsCallable c) + { + SharedState.CurrentCallable = new CallableDetails(c); + return base.OnCallableDeclaration(c); + } + + public override QsSpecialization OnBodySpecialization(QsSpecialization spec) + { + SharedState.InBody = true; + var rtrn = base.OnBodySpecialization(spec); + SharedState.InBody = false; + return rtrn; + } + + public override QsSpecialization OnAdjointSpecialization(QsSpecialization spec) + { + SharedState.InAdjoint = true; + var rtrn = base.OnAdjointSpecialization(spec); + SharedState.InAdjoint = false; + return rtrn; + } + + public override QsSpecialization OnControlledSpecialization(QsSpecialization spec) + { + SharedState.InControlled = true; + var rtrn = base.OnControlledSpecialization(spec); + SharedState.InControlled = false; + return rtrn; + } + + public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being hoisted + + public override QsNamespace OnNamespace(QsNamespace ns) + { + // Generated operations list will be populated in the transform + SharedState.GeneratedOperations = new List(); + return base.OnNamespace(ns) + .WithElements(elems => elems.AddRange(SharedState.GeneratedOperations.Select(op => QsNamespaceElement.NewQsCallable(op)))); + } + } + + protected class StatementKindTransformation : StatementKindTransformation + { + public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + // ToDo: This logic should be externalized at some point to make the Hoisting more general + //private bool IsScopeSingleCall(QsScope contents) + //{ + // if (contents.Statements.Length != 1) return false; + // + // return contents.Statements[0].Statement is QsStatementKind.QsExpressionStatement expr + // && expr.Item.Expression is ExpressionKind.CallLikeExpression call + // && !TypedExpression.IsPartialApplication(expr.Item.Expression) + // && call.Item1.Expression is ExpressionKind.Identifier; + //} + + public override QsStatementKind OnConjugation(QsConjugation stm) + { + var superInWithinBlock = SharedState.InWithinBlock; + SharedState.InWithinBlock = true; + var (_, outer) = this.OnPositionedBlock(QsNullable.Null, stm.OuterTransformation); + SharedState.InWithinBlock = superInWithinBlock; + + var (_, inner) = this.OnPositionedBlock(QsNullable.Null, stm.InnerTransformation); + + return QsStatementKind.NewQsConjugation(new QsConjugation(outer, inner)); + } + + public override QsStatementKind OnReturnStatement(TypedExpression ex) + { + SharedState.IsValidScope = false; + return base.OnReturnStatement(ex); + } + + public override QsStatementKind OnValueUpdate(QsValueUpdate stm) + { + // If lhs contains an identifier found in the scope's known variables (variables from the super-scope), the scope is not valid + var lhs = this.Expressions.OnTypedExpression(stm.Lhs); + + if (SharedState.ContainsHoistParamRef) + { + SharedState.IsValidScope = false; + } + + var rhs = this.Expressions.OnTypedExpression(stm.Rhs); + return QsStatementKind.NewQsValueUpdate(new QsValueUpdate(lhs, rhs)); + } + + public override QsStatementKind OnStatementKind(QsStatementKind kind) + { + SharedState.ContainsHoistParamRef = false; // Every statement kind starts off false + return base.OnStatementKind(kind); + } + } + + public class ExpressionTransformation : ExpressionTransformation + { + public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override TypedExpression OnTypedExpression(TypedExpression ex) + { + var contextContainsHoistParamRef = SharedState.ContainsHoistParamRef; + SharedState.ContainsHoistParamRef = false; + var rtrn = base.OnTypedExpression(ex); + + // If the sub context contains a reference, then the super context contains a reference, + // otherwise return the super context to its original value + if (!SharedState.ContainsHoistParamRef) + { + SharedState.ContainsHoistParamRef = contextContainsHoistParamRef; + } + + return rtrn; + } + } + + public class ExpressionKindTransformation : ExpressionKindTransformation + { + public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) + { + if (sym is Identifier.LocalVariable local && + SharedState.CurrentHoistParams.Any(param => param.VariableName.Equals(local.Item))) + { + SharedState.ContainsHoistParamRef = true; + } + return base.OnIdentifier(sym, tArgs); + } + } + } + + /// + /// Transformation that updates the contents of newly generated operations by: + /// 1. Rerouting the origins of type parameter references to the new operation + /// 2. Changes the IsMutable info on variable that used to be mutable, but are now immutable params to the operation + /// + public class UpdateGeneratedOp : SyntaxTreeTransformation + { + public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + { + var filter = new UpdateGeneratedOp(parameters, oldName, newName); + + return filter.Namespaces.OnCallableDeclaration(qsCallable); + } + + public class TransformationState + { + public bool IsRecursiveIdentifier = false; + public readonly ImmutableArray>> Parameters; + public readonly QsQualifiedName OldName; + public readonly QsQualifiedName NewName; + + public TransformationState(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + { + Parameters = parameters; + OldName = oldName; + NewName = newName; + } + } + + private UpdateGeneratedOp(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + : base(new TransformationState(parameters, oldName, newName)) + { + this.Expressions = new ExpressionTransformation(this); + this.ExpressionKinds = new ExpressionKindTransformation(this); + this.Types = new TypeTransformation(this); + } + + private class ExpressionTransformation : ExpressionTransformation + { + public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override ImmutableDictionary>, ResolvedType> OnTypeParamResolutions(ImmutableDictionary>, ResolvedType> typeParams) + { + // Prevent keys from having their names updated + return typeParams.ToImmutableDictionary(kvp => kvp.Key, kvp => this.Types.OnType(kvp.Value)); + } + + public override TypedExpression OnTypedExpression(TypedExpression ex) + { + // Checks if expression is mutable identifier that is in parameter list + if (ex.InferredInformation.IsMutable && + ex.Expression is ExpressionKind.Identifier id && + id.Item1 is Identifier.LocalVariable variable && + SharedState.Parameters.Any(x => x.VariableName.Equals(variable))) + { + // Set the mutability to false + ex = new TypedExpression( + ex.Expression, + ex.TypeArguments, + ex.ResolvedType, + new InferredExpressionInformation(false, ex.InferredInformation.HasLocalQuantumDependency), + ex.Range); + } + + // Prevent IsRecursiveIdentifier from propagating beyond the typed expression it is referring to + var isRecursiveIdentifier = SharedState.IsRecursiveIdentifier; + var rtrn = base.OnTypedExpression(ex); + SharedState.IsRecursiveIdentifier = isRecursiveIdentifier; + return rtrn; + } + } + + private class ExpressionKindTransformation : ExpressionKindTransformation + { + public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) + { + var rtrn = base.OnIdentifier(sym, tArgs); + + // Check if this is a recursive identifier + // In this context, that is a call back to the original callable from the newly generated operation + if (sym is Identifier.GlobalCallable callable && SharedState.OldName.Equals(callable.Item)) + { + // Setting this flag will prevent the rerouting logic from processing the resolved type of the recursive identifier expression. + // This is necessary because we don't want any type parameters from the original callable from being rerouted to the new generated + // operation's type parameters in the definition of the identifier. + SharedState.IsRecursiveIdentifier = true; + } + return rtrn; + } + } + + private class TypeTransformation : TypeTransformation + { + public TypeTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override ResolvedTypeKind OnTypeParameter(QsTypeParameter tp) + { + // Reroute a type parameter's origin to the newly generated operation + if (!SharedState.IsRecursiveIdentifier && SharedState.OldName.Equals(tp.Origin)) + { + tp = new QsTypeParameter(SharedState.NewName, tp.TypeName, tp.Range); + } + + return base.OnTypeParameter(tp); + } + } + } + //} +} From 113716ab7985e64889ab8fca5dbca96485159a06 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 6 Mar 2020 16:15:42 -0800 Subject: [PATCH 065/135] Hoisting logic should be fully separated, need to rename somethings still. --- .../Transformations/ClassicallyControlled.cs | 635 +--------------- .../ClassicallyControlledUtils.cs | 19 +- .../Transformations/HoistContent.cs | 688 +++++++++--------- 3 files changed, 357 insertions(+), 985 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index b39708f190..c56dceec79 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -9,8 +9,7 @@ using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; using Microsoft.Quantum.QsCompiler.Transformations.Core; -using Microsoft.Quantum.QsCompiler.Transformations.SearchAndReplace; -using Transformations; +using Microsoft.Quantum.QsCompiler.Transformations.LyftContent; namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlled { @@ -230,7 +229,11 @@ private TypedExpression CreateApplyConditionallyExpression(TypedExpression condi var equality = Utils.CreateValueTupleExpression(equalityId, equalityArgs); var inequality = Utils.CreateValueTupleExpression(inequalityId, inequalityArgs); - var controlArgs = Utils.CreateValueTupleExpression(Utils.CreateValueArray(conditionExpr1), Utils.CreateValueArray(conditionExpr2), equality, inequality); + var controlArgs = Utils.CreateValueTupleExpression( + Utils.CreateValueArray(ResolvedType.New(ResolvedTypeKind.Result), conditionExpr1), + Utils.CreateValueArray(ResolvedType.New(ResolvedTypeKind.Result), conditionExpr2), + equality, + inequality); var targetArgsTypes = ImmutableArray.Create(equalityArgs.ResolvedType, inequalityArgs.ResolvedType); return CreateControlCall(controlOpInfo, props, controlArgs, targetArgsTypes); @@ -661,634 +664,18 @@ public override QsScope OnScope(QsScope scope) } } - - /// - /// Transformation handling the first pass task of hoisting of the contents of conditional statements. - /// If blocks are first validated to see if they can safely be hoisted into their own operation. - /// Validation requirements are that there are no return statements and that there are no set statements - /// on mutables declared outside the block. Setting mutables declared inside the block is valid. - /// If the block is valid, and there is more than one statement in the block, a new operation with the - /// block's contents is generated, having all the same type parameters as the calling context - /// and all known variables at the start of the block become parameters to the new operation. - /// The contents of the conditional block are then replaced with a call to the new operation with all - /// the type parameters and known variables being forwarded to the new operation as arguments. - /// - internal static class HoistTransformation // this class can be made public once its functionality is no longer tied to the classically-controlled transformation + internal static class HoistTransformation { - internal static QsCompilation Apply(QsCompilation compilation) => HoistContents.Apply(compilation); - - private class HoistContents : SyntaxTreeTransformation - { - public static QsCompilation Apply(QsCompilation compilation) - { - var filter = new HoistContents(); - - return new QsCompilation(compilation.Namespaces.Select(ns => filter.Namespaces.OnNamespace(ns)).ToImmutableArray(), compilation.EntryPoints); - } - - public class CallableDetails - { - public QsCallable Callable; - public QsSpecialization Adjoint; - public QsSpecialization Controlled; - public QsSpecialization ControlledAdjoint; - public QsNullable> TypeParamTypes; - - public CallableDetails(QsCallable callable) - { - Callable = callable; - Adjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsAdjoint); - Controlled = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlled); - ControlledAdjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlledAdjoint); - TypeParamTypes = callable.Signature.TypeParameters.Any(param => param.IsValidName) - ? QsNullable>.NewValue(callable.Signature.TypeParameters - .Where(param => param.IsValidName) - .Select(param => - ResolvedType.New(ResolvedTypeKind.NewTypeParameter(new QsTypeParameter( - callable.FullName, - ((QsLocalSymbol.ValidName)param).Item, - QsNullable>.Null - )))) - .ToImmutableArray()) - : QsNullable>.Null; - } - } - - public class TransformationState - { - public bool IsValidScope = true; - public List ControlOperations = null; - public ImmutableArray>> CurrentHoistParams = - ImmutableArray>>.Empty; - public bool ContainsHoistParamRef = false; - - public CallableDetails CurrentCallable = null; - public bool InBody = false; - public bool InAdjoint = false; - public bool InControlled = false; - public bool InWithinBlock = false; - - private (ResolvedSignature, IEnumerable) MakeSpecializations( - QsQualifiedName callableName, ResolvedType argsType, SpecializationImplementation bodyImplementation) - { - QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature, SpecializationImplementation impl) => - new QsSpecialization( - kind, - callableName, - ImmutableArray.Empty, - CurrentCallable.Callable.SourceFile, - QsNullable.Null, - QsNullable>.Null, - signature, - impl, - ImmutableArray.Empty, - QsComments.Empty); - - var adj = CurrentCallable.Adjoint; - var ctl = CurrentCallable.Controlled; - var ctlAdj = CurrentCallable.ControlledAdjoint; - - bool addAdjoint = false; - bool addControlled = false; - - if (InWithinBlock) - { - addAdjoint = true; - addControlled = false; - } - else if (InBody) - { - if (adj != null && adj.Implementation is SpecializationImplementation.Generated adjGen) addAdjoint = adjGen.Item.IsInvert; - if (ctl != null && ctl.Implementation is SpecializationImplementation.Generated ctlGen) addControlled = ctlGen.Item.IsDistribute; - if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated ctlAdjGen) - { - addAdjoint = addAdjoint || ctlAdjGen.Item.IsInvert && ctl.Implementation.IsGenerated; - addControlled = addControlled || ctlAdjGen.Item.IsDistribute && adj.Implementation.IsGenerated; - } - } - else if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated gen) - { - addControlled = InAdjoint && gen.Item.IsDistribute; - addAdjoint = InControlled && gen.Item.IsInvert; - } - - var props = new List(); - if (addAdjoint) props.Add(OpProperty.Adjointable); - if (addControlled) props.Add(OpProperty.Controllable); - var newSig = new ResolvedSignature( - CurrentCallable.Callable.Signature.TypeParameters, - argsType, - ResolvedType.New(ResolvedTypeKind.UnitType), - new CallableInformation(ResolvedCharacteristics.FromProperties(props), InferredCallableInformation.NoInformation)); - - var controlledSig = new ResolvedSignature( - newSig.TypeParameters, - ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Create( - ResolvedType.New(ResolvedTypeKind.NewArrayType(ResolvedType.New(ResolvedTypeKind.Qubit))), - newSig.ArgumentType))), - newSig.ReturnType, - newSig.Information); - - var specializations = new List() { MakeSpec(QsSpecializationKind.QsBody, newSig, bodyImplementation) }; - - if (addAdjoint) - { - specializations.Add(MakeSpec( - QsSpecializationKind.QsAdjoint, - newSig, - SpecializationImplementation.NewGenerated(QsGeneratorDirective.Invert))); - } - - if (addControlled) - { - specializations.Add(MakeSpec( - QsSpecializationKind.QsControlled, - controlledSig, - SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); - } - - if (addAdjoint && addControlled) - { - specializations.Add(MakeSpec( - QsSpecializationKind.QsControlledAdjoint, - controlledSig, - SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); - } - - return (newSig, specializations); - } - - public (QsCallable, ResolvedType) GenerateOperation(QsScope contents) - { - var newName = UniqueVariableNames.PrependGuid(CurrentCallable.Callable.FullName); - - var knownVariables = contents.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : contents.KnownSymbols.Variables; - - var parameters = QsTuple>.NewQsTuple(knownVariables - .Select(var => QsTuple>.NewQsTupleItem(new LocalVariableDeclaration( - QsLocalSymbol.NewValidName(var.VariableName), - var.Type, - var.InferredInformation, - var.Position, - var.Range))) - .ToImmutableArray()); - - var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); - if (knownVariables.Length == 1) - { - paramTypes = knownVariables.First().Type; - } - else if (knownVariables.Length > 1) - { - paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables - .Select(var => var.Type) - .ToImmutableArray())); - } - - var (signature, specializations) = MakeSpecializations(newName, paramTypes, SpecializationImplementation.NewProvided(parameters, contents)); - - var controlCallable = new QsCallable( - QsCallableKind.Operation, - newName, - ImmutableArray.Empty, - CurrentCallable.Callable.SourceFile, - QsNullable.Null, - signature, - parameters, - specializations.ToImmutableArray(), - ImmutableArray.Empty, - QsComments.Empty); - - var updatedCallable = UpdateGeneratedOp.Apply(controlCallable, knownVariables, CurrentCallable.Callable.FullName, newName); - - return (updatedCallable, signature.ArgumentType); - } - } - - private HoistContents() : base(new TransformationState()) - { - this.Namespaces = new NamespaceTransformation(this); - this.StatementKinds = new StatementKindTransformation(this); - this.Expressions = new ExpressionTransformation(this); - this.ExpressionKinds = new ExpressionKindTransformation(this); - this.Types = new TypeTransformation(this, TransformationOptions.Disabled); - } - - private class NamespaceTransformation : NamespaceTransformation - { - public NamespaceTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override QsCallable OnCallableDeclaration(QsCallable c) - { - SharedState.CurrentCallable = new CallableDetails(c); - return base.OnCallableDeclaration(c); - } - - public override QsSpecialization OnBodySpecialization(QsSpecialization spec) - { - SharedState.InBody = true; - var rtrn = base.OnBodySpecialization(spec); - SharedState.InBody = false; - return rtrn; - } - - public override QsSpecialization OnAdjointSpecialization(QsSpecialization spec) - { - SharedState.InAdjoint = true; - var rtrn = base.OnAdjointSpecialization(spec); - SharedState.InAdjoint = false; - return rtrn; - } - - public override QsSpecialization OnControlledSpecialization(QsSpecialization spec) - { - SharedState.InControlled = true; - var rtrn = base.OnControlledSpecialization(spec); - SharedState.InControlled = false; - return rtrn; - } - - public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being hoisted - - public override QsNamespace OnNamespace(QsNamespace ns) - { - // Control operations list will be populated in the transform - SharedState.ControlOperations = new List(); - return base.OnNamespace(ns) - .WithElements(elems => elems.AddRange(SharedState.ControlOperations.Select(op => QsNamespaceElement.NewQsCallable(op)))); - } - } - - private class StatementKindTransformation : StatementKindTransformation - { - public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - private (QsCallable, QsStatement) HoistBody(QsScope body) - { - var (targetOp, originalArgumentType) = SharedState.GenerateOperation(body); - var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( - Tuple.Create( - originalArgumentType, - ResolvedType.New(ResolvedTypeKind.UnitType)), - targetOp.Signature.Information)); - - var targetTypeArgTypes = SharedState.CurrentCallable.TypeParamTypes; - var targetOpId = new TypedExpression - ( - ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetOp.FullName), targetTypeArgTypes), - targetTypeArgTypes.IsNull - ? TypeArgsResolution.Empty - : targetTypeArgTypes.Item - .Select(type => Tuple.Create(targetOp.FullName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) - .ToImmutableArray(), - targetOpType, - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - - var knownSymbols = body.KnownSymbols.Variables; - - TypedExpression targetArgs = null; - if (knownSymbols.Any()) - { - targetArgs = Utils.CreateValueTupleExpression(knownSymbols.Select(var => Utils.CreateIdentifierExpression( - Identifier.NewLocalVariable(var.VariableName), - TypeArgsResolution.Empty, - var.Type)) - .ToArray()); - } - else - { - targetArgs = new TypedExpression - ( - ExpressionKind.UnitValue, - TypeArgsResolution.Empty, - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - } - - var call = new TypedExpression - ( - ExpressionKind.NewCallLikeExpression(targetOpId, targetArgs), - targetTypeArgTypes.IsNull - ? TypeArgsResolution.Empty - : targetTypeArgTypes.Item - .Select(type => Tuple.Create(SharedState.CurrentCallable.Callable.FullName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) - .ToImmutableArray(), - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, true), - QsNullable>.Null - ); - - return (targetOp, new QsStatement( - QsStatementKind.NewQsExpressionStatement(call), - LocalDeclarations.Empty, - QsNullable.Null, - QsComments.Empty)); - } - - // ToDo: This logic should be externalized at some point to make the Hoisting more general - private bool IsScopeSingleCall(QsScope contents) - { - if (contents.Statements.Length != 1) return false; - - return contents.Statements[0].Statement is QsStatementKind.QsExpressionStatement expr - && expr.Item.Expression is ExpressionKind.CallLikeExpression call - && !TypedExpression.IsPartialApplication(expr.Item.Expression) - && call.Item1.Expression is ExpressionKind.Identifier; - } - - public override QsStatementKind OnConjugation(QsConjugation stm) - { - var superInWithinBlock = SharedState.InWithinBlock; - SharedState.InWithinBlock = true; - var (_, outer) = this.OnPositionedBlock(QsNullable.Null, stm.OuterTransformation); - SharedState.InWithinBlock = superInWithinBlock; - - var (_, inner) = this.OnPositionedBlock(QsNullable.Null, stm.InnerTransformation); - - return QsStatementKind.NewQsConjugation(new QsConjugation(outer, inner)); - } - - public override QsStatementKind OnReturnStatement(TypedExpression ex) - { - SharedState.IsValidScope = false; - return base.OnReturnStatement(ex); - } - - public override QsStatementKind OnValueUpdate(QsValueUpdate stm) - { - // If lhs contains an identifier found in the scope's known variables (variables from the super-scope), the scope is not valid - var lhs = this.Expressions.OnTypedExpression(stm.Lhs); - - if (SharedState.ContainsHoistParamRef) - { - SharedState.IsValidScope = false; - } - - var rhs = this.Expressions.OnTypedExpression(stm.Rhs); - return QsStatementKind.NewQsValueUpdate(new QsValueUpdate(lhs, rhs)); - } - - public override QsStatementKind OnConditionalStatement(QsConditionalStatement stm) - { - var contextValidScope = SharedState.IsValidScope; - var contextHoistParams = SharedState.CurrentHoistParams; - - var isHoistValid = true; - - var newConditionBlocks = new List>(); - var generatedOperations = new List(); - foreach (var conditionBlock in stm.ConditionalBlocks) - { - SharedState.IsValidScope = true; - SharedState.CurrentHoistParams = conditionBlock.Item2.Body.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : conditionBlock.Item2.Body.KnownSymbols.Variables; - - var (expr, block) = this.OnPositionedBlock(QsNullable.NewValue(conditionBlock.Item1), conditionBlock.Item2); - - // ToDo: Reduce the number of unnecessary generated operations by generalizing - // the condition logic for the conversion and using that condition here - //var (isExprCondition, _, _) = IsConditionedOnResultLiteralExpression(expr.Item); - - if (SharedState.IsValidScope) // if sub-scope is valid, hoist content - { - if (/*isExprCondition &&*/ !IsScopeSingleCall(block.Body)) - { - // Hoist the scope to its own operation - var (callable, call) = HoistBody(block.Body); - block = new QsPositionedBlock( - new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), - block.Location, - block.Comments); - newConditionBlocks.Add(Tuple.Create(expr.Item, block)); - generatedOperations.Add(callable); - } - else if(block.Body.Statements.Length > 0) - { - newConditionBlocks.Add(Tuple.Create(expr.Item, block)); - } - } - else - { - isHoistValid = false; - break; - } - } - - var newDefault = QsNullable.Null; - if (isHoistValid && stm.Default.IsValue) - { - SharedState.IsValidScope = true; - SharedState.CurrentHoistParams = stm.Default.Item.Body.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : stm.Default.Item.Body.KnownSymbols.Variables; - - var (_, block) = this.OnPositionedBlock(QsNullable.Null, stm.Default.Item); - if (SharedState.IsValidScope) - { - if (!IsScopeSingleCall(block.Body)) // if sub-scope is valid, hoist content - { - // Hoist the scope to its own operation - var (callable, call) = HoistBody(block.Body); - block = new QsPositionedBlock( - new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), - block.Location, - block.Comments); - newDefault = QsNullable.NewValue(block); - generatedOperations.Add(callable); - } - else if(block.Body.Statements.Length > 0) - { - newDefault = QsNullable.NewValue(block); - } - } - else - { - isHoistValid = false; - } - } - - if (isHoistValid) - { - SharedState.ControlOperations.AddRange(generatedOperations); - } - - SharedState.CurrentHoistParams = contextHoistParams; - SharedState.IsValidScope = contextValidScope; - - return isHoistValid - ? QsStatementKind.NewQsConditionalStatement( - new QsConditionalStatement(newConditionBlocks.ToImmutableArray(), newDefault)) - : QsStatementKind.NewQsConditionalStatement( - new QsConditionalStatement(stm.ConditionalBlocks, stm.Default)); - } - - public override QsStatementKind OnStatementKind(QsStatementKind kind) - { - SharedState.ContainsHoistParamRef = false; // Every statement kind starts off false - return base.OnStatementKind(kind); - } - } - - private class ExpressionTransformation : ExpressionTransformation - { - public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override TypedExpression OnTypedExpression(TypedExpression ex) - { - var contextContainsHoistParamRef = SharedState.ContainsHoistParamRef; - SharedState.ContainsHoistParamRef = false; - var rtrn = base.OnTypedExpression(ex); - - // If the sub context contains a reference, then the super context contains a reference, - // otherwise return the super context to its original value - if (!SharedState.ContainsHoistParamRef) - { - SharedState.ContainsHoistParamRef = contextContainsHoistParamRef; - } - - return rtrn; - } - } - - private class ExpressionKindTransformation : ExpressionKindTransformation - { - public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) - { - if (sym is Identifier.LocalVariable local && - SharedState.CurrentHoistParams.Any(param => param.VariableName.Equals(local.Item))) - { - SharedState.ContainsHoistParamRef = true; - } - return base.OnIdentifier(sym, tArgs); - } - } - } - - /// - /// Transformation that updates the contents of newly generated operations by: - /// 1. Rerouting the origins of type parameter references to the new operation - /// 2. Changes the IsMutable info on variable that used to be mutable, but are now immutable params to the operation - /// - private class UpdateGeneratedOp : SyntaxTreeTransformation + public static QsCompilation Apply(QsCompilation compilation) { - public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) - { - var filter = new UpdateGeneratedOp(parameters, oldName, newName); + var filter = new MyLift(); - return filter.Namespaces.OnCallableDeclaration(qsCallable); - } - - public class TransformationState - { - public bool IsRecursiveIdentifier = false; - public readonly ImmutableArray>> Parameters; - public readonly QsQualifiedName OldName; - public readonly QsQualifiedName NewName; - - public TransformationState(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) - { - Parameters = parameters; - OldName = oldName; - NewName = newName; - } - } - - private UpdateGeneratedOp(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) - : base(new TransformationState(parameters, oldName, newName)) - { - this.Expressions = new ExpressionTransformation(this); - this.ExpressionKinds = new ExpressionKindTransformation(this); - this.Types = new TypeTransformation(this); - } - - private class ExpressionTransformation : ExpressionTransformation - { - public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override ImmutableDictionary>, ResolvedType> OnTypeParamResolutions(ImmutableDictionary>, ResolvedType> typeParams) - { - // Prevent keys from having their names updated - return typeParams.ToImmutableDictionary(kvp => kvp.Key, kvp => this.Types.OnType(kvp.Value)); - } - - public override TypedExpression OnTypedExpression(TypedExpression ex) - { - // Checks if expression is mutable identifier that is in parameter list - if (ex.InferredInformation.IsMutable && - ex.Expression is ExpressionKind.Identifier id && - id.Item1 is Identifier.LocalVariable variable && - SharedState.Parameters.Any(x => x.VariableName.Equals(variable))) - { - // Set the mutability to false - ex = new TypedExpression( - ex.Expression, - ex.TypeArguments, - ex.ResolvedType, - new InferredExpressionInformation(false, ex.InferredInformation.HasLocalQuantumDependency), - ex.Range); - } - - // Prevent IsRecursiveIdentifier from propagating beyond the typed expression it is referring to - var isRecursiveIdentifier = SharedState.IsRecursiveIdentifier; - var rtrn = base.OnTypedExpression(ex); - SharedState.IsRecursiveIdentifier = isRecursiveIdentifier; - return rtrn; - } - } - - private class ExpressionKindTransformation : ExpressionKindTransformation - { - public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) - { - var rtrn = base.OnIdentifier(sym, tArgs); - - // Then check if this is a recursive identifier - // In this context, that is a call back to the original callable from the newly generated operation - if (sym is Identifier.GlobalCallable callable && SharedState.OldName.Equals(callable.Item)) - { - // Setting this flag will prevent the rerouting logic from processing the resolved type of the recursive identifier expression. - // This is necessary because we don't want any type parameters from the original callable from being rerouted to the new generated - // operation's type parameters in the definition of the identifier. - SharedState.IsRecursiveIdentifier = true; - } - return rtrn; - } - } - - private class TypeTransformation : TypeTransformation - { - public TypeTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override ResolvedTypeKind OnTypeParameter(QsTypeParameter tp) - { - // Reroute a type parameter's origin to the newly generated operation - if (!SharedState.IsRecursiveIdentifier && SharedState.OldName.Equals(tp.Origin)) - { - tp = new QsTypeParameter(SharedState.NewName, tp.TypeName, tp.Range); - } - - return base.OnTypeParameter(tp); - } - } + return new QsCompilation(compilation.Namespaces.Select(ns => filter.Namespaces.OnNamespace(ns)).ToImmutableArray(), compilation.EntryPoints); } - } - internal static class HoistTransformation2 - { private class MyLift : LiftContent { - private MyLift() : base() + public MyLift() : base() { this.StatementKinds = new StatementKindTransformation(this); } diff --git a/src/QsCompiler/Transformations/ClassicallyControlledUtils.cs b/src/QsCompiler/Transformations/ClassicallyControlledUtils.cs index 64fc309581..acbe07bacf 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlledUtils.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlledUtils.cs @@ -18,7 +18,7 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlled /// /// These tools are specific to the classically-controlled transformation and are not intended for wider use in their current state. - /// They rely on the specific context in which they are invoked during that transformation and are not general purpuse tools. + /// They rely on the specific context in which they are invoked during that transformation and are not general purpose tools. /// internal static class Utils { @@ -45,7 +45,7 @@ internal static TypedExpression CreateValueTupleExpression(params TypedExpressio ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), TypeArgsResolution.Empty, ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), - new InferredExpressionInformation(false, false), + new InferredExpressionInformation(false, expressions.Any(exp => exp.InferredInformation.HasLocalQuantumDependency)), QsNullable>.Null ); @@ -66,24 +66,13 @@ internal static TypedExpression CreateCallLikeExpression(TypedExpression id, Typ /// none of the given expressions should have a local quantum dependency, /// and all range information should be stripped from each given expression. /// - internal static TypedExpression CreateValueArray(params TypedExpression[] expressions) + internal static TypedExpression CreateValueArray(ResolvedType baseType, params TypedExpression[] expressions) { - ResolvedType type = null; - if (expressions.Any()) - { - type = expressions.First().ResolvedType; - QsCompilerError.Verify(expressions.All(expr => expr.ResolvedType.Equals(type))); - } - else - { - type = ResolvedType.New(ResolvedTypeKind.UnitType); - } - return new TypedExpression ( ExpressionKind.NewValueArray(expressions.ToImmutableArray()), TypeArgsResolution.Empty, - ResolvedType.New(ResolvedTypeKind.NewArrayType(type)), + ResolvedType.New(ResolvedTypeKind.NewArrayType(baseType)), new InferredExpressionInformation(false, false), QsNullable>.Null ); diff --git a/src/QsCompiler/Transformations/HoistContent.cs b/src/QsCompiler/Transformations/HoistContent.cs index 67f301b28d..958ded37ad 100644 --- a/src/QsCompiler/Transformations/HoistContent.cs +++ b/src/QsCompiler/Transformations/HoistContent.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -8,7 +11,8 @@ using Microsoft.Quantum.QsCompiler.Transformations.Core; using Microsoft.Quantum.QsCompiler.Transformations.SearchAndReplace; -namespace Transformations + +namespace Microsoft.Quantum.QsCompiler.Transformations.LyftContent { using ExpressionKind = QsExpressionKind; using ResolvedTypeKind = QsTypeKind; @@ -25,418 +29,410 @@ namespace Transformations /// The contents of the conditional block are then replaced with a call to the new operation with all /// the type parameters and known variables being forwarded to the new operation as arguments. /// - //internal static class HoistContent - //{ - // internal static QsCompilation Apply(QsCompilation compilation) => LiftContent.Apply(compilation); + public class LiftContent : SyntaxTreeTransformation + { - public class LiftContent : SyntaxTreeTransformation + public class CallableDetails { - public static QsCompilation Apply(QsCompilation compilation) - { - var filter = new LiftContent(); + public QsCallable Callable; + public QsSpecialization Adjoint; + public QsSpecialization Controlled; + public QsSpecialization ControlledAdjoint; + public QsNullable> TypeParameters; - return new QsCompilation(compilation.Namespaces.Select(ns => filter.Namespaces.OnNamespace(ns)).ToImmutableArray(), compilation.EntryPoints); + public CallableDetails(QsCallable callable) + { + Callable = callable; + Adjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsAdjoint); + Controlled = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlled); + ControlledAdjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlledAdjoint); + TypeParameters = callable.Signature.TypeParameters.Any(param => param.IsValidName) + ? QsNullable>.NewValue(callable.Signature.TypeParameters + .Where(param => param.IsValidName) + .Select(param => + ResolvedType.New(ResolvedTypeKind.NewTypeParameter(new QsTypeParameter( + callable.FullName, + ((QsLocalSymbol.ValidName)param).Item, + QsNullable>.Null + )))) + .ToImmutableArray()) + : QsNullable>.Null; } + } - public class CallableDetails + public class TransformationState + { + public bool IsValidScope = true; + public List GeneratedOperations = null; + public ImmutableArray>> CurrentHoistParams = + ImmutableArray>>.Empty; + public bool ContainsHoistParamRef = false; + + public CallableDetails CurrentCallable = null; + public bool InBody = false; + public bool InAdjoint = false; + public bool InControlled = false; + public bool InWithinBlock = false; + + private (ResolvedSignature, IEnumerable) MakeSpecializations( + QsQualifiedName callableName, ResolvedType paramsType, SpecializationImplementation bodyImplementation) { - public QsCallable Callable; - public QsSpecialization Adjoint; - public QsSpecialization Controlled; - public QsSpecialization ControlledAdjoint; - public QsNullable> TypeArguments; + QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature, SpecializationImplementation impl) => + new QsSpecialization( + kind, + callableName, + ImmutableArray.Empty, + CurrentCallable.Callable.SourceFile, + QsNullable.Null, + QsNullable>.Null, + signature, + impl, + ImmutableArray.Empty, + QsComments.Empty); - public CallableDetails(QsCallable callable) + var adj = CurrentCallable.Adjoint; + var ctl = CurrentCallable.Controlled; + var ctlAdj = CurrentCallable.ControlledAdjoint; + + bool addAdjoint = false; + bool addControlled = false; + bool isSelfAdjoint = false; + + if (InWithinBlock) { - Callable = callable; - Adjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsAdjoint); - Controlled = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlled); - ControlledAdjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlledAdjoint); - TypeArguments = callable.Signature.TypeParameters.Any(param => param.IsValidName) - ? QsNullable>.NewValue(callable.Signature.TypeParameters - .Where(param => param.IsValidName) - .Select(param => - ResolvedType.New(ResolvedTypeKind.NewTypeParameter(new QsTypeParameter( - callable.FullName, - ((QsLocalSymbol.ValidName)param).Item, - QsNullable>.Null - )))) - .ToImmutableArray()) - : QsNullable>.Null; + addAdjoint = true; + addControlled = false; } - } - - public class TransformationState - { - public bool IsValidScope = true; - public List GeneratedOperations = null; - public ImmutableArray>> CurrentHoistParams = - ImmutableArray>>.Empty; - public bool ContainsHoistParamRef = false; - - public CallableDetails CurrentCallable = null; - public bool InBody = false; - public bool InAdjoint = false; - public bool InControlled = false; - public bool InWithinBlock = false; - - private (ResolvedSignature, IEnumerable) MakeSpecializations( - QsQualifiedName callableName, ResolvedType argsType, SpecializationImplementation bodyImplementation) + else if (InBody) { - QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature, SpecializationImplementation impl) => - new QsSpecialization( - kind, - callableName, - ImmutableArray.Empty, - CurrentCallable.Callable.SourceFile, - QsNullable.Null, - QsNullable>.Null, - signature, - impl, - ImmutableArray.Empty, - QsComments.Empty); - - var adj = CurrentCallable.Adjoint; - var ctl = CurrentCallable.Controlled; - var ctlAdj = CurrentCallable.ControlledAdjoint; - - bool addAdjoint = false; - bool addControlled = false; - - if (InWithinBlock) + if (adj != null && adj.Implementation is SpecializationImplementation.Generated adjGen) { - addAdjoint = true; - addControlled = false; + addAdjoint = adjGen.Item.IsInvert; + isSelfAdjoint = adjGen.Item.IsSelfInverse; } - else if (InBody) + if (ctl != null && ctl.Implementation is SpecializationImplementation.Generated ctlGen) addControlled = ctlGen.Item.IsDistribute; + if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated ctlAdjGen) { - if (adj != null && adj.Implementation is SpecializationImplementation.Generated adjGen) addAdjoint = adjGen.Item.IsInvert; - if (ctl != null && ctl.Implementation is SpecializationImplementation.Generated ctlGen) addControlled = ctlGen.Item.IsDistribute; - if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated ctlAdjGen) - { - addAdjoint = addAdjoint || ctlAdjGen.Item.IsInvert && ctl.Implementation.IsGenerated; - addControlled = addControlled || ctlAdjGen.Item.IsDistribute && adj.Implementation.IsGenerated; - } - } - else if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated gen) - { - addControlled = InAdjoint && gen.Item.IsDistribute; - addAdjoint = InControlled && gen.Item.IsInvert; + addAdjoint = addAdjoint || ctlAdjGen.Item.IsInvert && ctl.Implementation.IsGenerated; + addControlled = addControlled || ctlAdjGen.Item.IsDistribute && adj.Implementation.IsGenerated; + isSelfAdjoint = isSelfAdjoint || ctlAdjGen.Item.IsSelfInverse; } + } + else if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated gen) + { + addControlled = InAdjoint && gen.Item.IsDistribute; + addAdjoint = InControlled && gen.Item.IsInvert; + isSelfAdjoint = gen.Item.IsSelfInverse; + } - var props = new List(); - if (addAdjoint) props.Add(OpProperty.Adjointable); - if (addControlled) props.Add(OpProperty.Controllable); - var newSig = new ResolvedSignature( - CurrentCallable.Callable.Signature.TypeParameters, - argsType, - ResolvedType.New(ResolvedTypeKind.UnitType), - new CallableInformation(ResolvedCharacteristics.FromProperties(props), InferredCallableInformation.NoInformation)); + var props = new List(); + if (addAdjoint) props.Add(OpProperty.Adjointable); + if (addControlled) props.Add(OpProperty.Controllable); + var newSig = new ResolvedSignature( + CurrentCallable.Callable.Signature.TypeParameters, + paramsType, + ResolvedType.New(ResolvedTypeKind.UnitType), + new CallableInformation(ResolvedCharacteristics.FromProperties(props), new InferredCallableInformation(isSelfAdjoint, false))); + + var controlledSig = new ResolvedSignature( + newSig.TypeParameters, + ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Create( + ResolvedType.New(ResolvedTypeKind.NewArrayType(ResolvedType.New(ResolvedTypeKind.Qubit))), + newSig.ArgumentType))), + newSig.ReturnType, + newSig.Information); + + var specializations = new List() { MakeSpec(QsSpecializationKind.QsBody, newSig, bodyImplementation) }; + + if (addAdjoint) + { + specializations.Add(MakeSpec( + QsSpecializationKind.QsAdjoint, + newSig, + SpecializationImplementation.NewGenerated(QsGeneratorDirective.Invert))); + } - var controlledSig = new ResolvedSignature( - newSig.TypeParameters, - ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Create( - ResolvedType.New(ResolvedTypeKind.NewArrayType(ResolvedType.New(ResolvedTypeKind.Qubit))), - newSig.ArgumentType))), - newSig.ReturnType, - newSig.Information); + if (addControlled) + { + specializations.Add(MakeSpec( + QsSpecializationKind.QsControlled, + controlledSig, + SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); + } - var specializations = new List() { MakeSpec(QsSpecializationKind.QsBody, newSig, bodyImplementation) }; + if (addAdjoint && addControlled) + { + specializations.Add(MakeSpec( + QsSpecializationKind.QsControlledAdjoint, + controlledSig, + SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); + } - if (addAdjoint) - { - specializations.Add(MakeSpec( - QsSpecializationKind.QsAdjoint, - newSig, - SpecializationImplementation.NewGenerated(QsGeneratorDirective.Invert))); - } + return (newSig, specializations); + } - if (addControlled) - { - specializations.Add(MakeSpec( - QsSpecializationKind.QsControlled, - controlledSig, - SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); - } + private (QsCallable, ResolvedType) GenerateOperation(QsScope contents) + { + var newName = UniqueVariableNames.PrependGuid(CurrentCallable.Callable.FullName); - if (addAdjoint && addControlled) - { - specializations.Add(MakeSpec( - QsSpecializationKind.QsControlledAdjoint, - controlledSig, - SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); - } + var knownVariables = contents.KnownSymbols.Variables; - return (newSig, specializations); - } + var parameters = QsTuple>.NewQsTuple(knownVariables + .Select(var => QsTuple>.NewQsTupleItem(new LocalVariableDeclaration( + QsLocalSymbol.NewValidName(var.VariableName), + var.Type, + new InferredExpressionInformation(false, false), + var.Position, + var.Range))) + .ToImmutableArray()); - private (QsCallable, ResolvedType) GenerateOperation(QsScope contents) + var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); + if (knownVariables.Length == 1) { - var newName = UniqueVariableNames.PrependGuid(CurrentCallable.Callable.FullName); - - var knownVariables = contents.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : contents.KnownSymbols.Variables; - - var parameters = QsTuple>.NewQsTuple(knownVariables - .Select(var => QsTuple>.NewQsTupleItem(new LocalVariableDeclaration( - QsLocalSymbol.NewValidName(var.VariableName), - var.Type, - var.InferredInformation, - var.Position, - var.Range))) - .ToImmutableArray()); - - var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); - if (knownVariables.Length == 1) - { - paramTypes = knownVariables.First().Type; - } - else if (knownVariables.Length > 1) - { - paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables - .Select(var => var.Type) - .ToImmutableArray())); - } + paramTypes = knownVariables.First().Type; + } + else if (knownVariables.Length > 1) + { + paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables + .Select(var => var.Type) + .ToImmutableArray())); + } - var (signature, specializations) = MakeSpecializations(newName, paramTypes, SpecializationImplementation.NewProvided(parameters, contents)); + var (signature, specializations) = MakeSpecializations(newName, paramTypes, SpecializationImplementation.NewProvided(parameters, contents)); - var controlCallable = new QsCallable( - QsCallableKind.Operation, - newName, - ImmutableArray.Empty, - CurrentCallable.Callable.SourceFile, - QsNullable.Null, - signature, - parameters, - specializations.ToImmutableArray(), - ImmutableArray.Empty, - QsComments.Empty); + var generatedCallable = new QsCallable( + QsCallableKind.Operation, + newName, + ImmutableArray.Empty, + CurrentCallable.Callable.SourceFile, + QsNullable.Null, + signature, + parameters, + specializations.ToImmutableArray(), + ImmutableArray.Empty, + QsComments.Empty); - var updatedCallable = UpdateGeneratedOp.Apply(controlCallable, knownVariables, CurrentCallable.Callable.FullName, newName); + // Change the origin of all type parameter references to use the new name and make all variables immutable + generatedCallable = UpdateGeneratedOp.Apply(generatedCallable, knownVariables, CurrentCallable.Callable.FullName, newName); - return (updatedCallable, signature.ArgumentType); - } + return (generatedCallable, signature.ArgumentType); + } - // ToDo: Doc comment - public (QsCallable, QsStatement) HoistBody(QsScope body) + // ToDo: Doc comment + public (QsCallable, QsStatement) HoistBody(QsScope body) + { + var (generatedOp, originalArgumentType) = GenerateOperation(body); + var generatedOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( + Tuple.Create( + originalArgumentType, + ResolvedType.New(ResolvedTypeKind.UnitType)), + generatedOp.Signature.Information)); + + // Foreword the type parameters of the parent callable to the type arguments of the call to the generated operation. + var typeArguments = CurrentCallable.TypeParameters; + var generatedOpId = new TypedExpression + ( + ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(generatedOp.FullName), typeArguments), + typeArguments.IsNull + ? TypeArgsResolution.Empty + : typeArguments.Item + .Select(type => Tuple.Create(generatedOp.FullName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + .ToImmutableArray(), + generatedOpType, + new InferredExpressionInformation(false, false), + QsNullable>.Null + ); + + var knownSymbols = body.KnownSymbols.Variables; + TypedExpression arguments = null; + if (knownSymbols.Any()) + { + var argumentArray = knownSymbols + .Select(var => new TypedExpression( + ExpressionKind.NewIdentifier( + Identifier.NewLocalVariable(var.VariableName), + QsNullable>.Null), + TypeArgsResolution.Empty, + var.Type, + var.InferredInformation, + QsNullable>.Null)) + .ToImmutableArray(); + + arguments = new TypedExpression( + ExpressionKind.NewValueTuple(argumentArray), + TypeArgsResolution.Empty, + ResolvedType.New(ResolvedTypeKind.NewTupleType(argumentArray.Select(expr => expr.ResolvedType).ToImmutableArray())), + new InferredExpressionInformation(false, argumentArray.Any(exp => exp.InferredInformation.HasLocalQuantumDependency)), + QsNullable>.Null); + } + else { - var (targetOp, originalArgumentType) = GenerateOperation(body); - var targetOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( - Tuple.Create( - originalArgumentType, - ResolvedType.New(ResolvedTypeKind.UnitType)), - targetOp.Signature.Information)); - - var targetTypeArgTypes = CurrentCallable.TypeArguments; - var targetOpId = new TypedExpression - ( - ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(targetOp.FullName), targetTypeArgTypes), - targetTypeArgTypes.IsNull - ? TypeArgsResolution.Empty - : targetTypeArgTypes.Item - .Select(type => Tuple.Create(targetOp.FullName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) - .ToImmutableArray(), - targetOpType, + arguments = new TypedExpression( + ExpressionKind.UnitValue, + TypeArgsResolution.Empty, + ResolvedType.New(ResolvedTypeKind.UnitType), new InferredExpressionInformation(false, false), - QsNullable>.Null - ); + QsNullable>.Null); + } - var knownSymbols = body.KnownSymbols.Variables; + var call = new TypedExpression( + ExpressionKind.NewCallLikeExpression(generatedOpId, arguments), + typeArguments.IsNull + ? TypeArgsResolution.Empty + : typeArguments.Item + .Select(type => Tuple.Create(CurrentCallable.Callable.FullName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) + .ToImmutableArray(), + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, true), + QsNullable>.Null); + + return (generatedOp, new QsStatement( + QsStatementKind.NewQsExpressionStatement(call), + LocalDeclarations.Empty, + QsNullable.Null, + QsComments.Empty)); + } + } - TypedExpression targetArgs = null; - if (knownSymbols.Any()) - { - targetArgs = Utils.CreateValueTupleExpression(knownSymbols.Select(var => Utils.CreateIdentifierExpression( - Identifier.NewLocalVariable(var.VariableName), - TypeArgsResolution.Empty, - var.Type)) - .ToArray()); - } - else - { - targetArgs = new TypedExpression - ( - ExpressionKind.UnitValue, - TypeArgsResolution.Empty, - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - } + protected LiftContent() : base(new TransformationState()) + { + this.Namespaces = new NamespaceTransformation(this); + this.StatementKinds = new StatementKindTransformation(this); + this.Expressions = new ExpressionTransformation(this); + this.ExpressionKinds = new ExpressionKindTransformation(this); + this.Types = new TypeTransformation(this, TransformationOptions.Disabled); + } - var call = new TypedExpression - ( - ExpressionKind.NewCallLikeExpression(targetOpId, targetArgs), - targetTypeArgTypes.IsNull - ? TypeArgsResolution.Empty - : targetTypeArgTypes.Item - .Select(type => Tuple.Create(CurrentCallable.Callable.FullName, ((ResolvedTypeKind.TypeParameter)type.Resolution).Item.TypeName, type)) - .ToImmutableArray(), - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, true), - QsNullable>.Null - ); + protected class NamespaceTransformation : NamespaceTransformation + { + public NamespaceTransformation(SyntaxTreeTransformation parent) : base(parent) { } - return (targetOp, new QsStatement( - QsStatementKind.NewQsExpressionStatement(call), - LocalDeclarations.Empty, - QsNullable.Null, - QsComments.Empty)); - } + public override QsCallable OnCallableDeclaration(QsCallable c) + { + SharedState.CurrentCallable = new CallableDetails(c); + return base.OnCallableDeclaration(c); } - public LiftContent() : base(new TransformationState()) + public override QsSpecialization OnBodySpecialization(QsSpecialization spec) { - this.Namespaces = new NamespaceTransformation(this); - this.StatementKinds = new StatementKindTransformation(this); - this.Expressions = new ExpressionTransformation(this); - this.ExpressionKinds = new ExpressionKindTransformation(this); - this.Types = new TypeTransformation(this, TransformationOptions.Disabled); + SharedState.InBody = true; + var rtrn = base.OnBodySpecialization(spec); + SharedState.InBody = false; + return rtrn; } - public class NamespaceTransformation : NamespaceTransformation + public override QsSpecialization OnAdjointSpecialization(QsSpecialization spec) { - public NamespaceTransformation(SyntaxTreeTransformation parent) : base(parent) { } + SharedState.InAdjoint = true; + var rtrn = base.OnAdjointSpecialization(spec); + SharedState.InAdjoint = false; + return rtrn; + } - public override QsCallable OnCallableDeclaration(QsCallable c) - { - SharedState.CurrentCallable = new CallableDetails(c); - return base.OnCallableDeclaration(c); - } + public override QsSpecialization OnControlledSpecialization(QsSpecialization spec) + { + SharedState.InControlled = true; + var rtrn = base.OnControlledSpecialization(spec); + SharedState.InControlled = false; + return rtrn; + } - public override QsSpecialization OnBodySpecialization(QsSpecialization spec) - { - SharedState.InBody = true; - var rtrn = base.OnBodySpecialization(spec); - SharedState.InBody = false; - return rtrn; - } + public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being hoisted - public override QsSpecialization OnAdjointSpecialization(QsSpecialization spec) - { - SharedState.InAdjoint = true; - var rtrn = base.OnAdjointSpecialization(spec); - SharedState.InAdjoint = false; - return rtrn; - } + public override QsNamespace OnNamespace(QsNamespace ns) + { + // Generated operations list will be populated in the transform + SharedState.GeneratedOperations = new List(); + return base.OnNamespace(ns) + .WithElements(elems => elems.AddRange(SharedState.GeneratedOperations.Select(op => QsNamespaceElement.NewQsCallable(op)))); + } + } - public override QsSpecialization OnControlledSpecialization(QsSpecialization spec) - { - SharedState.InControlled = true; - var rtrn = base.OnControlledSpecialization(spec); - SharedState.InControlled = false; - return rtrn; - } + protected class StatementKindTransformation : StatementKindTransformation + { + public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } - public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being hoisted + public override QsStatementKind OnConjugation(QsConjugation stm) + { + var superInWithinBlock = SharedState.InWithinBlock; + SharedState.InWithinBlock = true; + var (_, outer) = this.OnPositionedBlock(QsNullable.Null, stm.OuterTransformation); + SharedState.InWithinBlock = superInWithinBlock; - public override QsNamespace OnNamespace(QsNamespace ns) - { - // Generated operations list will be populated in the transform - SharedState.GeneratedOperations = new List(); - return base.OnNamespace(ns) - .WithElements(elems => elems.AddRange(SharedState.GeneratedOperations.Select(op => QsNamespaceElement.NewQsCallable(op)))); - } + var (_, inner) = this.OnPositionedBlock(QsNullable.Null, stm.InnerTransformation); + + return QsStatementKind.NewQsConjugation(new QsConjugation(outer, inner)); } - protected class StatementKindTransformation : StatementKindTransformation + public override QsStatementKind OnReturnStatement(TypedExpression ex) { - public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - // ToDo: This logic should be externalized at some point to make the Hoisting more general - //private bool IsScopeSingleCall(QsScope contents) - //{ - // if (contents.Statements.Length != 1) return false; - // - // return contents.Statements[0].Statement is QsStatementKind.QsExpressionStatement expr - // && expr.Item.Expression is ExpressionKind.CallLikeExpression call - // && !TypedExpression.IsPartialApplication(expr.Item.Expression) - // && call.Item1.Expression is ExpressionKind.Identifier; - //} - - public override QsStatementKind OnConjugation(QsConjugation stm) - { - var superInWithinBlock = SharedState.InWithinBlock; - SharedState.InWithinBlock = true; - var (_, outer) = this.OnPositionedBlock(QsNullable.Null, stm.OuterTransformation); - SharedState.InWithinBlock = superInWithinBlock; - - var (_, inner) = this.OnPositionedBlock(QsNullable.Null, stm.InnerTransformation); + SharedState.IsValidScope = false; + return base.OnReturnStatement(ex); + } - return QsStatementKind.NewQsConjugation(new QsConjugation(outer, inner)); - } + public override QsStatementKind OnValueUpdate(QsValueUpdate stm) + { + // If lhs contains an identifier found in the scope's known variables (variables from the super-scope), the scope is not valid + var lhs = this.Expressions.OnTypedExpression(stm.Lhs); - public override QsStatementKind OnReturnStatement(TypedExpression ex) + if (SharedState.ContainsHoistParamRef) { SharedState.IsValidScope = false; - return base.OnReturnStatement(ex); } - public override QsStatementKind OnValueUpdate(QsValueUpdate stm) - { - // If lhs contains an identifier found in the scope's known variables (variables from the super-scope), the scope is not valid - var lhs = this.Expressions.OnTypedExpression(stm.Lhs); - - if (SharedState.ContainsHoistParamRef) - { - SharedState.IsValidScope = false; - } - - var rhs = this.Expressions.OnTypedExpression(stm.Rhs); - return QsStatementKind.NewQsValueUpdate(new QsValueUpdate(lhs, rhs)); - } - - public override QsStatementKind OnStatementKind(QsStatementKind kind) - { - SharedState.ContainsHoistParamRef = false; // Every statement kind starts off false - return base.OnStatementKind(kind); - } + var rhs = this.Expressions.OnTypedExpression(stm.Rhs); + return QsStatementKind.NewQsValueUpdate(new QsValueUpdate(lhs, rhs)); } - public class ExpressionTransformation : ExpressionTransformation + public override QsStatementKind OnStatementKind(QsStatementKind kind) { - public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } + SharedState.ContainsHoistParamRef = false; // Every statement kind starts off false + return base.OnStatementKind(kind); + } + } - public override TypedExpression OnTypedExpression(TypedExpression ex) - { - var contextContainsHoistParamRef = SharedState.ContainsHoistParamRef; - SharedState.ContainsHoistParamRef = false; - var rtrn = base.OnTypedExpression(ex); + protected class ExpressionTransformation : ExpressionTransformation + { + public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } - // If the sub context contains a reference, then the super context contains a reference, - // otherwise return the super context to its original value - if (!SharedState.ContainsHoistParamRef) - { - SharedState.ContainsHoistParamRef = contextContainsHoistParamRef; - } + public override TypedExpression OnTypedExpression(TypedExpression ex) + { + var contextContainsHoistParamRef = SharedState.ContainsHoistParamRef; + SharedState.ContainsHoistParamRef = false; + var rtrn = base.OnTypedExpression(ex); - return rtrn; + // If the sub context contains a reference, then the super context contains a reference, + // otherwise return the super context to its original value + if (!SharedState.ContainsHoistParamRef) + { + SharedState.ContainsHoistParamRef = contextContainsHoistParamRef; } + + return rtrn; } + } - public class ExpressionKindTransformation : ExpressionKindTransformation - { - public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + protected class ExpressionKindTransformation : ExpressionKindTransformation + { + public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } - public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) + public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) + { + if (sym is Identifier.LocalVariable local && + SharedState.CurrentHoistParams.Any(param => param.VariableName.Equals(local.Item))) { - if (sym is Identifier.LocalVariable local && - SharedState.CurrentHoistParams.Any(param => param.VariableName.Equals(local.Item))) - { - SharedState.ContainsHoistParamRef = true; - } - return base.OnIdentifier(sym, tArgs); + SharedState.ContainsHoistParamRef = true; } + return base.OnIdentifier(sym, tArgs); } } /// /// Transformation that updates the contents of newly generated operations by: /// 1. Rerouting the origins of type parameter references to the new operation - /// 2. Changes the IsMutable info on variable that used to be mutable, but are now immutable params to the operation + /// 2. Changes the IsMutable and HasLocalQuantumDependency info on parameter references to be false /// - public class UpdateGeneratedOp : SyntaxTreeTransformation + private class UpdateGeneratedOp : SyntaxTreeTransformation { public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) { @@ -481,17 +477,17 @@ public override ImmutableDictionary>, public override TypedExpression OnTypedExpression(TypedExpression ex) { // Checks if expression is mutable identifier that is in parameter list - if (ex.InferredInformation.IsMutable && - ex.Expression is ExpressionKind.Identifier id && - id.Item1 is Identifier.LocalVariable variable && - SharedState.Parameters.Any(x => x.VariableName.Equals(variable))) + if ((ex.InferredInformation.IsMutable || ex.InferredInformation.HasLocalQuantumDependency) + && ex.Expression is ExpressionKind.Identifier id + && id.Item1 is Identifier.LocalVariable variable + && SharedState.Parameters.Any(x => x.VariableName.Equals(variable))) { // Set the mutability to false ex = new TypedExpression( ex.Expression, ex.TypeArguments, ex.ResolvedType, - new InferredExpressionInformation(false, ex.InferredInformation.HasLocalQuantumDependency), + new InferredExpressionInformation(false, false), // parameter references cannot be mutable or have local quantum dependency ex.Range); } @@ -540,5 +536,5 @@ public override ResolvedTypeKind OnTypeParameter(QsTypeParameter tp) } } } - //} + } } From d0346930d4be833abb142454019b0504e3ac11a6 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Mon, 9 Mar 2020 14:10:31 -0700 Subject: [PATCH 066/135] Removed the Utils class for transformations. --- .../Transformations/ClassicallyControlled.cs | 164 ++++++++++++++---- .../ClassicallyControlledUtils.cs | 132 -------------- .../Transformations/HoistContent.cs | 13 +- 3 files changed, 135 insertions(+), 174 deletions(-) delete mode 100644 src/QsCompiler/Transformations/ClassicallyControlledUtils.cs diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index c56dceec79..5754e74f86 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -11,6 +11,7 @@ using Microsoft.Quantum.QsCompiler.Transformations.Core; using Microsoft.Quantum.QsCompiler.Transformations.LyftContent; + namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlled { using ExpressionKind = QsExpressionKind; @@ -72,6 +73,29 @@ private class StatementTransformation : StatementTransformation parent) : base(parent) { } + /// + /// Get the combined type resolutions for a pair of nested resolutions, + /// resolving references in the inner resolutions to the outer resolutions. + /// + private TypeArgsResolution GetCombinedTypeResolution(TypeArgsResolution outer, TypeArgsResolution inner) + { + var outerDict = outer.ToDictionary(x => (x.Item1, x.Item2), x => x.Item3); + return inner.Select(innerRes => + { + if (innerRes.Item3.Resolution is ResolvedTypeKind.TypeParameter typeParam && + outerDict.TryGetValue((typeParam.Item.Origin, typeParam.Item.TypeName), out var outerRes)) + { + outerDict.Remove((typeParam.Item.Origin, typeParam.Item.TypeName)); + return Tuple.Create(innerRes.Item1, innerRes.Item2, outerRes); + } + else + { + return innerRes; + } + }) + .Concat(outerDict.Select(x => Tuple.Create(x.Key.Item1, x.Key.Item2, x.Value))).ToImmutableArray(); + } + /// /// Checks if the scope is valid for conversion to an operation call from the conditional control API. /// It is valid if there is exactly one statement in it and that statement is a call like expression statement. @@ -94,7 +118,7 @@ public StatementTransformation(SyntaxTreeTransformation par var callTypeArguments = expr.Item.TypeArguments; var idTypeArguments = call.Item1.TypeArguments; - var combinedTypeArguments = Utils.GetCombinedTypeResolution(callTypeArguments, idTypeArguments); + var combinedTypeArguments = GetCombinedTypeResolution(callTypeArguments, idTypeArguments); // This relies on anything having type parameters must be a global callable. var newCallIdentifier = call.Item1; @@ -132,6 +156,56 @@ public StatementTransformation(SyntaxTreeTransformation par return (false, null, null); } + /// + /// Gets an identifier and argument tuple for the built in operation NoOp. + /// + private (TypedExpression, TypedExpression) GetNoOp() + { + var opInfo = BuiltIn.NoOp; + + var properties = new[] { OpProperty.Adjointable, OpProperty.Controllable }; + var characteristics = new CallableInformation( + ResolvedCharacteristics.FromProperties(properties), + InferredCallableInformation.NoInformation); // ToDo: Set the IsSelfAdjoint flag appropriately + + var unitType = ResolvedType.New(ResolvedTypeKind.UnitType); + var operationType = ResolvedType.New(ResolvedTypeKind.NewOperation( + Tuple.Create(unitType, unitType), + characteristics)); + + var args = new TypedExpression( + ExpressionKind.UnitValue, + TypeArgsResolution.Empty, + unitType, + new InferredExpressionInformation(false, false), + QsNullable>.Null); + var typeArgs = ImmutableArray.Create(unitType); + + var identifier = new TypedExpression( + ExpressionKind.NewIdentifier( + Identifier.NewGlobalCallable(new QsQualifiedName(opInfo.Namespace, opInfo.Name)), + QsNullable>.NewValue(typeArgs)), + typeArgs + .Zip(opInfo.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(opInfo.Namespace, opInfo.Name), param, type)) + .ToImmutableArray(), + operationType, + new InferredExpressionInformation(false, false), + QsNullable>.Null); + + return (identifier, args); + } + + /// + /// Creates a value tuple expression containing the given expressions. + /// + private TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => + new TypedExpression( + ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), + TypeArgsResolution.Empty, + ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), + new InferredExpressionInformation(false, expressions.Any(exp => exp.InferredInformation.HasLocalQuantumDependency)), + QsNullable>.Null); + #region Condition Converting Logic /// @@ -140,13 +214,28 @@ public StatementTransformation(SyntaxTreeTransformation par /// private TypedExpression CreateControlCall(BuiltIn opInfo, IEnumerable properties, TypedExpression args, IEnumerable typeArgs) { + var characteristics = new CallableInformation( + ResolvedCharacteristics.FromProperties(properties), + InferredCallableInformation.NoInformation); // ToDo: Set the IsSelfAdjoint flag appropriately + + var unitType = ResolvedType.New(ResolvedTypeKind.UnitType); + var operationType = ResolvedType.New(ResolvedTypeKind.NewOperation( + Tuple.Create(args.ResolvedType, unitType), + characteristics)); + // Build the surrounding control call - var identifier = Utils.CreateIdentifierExpression( - Identifier.NewGlobalCallable(new QsQualifiedName(opInfo.Namespace, opInfo.Name)), + var identifier = new TypedExpression( + ExpressionKind.NewIdentifier( + Identifier.NewGlobalCallable(new QsQualifiedName(opInfo.Namespace, opInfo.Name)), + typeArgs.Any() + ? QsNullable>.NewValue(typeArgs.ToImmutableArray()) + : QsNullable>.Null), typeArgs .Zip(opInfo.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(opInfo.Namespace, opInfo.Name), param, type)) .ToImmutableArray(), - Utils.GetOperationType(properties, args.ResolvedType)); + operationType, + new InferredExpressionInformation(false, false), + QsNullable>.Null); // Creates type resolutions for the call expression var opTypeArgResolutions = typeArgs @@ -164,7 +253,12 @@ x.Resolution is ResolvedTypeKind.TupleType tup }) .ToImmutableArray(); - return Utils.CreateCallLikeExpression(identifier, args, opTypeArgResolutions); + return new TypedExpression( + ExpressionKind.NewCallLikeExpression(identifier, args), + opTypeArgResolutions, + unitType, + new InferredExpressionInformation(false, true), + QsNullable>.Null); } /// @@ -190,11 +284,11 @@ private TypedExpression CreateApplyConditionallyExpression(TypedExpression condi if (equalityScope == null) { - (equalityId, equalityArgs) = Utils.GetNoOp(); + (equalityId, equalityArgs) = GetNoOp(); } else if (inequalityScope == null) { - (inequalityId, inequalityArgs) = Utils.GetNoOp(); + (inequalityId, inequalityArgs) = GetNoOp(); } // Get characteristic properties from global id @@ -227,11 +321,21 @@ private TypedExpression CreateApplyConditionallyExpression(TypedExpression condi controlOpInfo = BuiltIn.ApplyConditionally; } - var equality = Utils.CreateValueTupleExpression(equalityId, equalityArgs); - var inequality = Utils.CreateValueTupleExpression(inequalityId, inequalityArgs); - var controlArgs = Utils.CreateValueTupleExpression( - Utils.CreateValueArray(ResolvedType.New(ResolvedTypeKind.Result), conditionExpr1), - Utils.CreateValueArray(ResolvedType.New(ResolvedTypeKind.Result), conditionExpr2), + // Takes a single TypedExpression of type Result and puts in into a + // value array expression with the given expression as its only item. + TypedExpression BoxResultInArray(TypedExpression expression) => + new TypedExpression( + ExpressionKind.NewValueArray(ImmutableArray.Create(expression)), + TypeArgsResolution.Empty, + ResolvedType.New(ResolvedTypeKind.NewArrayType(ResolvedType.New(ResolvedTypeKind.Result))), + new InferredExpressionInformation(false, expression.InferredInformation.HasLocalQuantumDependency), + QsNullable>.Null); + + var equality = CreateValueTupleExpression(equalityId, equalityArgs); + var inequality = CreateValueTupleExpression(inequalityId, inequalityArgs); + var controlArgs = CreateValueTupleExpression( + BoxResultInArray(conditionExpr1), + BoxResultInArray(conditionExpr2), equality, inequality); var targetArgsTypes = ImmutableArray.Create(equalityArgs.ResolvedType, inequalityArgs.ResolvedType); @@ -288,10 +392,10 @@ private TypedExpression CreateApplyIfExpression(QsResult result, TypedExpression (TypedExpression, ImmutableArray) GetArgs(TypedExpression zeroId, TypedExpression zeroArgs, TypedExpression oneId, TypedExpression oneArgs) => ( - Utils.CreateValueTupleExpression( + CreateValueTupleExpression( conditionExpression, - Utils.CreateValueTupleExpression(zeroId, zeroArgs), - Utils.CreateValueTupleExpression(oneId, oneArgs)), + CreateValueTupleExpression(zeroId, zeroArgs), + CreateValueTupleExpression(oneId, oneArgs)), ImmutableArray.Create(zeroArgs.ResolvedType, oneArgs.ResolvedType) ); @@ -327,9 +431,9 @@ private TypedExpression CreateApplyIfExpression(QsResult result, TypedExpression : BuiltIn.ApplyIfOne; } - controlArgs = Utils.CreateValueTupleExpression( + controlArgs = CreateValueTupleExpression( conditionExpression, - Utils.CreateValueTupleExpression(conditionId, conditionArgs)); + CreateValueTupleExpression(conditionId, conditionArgs)); targetArgsTypes = ImmutableArray.Create(conditionArgs.ResolvedType); } @@ -522,15 +626,11 @@ private bool IsConditionedOnResultInequalityExpression(TypedExpression expressio var subCondition = new QsConditionalStatement(conditionStatment.ConditionalBlocks.RemoveAt(0), conditionStatment.Default); var secondConditionBlock = conditionStatment.ConditionalBlocks[1].Item2; - - var subIfStatment = new QsStatement - ( + var subIfStatment = new QsStatement( QsStatementKind.NewQsConditionalStatement(subCondition), LocalDeclarations.Empty, secondConditionBlock.Location, - secondConditionBlock.Comments - ); - + secondConditionBlock.Comments); var newDefault = QsNullable.NewValue(new QsPositionedBlock( new QsScope(ImmutableArray.Create(subIfStatment), secondConditionBlock.Body.KnownSymbols), secondConditionBlock.Location, @@ -553,13 +653,11 @@ private bool IsConditionedOnResultInequalityExpression(TypedExpression expressio if (condition.Expression is ExpressionKind.OR orCondition) { var subCondition = new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(orCondition.Item2, block)), conditionStatment.Default); - var subIfStatment = new QsStatement - ( + var subIfStatment = new QsStatement( QsStatementKind.NewQsConditionalStatement(subCondition), LocalDeclarations.Empty, block.Location, - QsComments.Empty - ); + QsComments.Empty); var newDefault = QsNullable.NewValue(new QsPositionedBlock( new QsScope(ImmutableArray.Create(subIfStatment), block.Body.KnownSymbols), block.Location, @@ -587,13 +685,11 @@ private bool IsConditionedOnResultInequalityExpression(TypedExpression expressio if (condition.Expression is ExpressionKind.AND andCondition) { var subCondition = new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(andCondition.Item2, block)), conditionStatment.Default); - var subIfStatment = new QsStatement - ( + var subIfStatment = new QsStatement( QsStatementKind.NewQsConditionalStatement(subCondition), LocalDeclarations.Empty, block.Location, - QsComments.Empty - ); + QsComments.Empty); var newBlock = new QsPositionedBlock( new QsScope(ImmutableArray.Create(subIfStatment), block.Body.KnownSymbols), block.Location, @@ -624,13 +720,11 @@ private QsStatement ReshapeConditional(QsStatement statement) (wasAndProcessed, stm) = ProcessAND(stm); } while (wasOrProcessed || wasAndProcessed); - return new QsStatement - ( + return new QsStatement( QsStatementKind.NewQsConditionalStatement(stm), statement.SymbolDeclarations, statement.Location, - statement.Comments - ); + statement.Comments); } return statement; } diff --git a/src/QsCompiler/Transformations/ClassicallyControlledUtils.cs b/src/QsCompiler/Transformations/ClassicallyControlledUtils.cs deleted file mode 100644 index acbe07bacf..0000000000 --- a/src/QsCompiler/Transformations/ClassicallyControlledUtils.cs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Microsoft.Quantum.QsCompiler.DataTypes; -using Microsoft.Quantum.QsCompiler.SyntaxTokens; -using Microsoft.Quantum.QsCompiler.SyntaxTree; - - -namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlled -{ - using ExpressionKind = QsExpressionKind; - using ResolvedTypeKind = QsTypeKind; - using TypeArgsResolution = ImmutableArray, ResolvedType>>; - - /// - /// These tools are specific to the classically-controlled transformation and are not intended for wider use in their current state. - /// They rely on the specific context in which they are invoked during that transformation and are not general purpose tools. - /// - internal static class Utils - { - internal static TypedExpression CreateIdentifierExpression(Identifier id, - TypeArgsResolution typeArgsMapping, ResolvedType resolvedType) => - new TypedExpression - ( - ExpressionKind.NewIdentifier( - id, - typeArgsMapping.Any() - ? QsNullable>.NewValue(typeArgsMapping - .Select(argMapping => argMapping.Item3) // This should preserve the order of the type args - .ToImmutableArray()) - : QsNullable>.Null), - typeArgsMapping, - resolvedType, - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - - internal static TypedExpression CreateValueTupleExpression(params TypedExpression[] expressions) => - new TypedExpression - ( - ExpressionKind.NewValueTuple(expressions.ToImmutableArray()), - TypeArgsResolution.Empty, - ResolvedType.New(ResolvedTypeKind.NewTupleType(expressions.Select(expr => expr.ResolvedType).ToImmutableArray())), - new InferredExpressionInformation(false, expressions.Any(exp => exp.InferredInformation.HasLocalQuantumDependency)), - QsNullable>.Null - ); - - internal static TypedExpression CreateCallLikeExpression(TypedExpression id, TypedExpression args, TypeArgsResolution typeRes) => - new TypedExpression - ( - ExpressionKind.NewCallLikeExpression(id, args), - typeRes, - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, true), - QsNullable>.Null - ); - - /// - /// Creates an array literal with the given items, setting the range information to Null. - /// If no items are given, creates an empty array of type Unit[]. - /// The resolved types for all of the given expressions must match, - /// none of the given expressions should have a local quantum dependency, - /// and all range information should be stripped from each given expression. - /// - internal static TypedExpression CreateValueArray(ResolvedType baseType, params TypedExpression[] expressions) - { - return new TypedExpression - ( - ExpressionKind.NewValueArray(expressions.ToImmutableArray()), - TypeArgsResolution.Empty, - ResolvedType.New(ResolvedTypeKind.NewArrayType(baseType)), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - } - - internal static ResolvedType GetOperationType(IEnumerable props, ResolvedType argumentType) - { - var characteristics = new CallableInformation( - ResolvedCharacteristics.FromProperties(props), - InferredCallableInformation.NoInformation); - - return ResolvedType.New(ResolvedTypeKind.NewOperation( - Tuple.Create(argumentType, ResolvedType.New(ResolvedTypeKind.UnitType)), - characteristics)); - } - - internal static TypeArgsResolution GetCombinedTypeResolution(TypeArgsResolution outer, TypeArgsResolution inner) - { - var outerDict = outer.ToDictionary(x => (x.Item1, x.Item2), x => x.Item3); - return inner.Select(innerRes => - { - if (innerRes.Item3.Resolution is ResolvedTypeKind.TypeParameter typeParam && - outerDict.TryGetValue((typeParam.Item.Origin, typeParam.Item.TypeName), out var outerRes)) - { - outerDict.Remove((typeParam.Item.Origin, typeParam.Item.TypeName)); - return Tuple.Create(innerRes.Item1, innerRes.Item2, outerRes); - } - else - { - return innerRes; - } - }) - .Concat(outerDict.Select(x => Tuple.Create(x.Key.Item1, x.Key.Item2, x.Value))).ToImmutableArray(); - } - - internal static (TypedExpression, TypedExpression) GetNoOp() - { - var identifier = Utils.CreateIdentifierExpression( - Identifier.NewGlobalCallable(new QsQualifiedName(BuiltIn.NoOp.Namespace, BuiltIn.NoOp.Name)), - BuiltIn.NoOp.TypeParameters - .Select(param => Tuple.Create(new QsQualifiedName(BuiltIn.NoOp.Namespace, BuiltIn.NoOp.Name), param, ResolvedType.New(ResolvedTypeKind.UnitType))) - .ToImmutableArray(), - Utils.GetOperationType(new[] { OpProperty.Adjointable, OpProperty.Controllable }, ResolvedType.New(ResolvedTypeKind.UnitType))); - - var args = new TypedExpression - ( - ExpressionKind.UnitValue, - TypeArgsResolution.Empty, - ResolvedType.New(ResolvedTypeKind.UnitType), - new InferredExpressionInformation(false, false), - QsNullable>.Null - ); - - return (identifier, args); - } - } -} diff --git a/src/QsCompiler/Transformations/HoistContent.cs b/src/QsCompiler/Transformations/HoistContent.cs index 958ded37ad..0b0f24e3eb 100644 --- a/src/QsCompiler/Transformations/HoistContent.cs +++ b/src/QsCompiler/Transformations/HoistContent.cs @@ -53,8 +53,7 @@ public CallableDetails(QsCallable callable) ResolvedType.New(ResolvedTypeKind.NewTypeParameter(new QsTypeParameter( callable.FullName, ((QsLocalSymbol.ValidName)param).Item, - QsNullable>.Null - )))) + QsNullable>.Null)))) .ToImmutableArray()) : QsNullable>.Null; } @@ -230,9 +229,10 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature // Foreword the type parameters of the parent callable to the type arguments of the call to the generated operation. var typeArguments = CurrentCallable.TypeParameters; - var generatedOpId = new TypedExpression - ( - ExpressionKind.NewIdentifier(Identifier.NewGlobalCallable(generatedOp.FullName), typeArguments), + var generatedOpId = new TypedExpression( + ExpressionKind.NewIdentifier( + Identifier.NewGlobalCallable(generatedOp.FullName), + typeArguments), typeArguments.IsNull ? TypeArgsResolution.Empty : typeArguments.Item @@ -240,8 +240,7 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature .ToImmutableArray(), generatedOpType, new InferredExpressionInformation(false, false), - QsNullable>.Null - ); + QsNullable>.Null); var knownSymbols = body.KnownSymbols.Variables; TypedExpression arguments = null; From 62793c6772a11cceb4260e0ede132290d4ee3fc8 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Mon, 9 Mar 2020 17:14:48 -0700 Subject: [PATCH 067/135] Renamed things to replace the term 'hoist' with 'lift'. --- .../Transformations/ClassicallyControlled.cs | 47 ++++++++--------- .../{HoistContent.cs => ContentLifting.cs} | 52 +++++++++++-------- 2 files changed, 52 insertions(+), 47 deletions(-) rename src/QsCompiler/Transformations/{HoistContent.cs => ContentLifting.cs} (91%) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index 5754e74f86..549cf6f572 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -9,7 +9,6 @@ using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; using Microsoft.Quantum.QsCompiler.Transformations.Core; -using Microsoft.Quantum.QsCompiler.Transformations.LyftContent; namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlled @@ -20,7 +19,7 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlled /// /// This transformation works in two passes. - /// 1st Pass: Hoist the contents of conditional statements into separate operations, where possible. + /// 1st Pass: Lift the contents of conditional statements into separate operations, where possible. /// 2nd Pass: On the way down the tree, reshape conditional statements to replace Elif's and /// top level OR and AND conditions with equivalent nested if-else statements. One the way back up /// the tree, convert conditional statements into interface calls, where possible. @@ -30,7 +29,7 @@ public static class ReplaceClassicalControl { public static QsCompilation Apply(QsCompilation compilation) { - compilation = HoistTransformation.Apply(compilation); + compilation = LiftConditionBlocks.Apply(compilation); return ConvertConditions.Apply(compilation); } @@ -758,23 +757,23 @@ public override QsScope OnScope(QsScope scope) } } - internal static class HoistTransformation + internal static class LiftConditionBlocks { public static QsCompilation Apply(QsCompilation compilation) { - var filter = new MyLift(); + var filter = new LiftContent(); return new QsCompilation(compilation.Namespaces.Select(ns => filter.Namespaces.OnNamespace(ns)).ToImmutableArray(), compilation.EntryPoints); } - private class MyLift : LiftContent + private class LiftContent : ContentLifting.LiftContent { - public MyLift() : base() + public LiftContent() : base() { this.StatementKinds = new StatementKindTransformation(this); } - private new class StatementKindTransformation : LiftContent.StatementKindTransformation + private new class StatementKindTransformation : ContentLifting.LiftContent.StatementKindTransformation { public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } @@ -791,16 +790,16 @@ private bool IsScopeSingleCall(QsScope contents) public override QsStatementKind OnConditionalStatement(QsConditionalStatement stm) { var contextValidScope = SharedState.IsValidScope; - var contextHoistParams = SharedState.CurrentHoistParams; + var contextParams = SharedState.GeneratedOpParams; - var isHoistValid = true; + var isValidLift = true; var newConditionBlocks = new List>(); var generatedOperations = new List(); foreach (var conditionBlock in stm.ConditionalBlocks) { SharedState.IsValidScope = true; - SharedState.CurrentHoistParams = conditionBlock.Item2.Body.KnownSymbols.IsEmpty + SharedState.GeneratedOpParams = conditionBlock.Item2.Body.KnownSymbols.IsEmpty ? ImmutableArray>>.Empty : conditionBlock.Item2.Body.KnownSymbols.Variables; @@ -810,12 +809,12 @@ public override QsStatementKind OnConditionalStatement(QsConditionalStatement st // the condition logic for the conversion and using that condition here //var (isExprCondition, _, _) = IsConditionedOnResultLiteralExpression(expr.Item); - if (SharedState.IsValidScope) // if sub-scope is valid, hoist content + if (SharedState.IsValidScope) // if sub-scope is valid, lift content { if (/*isExprCondition &&*/ !IsScopeSingleCall(block.Body)) { - // Hoist the scope to its own operation - var (callable, call) = SharedState.HoistBody(block.Body); + // Lift the scope to its own operation + var (callable, call) = SharedState.LiftBody(block.Body); block = new QsPositionedBlock( new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), block.Location, @@ -830,26 +829,26 @@ public override QsStatementKind OnConditionalStatement(QsConditionalStatement st } else { - isHoistValid = false; + isValidLift = false; break; } } var newDefault = QsNullable.Null; - if (isHoistValid && stm.Default.IsValue) + if (isValidLift && stm.Default.IsValue) { SharedState.IsValidScope = true; - SharedState.CurrentHoistParams = stm.Default.Item.Body.KnownSymbols.IsEmpty + SharedState.GeneratedOpParams = stm.Default.Item.Body.KnownSymbols.IsEmpty ? ImmutableArray>>.Empty : stm.Default.Item.Body.KnownSymbols.Variables; var (_, block) = this.OnPositionedBlock(QsNullable.Null, stm.Default.Item); if (SharedState.IsValidScope) { - if (!IsScopeSingleCall(block.Body)) // if sub-scope is valid, hoist content + if (!IsScopeSingleCall(block.Body)) // if sub-scope is valid, lift content { - // Hoist the scope to its own operation - var (callable, call) = SharedState.HoistBody(block.Body); + // Lift the scope to its own operation + var (callable, call) = SharedState.LiftBody(block.Body); block = new QsPositionedBlock( new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), block.Location, @@ -864,19 +863,19 @@ public override QsStatementKind OnConditionalStatement(QsConditionalStatement st } else { - isHoistValid = false; + isValidLift = false; } } - if (isHoistValid) + if (isValidLift) { SharedState.GeneratedOperations.AddRange(generatedOperations); } - SharedState.CurrentHoistParams = contextHoistParams; + SharedState.GeneratedOpParams = contextParams; SharedState.IsValidScope = contextValidScope; - return isHoistValid + return isValidLift ? QsStatementKind.NewQsConditionalStatement( new QsConditionalStatement(newConditionBlocks.ToImmutableArray(), newDefault)) : QsStatementKind.NewQsConditionalStatement( diff --git a/src/QsCompiler/Transformations/HoistContent.cs b/src/QsCompiler/Transformations/ContentLifting.cs similarity index 91% rename from src/QsCompiler/Transformations/HoistContent.cs rename to src/QsCompiler/Transformations/ContentLifting.cs index 0b0f24e3eb..9ddf74b0dd 100644 --- a/src/QsCompiler/Transformations/HoistContent.cs +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -12,22 +12,23 @@ using Microsoft.Quantum.QsCompiler.Transformations.SearchAndReplace; -namespace Microsoft.Quantum.QsCompiler.Transformations.LyftContent +namespace Microsoft.Quantum.QsCompiler.Transformations.ContentLifting { using ExpressionKind = QsExpressionKind; using ResolvedTypeKind = QsTypeKind; using TypeArgsResolution = ImmutableArray, ResolvedType>>; /// - /// Transformation handling the first pass task of hoisting of the contents of conditional statements. - /// If blocks are first validated to see if they can safely be hoisted into their own operation. + /// Transformation handling the task of lifting the contents of code blocks into generated operations. + /// The transformation provides validation to see if any given block can safely be lifted into its own operation. /// Validation requirements are that there are no return statements and that there are no set statements /// on mutables declared outside the block. Setting mutables declared inside the block is valid. - /// If the block is valid, and there is more than one statement in the block, a new operation with the - /// block's contents is generated, having all the same type parameters as the calling context - /// and all known variables at the start of the block become parameters to the new operation. - /// The contents of the conditional block are then replaced with a call to the new operation with all - /// the type parameters and known variables being forwarded to the new operation as arguments. + /// A block can be checked by setting the SharedState.IsValidScope to true before traversing the scope, + /// then checking the SharedState.IsValidScope after traversal. Blocks should be validated before calling + /// the SharedState.LiftBody function, which will generate a new operation with the block's contents, + /// having all the same type parameters as the calling context and all known variables at the start of + /// the block become parameters to the new operation. A call to the new operation is also returned with + /// all the type parameters and known variables being forwarded to the new operation as arguments. /// public class LiftContent : SyntaxTreeTransformation { @@ -63,9 +64,9 @@ public class TransformationState { public bool IsValidScope = true; public List GeneratedOperations = null; - public ImmutableArray>> CurrentHoistParams = + public ImmutableArray>> GeneratedOpParams = ImmutableArray>>.Empty; - public bool ContainsHoistParamRef = false; + public bool ContainsParamRef = false; public CallableDetails CurrentCallable = null; public bool InBody = false; @@ -217,8 +218,16 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature return (generatedCallable, signature.ArgumentType); } - // ToDo: Doc comment - public (QsCallable, QsStatement) HoistBody(QsScope body) + /// + /// Generates a new operation with the body's contents, having all the same type parameters + /// as the current callable and all known variables at the start of the block become + /// parameters to the new operation. The generated operation is returned, along with a call + /// to the new operation is also returned with all the type parameters and known + /// variables being forwarded to the new operation as arguments. + /// + /// The given body should be validated with the SharedState.IsValidScope before using this function. + /// + public (QsCallable, QsStatement) LiftBody(QsScope body) { var (generatedOp, originalArgumentType) = GenerateOperation(body); var generatedOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( @@ -336,7 +345,7 @@ public override QsSpecialization OnControlledSpecialization(QsSpecialization spe return rtrn; } - public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being hoisted + public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being lifted public override QsNamespace OnNamespace(QsNamespace ns) { @@ -374,7 +383,7 @@ public override QsStatementKind OnValueUpdate(QsValueUpdate stm) // If lhs contains an identifier found in the scope's known variables (variables from the super-scope), the scope is not valid var lhs = this.Expressions.OnTypedExpression(stm.Lhs); - if (SharedState.ContainsHoistParamRef) + if (SharedState.ContainsParamRef) { SharedState.IsValidScope = false; } @@ -385,7 +394,7 @@ public override QsStatementKind OnValueUpdate(QsValueUpdate stm) public override QsStatementKind OnStatementKind(QsStatementKind kind) { - SharedState.ContainsHoistParamRef = false; // Every statement kind starts off false + SharedState.ContainsParamRef = false; // Every statement kind starts off false return base.OnStatementKind(kind); } } @@ -396,16 +405,13 @@ public ExpressionTransformation(SyntaxTreeTransformation pa public override TypedExpression OnTypedExpression(TypedExpression ex) { - var contextContainsHoistParamRef = SharedState.ContainsHoistParamRef; - SharedState.ContainsHoistParamRef = false; + var contextContainsParamRef = SharedState.ContainsParamRef; + SharedState.ContainsParamRef = false; var rtrn = base.OnTypedExpression(ex); // If the sub context contains a reference, then the super context contains a reference, // otherwise return the super context to its original value - if (!SharedState.ContainsHoistParamRef) - { - SharedState.ContainsHoistParamRef = contextContainsHoistParamRef; - } + SharedState.ContainsParamRef |= contextContainsParamRef; return rtrn; } @@ -418,9 +424,9 @@ public ExpressionKindTransformation(SyntaxTreeTransformation> tArgs) { if (sym is Identifier.LocalVariable local && - SharedState.CurrentHoistParams.Any(param => param.VariableName.Equals(local.Item))) + SharedState.GeneratedOpParams.Any(param => param.VariableName.Equals(local.Item))) { - SharedState.ContainsHoistParamRef = true; + SharedState.ContainsParamRef = true; } return base.OnIdentifier(sym, tArgs); } From 1e5642794312414bcb8d6d2897917712d961acfc Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 10 Mar 2020 10:54:48 -0700 Subject: [PATCH 068/135] Added ToDo's for type specialization. Moved the logic that blocks function lifting to the classical-control specific lifting transformation. --- src/QsCompiler/Transformations/ClassicallyControlled.cs | 8 ++++++++ src/QsCompiler/Transformations/ContentLifting.cs | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index 549cf6f572..0cd440574f 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -770,9 +770,17 @@ private class LiftContent : ContentLifting.LiftContent { public LiftContent() : base() { + this.Namespaces = new NamespaceTransformation(this); this.StatementKinds = new StatementKindTransformation(this); } + private new class NamespaceTransformation : ContentLifting.LiftContent.NamespaceTransformation + { + public NamespaceTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being lifted + } + private new class StatementKindTransformation : ContentLifting.LiftContent.StatementKindTransformation { public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } diff --git a/src/QsCompiler/Transformations/ContentLifting.cs b/src/QsCompiler/Transformations/ContentLifting.cs index 9ddf74b0dd..43255a0a37 100644 --- a/src/QsCompiler/Transformations/ContentLifting.cs +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -44,9 +44,11 @@ public class CallableDetails public CallableDetails(QsCallable callable) { Callable = callable; + // ToDo: this may need to be adapted once we support type specializations Adjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsAdjoint); Controlled = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlled); ControlledAdjoint = callable.Specializations.FirstOrDefault(spec => spec.Kind == QsSpecializationKind.QsControlledAdjoint); + // ToDo: this may need to be per-specialization TypeParameters = callable.Signature.TypeParameters.Any(param => param.IsValidName) ? QsNullable>.NewValue(callable.Signature.TypeParameters .Where(param => param.IsValidName) @@ -345,8 +347,6 @@ public override QsSpecialization OnControlledSpecialization(QsSpecialization spe return rtrn; } - public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being lifted - public override QsNamespace OnNamespace(QsNamespace ns) { // Generated operations list will be populated in the transform From 5a823a3081d14cc8218dd68b3b90970d3f801234 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 10 Mar 2020 11:17:57 -0700 Subject: [PATCH 069/135] Put the function-blocking logic back into base lifting transformation. --- src/QsCompiler/Transformations/ClassicallyControlled.cs | 8 -------- src/QsCompiler/Transformations/ContentLifting.cs | 2 ++ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index 0cd440574f..549cf6f572 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -770,17 +770,9 @@ private class LiftContent : ContentLifting.LiftContent { public LiftContent() : base() { - this.Namespaces = new NamespaceTransformation(this); this.StatementKinds = new StatementKindTransformation(this); } - private new class NamespaceTransformation : ContentLifting.LiftContent.NamespaceTransformation - { - public NamespaceTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being lifted - } - private new class StatementKindTransformation : ContentLifting.LiftContent.StatementKindTransformation { public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } diff --git a/src/QsCompiler/Transformations/ContentLifting.cs b/src/QsCompiler/Transformations/ContentLifting.cs index 43255a0a37..322edd6f4e 100644 --- a/src/QsCompiler/Transformations/ContentLifting.cs +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -347,6 +347,8 @@ public override QsSpecialization OnControlledSpecialization(QsSpecialization spe return rtrn; } + public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being lifted + public override QsNamespace OnNamespace(QsNamespace ns) { // Generated operations list will be populated in the transform From 16f5b7288fcc9852e19162e29b8304ea1a440879 Mon Sep 17 00:00:00 2001 From: bettinaheim <34236215+bettinaheim@users.noreply.github.com> Date: Tue, 10 Mar 2020 15:54:53 -0700 Subject: [PATCH 070/135] Access modifiers for callable lifting PR (#366) --- .../Transformations/ContentLifting.cs | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/QsCompiler/Transformations/ContentLifting.cs b/src/QsCompiler/Transformations/ContentLifting.cs index 322edd6f4e..10ca5eaf26 100644 --- a/src/QsCompiler/Transformations/ContentLifting.cs +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -32,16 +32,15 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ContentLifting /// public class LiftContent : SyntaxTreeTransformation { - - public class CallableDetails + internal class CallableDetails { - public QsCallable Callable; - public QsSpecialization Adjoint; - public QsSpecialization Controlled; - public QsSpecialization ControlledAdjoint; - public QsNullable> TypeParameters; + internal readonly QsCallable Callable; + internal readonly QsSpecialization Adjoint; + internal readonly QsSpecialization Controlled; + internal readonly QsSpecialization ControlledAdjoint; + internal readonly QsNullable> TypeParameters; - public CallableDetails(QsCallable callable) + internal CallableDetails(QsCallable callable) { Callable = callable; // ToDo: this may need to be adapted once we support type specializations @@ -64,17 +63,22 @@ public CallableDetails(QsCallable callable) public class TransformationState { + // ToDo: It should be possible to make these three properties private, + // if we absorb the corresponding logic into LiftBody. public bool IsValidScope = true; - public List GeneratedOperations = null; - public ImmutableArray>> GeneratedOpParams = + internal bool ContainsParamRef = false; + internal ImmutableArray>> GeneratedOpParams = ImmutableArray>>.Empty; - public bool ContainsParamRef = false; - public CallableDetails CurrentCallable = null; - public bool InBody = false; - public bool InAdjoint = false; - public bool InControlled = false; - public bool InWithinBlock = false; + internal CallableDetails CurrentCallable = null; + + protected internal bool InBody = false; + protected internal bool InAdjoint = false; + protected internal bool InControlled = false; + protected internal bool InControlledAdjoint = false; + protected internal bool InWithinBlock = false; + + protected internal List GeneratedOperations = null; private (ResolvedSignature, IEnumerable) MakeSpecializations( QsQualifiedName callableName, ResolvedType paramsType, SpecializationImplementation bodyImplementation) @@ -304,7 +308,7 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature } } - protected LiftContent() : base(new TransformationState()) + public LiftContent() : base(new TransformationState()) { this.Namespaces = new NamespaceTransformation(this); this.StatementKinds = new StatementKindTransformation(this); @@ -347,6 +351,14 @@ public override QsSpecialization OnControlledSpecialization(QsSpecialization spe return rtrn; } + public override QsSpecialization OnControlledAdjointSpecialization(QsSpecialization spec) + { + SharedState.InControlledAdjoint = true; + var rtrn = base.OnControlledAdjointSpecialization(spec); + SharedState.InControlledAdjoint = false; + return rtrn; + } + public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being lifted public override QsNamespace OnNamespace(QsNamespace ns) From cd00b3c01bbb142c724bd6718e7c11a143a0e9ea Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 10 Mar 2020 16:20:12 -0700 Subject: [PATCH 071/135] WIP --- src/QsCompiler/Core/Dependencies.fs | 1 - src/QsCompiler/Transformations/ClassicallyControlled.cs | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index 36f4bf7d75..acc11d8d8b 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -6,7 +6,6 @@ namespace Microsoft.Quantum.QsCompiler open System.Collections.Immutable open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.SyntaxTree -open Microsoft.Quantum.QsCompiler.SyntaxTokens type BuiltIn = { diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index 549cf6f572..3d723c99f1 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -783,8 +783,9 @@ private bool IsScopeSingleCall(QsScope contents) return contents.Statements[0].Statement is QsStatementKind.QsExpressionStatement expr && expr.Item.Expression is ExpressionKind.CallLikeExpression call - && !TypedExpression.IsPartialApplication(expr.Item.Expression) - && call.Item1.Expression is ExpressionKind.Identifier; + && call.Item1.ResolvedType.Resolution.IsOperation + && call.Item1.Expression is ExpressionKind.Identifier + && !TypedExpression.IsPartialApplication(expr.Item.Expression); } public override QsStatementKind OnConditionalStatement(QsConditionalStatement stm) From 246f89d3a615421b703362f2703891dd020d8594 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Tue, 10 Mar 2020 16:58:54 -0700 Subject: [PATCH 072/135] small fixes --- .../Transformations/ClassicallyControlled.cs | 2 +- .../Transformations/ContentLifting.cs | 24 ++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index 3d723c99f1..ad96e0cdc3 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -156,7 +156,7 @@ private TypeArgsResolution GetCombinedTypeResolution(TypeArgsResolution outer, T } /// - /// Gets an identifier and argument tuple for the built in operation NoOp. + /// Gets an identifier and argument tuple for the built-in operation NoOp. /// private (TypedExpression, TypedExpression) GetNoOp() { diff --git a/src/QsCompiler/Transformations/ContentLifting.cs b/src/QsCompiler/Transformations/ContentLifting.cs index 10ca5eaf26..5a9ac8a99f 100644 --- a/src/QsCompiler/Transformations/ContentLifting.cs +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -19,16 +19,17 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ContentLifting using TypeArgsResolution = ImmutableArray, ResolvedType>>; /// - /// Transformation handling the task of lifting the contents of code blocks into generated operations. + /// This transformation handles the task of lifting the contents of code blocks into generated operations. /// The transformation provides validation to see if any given block can safely be lifted into its own operation. /// Validation requirements are that there are no return statements and that there are no set statements /// on mutables declared outside the block. Setting mutables declared inside the block is valid. /// A block can be checked by setting the SharedState.IsValidScope to true before traversing the scope, /// then checking the SharedState.IsValidScope after traversal. Blocks should be validated before calling - /// the SharedState.LiftBody function, which will generate a new operation with the block's contents, - /// having all the same type parameters as the calling context and all known variables at the start of - /// the block become parameters to the new operation. A call to the new operation is also returned with - /// all the type parameters and known variables being forwarded to the new operation as arguments. + /// the SharedState.LiftBody function, which will generate a new operation with the block's contents. + /// All the known variables at the start of the block will become parameters to the new operation, and + /// the operation will have all the valid type parameters of the calling context as type parameters. + /// A call to the new operation is also returned with all the valid type parameters and known variables + /// being forwarded to the new operation as arguments. /// public class LiftContent : SyntaxTreeTransformation { @@ -225,11 +226,12 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature } /// - /// Generates a new operation with the body's contents, having all the same type parameters - /// as the current callable and all known variables at the start of the block become - /// parameters to the new operation. The generated operation is returned, along with a call - /// to the new operation is also returned with all the type parameters and known - /// variables being forwarded to the new operation as arguments. + /// Generates a new operation with the body's contents. All the known variables at the + /// start of the block will become parameters to the new operation, and the operation + /// will have all the valid type parameters of the calling context as type parameters. + /// The generated operation is returned, along with a call to the new operation is + /// also returned with all the type parameters and known variables being forwarded to + /// the new operation as arguments. /// /// The given body should be validated with the SharedState.IsValidScope before using this function. /// @@ -242,7 +244,7 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature ResolvedType.New(ResolvedTypeKind.UnitType)), generatedOp.Signature.Information)); - // Foreword the type parameters of the parent callable to the type arguments of the call to the generated operation. + // Forward the type parameters of the parent callable to the type arguments of the call to the generated operation. var typeArguments = CurrentCallable.TypeParameters; var generatedOpId = new TypedExpression( ExpressionKind.NewIdentifier( From 2cf5a3578843b5c4486336f196ef359f79923499 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 10 Mar 2020 20:37:28 -0700 Subject: [PATCH 073/135] test --- .../Tests.Compiler/CommandLineTests.fs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs index a880acafb0..cbd52665f5 100644 --- a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs +++ b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs @@ -155,6 +155,29 @@ let ``diagnose outputs`` () = Assert.Equal(ReturnCode.INVALID_ARGUMENTS, result) +[] +let ``options from response files`` () = + let configFile = ("TestCases", "qsc-config.txt") |> Path.Combine + let configArgs = + [| + "-i" + ("TestCases","General.qs") |> Path.Combine + "--trim 0" + |] + File.WriteAllText(configFile, String.Join (" ", configArgs)) + let commandLineArgs = + [| + "-i" + ("TestCases","FunctorGeneration.qs") |> Path.Combine + "--trim 2" + "--response-files" + configFile + |] + + let result = Program.Main commandLineArgs + Assert.Equal(ReturnCode.SUCCESS, result) + + [] let ``generate docs`` () = let docsFolder = ("TestCases", "docs.Out") |> Path.Combine From 28d6d787b31a32fd47501514beb5ec48e4e29960 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 10 Mar 2020 20:58:35 -0700 Subject: [PATCH 074/135] fixing typo --- src/QsCompiler/CommandLineTool/Commands/Build.cs | 2 +- src/QsCompiler/CommandLineTool/Options.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index c2dd49c0c2..9450a86433 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -129,7 +129,7 @@ public static int Run(BuildOptions options, ConsoleLogger logger) options.PerfFolder ??= fromResponseFiles.PerfFolder; options.Plugins = (options.Plugins ?? new string[0]).Concat(fromResponseFiles.Plugins ?? new string[0]); options.ProjectName ??= fromResponseFiles.ProjectName; - options.References = (options.References ?? new string[0]).Concat(options.References ?? new string[0]); + options.References = (options.References ?? new string[0]).Concat(fromResponseFiles.References ?? new string[0]); options.ResponseFiles = fromResponseFiles.ResponseFiles; options.TargetPackage ??= fromResponseFiles.TargetPackage; options.TrimLevel = options.TrimLevel != DefaultOptions.TrimLevel ? options.TrimLevel : fromResponseFiles.TrimLevel; diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs index 82b4ed88e6..5b3b5ea8ed 100644 --- a/src/QsCompiler/CommandLineTool/Options.cs +++ b/src/QsCompiler/CommandLineTool/Options.cs @@ -38,7 +38,7 @@ public class CompilationOptions : Options HelpText = "[Experimental feature] Path to the .NET Core dll(s) defining additional transformations to include in the compilation process.")] public IEnumerable Plugins { get; set; } - [Option("target-package", Required = false, + [Option("target-package", Required = false, SetName = CODE_MODE, HelpText = "Path to the NuGet package containing target specific information and implementations.")] public string TargetPackage { get; set; } From 5a26e212a549c3cc471d6dfff84e4b69b7c58255 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 10 Mar 2020 21:22:23 -0700 Subject: [PATCH 075/135] going with this test --- src/QsCompiler/CommandLineTool/Commands/Build.cs | 2 +- src/QsCompiler/CommandLineTool/Options.cs | 1 - src/QsCompiler/Tests.Compiler/CommandLineTests.fs | 11 +++++++---- .../Tests.Compiler/LocalVerificationTests.fs | 6 +++++- .../Tests.Compiler/TestCases/LinkingTests/Core.qs | 6 ------ .../TestCases/LinkingTests/Diagnostics.qs | 9 +++++++++ src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj | 3 +++ 7 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Diagnostics.qs diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index 9450a86433..1ec8d2c9e6 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -34,7 +34,7 @@ public static IEnumerable UsageExamples } } - [Option("response-files", Required = true, SetName = RESPONSE_FILES, + [Option("response-files", Required = false, HelpText = "Response file(s) providing command arguments. Required only if no other arguments are specified. Non-default values for options specified via command line take precedence.")] public IEnumerable ResponseFiles { get; set; } diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs index 5b3b5ea8ed..60acbbeef3 100644 --- a/src/QsCompiler/CommandLineTool/Options.cs +++ b/src/QsCompiler/CommandLineTool/Options.cs @@ -82,7 +82,6 @@ public enum LogFormat // Note: items in one set are mutually exclusive with items from other sets protected const string CODE_MODE = "codeMode"; protected const string SNIPPET_MODE = "snippetMode"; - protected const string RESPONSE_FILES = "responseFiles"; [Option('v', "verbosity", Required = false, Default = DefaultOptions.Verbosity, HelpText = "Specifies the verbosity of the logged output. Valid values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic].")] diff --git a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs index cbd52665f5..5dbc692479 100644 --- a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs +++ b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs @@ -161,15 +161,18 @@ let ``options from response files`` () = let configArgs = [| "-i" - ("TestCases","General.qs") |> Path.Combine - "--trim 0" + ("TestCases","LinkingTests","Core.qs") |> Path.Combine + "--trim" + "0" |] File.WriteAllText(configFile, String.Join (" ", configArgs)) let commandLineArgs = [| + "build" "-i" - ("TestCases","FunctorGeneration.qs") |> Path.Combine - "--trim 2" + ("TestCases","LinkingTests","Diagnostics.qs") |> Path.Combine + "--trim" + "2" "--response-files" configFile |] diff --git a/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs b/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs index 2570fd7295..6cc5e4fc59 100644 --- a/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs @@ -14,7 +14,11 @@ open Xunit.Abstractions type LocalVerificationTests (output:ITestOutputHelper) = - inherit CompilerTests(CompilerTests.Compile "TestCases" ["General.qs"; "LocalVerification.qs"; "Types.qs"; Path.Combine ("LinkingTests", "Core.qs")], output) + inherit CompilerTests( + CompilerTests.Compile "TestCases" [ + "General.qs"; "LocalVerification.qs"; "Types.qs"; + Path.Combine ("LinkingTests", "Core.qs"); Path.Combine ("LinkingTests", "Diagnostics.qs") + ], output) member private this.Expect name (diag : IEnumerable) = let ns = "Microsoft.Quantum.Testing.LocalVerification" |> NonNullable<_>.New diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Core.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Core.qs index 66fe60a6f3..64005ce6d0 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Core.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Core.qs @@ -19,9 +19,3 @@ namespace Microsoft.Quantum.Core { } } -namespace Microsoft.Quantum.Diagnostics { - - // needs to be available for testing - @ Attribute() - newtype Test = String; -} \ No newline at end of file diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Diagnostics.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Diagnostics.qs new file mode 100644 index 0000000000..ad5b6e9b25 --- /dev/null +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Diagnostics.qs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Diagnostics { + + // needs to be available for testing + @ Attribute() + newtype Test = String; +} \ No newline at end of file diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index b7f2118e59..a3e4648e0d 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -23,6 +23,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest From d4b629342e3d75c490bf1f47381ec7c738fcb56b Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 10 Mar 2020 22:14:21 -0700 Subject: [PATCH 076/135] file structure --- src/QsCompiler/Compiler/CompilationLoader.cs | 227 ++++++++++-------- .../TestCases/LinkingTests/Diagnostics.qs | 3 +- 2 files changed, 127 insertions(+), 103 deletions(-) diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 0584750f03..425844ffff 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -428,38 +428,9 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference if (!Uri.TryCreate(Assembly.GetExecutingAssembly().CodeBase, UriKind.Absolute, out Uri thisDllUri)) { thisDllUri = new Uri(Path.GetFullPath(".", "CompilationLoader.cs")); } - QsCompilation ExecuteAsAtomicTransformation(RewriteSteps.LoadedStep rewriteStep, ref Status status) - { - status = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out var transformed); - return status == Status.Succeeded ? transformed : this.CompilationOutput; - } - if (!String.IsNullOrWhiteSpace(this.Config.TargetPackageAssembly)) { - try - { - var targetDll = Path.GetFullPath(this.Config.TargetPackageAssembly); - var loaded = AssemblyLoader.LoadReferencedAssembly( - targetDll, - out var targetIntrinsics, - ex => this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ex)); - - if (loaded) - { - var rewriteStep = new RewriteSteps.LoadedStep(new IntrinsicResolution(targetIntrinsics), typeof(IRewriteStep), thisDllUri); - this.CompilationOutput = ExecuteAsAtomicTransformation(rewriteStep, ref this.CompilationStatus.TargetSpecificReplacements); - } - else - { - this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ErrorCode.FailedToLoadTargetPackageAssembly, new[] { targetDll }); - targetIntrinsics = null; - } - } - catch (Exception ex) - { - this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ErrorCode.InvalidTargetPackageAssemblyPath, new[] { this.Config.TargetPackageAssembly }); - this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ex); - } + this.ReplaceTargetSpecificImplementations(thisDllUri, out this.CompilationOutput); } if (this.Config.ConvertClassicalControl) @@ -550,66 +521,6 @@ QsCompilation ExecuteAsAtomicTransformation(RewriteSteps.LoadedStep rewriteStep, RaiseCompilationTaskEnd(null, "OverallCompilation"); } - /// - /// Executes the given rewrite step on the given compilation, returning a transformed compilation as an out parameter. - /// Catches and logs any thrown exception. Returns the status of the rewrite step. - /// Throws an ArgumentNullException if the rewrite step to execute or the given compilation is null. - /// - private Status ExecuteRewriteStep(RewriteSteps.LoadedStep rewriteStep, QsCompilation compilation, out QsCompilation transformed) - { - if (rewriteStep == null) throw new ArgumentNullException(nameof(rewriteStep)); - if (compilation == null) throw new ArgumentNullException(nameof(compilation)); - - string GetDiagnosticsCode(DiagnosticSeverity severity) => - rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Error ? Errors.Code(ErrorCode.CsharpGenerationGeneratedError) : - rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Warning ? Warnings.Code(WarningCode.CsharpGenerationGeneratedWarning) : - rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Information ? Informations.Code(InformationCode.CsharpGenerationGeneratedInfo) : - null; - - Status LogDiagnostics(Status status = Status.Succeeded) - { - try - { - foreach (var diagnostic in rewriteStep.GeneratedDiagnostics ?? ImmutableArray.Empty) - { this.LogAndUpdate(ref status, RewriteSteps.LoadedStep.ConvertDiagnostic(diagnostic, GetDiagnosticsCode)); } - } - catch { this.LogAndUpdate(ref status, Warning(WarningCode.RewriteStepDiagnosticsGenerationFailed, new[] { rewriteStep.Name })); } - return status; - } - - var status = Status.Succeeded; - var messageSource = ProjectManager.MessageSource(rewriteStep.Origin); - Diagnostic Warning(WarningCode code, params string[] args) => Warnings.LoadWarning(code, args, messageSource); - try - { - transformed = compilation; - var preconditionFailed = rewriteStep.ImplementsPreconditionVerification && !rewriteStep.PreconditionVerification(compilation); - if (preconditionFailed) - { - LogDiagnostics(); - this.LogAndUpdate(ref status, Warning(WarningCode.PreconditionVerificationFailed, new[] { rewriteStep.Name, messageSource })); - return status; - } - - var transformationFailed = rewriteStep.ImplementsTransformation && !rewriteStep.Transformation(compilation, out transformed); - var postconditionFailed = this.Config.EnableAdditionalChecks && rewriteStep.ImplementsPostconditionVerification && !rewriteStep.PostconditionVerification(transformed); - LogDiagnostics(); - - if (transformationFailed) this.LogAndUpdate(ref status, ErrorCode.RewriteStepExecutionFailed, new[] { rewriteStep.Name, messageSource }); - if (postconditionFailed) this.LogAndUpdate(ref status, ErrorCode.PostconditionVerificationFailed, new[] { rewriteStep.Name, messageSource }); - return status; - } - catch (Exception ex) - { - this.LogAndUpdate(ref status, ex); - var isLoadException = ex is FileLoadException || ex.InnerException is FileLoadException; - if (isLoadException) this.LogAndUpdate(ref status, ErrorCode.FileNotFoundDuringPluginExecution, new[] { rewriteStep.Name, messageSource }); - else this.LogAndUpdate(ref status, ErrorCode.PluginExecutionFailed, new[] { rewriteStep.Name, messageSource }); - transformed = null; - } - return status; - } - /// /// Builds the compilation of the specified source files and references, /// executing the compilation steps specified by the given options. @@ -727,6 +638,130 @@ private void PrintLoadedRewriteSteps(IEnumerable rewrit } + // private helper methods used during construction + + /// + /// Raises a compilation task start event. + /// + private void RaiseCompilationTaskStart(string parentTaskName, string taskName) => + CompilationTaskEvent?.Invoke(this, new CompilationTaskEventArgs(CompilationTaskEventType.Start, parentTaskName, taskName)); + + /// + /// Raises a compilation task end event. + /// + private void RaiseCompilationTaskEnd(string parentTaskName, string taskName) => + CompilationTaskEvent?.Invoke(this, new CompilationTaskEventArgs(CompilationTaskEventType.End, parentTaskName, taskName)); + + /// + /// Executes the given rewrite step on the current CompilationOutput, and updates the given status accordingly. + /// Sets the CompilationOutput to the transformed compilation if the status indicates success. + /// + private QsCompilation ExecuteAsAtomicTransformation(RewriteSteps.LoadedStep rewriteStep, ref Status status) + { + status = this.ExecuteRewriteStep(rewriteStep, this.CompilationOutput, out var transformed); + return status == Status.Succeeded ? transformed : this.CompilationOutput; + } + + /// + /// Attempts to load the target package assembly specified in the configuration, + /// logging diagnostics when the loading fails or the corresponding configuration is not specified. + /// Updates the compilation status accordingly. + /// Executes the transformation to replace target specific implementations as atomic rewrite step, + /// returning the transformed compilation as out parameter. + /// Sets the out parameter to the unmodified CompilationOutput if the replacement fails. + /// Returns a boolean value indicating whether the returned compilation has been modified. + /// + private bool ReplaceTargetSpecificImplementations(Uri rewriteStepOrigin, out QsCompilation transformed) + { + try + { + var targetDll = Path.GetFullPath(this.Config.TargetPackageAssembly); + var loaded = AssemblyLoader.LoadReferencedAssembly( + targetDll, + out var targetIntrinsics, + ex => this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ex)); + + if (loaded) + { + var rewriteStep = new RewriteSteps.LoadedStep(new IntrinsicResolution(targetIntrinsics), typeof(IRewriteStep), rewriteStepOrigin); + transformed = ExecuteAsAtomicTransformation(rewriteStep, ref this.CompilationStatus.TargetSpecificReplacements); + return true; + } + else + { + this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ErrorCode.FailedToLoadTargetPackageAssembly, new[] { targetDll }); + } + } + catch (Exception ex) + { + this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ErrorCode.InvalidTargetPackageAssemblyPath, new[] { this.Config.TargetPackageAssembly }); + this.LogAndUpdate(ref this.CompilationStatus.TargetSpecificReplacements, ex); + } + transformed = this.CompilationOutput; + return false; + } + + /// + /// Executes the given rewrite step on the given compilation, returning a transformed compilation as an out parameter. + /// Catches and logs any thrown exception. Returns the status of the rewrite step. + /// Throws an ArgumentNullException if the rewrite step to execute or the given compilation is null. + /// + private Status ExecuteRewriteStep(RewriteSteps.LoadedStep rewriteStep, QsCompilation compilation, out QsCompilation transformed) + { + if (rewriteStep == null) throw new ArgumentNullException(nameof(rewriteStep)); + if (compilation == null) throw new ArgumentNullException(nameof(compilation)); + + string GetDiagnosticsCode(DiagnosticSeverity severity) => + rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Error ? Errors.Code(ErrorCode.CsharpGenerationGeneratedError) : + rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Warning ? Warnings.Code(WarningCode.CsharpGenerationGeneratedWarning) : + rewriteStep.Name == "CsharpGeneration" && severity == DiagnosticSeverity.Information ? Informations.Code(InformationCode.CsharpGenerationGeneratedInfo) : + null; + + Status LogDiagnostics(Status status = Status.Succeeded) + { + try + { + foreach (var diagnostic in rewriteStep.GeneratedDiagnostics ?? ImmutableArray.Empty) + { this.LogAndUpdate(ref status, RewriteSteps.LoadedStep.ConvertDiagnostic(diagnostic, GetDiagnosticsCode)); } + } + catch { this.LogAndUpdate(ref status, Warning(WarningCode.RewriteStepDiagnosticsGenerationFailed, new[] { rewriteStep.Name })); } + return status; + } + + var status = Status.Succeeded; + var messageSource = ProjectManager.MessageSource(rewriteStep.Origin); + Diagnostic Warning(WarningCode code, params string[] args) => Warnings.LoadWarning(code, args, messageSource); + try + { + transformed = compilation; + var preconditionFailed = rewriteStep.ImplementsPreconditionVerification && !rewriteStep.PreconditionVerification(compilation); + if (preconditionFailed) + { + LogDiagnostics(); + this.LogAndUpdate(ref status, Warning(WarningCode.PreconditionVerificationFailed, new[] { rewriteStep.Name, messageSource })); + return status; + } + + var transformationFailed = rewriteStep.ImplementsTransformation && !rewriteStep.Transformation(compilation, out transformed); + var postconditionFailed = this.Config.EnableAdditionalChecks && rewriteStep.ImplementsPostconditionVerification && !rewriteStep.PostconditionVerification(transformed); + LogDiagnostics(); + + if (transformationFailed) this.LogAndUpdate(ref status, ErrorCode.RewriteStepExecutionFailed, new[] { rewriteStep.Name, messageSource }); + if (postconditionFailed) this.LogAndUpdate(ref status, ErrorCode.PostconditionVerificationFailed, new[] { rewriteStep.Name, messageSource }); + return status; + } + catch (Exception ex) + { + this.LogAndUpdate(ref status, ex); + var isLoadException = ex is FileLoadException || ex.InnerException is FileLoadException; + if (isLoadException) this.LogAndUpdate(ref status, ErrorCode.FileNotFoundDuringPluginExecution, new[] { rewriteStep.Name, messageSource }); + else this.LogAndUpdate(ref status, ErrorCode.PluginExecutionFailed, new[] { rewriteStep.Name, messageSource }); + transformed = null; + } + return status; + } + + // routines for loading from and dumping to files /// @@ -951,17 +986,5 @@ string FullDirectoryName(string dir) => File.WriteAllText(targetFile, content); return targetFile; } - - /// - /// Raises a compilation task start event. - /// - private void RaiseCompilationTaskStart (string parentTaskName, string taskName) => - CompilationTaskEvent?.Invoke(this, new CompilationTaskEventArgs(CompilationTaskEventType.Start, parentTaskName, taskName)); - - /// - /// Raises a compilation task end event. - /// - private void RaiseCompilationTaskEnd(string parentTaskName, string taskName) => - CompilationTaskEvent?.Invoke(this, new CompilationTaskEventArgs(CompilationTaskEventType.End, parentTaskName, taskName)); } } diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Diagnostics.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Diagnostics.qs index ad5b6e9b25..6a691e0946 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Diagnostics.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/Diagnostics.qs @@ -6,4 +6,5 @@ namespace Microsoft.Quantum.Diagnostics { // needs to be available for testing @ Attribute() newtype Test = String; -} \ No newline at end of file +} + From e973d379a439161ba272ad87bced4d524bf4c881 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 10 Mar 2020 23:32:36 -0700 Subject: [PATCH 077/135] let's stick with that --- src/QsCompiler/CommandLineTool/Commands/Build.cs | 2 +- src/QsCompiler/CommandLineTool/Options.cs | 1 + src/QsCompiler/Tests.Compiler/CommandLineTests.fs | 3 +-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index 1ec8d2c9e6..9450a86433 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -34,7 +34,7 @@ public static IEnumerable UsageExamples } } - [Option("response-files", Required = false, + [Option("response-files", Required = true, SetName = RESPONSE_FILES, HelpText = "Response file(s) providing command arguments. Required only if no other arguments are specified. Non-default values for options specified via command line take precedence.")] public IEnumerable ResponseFiles { get; set; } diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs index 60acbbeef3..5b3b5ea8ed 100644 --- a/src/QsCompiler/CommandLineTool/Options.cs +++ b/src/QsCompiler/CommandLineTool/Options.cs @@ -82,6 +82,7 @@ public enum LogFormat // Note: items in one set are mutually exclusive with items from other sets protected const string CODE_MODE = "codeMode"; protected const string SNIPPET_MODE = "snippetMode"; + protected const string RESPONSE_FILES = "responseFiles"; [Option('v', "verbosity", Required = false, Default = DefaultOptions.Verbosity, HelpText = "Specifies the verbosity of the logged output. Valid values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic].")] diff --git a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs index 5dbc692479..9e013ca437 100644 --- a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs +++ b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs @@ -162,6 +162,7 @@ let ``options from response files`` () = [| "-i" ("TestCases","LinkingTests","Core.qs") |> Path.Combine + ("TestCases","LinkingTests","Diagnostics.qs") |> Path.Combine "--trim" "0" |] @@ -169,8 +170,6 @@ let ``options from response files`` () = let commandLineArgs = [| "build" - "-i" - ("TestCases","LinkingTests","Diagnostics.qs") |> Path.Combine "--trim" "2" "--response-files" From d44ecd39d1c21a78b52a0744986f416bb89c92a2 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 11 Mar 2020 09:33:04 -0700 Subject: [PATCH 078/135] Move internal keyword to operations syntax group --- .../syntaxes/qsharp.tmLanguage.json.v.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template b/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template index 581b1a7833..4dbc1671fd 100644 --- a/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template +++ b/src/VSCodeExtension/syntaxes/qsharp.tmLanguage.json.v.template @@ -50,7 +50,7 @@ }, { "name": "keyword.other.qsharp", - "match": "\\b(internal|new|not|and|or|w\/)\\b" + "match": "\\b(new|not|and|or|w\/)\\b" }, { "comment": "C# reserved words which can't be used in Q#, A-D", @@ -78,7 +78,7 @@ "patterns": [ { "name": "keyword.other.qsharp", - "match": "\\b(namespace|open|as|newtype|operation|function|body|(a|A)djoint|(c|C)ontrolled|self|auto|distribute|invert|intrinsic)\\b" + "match": "\\b(namespace|open|as|internal|newtype|operation|function|body|(a|A)djoint|(c|C)ontrolled|self|auto|distribute|invert|intrinsic)\\b" } ] }, From 15ea32e5447f847121cf6badb6e6377a81a59667 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 11 Mar 2020 09:42:03 -0700 Subject: [PATCH 079/135] Move "References" tests to other test groups --- .../Tests.Compiler/AccessModifierTests.fs | 9 +++---- .../TestCases/AccessModifiers.qs | 26 +++++++++---------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index a952958e57..5480b88ab3 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -27,10 +27,13 @@ type AccessModifierTests (output) = [] member this.``Callables`` () = this.Expect "CallableUseOK" [] + this.Expect "CallableReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] [] member this.``Types`` () = this.Expect "TypeUseOK" [] + this.Expect "TypeReferenceInternalInaccessible" [Error ErrorCode.InaccessibleType] + this.Expect "TypeConstructorReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] [] member this.``Callable signatures`` () = @@ -48,9 +51,3 @@ type AccessModifierTests (output) = this.Expect "PublicTypeLeaksInternalType2" [Error ErrorCode.TypeLessAccessibleThanParentType] this.Expect "PublicTypeLeaksInternalType3" [Error ErrorCode.TypeLessAccessibleThanParentType] this.Expect "InternalTypeInternalTypeOK" [] - - [] - member this.``References`` () = - this.Expect "CallableReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] - this.Expect "TypeReferenceInternalInaccessible" [Error ErrorCode.InaccessibleType] - this.Expect "TypeConstructorReferenceInternalInaccessible" [Error ErrorCode.InaccessibleCallable] diff --git a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs index fda930c5ca..4292d5d09b 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/AccessModifiers.qs @@ -21,6 +21,10 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { B.InternalFunctionB(); } + function CallableReferenceInternalInaccessible () : Unit { + InternalFunctionC(); + } + // Types function TypeUseOK () : Unit { @@ -32,6 +36,14 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { let itbs = new B.InternalTypeB[1]; } + function TypeReferenceInternalInaccessible () : Unit { + let itcs = new InternalTypeC[1]; + } + + function TypeConstructorReferenceInternalInaccessible () : Unit { + let itc = InternalTypeC(); + } + // Callable signatures function CallableLeaksInternalTypeIn1 (x : InternalType) : Unit {} @@ -65,20 +77,6 @@ namespace Microsoft.Quantum.Testing.AccessModifiers { newtype PublicTypeLeaksInternalType3 = (Int, (InternalType, Bool)); internal newtype InternalTypeInternalTypeOK = InternalType; - - // References - - function CallableReferenceInternalInaccessible () : Unit { - InternalFunctionC(); - } - - function TypeReferenceInternalInaccessible () : Unit { - let itcs = new InternalTypeC[1]; - } - - function TypeConstructorReferenceInternalInaccessible () : Unit { - let itc = InternalTypeC(); - } } /// This namespace contains additional definitions of types and callables meant to be used by the From b4602084c1315cd23c412415f66f1516553f7c57 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Wed, 11 Mar 2020 09:59:45 -0700 Subject: [PATCH 080/135] protected constructor --- src/QsCompiler/Transformations/ContentLifting.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/Transformations/ContentLifting.cs b/src/QsCompiler/Transformations/ContentLifting.cs index 5a9ac8a99f..ec8e146a94 100644 --- a/src/QsCompiler/Transformations/ContentLifting.cs +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -310,7 +310,7 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature } } - public LiftContent() : base(new TransformationState()) + protected LiftContent() : base(new TransformationState()) { this.Namespaces = new NamespaceTransformation(this); this.StatementKinds = new StatementKindTransformation(this); From 0c7441bac68ae6a37967b41c258d44be6de8d1ab Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Wed, 11 Mar 2020 11:56:27 -0700 Subject: [PATCH 081/135] WIP --- src/QsCompiler/Core/Dependencies.fs | 153 +++++++++++++--------------- 1 file changed, 72 insertions(+), 81 deletions(-) diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index acc11d8d8b..cd9c351f53 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -8,13 +8,27 @@ open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.SyntaxTree +type BuiltInKindFunction = {TypeParameters : ImmutableArray>} +type BuiltInKindOperation = {TypeParameters : ImmutableArray>; IsSelfAdjoint : bool} + +type BuiltInKind = + | Attribute + | Function of BuiltInKindFunction + | Operation of BuiltInKindOperation + type BuiltIn = { + /// contains the fully qualified name of the built-in + FullName : QsQualifiedName + /// contains the specific kind of built-in this is, as well as information specific to that kind + Kind : BuiltInKind + + /// contains the name of the callable - Name : NonNullable + //Name : NonNullable /// contains the name of the namespace in which the callable is defined - Namespace : NonNullable + //Namespace : NonNullable /// contains the names of the type parameters without the leading tick (') - TypeParameters : ImmutableArray> + //TypeParameters : ImmutableArray> } with static member CoreNamespace = NonNullable.New "Microsoft.Quantum.Core" @@ -35,184 +49,161 @@ type BuiltIn = { /// 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.Namespace.Value && tId.Name.Value = BuiltIn.EntryPoint.Name.Value + | Value tId -> tId.Namespace.Value = BuiltIn.EntryPoint.FullName.Namespace.Value && tId.Name.Value = BuiltIn.EntryPoint.FullName.Name.Value | Null -> false /// Returns true if the given attribute marks the corresponding declaration as deprecated. static member MarksDeprecation (att : QsDeclarationAttribute) = att.TypeId |> function - | Value tId -> tId.Namespace.Value = BuiltIn.Deprecated.Namespace.Value && tId.Name.Value = BuiltIn.Deprecated.Name.Value + | Value tId -> tId.Namespace.Value = BuiltIn.Deprecated.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Deprecated.FullName.Name.Value | Null -> false /// Returns true if the given attribute marks the corresponding declaration as unit test. static member MarksTestOperation (att : QsDeclarationAttribute) = att.TypeId |> function - | Value tId -> tId.Namespace.Value = BuiltIn.Test.Namespace.Value && tId.Name.Value = BuiltIn.Test.Name.Value + | Value tId -> tId.Namespace.Value = BuiltIn.Test.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Test.FullName.Name.Value | Null -> false // hard dependencies in Microsoft.Quantum.Core static member Length = { - Name = "Length" |> NonNullable.New - Namespace = BuiltIn.CoreNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + FullName = {Name = "Length" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} + Kind = Function {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New)} } static member RangeReverse = { - Name = "RangeReverse" |> NonNullable.New - Namespace = BuiltIn.CoreNamespace - TypeParameters = ImmutableArray.Empty + FullName = {Name = "RangeReverse" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} + Kind = Function {TypeParameters = ImmutableArray.Empty} } static member Attribute = { - Name = "Attribute" |> NonNullable.New - Namespace = BuiltIn.CoreNamespace - TypeParameters = ImmutableArray.Empty + FullName = {Name = "Attribute" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} + Kind = Attribute } - static member EntryPoint = { - Name = "EntryPoint" |> NonNullable.New - Namespace = BuiltIn.CoreNamespace - TypeParameters = ImmutableArray.Empty + static member EntryPoint : BuiltIn = { + FullName = {Name = "EntryPoint" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} + Kind = Attribute } - static member Deprecated = { - Name = "Deprecated" |> NonNullable.New - Namespace = BuiltIn.CoreNamespace - TypeParameters = ImmutableArray.Empty + static member Deprecated : BuiltIn = { + FullName = {Name = "Deprecated" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} + Kind = Attribute } - static member Test = { - Name = "Test" |> NonNullable.New - Namespace = BuiltIn.DiagnosticsNamespace - TypeParameters = ImmutableArray.Empty + static member Test : BuiltIn = { + FullName = {Name = "Test" |> NonNullable.New; Namespace = BuiltIn.DiagnosticsNamespace} + Kind = Attribute } // hard dependencies in Microsoft.Quantum.Canon static member NoOp = { - Name = "NoOp" |> NonNullable.New - Namespace = BuiltIn.CanonNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + FullName = {Name = "NoOp" |> NonNullable.New; Namespace = BuiltIn.CanonNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} + } // hard dependencies in Microsoft.Quantum.Simulation.QuantumProcessor.Extensions // This is expected to have type <'T, 'U>((Result[], Result[], (('T => Unit), 'T) , (('U => Unit), 'U)) => Unit) static member ApplyConditionally = { - Name = "ApplyConditionally" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) + FullName = {Name = "ApplyConditionally" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T, 'U>((Result[], Result[], (('T => Unit is Adj), 'T) , (('U => Unit is Adj), 'U)) => Unit is Adj) static member ApplyConditionallyA = { - Name = "ApplyConditionallyA" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) + FullName = {Name = "ApplyConditionallyA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T, 'U>((Result[], Result[], (('T => Unit is Ctl), 'T) , (('U => Unit is Ctl), 'U)) => Unit is Ctl) static member ApplyConditionallyC = { - Name = "ApplyConditionallyC" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) + FullName = {Name = "ApplyConditionallyC" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T, 'U>((Result[], Result[], (('T => Unit is Adj + Ctl), 'T) , (('U => Unit is Adj + Ctl), 'U)) => Unit is Adj + Ctl) static member ApplyConditionallyCA = { - Name = "ApplyConditionallyCA" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) + FullName = {Name = "ApplyConditionallyCA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T>((Result, (('T => Unit), 'T)) => Unit) static member ApplyIfZero = { - Name = "ApplyIfZero" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + FullName = {Name = "ApplyIfZero" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T>((Result, (('T => Unit is Adj), 'T)) => Unit is Adj) static member ApplyIfZeroA = { - Name = "ApplyIfZeroA" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + FullName = {Name = "ApplyIfZeroA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T>((Result, (('T => Unit is Ctl), 'T)) => Unit is Ctl) static member ApplyIfZeroC = { - Name = "ApplyIfZeroC" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + FullName = {Name = "ApplyIfZeroC" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T>((Result, (('T => Unit is Adj + Ctl), 'T)) => Unit is Adj + Ctl) static member ApplyIfZeroCA = { - Name = "ApplyIfZeroCA" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + FullName = {Name = "ApplyIfZeroCA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T>((Result, (('T => Unit), 'T)) => Unit) static member ApplyIfOne = { - Name = "ApplyIfOne" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + FullName = {Name = "ApplyIfOne" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T>((Result, (('T => Unit is Adj), 'T)) => Unit is Adj) static member ApplyIfOneA = { - Name = "ApplyIfOneA" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + FullName = {Name = "ApplyIfOneA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T>((Result, (('T => Unit is Ctl), 'T)) => Unit is Ctl) static member ApplyIfOneC = { - Name = "ApplyIfOneC" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + FullName = {Name = "ApplyIfOneC" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T>((Result, (('T => Unit is Adj + Ctl), 'T)) => Unit is Adj + Ctl) static member ApplyIfOneCA = { - Name = "ApplyIfOneCA" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New) + FullName = {Name = "ApplyIfOneCA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T, 'U>((Result, (('T => Unit), 'T), (('U => Unit), 'U)) => Unit) static member ApplyIfElseR = { - Name = "ApplyIfElseR" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) + FullName = {Name = "ApplyIfElseR" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T, 'U>((Result, (('T => Unit is Adj), 'T), (('U => Unit is Adj), 'U)) => Unit is Adj) static member ApplyIfElseRA = { - Name = "ApplyIfElseRA" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) + FullName = {Name = "ApplyIfElseRA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T, 'U>((Result, (('T => Unit is Ctl), 'T), (('U => Unit is Ctl), 'U)) => Unit is Ctl) static member ApplyIfElseRC = { - Name = "ApplyIfElseRC" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) + FullName = {Name = "ApplyIfElseRC" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} } // This is expected to have type <'T, 'U>((Result, (('T => Unit is Adj + Ctl), 'T), (('U => Unit is Adj + Ctl), 'U)) => Unit is Adj + Ctl) static member ApplyIfElseRCA = { - Name = "ApplyIfElseRCA" |> NonNullable.New - Namespace = BuiltIn.ClassicallyControlledNamespace - TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New) + FullName = {Name = "ApplyIfElseRCA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} + Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} } // "weak dependencies" in other namespaces (e.g. things used for code actions) static member IndexRange = { - Name = "IndexRange" |> NonNullable.New - Namespace = BuiltIn.StandardArrayNamespace - TypeParameters = ImmutableArray.Empty + FullName = {Name = "IndexRange" |> NonNullable.New; Namespace = BuiltIn.StandardArrayNamespace} + Kind = Function {TypeParameters = ImmutableArray.Empty} } From 4c24029fb5ec9a3adb1e9ce6e49651f9047b15a7 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Wed, 11 Mar 2020 13:03:32 -0700 Subject: [PATCH 082/135] Using named tuple fields --- src/QsCompiler/Core/Dependencies.fs | 47 ++++++++++++++--------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index cd9c351f53..cb7822ad76 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -8,13 +8,10 @@ open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.SyntaxTree -type BuiltInKindFunction = {TypeParameters : ImmutableArray>} -type BuiltInKindOperation = {TypeParameters : ImmutableArray>; IsSelfAdjoint : bool} - type BuiltInKind = | Attribute - | Function of BuiltInKindFunction - | Operation of BuiltInKindOperation + | Function of TypeParameters : ImmutableArray> + | Operation of TypeParameters : ImmutableArray> * IsSelfAdjoint : bool type BuiltIn = { /// contains the fully qualified name of the built-in @@ -67,12 +64,12 @@ type BuiltIn = { static member Length = { FullName = {Name = "Length" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} - Kind = Function {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New)} + Kind = Function (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New)) } static member RangeReverse = { FullName = {Name = "RangeReverse" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} - Kind = Function {TypeParameters = ImmutableArray.Empty} + Kind = Function (TypeParameters = ImmutableArray.Empty) } static member Attribute = { @@ -99,7 +96,7 @@ type BuiltIn = { static member NoOp = { FullName = {Name = "NoOp" |> NonNullable.New; Namespace = BuiltIn.CanonNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New), IsSelfAdjoint = false) } @@ -108,102 +105,102 @@ type BuiltIn = { // This is expected to have type <'T, 'U>((Result[], Result[], (('T => Unit), 'T) , (('U => Unit), 'U)) => Unit) static member ApplyConditionally = { FullName = {Name = "ApplyConditionally" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T, 'U>((Result[], Result[], (('T => Unit is Adj), 'T) , (('U => Unit is Adj), 'U)) => Unit is Adj) static member ApplyConditionallyA = { FullName = {Name = "ApplyConditionallyA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T, 'U>((Result[], Result[], (('T => Unit is Ctl), 'T) , (('U => Unit is Ctl), 'U)) => Unit is Ctl) static member ApplyConditionallyC = { FullName = {Name = "ApplyConditionallyC" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T, 'U>((Result[], Result[], (('T => Unit is Adj + Ctl), 'T) , (('U => Unit is Adj + Ctl), 'U)) => Unit is Adj + Ctl) static member ApplyConditionallyCA = { FullName = {Name = "ApplyConditionallyCA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T>((Result, (('T => Unit), 'T)) => Unit) static member ApplyIfZero = { FullName = {Name = "ApplyIfZero" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T>((Result, (('T => Unit is Adj), 'T)) => Unit is Adj) static member ApplyIfZeroA = { FullName = {Name = "ApplyIfZeroA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T>((Result, (('T => Unit is Ctl), 'T)) => Unit is Ctl) static member ApplyIfZeroC = { FullName = {Name = "ApplyIfZeroC" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T>((Result, (('T => Unit is Adj + Ctl), 'T)) => Unit is Adj + Ctl) static member ApplyIfZeroCA = { FullName = {Name = "ApplyIfZeroCA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T>((Result, (('T => Unit), 'T)) => Unit) static member ApplyIfOne = { FullName = {Name = "ApplyIfOne" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T>((Result, (('T => Unit is Adj), 'T)) => Unit is Adj) static member ApplyIfOneA = { FullName = {Name = "ApplyIfOneA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T>((Result, (('T => Unit is Ctl), 'T)) => Unit is Ctl) static member ApplyIfOneC = { FullName = {Name = "ApplyIfOneC" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T>((Result, (('T => Unit is Adj + Ctl), 'T)) => Unit is Adj + Ctl) static member ApplyIfOneCA = { FullName = {Name = "ApplyIfOneCA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T, 'U>((Result, (('T => Unit), 'T), (('U => Unit), 'U)) => Unit) static member ApplyIfElseR = { FullName = {Name = "ApplyIfElseR" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T, 'U>((Result, (('T => Unit is Adj), 'T), (('U => Unit is Adj), 'U)) => Unit is Adj) static member ApplyIfElseRA = { FullName = {Name = "ApplyIfElseRA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T, 'U>((Result, (('T => Unit is Ctl), 'T), (('U => Unit is Ctl), 'U)) => Unit is Ctl) static member ApplyIfElseRC = { FullName = {Name = "ApplyIfElseRC" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New), IsSelfAdjoint = false) } // This is expected to have type <'T, 'U>((Result, (('T => Unit is Adj + Ctl), 'T), (('U => Unit is Adj + Ctl), 'U)) => Unit is Adj + Ctl) static member ApplyIfElseRCA = { FullName = {Name = "ApplyIfElseRCA" |> NonNullable.New; Namespace = BuiltIn.ClassicallyControlledNamespace} - Kind = Operation {TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New); IsSelfAdjoint = false} + Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New), IsSelfAdjoint = false) } // "weak dependencies" in other namespaces (e.g. things used for code actions) static member IndexRange = { FullName = {Name = "IndexRange" |> NonNullable.New; Namespace = BuiltIn.StandardArrayNamespace} - Kind = Function {TypeParameters = ImmutableArray.Empty} + Kind = Function (TypeParameters = ImmutableArray.Empty) } From 9ed1e8d360c23203ee99073bfd7e9f7fb6352692 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Wed, 11 Mar 2020 16:46:17 -0700 Subject: [PATCH 083/135] Moved some of the logic for validation into the Lift function. --- .../Transformations/ClassicallyControlled.cs | 46 ++++++++++--------- .../Transformations/ContentLifting.cs | 17 +++++-- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index ad96e0cdc3..d935b8af39 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -809,13 +809,17 @@ public override QsStatementKind OnConditionalStatement(QsConditionalStatement st // ToDo: Reduce the number of unnecessary generated operations by generalizing // the condition logic for the conversion and using that condition here //var (isExprCondition, _, _) = IsConditionedOnResultLiteralExpression(expr.Item); - - if (SharedState.IsValidScope) // if sub-scope is valid, lift content + + if (IsScopeSingleCall(block.Body)) { - if (/*isExprCondition &&*/ !IsScopeSingleCall(block.Body)) + newConditionBlocks.Add(Tuple.Create(expr.Item, block)); + } + // ToDo: We may want to prevent empty blocks from getting lifted + else //if(block.Body.Statements.Length > 0) + { + // Lift the scope to its own operation + if (SharedState.LiftBody(block.Body, out var callable, out var call)) { - // Lift the scope to its own operation - var (callable, call) = SharedState.LiftBody(block.Body); block = new QsPositionedBlock( new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), block.Location, @@ -823,16 +827,12 @@ public override QsStatementKind OnConditionalStatement(QsConditionalStatement st newConditionBlocks.Add(Tuple.Create(expr.Item, block)); generatedOperations.Add(callable); } - else if (block.Body.Statements.Length > 0) + else { - newConditionBlocks.Add(Tuple.Create(expr.Item, block)); + isValidLift = false; + break; } } - else - { - isValidLift = false; - break; - } } var newDefault = QsNullable.Null; @@ -844,12 +844,18 @@ public override QsStatementKind OnConditionalStatement(QsConditionalStatement st : stm.Default.Item.Body.KnownSymbols.Variables; var (_, block) = this.OnPositionedBlock(QsNullable.Null, stm.Default.Item); - if (SharedState.IsValidScope) + + + if (IsScopeSingleCall(block.Body)) { - if (!IsScopeSingleCall(block.Body)) // if sub-scope is valid, lift content + newDefault = QsNullable.NewValue(block); + } + // ToDo: We may want to prevent empty blocks from getting lifted + else //if(block.Body.Statements.Length > 0) + { + // Lift the scope to its own operation + if (SharedState.LiftBody(block.Body, out var callable, out var call)) { - // Lift the scope to its own operation - var (callable, call) = SharedState.LiftBody(block.Body); block = new QsPositionedBlock( new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), block.Location, @@ -857,15 +863,11 @@ public override QsStatementKind OnConditionalStatement(QsConditionalStatement st newDefault = QsNullable.NewValue(block); generatedOperations.Add(callable); } - else if (block.Body.Statements.Length > 0) + else { - newDefault = QsNullable.NewValue(block); + isValidLift = false; } } - else - { - isValidLift = false; - } } if (isValidLift) diff --git a/src/QsCompiler/Transformations/ContentLifting.cs b/src/QsCompiler/Transformations/ContentLifting.cs index ec8e146a94..6c62790683 100644 --- a/src/QsCompiler/Transformations/ContentLifting.cs +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -235,8 +235,15 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature /// /// The given body should be validated with the SharedState.IsValidScope before using this function. /// - public (QsCallable, QsStatement) LiftBody(QsScope body) + public bool LiftBody(QsScope body, out QsCallable callable, out QsStatement callStatement) { + if (!IsValidScope) + { + callable = null; + callStatement = null; + return false; + } + var (generatedOp, originalArgumentType) = GenerateOperation(body); var generatedOpType = ResolvedType.New(ResolvedTypeKind.NewOperation( Tuple.Create( @@ -302,11 +309,15 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature new InferredExpressionInformation(false, true), QsNullable>.Null); - return (generatedOp, new QsStatement( + // set output parameters + callable = generatedOp; + callStatement = new QsStatement( QsStatementKind.NewQsExpressionStatement(call), LocalDeclarations.Empty, QsNullable.Null, - QsComments.Empty)); + QsComments.Empty); + + return true; } } From 6543becb75f20d1e65e24d62b42b4ac33cdf5151 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 12 Mar 2020 09:09:27 -0700 Subject: [PATCH 084/135] allowing custom TransformationState for inheriting transformations --- .../Transformations/ClassicallyControlled.cs | 11 +- .../Transformations/ContentLifting.cs | 303 +++++++++--------- 2 files changed, 167 insertions(+), 147 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index ad96e0cdc3..3dfe372774 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -766,14 +766,19 @@ public static QsCompilation Apply(QsCompilation compilation) return new QsCompilation(compilation.Namespaces.Select(ns => filter.Namespaces.OnNamespace(ns)).ToImmutableArray(), compilation.EntryPoints); } - private class LiftContent : ContentLifting.LiftContent + private class LiftContent : ContentLifting.LiftContent { - public LiftContent() : base() + internal class TransformationState : ContentLifting.LiftContent.TransformationState + { + // here is where additional handles would go + } + + public LiftContent() : base(new TransformationState()) { this.StatementKinds = new StatementKindTransformation(this); } - private new class StatementKindTransformation : ContentLifting.LiftContent.StatementKindTransformation + private new class StatementKindTransformation : ContentLifting.LiftContent.StatementKindTransformation { public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } diff --git a/src/QsCompiler/Transformations/ContentLifting.cs b/src/QsCompiler/Transformations/ContentLifting.cs index ec8e146a94..b5761f2cdf 100644 --- a/src/QsCompiler/Transformations/ContentLifting.cs +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -19,19 +19,9 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ContentLifting using TypeArgsResolution = ImmutableArray, ResolvedType>>; /// - /// This transformation handles the task of lifting the contents of code blocks into generated operations. - /// The transformation provides validation to see if any given block can safely be lifted into its own operation. - /// Validation requirements are that there are no return statements and that there are no set statements - /// on mutables declared outside the block. Setting mutables declared inside the block is valid. - /// A block can be checked by setting the SharedState.IsValidScope to true before traversing the scope, - /// then checking the SharedState.IsValidScope after traversal. Blocks should be validated before calling - /// the SharedState.LiftBody function, which will generate a new operation with the block's contents. - /// All the known variables at the start of the block will become parameters to the new operation, and - /// the operation will have all the valid type parameters of the calling context as type parameters. - /// A call to the new operation is also returned with all the valid type parameters and known variables - /// being forwarded to the new operation as arguments. + /// Static class to accumulate all type parameter independent subclasses used by LiftContent. /// - public class LiftContent : SyntaxTreeTransformation + public static class LiftContent { internal class CallableDetails { @@ -64,22 +54,30 @@ internal CallableDetails(QsCallable callable) public class TransformationState { + public TransformationState() + { + IsValidScope = true; + ContainsParamRef = false; + GeneratedOpParams = ImmutableArray>>.Empty; + } + // ToDo: It should be possible to make these three properties private, // if we absorb the corresponding logic into LiftBody. - public bool IsValidScope = true; - internal bool ContainsParamRef = false; - internal ImmutableArray>> GeneratedOpParams = - ImmutableArray>>.Empty; + public bool IsValidScope { get; set; } + internal bool ContainsParamRef { get; set; } + internal ImmutableArray>> GeneratedOpParams { get; set; } - internal CallableDetails CurrentCallable = null; + internal CallableDetails CurrentCallable { get; set; } - protected internal bool InBody = false; - protected internal bool InAdjoint = false; - protected internal bool InControlled = false; - protected internal bool InControlledAdjoint = false; - protected internal bool InWithinBlock = false; + protected internal bool InBody { get; set; } + protected internal bool InAdjoint { get; set; } + protected internal bool InControlled { get; set; } + protected internal bool InControlledAdjoint { get; set; } + protected internal bool InWithinBlock { get; set; } - protected internal List GeneratedOperations = null; + protected internal List GeneratedOperations { get; set; } + + //bool ITransformationState.IsValidScope { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } private (ResolvedSignature, IEnumerable) MakeSpecializations( QsQualifiedName callableName, ResolvedType paramsType, SpecializationImplementation bodyImplementation) @@ -310,22 +308,149 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature } } - protected LiftContent() : base(new TransformationState()) + /// + /// Transformation that updates the contents of newly generated operations by: + /// 1. Rerouting the origins of type parameter references to the new operation + /// 2. Changes the IsMutable and HasLocalQuantumDependency info on parameter references to be false + /// + private class UpdateGeneratedOp : SyntaxTreeTransformation + { + public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + { + var filter = new UpdateGeneratedOp(parameters, oldName, newName); + + return filter.Namespaces.OnCallableDeclaration(qsCallable); + } + + public class TransformationState + { + public bool IsRecursiveIdentifier = false; + public readonly ImmutableArray>> Parameters; + public readonly QsQualifiedName OldName; + public readonly QsQualifiedName NewName; + + public TransformationState(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + { + Parameters = parameters; + OldName = oldName; + NewName = newName; + } + } + + private UpdateGeneratedOp(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + : base(new TransformationState(parameters, oldName, newName)) + { + this.Expressions = new ExpressionTransformation(this); + this.ExpressionKinds = new ExpressionKindTransformation(this); + this.Types = new TypeTransformation(this); + } + + private class ExpressionTransformation : ExpressionTransformation + { + public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override ImmutableDictionary>, ResolvedType> OnTypeParamResolutions(ImmutableDictionary>, ResolvedType> typeParams) + { + // Prevent keys from having their names updated + return typeParams.ToImmutableDictionary(kvp => kvp.Key, kvp => this.Types.OnType(kvp.Value)); + } + + public override TypedExpression OnTypedExpression(TypedExpression ex) + { + // Checks if expression is mutable identifier that is in parameter list + if ((ex.InferredInformation.IsMutable || ex.InferredInformation.HasLocalQuantumDependency) + && ex.Expression is ExpressionKind.Identifier id + && id.Item1 is Identifier.LocalVariable variable + && SharedState.Parameters.Any(x => x.VariableName.Equals(variable))) + { + // Set the mutability to false + ex = new TypedExpression( + ex.Expression, + ex.TypeArguments, + ex.ResolvedType, + new InferredExpressionInformation(false, false), // parameter references cannot be mutable or have local quantum dependency + ex.Range); + } + + // Prevent IsRecursiveIdentifier from propagating beyond the typed expression it is referring to + var isRecursiveIdentifier = SharedState.IsRecursiveIdentifier; + var rtrn = base.OnTypedExpression(ex); + SharedState.IsRecursiveIdentifier = isRecursiveIdentifier; + return rtrn; + } + } + + private class ExpressionKindTransformation : ExpressionKindTransformation + { + public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) + { + var rtrn = base.OnIdentifier(sym, tArgs); + + // Check if this is a recursive identifier + // In this context, that is a call back to the original callable from the newly generated operation + if (sym is Identifier.GlobalCallable callable && SharedState.OldName.Equals(callable.Item)) + { + // Setting this flag will prevent the rerouting logic from processing the resolved type of the recursive identifier expression. + // This is necessary because we don't want any type parameters from the original callable from being rerouted to the new generated + // operation's type parameters in the definition of the identifier. + SharedState.IsRecursiveIdentifier = true; + } + return rtrn; + } + } + + private class TypeTransformation : TypeTransformation + { + public TypeTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override ResolvedTypeKind OnTypeParameter(QsTypeParameter tp) + { + // Reroute a type parameter's origin to the newly generated operation + if (!SharedState.IsRecursiveIdentifier && SharedState.OldName.Equals(tp.Origin)) + { + tp = new QsTypeParameter(SharedState.NewName, tp.TypeName, tp.Range); + } + + return base.OnTypeParameter(tp); + } + } + } + } + + /// + /// This transformation handles the task of lifting the contents of code blocks into generated operations. + /// The transformation provides validation to see if any given block can safely be lifted into its own operation. + /// Validation requirements are that there are no return statements and that there are no set statements + /// on mutables declared outside the block. Setting mutables declared inside the block is valid. + /// A block can be checked by setting the SharedState.IsValidScope to true before traversing the scope, + /// then checking the SharedState.IsValidScope after traversal. Blocks should be validated before calling + /// the SharedState.LiftBody function, which will generate a new operation with the block's contents. + /// All the known variables at the start of the block will become parameters to the new operation, and + /// the operation will have all the valid type parameters of the calling context as type parameters. + /// A call to the new operation is also returned with all the valid type parameters and known variables + /// being forwarded to the new operation as arguments. + /// + public class LiftContent : SyntaxTreeTransformation + where T : LiftContent.TransformationState + { + protected LiftContent(T state) : base(state) { this.Namespaces = new NamespaceTransformation(this); this.StatementKinds = new StatementKindTransformation(this); this.Expressions = new ExpressionTransformation(this); this.ExpressionKinds = new ExpressionKindTransformation(this); - this.Types = new TypeTransformation(this, TransformationOptions.Disabled); + this.Types = new TypeTransformation(this, TransformationOptions.Disabled); } - protected class NamespaceTransformation : NamespaceTransformation + protected class NamespaceTransformation : NamespaceTransformation { - public NamespaceTransformation(SyntaxTreeTransformation parent) : base(parent) { } + public NamespaceTransformation(SyntaxTreeTransformation parent) : base(parent) { } public override QsCallable OnCallableDeclaration(QsCallable c) { - SharedState.CurrentCallable = new CallableDetails(c); + SharedState.CurrentCallable = new LiftContent.CallableDetails(c); return base.OnCallableDeclaration(c); } @@ -372,9 +497,9 @@ public override QsNamespace OnNamespace(QsNamespace ns) } } - protected class StatementKindTransformation : StatementKindTransformation + protected class StatementKindTransformation : StatementKindTransformation { - public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } public override QsStatementKind OnConjugation(QsConjugation stm) { @@ -415,9 +540,9 @@ public override QsStatementKind OnStatementKind(QsStatementKind kind) } } - protected class ExpressionTransformation : ExpressionTransformation + protected class ExpressionTransformation : ExpressionTransformation { - public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } + public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } public override TypedExpression OnTypedExpression(TypedExpression ex) { @@ -433,9 +558,9 @@ public override TypedExpression OnTypedExpression(TypedExpression ex) } } - protected class ExpressionKindTransformation : ExpressionKindTransformation + protected class ExpressionKindTransformation : ExpressionKindTransformation { - public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) { @@ -447,115 +572,5 @@ public override ExpressionKind OnIdentifier(Identifier sym, QsNullable - /// Transformation that updates the contents of newly generated operations by: - /// 1. Rerouting the origins of type parameter references to the new operation - /// 2. Changes the IsMutable and HasLocalQuantumDependency info on parameter references to be false - /// - private class UpdateGeneratedOp : SyntaxTreeTransformation - { - public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) - { - var filter = new UpdateGeneratedOp(parameters, oldName, newName); - - return filter.Namespaces.OnCallableDeclaration(qsCallable); - } - - public class TransformationState - { - public bool IsRecursiveIdentifier = false; - public readonly ImmutableArray>> Parameters; - public readonly QsQualifiedName OldName; - public readonly QsQualifiedName NewName; - - public TransformationState(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) - { - Parameters = parameters; - OldName = oldName; - NewName = newName; - } - } - - private UpdateGeneratedOp(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) - : base(new TransformationState(parameters, oldName, newName)) - { - this.Expressions = new ExpressionTransformation(this); - this.ExpressionKinds = new ExpressionKindTransformation(this); - this.Types = new TypeTransformation(this); - } - - private class ExpressionTransformation : ExpressionTransformation - { - public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override ImmutableDictionary>, ResolvedType> OnTypeParamResolutions(ImmutableDictionary>, ResolvedType> typeParams) - { - // Prevent keys from having their names updated - return typeParams.ToImmutableDictionary(kvp => kvp.Key, kvp => this.Types.OnType(kvp.Value)); - } - - public override TypedExpression OnTypedExpression(TypedExpression ex) - { - // Checks if expression is mutable identifier that is in parameter list - if ((ex.InferredInformation.IsMutable || ex.InferredInformation.HasLocalQuantumDependency) - && ex.Expression is ExpressionKind.Identifier id - && id.Item1 is Identifier.LocalVariable variable - && SharedState.Parameters.Any(x => x.VariableName.Equals(variable))) - { - // Set the mutability to false - ex = new TypedExpression( - ex.Expression, - ex.TypeArguments, - ex.ResolvedType, - new InferredExpressionInformation(false, false), // parameter references cannot be mutable or have local quantum dependency - ex.Range); - } - - // Prevent IsRecursiveIdentifier from propagating beyond the typed expression it is referring to - var isRecursiveIdentifier = SharedState.IsRecursiveIdentifier; - var rtrn = base.OnTypedExpression(ex); - SharedState.IsRecursiveIdentifier = isRecursiveIdentifier; - return rtrn; - } - } - - private class ExpressionKindTransformation : ExpressionKindTransformation - { - public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) - { - var rtrn = base.OnIdentifier(sym, tArgs); - - // Check if this is a recursive identifier - // In this context, that is a call back to the original callable from the newly generated operation - if (sym is Identifier.GlobalCallable callable && SharedState.OldName.Equals(callable.Item)) - { - // Setting this flag will prevent the rerouting logic from processing the resolved type of the recursive identifier expression. - // This is necessary because we don't want any type parameters from the original callable from being rerouted to the new generated - // operation's type parameters in the definition of the identifier. - SharedState.IsRecursiveIdentifier = true; - } - return rtrn; - } - } - - private class TypeTransformation : TypeTransformation - { - public TypeTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override ResolvedTypeKind OnTypeParameter(QsTypeParameter tp) - { - // Reroute a type parameter's origin to the newly generated operation - if (!SharedState.IsRecursiveIdentifier && SharedState.OldName.Equals(tp.Origin)) - { - tp = new QsTypeParameter(SharedState.NewName, tp.TypeName, tp.Range); - } - - return base.OnTypeParameter(tp); - } - } - } } } From 09f09c5e132dfd1ac7bafd08346f94d7c3a397fb Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 12 Mar 2020 09:21:36 -0700 Subject: [PATCH 085/135] accidentally committed changes to the TransformationState class --- .../Transformations/ContentLifting.cs | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/QsCompiler/Transformations/ContentLifting.cs b/src/QsCompiler/Transformations/ContentLifting.cs index b5761f2cdf..47da4ddd2b 100644 --- a/src/QsCompiler/Transformations/ContentLifting.cs +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -54,30 +54,22 @@ internal CallableDetails(QsCallable callable) public class TransformationState { - public TransformationState() - { - IsValidScope = true; - ContainsParamRef = false; - GeneratedOpParams = ImmutableArray>>.Empty; - } - // ToDo: It should be possible to make these three properties private, // if we absorb the corresponding logic into LiftBody. - public bool IsValidScope { get; set; } - internal bool ContainsParamRef { get; set; } - internal ImmutableArray>> GeneratedOpParams { get; set; } - - internal CallableDetails CurrentCallable { get; set; } + public bool IsValidScope = true; + internal bool ContainsParamRef = false; + internal ImmutableArray>> GeneratedOpParams = + ImmutableArray>>.Empty; - protected internal bool InBody { get; set; } - protected internal bool InAdjoint { get; set; } - protected internal bool InControlled { get; set; } - protected internal bool InControlledAdjoint { get; set; } - protected internal bool InWithinBlock { get; set; } + internal CallableDetails CurrentCallable = null; - protected internal List GeneratedOperations { get; set; } + protected internal bool InBody = false; + protected internal bool InAdjoint = false; + protected internal bool InControlled = false; + protected internal bool InControlledAdjoint = false; + protected internal bool InWithinBlock = false; - //bool ITransformationState.IsValidScope { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + protected internal List GeneratedOperations = null; private (ResolvedSignature, IEnumerable) MakeSpecializations( QsQualifiedName callableName, ResolvedType paramsType, SpecializationImplementation bodyImplementation) From fe4b6eaaaff17eb6446c2d60ad6663ce82f86bcb Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Thu, 12 Mar 2020 16:50:33 -0700 Subject: [PATCH 086/135] Nugged the code closer to having the validation logic in the lift function. --- .../Transformations/ClassicallyControlled.cs | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index 61a80dcec9..f99336272c 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -769,8 +769,8 @@ public static QsCompilation Apply(QsCompilation compilation) private class LiftContent : ContentLifting.LiftContent { internal class TransformationState : ContentLifting.LiftContent.TransformationState - { - // here is where additional handles would go + { + internal bool IsConditionLiftable = false; } public LiftContent() : base(new TransformationState()) @@ -795,26 +795,25 @@ private bool IsScopeSingleCall(QsScope contents) public override QsStatementKind OnConditionalStatement(QsConditionalStatement stm) { - var contextValidScope = SharedState.IsValidScope; - var contextParams = SharedState.GeneratedOpParams; - - var isValidLift = true; + var contextIsConditionLiftable = SharedState.IsConditionLiftable; + SharedState.IsConditionLiftable = true; var newConditionBlocks = new List>(); var generatedOperations = new List(); foreach (var conditionBlock in stm.ConditionalBlocks) { + var contextValidScope = SharedState.IsValidScope; + var contextParams = SharedState.GeneratedOpParams; + SharedState.IsValidScope = true; - SharedState.GeneratedOpParams = conditionBlock.Item2.Body.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : conditionBlock.Item2.Body.KnownSymbols.Variables; + SharedState.GeneratedOpParams = conditionBlock.Item2.Body.KnownSymbols.Variables; var (expr, block) = this.OnPositionedBlock(QsNullable.NewValue(conditionBlock.Item1), conditionBlock.Item2); // ToDo: Reduce the number of unnecessary generated operations by generalizing // the condition logic for the conversion and using that condition here //var (isExprCondition, _, _) = IsConditionedOnResultLiteralExpression(expr.Item); - + if (IsScopeSingleCall(block.Body)) { newConditionBlocks.Add(Tuple.Create(expr.Item, block)); @@ -834,23 +833,27 @@ public override QsStatementKind OnConditionalStatement(QsConditionalStatement st } else { - isValidLift = false; - break; + SharedState.IsConditionLiftable = false; } } + + SharedState.GeneratedOpParams = contextParams; + SharedState.IsValidScope = contextValidScope; + + if (!SharedState.IsConditionLiftable) break; } var newDefault = QsNullable.Null; - if (isValidLift && stm.Default.IsValue) + if (SharedState.IsConditionLiftable && stm.Default.IsValue) { + var contextValidScope = SharedState.IsValidScope; + var contextParams = SharedState.GeneratedOpParams; + SharedState.IsValidScope = true; - SharedState.GeneratedOpParams = stm.Default.Item.Body.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : stm.Default.Item.Body.KnownSymbols.Variables; + SharedState.GeneratedOpParams = stm.Default.Item.Body.KnownSymbols.Variables; var (_, block) = this.OnPositionedBlock(QsNullable.Null, stm.Default.Item); - if (IsScopeSingleCall(block.Body)) { newDefault = QsNullable.NewValue(block); @@ -870,24 +873,28 @@ public override QsStatementKind OnConditionalStatement(QsConditionalStatement st } else { - isValidLift = false; + SharedState.IsConditionLiftable = false; } } + + SharedState.GeneratedOpParams = contextParams; + SharedState.IsValidScope = contextValidScope; } - if (isValidLift) + if (SharedState.IsConditionLiftable) { SharedState.GeneratedOperations.AddRange(generatedOperations); } - SharedState.GeneratedOpParams = contextParams; - SharedState.IsValidScope = contextValidScope; - - return isValidLift + var rtrn = SharedState.IsConditionLiftable ? QsStatementKind.NewQsConditionalStatement( new QsConditionalStatement(newConditionBlocks.ToImmutableArray(), newDefault)) : QsStatementKind.NewQsConditionalStatement( new QsConditionalStatement(stm.ConditionalBlocks, stm.Default)); + + SharedState.IsConditionLiftable = contextIsConditionLiftable; + + return rtrn; } } } From 1f2928245395357b1caf3d6f4496cc37de6fec7d Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 12 Mar 2020 23:51:12 -0700 Subject: [PATCH 087/135] Allow shadowing imported internal callables --- .../CompilationManager/CompilationUnit.cs | 3 +- src/QsCompiler/Core/SymbolTable.fs | 297 +++++++++++------- src/QsCompiler/DataStructures/SyntaxTree.fs | 2 + 3 files changed, 196 insertions(+), 106 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index d300341b85..5aa6cb8a8f 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -473,7 +473,8 @@ private QsCallable GetImportedCallable(CallableDeclarationHeader header) if (header == null) throw new ArgumentNullException(nameof(header)); var importedSpecs = this.GlobalSymbols.ImportedSpecializations(header.QualifiedName); var definedSpecs = this.GlobalSymbols.DefinedSpecializations(header.QualifiedName); - QsCompilerError.Verify(definedSpecs.Length == 0, "external specializations are currently not supported"); + // TODO: This assertion is not valid if a defined callable is shadowing an internal imported callable. + // QsCompilerError.Verify(definedSpecs.Length == 0, "external specializations are currently not supported"); var specializations = importedSpecs.Select(imported => { var (specHeader, implementation) = imported; diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 19a5744b8e..8ac3f12d6c 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -13,7 +13,7 @@ open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.SymbolResolution open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxGenerator -open Microsoft.Quantum.QsCompiler.SyntaxTokens +open Microsoft.Quantum.QsCompiler.SyntaxTokens open Microsoft.Quantum.QsCompiler.SyntaxTree open Newtonsoft.Json @@ -29,7 +29,49 @@ type ResolutionResult<'T> = | Inaccessible /// No declaration with that name was found. | NotFound - + + +/// Tools for processing resolution results. +module private ResolutionResult = + /// Applies the mapping function to the value of a Found result. If the result is not a Found, returns it unchanged. + let internal map mapping = function + | Found value -> Found (mapping value) + | Ambiguous namespaces -> Ambiguous namespaces + | Inaccessible -> Inaccessible + | NotFound -> NotFound + + /// Converts the resolution result to a 0- or 1-element list containing the Found value if the result is a Found. + let private toList = function + | Found value -> [value] + | _ -> [] + + /// Converts the resolution result to an option containing the Found value. + let internal toOption = function + | Found value -> Some value + | _ -> None + + /// Returns the first result matching Found, Inaccessible or NotFound, in decreasing order of priority. If the + /// sequence contains a Found result, the rest of the sequence after it is not evaluated. + let internal firstResolution results = + results + |> Seq.tryPick (function | Found callable -> Some (Found callable) | _ -> None) + |> Option.orElseWith (fun () -> + results |> Seq.tryPick (function | Inaccessible -> Some (Inaccessible) | _ -> None)) + |> Option.defaultValue NotFound + + /// Returns a Found result if there is only one in the sequence. If there is more than one, returns an Ambiguous + /// result. Otherwise, returns the same value as ResolutionResult.firstResolution. + let internal uniqueResolution nsGetter results = + let found = results |> Seq.filter (function | Found _ -> true | _ -> false) + if Seq.length found > 1 + then found + |> Seq.map (map nsGetter >> toList) + |> Seq.concat + |> Ambiguous + else found + |> Seq.tryExactlyOne + |> Option.defaultWith (fun () -> firstResolution results) + /// Represents the partial declaration of a namespace in a single file. /// @@ -261,11 +303,18 @@ and Namespace private let mutable TypesDefinedInAllSourcesCache = null let mutable CallablesDefinedInAllSourcesCache = null - /// Given a non-nullable string, returns true if either a callable or a type with that name already exists - /// either in a source file, or in a referenced assembly. - let IsDefined arg = - CallablesInReferences.ContainsKey arg || TypesInReferences.ContainsKey arg || - Parts.Values.Any (fun partial -> partial.ContainsCallable arg || partial.ContainsType arg) + /// Returns true if the name is available for use in a new declaration. + let isNameAvailable name = + let isAvailableWith declarationGetter accessibilityGetter sameAssembly = + match declarationGetter name with + | true, value -> not <| Namespace.IsDeclarationAccessible sameAssembly (accessibilityGetter value) + | false, _ -> true + + isAvailableWith CallablesInReferences.TryGetValue (fun c -> c.Modifiers.Access) false && + isAvailableWith TypesInReferences.TryGetValue (fun t -> t.Modifiers.Access) false && + Parts.Values.All (fun partial -> + isAvailableWith partial.TryGetCallable (fun c -> (snd c).Modifiers.Access) true && + isAvailableWith partial.TryGetType (fun t -> t.Modifiers.Access) true) /// Given a selector, determines the source files for which the given selector returns a Value. /// Returns that Value if exactly one such source file exists, or Null if no such file exists. @@ -277,6 +326,12 @@ and Namespace private if sources.Any() then Value (sources.Single()) else Null + /// Returns whether a declaration is accessible from the calling location, given whether the calling location is in + /// the same assembly as the declaration, and the declaration's access modifier. + static member internal IsDeclarationAccessible sameAssembly = function + | DefaultAccess -> true + | Internal -> sameAssembly + /// name of the namespace member this.Name = name /// Immutable array with the names of all source files that contain (a part of) the namespace. @@ -429,12 +484,17 @@ and Namespace private /// Returns all specializations for the callable with the given name defined in a source file associated with this namespace, /// This excludes specializations that are defined in files contained in referenced assemblies. - /// Throws an ArgumentException if no callable with the given name is defined in this namespace. - member internal this.SpecializationsDefinedInAllSources cName = + /// Throws an ArgumentException if no callable with the given name is defined in this namespace. + member internal this.SpecializationsDefinedInAllSources cName = match this.TryFindCallable cName with - | Value _ -> (Parts.Values.SelectMany (fun partial -> - partial.GetSpecializations cName |> Seq.map (fun (kind, decl) -> kind, (partial.Source, decl)))).ToImmutableArray() - | Null -> ArgumentException "no callable with the given name exist within the namespace" |> raise + | Found _ -> + (Parts.Values.SelectMany (fun partial -> + partial.GetSpecializations cName + |> Seq.map (fun (kind, decl) -> kind, (partial.Source, decl)))).ToImmutableArray() + | _ -> + // TODO: + // ArgumentException "no callable with the given name exist within the namespace" |> raise + ImmutableArray<_>.Empty /// If this namespace contains a declaration for the given type name, returns a Value with: /// @@ -459,30 +519,47 @@ and Namespace private | true, tDecl -> Some (partialNS.Source, tDecl.DefinedAttributes |> SymbolResolution.TryFindRedirectInUnresolved checkDeprecation, true, tDecl.Modifiers) | false, _ -> None) - /// If this namespace contains a declaration for the given callable name, returns a Value with: + /// Returns a resolution result for the callable with the given name containing the name of the source file or + /// referenced assembly in which it is declared, and a string indicating the redirection if it has been deprecated. /// - /// (1) the name of the source file or the name of the file within a referenced assembly in which it is - /// declared; - /// (2) a string option indicating the redirection for the type if it has been deprecated; - /// (3) true if the declaration is declared in the assembly currently being compiled, or false if it is declared - /// in a referenced assembly; - /// (4) the modifiers for the declaration. - /// - /// Returns Null otherwise. + /// If the given callable corresponds to the (auto-generated) type constructor for a user defined type, returns the + /// file in which that type is declared as the source. /// - /// If the given callable corresponds to the (auto-generated) type constructor for a user defined type, - /// returns the file in which that type is declared as source. - /// Whether the callable has been deprecated is determined by checking the associated attributes for an attribute with the corresponding name. - /// Note that if the type is declared in a source files, the *unresolved* attributes will be checked. - /// In that case checkDeprecation is used to validate the namespace qualification of the attribute. - /// If checkDeprecation is not specified, it is assumed that no qualification is needed in the relevant namespace and source file. - member this.TryFindCallable (cName, ?checkDeprecation : (string -> bool)) = - let checkDeprecation = defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) - match CallablesInReferences.TryGetValue cName with - | true, cDecl -> Value (cDecl.SourceFile, cDecl.Attributes |> SymbolResolution.TryFindRedirect, false, cDecl.Modifiers) - | false, _ -> FromSingleSource (fun partialNS -> partialNS.TryGetCallable cName |> function - | true, (_, cDecl) -> Some (partialNS.Source, cDecl.DefinedAttributes |> SymbolResolution.TryFindRedirectInUnresolved checkDeprecation, true, cDecl.Modifiers) - | false, _ -> None) + /// Whether the callable has been deprecated is determined by checking the associated attributes for an attribute + /// with the corresponding name. Note that if the type is declared in a source files, the *unresolved* attributes + /// will be checked. In that case checkDeprecation is used to validate the namespace qualification of the attribute. + /// If checkDeprecation is not specified, it is assumed that no qualification is needed in the relevant namespace + /// and source file. + member this.TryFindCallable (cName, ?checkDeprecation : (string -> bool)) + : ResolutionResult * QsNullable> = + let checkDeprecation = + defaultArg checkDeprecation + (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) + + let findInPartial (partial : PartialNamespace) = + match partial.TryGetCallable cName with + | true, (_, callable) -> + Some (partial.Source, + SymbolResolution.TryFindRedirectInUnresolved checkDeprecation callable.DefinedAttributes, + callable.Modifiers.Access) + | false, _ -> None + + seq { + yield match CallablesInReferences.TryGetValue cName with + | true, callable -> + if Namespace.IsDeclarationAccessible false callable.Modifiers.Access + then Found (callable.SourceFile, SymbolResolution.TryFindRedirect callable.Attributes) + else Inaccessible + | false, _ -> NotFound + + yield match FromSingleSource findInPartial with + | Value (source, deprecation, access) -> + if Namespace.IsDeclarationAccessible true access + then Found (source, deprecation) + else Inaccessible + | Null -> NotFound + } + |> ResolutionResult.firstResolution /// Sets the resolution for the type with the given name in the given source file to the given type, /// and replaces the resolved attributes with the given values. @@ -568,8 +645,8 @@ and Namespace private /// If a type or callable with that name already exists, returns an array of suitable diagnostics. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. member this.TryAddType (source, location) ((tName, tRange), typeTuple, attributes, modifiers, documentation) : QsCompilerDiagnostic[] = - match Parts.TryGetValue source with - | true, partial when not (IsDefined tName) -> + match Parts.TryGetValue source with + | true, partial when isNameAvailable tName -> TypesDefinedInAllSourcesCache <- null CallablesDefinedInAllSourcesCache <- null partial.AddType location (tName, typeTuple, attributes, modifiers, documentation); [||] @@ -578,18 +655,18 @@ and Namespace private | Null -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeConstructorOverlapWithCallable, [tName.Value]) |] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise - /// If no callable (function, operation, or type constructor) with the given name exists in this namespace, - /// adds a declaration for the callable of the given kind (operation or function) with the given name and signature - /// to the given source, and returns an empty array. + /// If no callable (function, operation, or type constructor) with the given name exists in this namespace, + /// adds a declaration for the callable of the given kind (operation or function) with the given name and signature + /// to the given source, and returns an empty array. /// The given location is associated with the callable declaration and accessible via the record properties Position and SymbolRange. /// If a callable with that name already exists, returns an array of suitable diagnostics. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. member this.TryAddCallableDeclaration (source, location) ((cName, cRange), (kind, signature), attributes, modifiers, documentation) = - match Parts.TryGetValue source with - | true, partial when not (IsDefined cName) -> + match Parts.TryGetValue source with + | true, partial when isNameAvailable cName -> CallablesDefinedInAllSourcesCache <- null partial.AddCallableDeclaration location (cName, (kind, signature), attributes, modifiers, documentation); [||] - | true, _ -> this.TryFindType cName |> function + | true, _ -> this.TryFindType cName |> function | Value _ -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableOverlapWithTypeConstructor, [cName.Value]) |] | Null -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableRedefinition, [cName.Value]) |] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise @@ -621,7 +698,7 @@ and Namespace private match Parts.TryGetValue source with | true, partial -> match this.TryFindCallable cName with - | Value (declSource, _, _, _) -> + | Found (declSource, _) -> let AddAndClearCache () = CallablesDefinedInAllSourcesCache <- null partial.AddCallableSpecialization location kind (cName, generator, attributes, documentation) @@ -637,7 +714,7 @@ and Namespace private | QsControlled -> [| location.Range |> QsCompilerDiagnostic.Error (ErrorCode.RequiredUnitReturnForControlled, []) |] | QsControlledAdjoint -> [| location.Range |> QsCompilerDiagnostic.Error (ErrorCode.RequiredUnitReturnForControlledAdjoint, []) |] else AddAndClearCache(); [||] - | Null -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.SpecializationForUnknownCallable, [cName.Value]) |] + | _ -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.SpecializationForUnknownCallable, [cName.Value]) |] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise /// Adds an auto-generated specialization of the given kind to the callable with the given name and declaration in the specified source file. @@ -733,6 +810,17 @@ and NamespaceManager | Value (nsName, source) -> [(nsName, source)] | Null -> importedNS |> List.choose (containsSource >> function | Value v -> Some v | Null -> None) + /// Calls the resolver function on each namespace opened within the given namespace name and source file, and + /// attempts to find an unambiguous resolution. + let resolveInOpenNamespaces resolver (nsName, source) = + let resolveWithNsName (ns : Namespace) = + resolver ns |> ResolutionResult.map (fun value -> (ns.Name, value)) + let currentNs, importedNs = OpenNamespaces (nsName, source) + List.Cons (currentNs, importedNs) + |> List.distinctBy (fun ns -> ns.Name) + |> Seq.map resolveWithNsName + |> ResolutionResult.uniqueResolution fst + /// Given a qualifier for a symbol name, returns the corresponding namespace as Some /// if such a namespace or such a namespace short name within the given parent namespace and source file exists. /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. @@ -762,13 +850,6 @@ and NamespaceManager | true, _ -> [builtIn.Namespace.Value]; | false, _ -> ArgumentException "no namespace with the given name exists" |> raise - /// Returns whether a declaration is accessible from the calling location, given whether the calling location is in - /// the same assembly as the declaration, and the declaration's access modifier. - let IsDeclarationAccessible sameAssembly access = - match access with - | DefaultAccess -> true - | Internal -> sameAssembly - /// Given the qualified or unqualified name of a type used within the given parent namespace and source file, /// determines if such a type is accessible, and returns its full name and the source file or referenced assembly in /// which it is defined as Some if it is. @@ -794,7 +875,7 @@ and NamespaceManager (fun ns -> ns.TryFindType (tName, checkQualificationForDeprecation)) (parentNS, source) let accessibleResolutions = allResolutions |> List.filter (fun (_, (_, _, sameAssembly, modifiers)) -> - IsDeclarationAccessible sameAssembly modifiers.Access) + Namespace.IsDeclarationAccessible sameAssembly modifiers.Access) match accessibleResolutions with | [] -> if List.isEmpty allResolutions @@ -816,7 +897,7 @@ and NamespaceManager | Some ns -> ns.TryFindType (symName, checkQualificationForDeprecation) |> function | Value (declSource, deprecation, sameAssembly, modifiers) -> - if IsDeclarationAccessible sameAssembly modifiers.Access + if Namespace.IsDeclarationAccessible sameAssembly modifiers.Access then buildAndReturn (ns.Name, declSource, deprecation, modifiers, [||]) else None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InaccessibleTypeInNamespace, [symName.Value; qualifier.Value]) |] | _ -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeInNamespace, [symName.Value; qualifier.Value]) |] @@ -1269,8 +1350,8 @@ and NamespaceManager Seq.append (Seq.map (fun callable -> callable, true) (this.DefinedCallables())) (Seq.map (fun callable -> callable, false) (this.ImportedCallables())) - |> Seq.filter (fun (callable, sameAssembly) -> - IsDeclarationAccessible sameAssembly callable.Modifiers.Access) + |> Seq.filter + (fun (callable, sameAssembly) -> Namespace.IsDeclarationAccessible sameAssembly callable.Modifiers.Access) |> Seq.map fst /// Returns the source file and TypeDeclarationHeader of all types imported from referenced assemblies, regardless @@ -1315,8 +1396,8 @@ and NamespaceManager Seq.append (Seq.map (fun qsType -> qsType, true) (this.DefinedTypes())) (Seq.map (fun qsType -> qsType, false) (this.ImportedTypes())) - |> Seq.filter (fun (qsType, sameAssembly) -> - IsDeclarationAccessible sameAssembly qsType.Modifiers.Access) + |> Seq.filter + (fun (qsType, sameAssembly) -> Namespace.IsDeclarationAccessible sameAssembly qsType.Modifiers.Access) |> Seq.map fst /// removes the given source file and all its content from all namespaces @@ -1401,8 +1482,11 @@ and NamespaceManager /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent /// namespace does not exist. member private this.TryGetCallableHeader (callableName : QsQualifiedName, declSource) (nsName, source) = - let BuildHeader fullName (source, kind, declaration : Resolution<_,_>) = - let fallback () = (declaration.Defined, [CallableInformation.Invalid]) |> this.ResolveCallableSignature (kind, callableName, source, declaration.Modifiers.Access) |> fst + let buildHeader fullName (source, kind, declaration : Resolution<_, _>) = + let fallback () = + (declaration.Defined, [CallableInformation.Invalid]) + |> this.ResolveCallableSignature (kind, callableName, source, declaration.Modifiers.Access) + |> fst let resolvedSignature, argTuple = declaration.Resolved.ValueOrApply fallback { Kind = kind @@ -1411,32 +1495,43 @@ and NamespaceManager Modifiers = declaration.Modifiers SourceFile = source Position = DeclarationHeader.Offset.Defined declaration.Position - SymbolRange = DeclarationHeader.Range.Defined declaration.Range + SymbolRange = DeclarationHeader.Range.Defined declaration.Range Signature = resolvedSignature ArgumentTuple = argTuple Documentation = declaration.Documentation } + + let findInSources (ns : Namespace) = function + | Some source -> + // OK to use CallableInSource because this is only evaluated if the callable is not in a + // reference. + let kind, declaration = ns.CallableInSource source callableName.Name + if Namespace.IsDeclarationAccessible true declaration.Modifiers.Access + then Found (buildHeader {callableName with Namespace = ns.Name} (source, kind, declaration)) + else Inaccessible + | None -> + match ns.CallablesDefinedInAllSources().TryGetValue callableName.Name with + | true, (source, (kind, declaration)) -> + if Namespace.IsDeclarationAccessible true declaration.Modifiers.Access + then Found (buildHeader {callableName with Namespace = ns.Name} (source, kind, declaration)) + else Inaccessible + | false, _ -> NotFound + syncRoot.EnterReadLock() try match (nsName, source) |> TryResolveQualifier callableName.Namespace with | None -> NotFound - | Some ns -> ns.CallablesInReferencedAssemblies.TryGetValue callableName.Name |> function - | true, cDecl -> - if IsDeclarationAccessible false cDecl.Modifiers.Access - then Found cDecl - else Inaccessible - | false, _ -> declSource |> function - | Some source -> - let kind, decl = ns.CallableInSource source callableName.Name // ok only because/if we have covered that the callable is not in a reference! - if IsDeclarationAccessible true decl.Modifiers.Access - then BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Found - else Inaccessible - | None -> - ns.CallablesDefinedInAllSources().TryGetValue callableName.Name |> function - | true, (source, (kind, decl)) -> - if IsDeclarationAccessible true decl.Modifiers.Access - then BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) |> Found - else Inaccessible - | false, _ -> NotFound + | Some ns -> + seq { + yield match ns.CallablesInReferencedAssemblies.TryGetValue callableName.Name with + | true, callable -> + if Namespace.IsDeclarationAccessible false callable.Modifiers.Access + then Found callable + else Inaccessible + | false, _ -> NotFound + + yield findInSources ns declSource + } + |> ResolutionResult.firstResolution finally syncRoot.ExitReadLock() /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader in a ResolutionResult if @@ -1453,22 +1548,18 @@ and NamespaceManager /// /// Returns an Ambiguous result with a list with namespaces containing a type with that name if the name cannot be /// uniquely resolved. - member this.TryResolveAndGetCallable cName (nsName, source) = + member this.TryResolveAndGetCallable cName (nsName, source) = syncRoot.EnterReadLock() - try - let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.TryFindCallable cName) - let accessibleResolutions = - allResolutions |> List.filter (fun (declaredNS, (_, _, sameAssembly, modifiers)) -> - IsDeclarationAccessible sameAssembly modifiers.Access) - match accessibleResolutions with - | [] -> if not <| List.isEmpty allResolutions then Inaccessible else NotFound - | [(declNS, (declSource, _, _, _))] -> - this.TryGetCallableHeader ({Namespace = declNS; Name = cName}, Some declSource) (nsName, source) |> function - | Found decl -> Found decl - | _ -> - QsCompilerError.Raise "Expected to find the header corresponding to a possible resolution" - NotFound - | _ -> accessibleResolutions.Select fst |> Ambiguous + try match resolveInOpenNamespaces (fun ns -> ns.TryFindCallable cName) (nsName, source) with + | Found (declaredNs, (declaredSource, _)) -> + let header = this.TryGetCallableHeader ({Namespace = declaredNs; Name = cName}, Some declaredSource) + (nsName, source) + QsCompilerError.Verify ((match header with | Found _ -> true | _ -> false), + "Expected to find the header corresponding to a possible resolution") + header + | Ambiguous namespaces -> Ambiguous namespaces + | Inaccessible -> Inaccessible + | NotFound -> NotFound finally syncRoot.ExitReadLock() /// Given a qualified type name, returns the corresponding TypeDeclarationHeader in a ResolutionResult if the @@ -1501,19 +1592,19 @@ and NamespaceManager | None -> NotFound | Some ns -> ns.TypesInReferencedAssemblies.TryGetValue typeName.Name |> function | true, tDecl -> - if IsDeclarationAccessible false tDecl.Modifiers.Access + if Namespace.IsDeclarationAccessible false tDecl.Modifiers.Access then Found tDecl else Inaccessible | false, _ -> declSource |> function | Some source -> let decl = ns.TypeInSource source typeName.Name // ok only because/if we have covered that the type is not in a reference! - if IsDeclarationAccessible true decl.Modifiers.Access + if Namespace.IsDeclarationAccessible true decl.Modifiers.Access then BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Found else Inaccessible | None -> ns.TypesDefinedInAllSources().TryGetValue typeName.Name |> function | true, (source, decl) -> - if IsDeclarationAccessible true decl.Modifiers.Access + if Namespace.IsDeclarationAccessible true decl.Modifiers.Access then BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Found else Inaccessible | false, _ -> NotFound @@ -1538,7 +1629,7 @@ and NamespaceManager let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.TryFindType tName) let accessibleResolutions = allResolutions |> List.filter (fun (declaredNS, (_, _, sameAssembly, modifiers)) -> - IsDeclarationAccessible sameAssembly modifiers.Access) + Namespace.IsDeclarationAccessible sameAssembly modifiers.Access) match accessibleResolutions with | [] -> if not <| List.isEmpty allResolutions then Inaccessible else NotFound | [(declNS, (declSource, _, _, _))] -> @@ -1566,13 +1657,9 @@ and NamespaceManager member this.NamespacesContainingCallable cName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! syncRoot.EnterReadLock() - try - let tryFindCallable (ns : Namespace) = - ns.TryFindCallable cName |> QsNullable<_>.Bind (fun (_, _, sameAssembly, modifiers) -> - if IsDeclarationAccessible sameAssembly modifiers.Access - then Value ns.Name - else Null) - (Namespaces.Values |> QsNullable<_>.Choose tryFindCallable).ToImmutableArray() + try Namespaces.Values + |> Seq.choose (fun ns -> ns.TryFindCallable cName |> ResolutionResult.toOption |> Option.map fst) + |> fun namespaces -> namespaces.ToImmutableArray () finally syncRoot.ExitReadLock() /// Returns the names of all namespaces in which a type is declared that has the given name and is accessible from @@ -1583,7 +1670,7 @@ and NamespaceManager try let tryFindType (ns : Namespace) = ns.TryFindType tName |> QsNullable<_>.Bind (fun (_, _, sameAssembly, modifiers) -> - if IsDeclarationAccessible sameAssembly modifiers.Access + if Namespace.IsDeclarationAccessible sameAssembly modifiers.Access then Value ns.Name else Null) (Namespaces.Values |> QsNullable<_>.Choose tryFindType).ToImmutableArray() diff --git a/src/QsCompiler/DataStructures/SyntaxTree.fs b/src/QsCompiler/DataStructures/SyntaxTree.fs index 87d83babb3..3d6e8b8fa1 100644 --- a/src/QsCompiler/DataStructures/SyntaxTree.fs +++ b/src/QsCompiler/DataStructures/SyntaxTree.fs @@ -661,6 +661,7 @@ type QsSpecialization = { with member this.AddAttribute att = {this with Attributes = this.Attributes.Add att} member this.WithImplementation impl = {this with Implementation = impl} + member this.WithParent (getName : Func<_,_>) = {this with Parent = getName.Invoke(this.Parent)} /// describes a Q# function, operation, or type constructor @@ -739,6 +740,7 @@ type QsCustomType = { } with member this.AddAttribute att = {this with Attributes = this.Attributes.Add att} + member this.WithFullName (getName : Func<_,_>) = {this with FullName = getName.Invoke(this.FullName)} /// Describes a valid Q# namespace element. From 9916982cb7ac6dc3ba30c651652c8d7e52598f0c Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 01:13:53 -0700 Subject: [PATCH 088/135] Mangle names in CompilationUnit.Build --- .../CompilationManager/CompilationUnit.cs | 60 +++++++++- .../Transformations/SearchAndReplace.cs | 105 ++++++++++++++++++ 2 files changed, 162 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 5aa6cb8a8f..f16096a4d1 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -5,13 +5,17 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; +using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Threading; using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.Diagnostics; using Microsoft.Quantum.QsCompiler.SymbolManagement; using Microsoft.Quantum.QsCompiler.SyntaxProcessing; +using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; +using Microsoft.Quantum.QsCompiler.Transformations.SearchAndReplace; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -473,7 +477,7 @@ private QsCallable GetImportedCallable(CallableDeclarationHeader header) if (header == null) throw new ArgumentNullException(nameof(header)); var importedSpecs = this.GlobalSymbols.ImportedSpecializations(header.QualifiedName); var definedSpecs = this.GlobalSymbols.DefinedSpecializations(header.QualifiedName); - // TODO: This assertion is not valid if a defined callable is shadowing an internal imported callable. + // TODO: This assertion is not valid if a defined callable is shadowing an imported internal callable. // QsCompilerError.Verify(definedSpecs.Length == 0, "external specializations are currently not supported"); var specializations = importedSpecs.Select(imported => { @@ -584,10 +588,10 @@ public QsCompilation Build() } // build the syntax tree - var callables = this.CompiledCallables.Values.Concat(this.GlobalSymbols.ImportedCallables().Select(this.GetImportedCallable)); var types = this.CompiledTypes.Values.Concat(this.GlobalSymbols.ImportedTypes().Select(this.GetImportedType)); - var tree = CompilationUnit.NewSyntaxTree(callables, types, this.GlobalSymbols.Documentation()); + var (taggedCallables, taggedTypes) = TagImportedInternalNames(callables, types); + var tree = NewSyntaxTree(taggedCallables, taggedTypes, this.GlobalSymbols.Documentation()); var entryPoints = tree.Callables().Where(c => c.Attributes.Any(BuiltIn.MarksEntryPoint)).Select(c => c.FullName).ToImmutableArray(); return new QsCompilation(tree, entryPoints); } @@ -687,5 +691,55 @@ internal LocalDeclarations TryGetLocalDeclarations(FileContentManager file, Posi var declarations = implementation?.LocalDeclarationsAt(pos.Subtract(specPos), includeDeclaredAtPosition); return this.PositionedDeclarations(parentCallable, callablePos, specPos, declarations); } + + /// + /// Tags the names of imported internal callables and types with a unique identifier based on the path to their + /// assembly, so that they do not conflict with callables and types defined locally. Renames all references to + /// the tagged declarations found in the given callables and types. + /// + /// The callables to rename and update references in. + /// The types to rename and update references in. + /// The tagged callables and types with references renamed everywhere. + private static (IEnumerable, IEnumerable) + TagImportedInternalNames(IEnumerable callables, IEnumerable types) + { + // Create a mapping from source file names to a short, unique identifying tag. + var tags = + callables.Select(callable => callable.SourceFile.Value) + .Concat(types.Select(type => type.SourceFile.Value)) + .Distinct() + .GroupBy(source => Regex.Replace(Path.GetFileNameWithoutExtension(source), "[^A-Za-z0-9_]", "")) + .SelectMany(group => + group.Count() == 1 + ? new[] { (key: group.Single(), value: group.Key) } + : group.Select((source, index) => (key: source, value: group.Key + index))) + .ToImmutableDictionary(item => item.key, item => item.value); + + QsQualifiedName GetNewName(QsQualifiedName name, string source) => + new QsQualifiedName(name.Namespace, NonNullable.New($"__{name.Name.Value}_{tags[source]}__")); + + ImmutableDictionary GetMappingForSourceGroup( + IGrouping group) => + // TODO: Is there another way besides file extension to check if the source file is a reference? + group + .Where(item => item.access.IsInternal && !item.source.EndsWith(".qs")) + .ToImmutableDictionary(item => item.name, item => GetNewName(item.name, item.source)); + + var transformations = + callables.Select(callable => + (name: callable.FullName, source: callable.SourceFile.Value, access: callable.Modifiers.Access)) + .Concat(types.Select(type => + (name: type.FullName, source: type.SourceFile.Value, access: type.Modifiers.Access))) + .GroupBy(item => item.source) + .ToImmutableDictionary( + group => group.Key, + group => new RenameReferences(GetMappingForSourceGroup(group))); + + var taggedCallables = callables.Select( + callable => transformations[callable.SourceFile.Value].Namespaces.OnCallableDeclaration(callable)); + var taggedTypes = types.Select( + type => transformations[type.SourceFile.Value].Namespaces.OnTypeDeclaration(type)); + return (taggedCallables, taggedTypes); + } } } diff --git a/src/QsCompiler/Transformations/SearchAndReplace.cs b/src/QsCompiler/Transformations/SearchAndReplace.cs index e12ed5dd81..e823e8b8ad 100644 --- a/src/QsCompiler/Transformations/SearchAndReplace.cs +++ b/src/QsCompiler/Transformations/SearchAndReplace.cs @@ -446,6 +446,111 @@ public override QsExpressionKind OnIdentifier(Identifier sym, QsNullable + /// A transformation that renames all references to each given qualified name. + /// + public class RenameReferences : SyntaxTreeTransformation + { + /// + /// Creates a new rename references transformation. + /// + /// The mapping from existing names to new names. + public RenameReferences(IImmutableDictionary names) + { + var state = new TransformationState(names); + Types = new TypeTransformation(this, state); + ExpressionKinds = new ExpressionKindTransformation(this, state); + Namespaces = new NamespaceTransformation(this, state); + } + + private class TransformationState + { + private readonly IImmutableDictionary names; + + internal TransformationState(IImmutableDictionary names) => + this.names = names; + + /// + /// Gets the renamed version of the qualified name if one exists; otherwise, returns the original name. + /// + /// The qualified name to rename. + /// + /// The renamed version of the qualified name if one exists; otherwise, returns the original name. + /// + internal QsQualifiedName GetNewName(QsQualifiedName name) => names.GetValueOrDefault(name) ?? name; + + /// + /// Gets the renamed version of the user-defined type if one exists; otherwise, returns the original one. + /// + /// + /// The renamed version of the user-defined type if one exists; otherwise, returns the original one. + /// + internal UserDefinedType RenameUdt(UserDefinedType udt) + { + var newName = GetNewName(new QsQualifiedName(udt.Namespace, udt.Name)); + return new UserDefinedType(newName.Namespace, newName.Name, udt.Range); + } + } + + private class TypeTransformation : Core.TypeTransformation + { + private readonly TransformationState state; + + public TypeTransformation(RenameReferences parent, TransformationState state) : base(parent) => + this.state = state; + + public override QsTypeKind OnUserDefinedType(UserDefinedType udt) => + base.OnUserDefinedType(state.RenameUdt(udt)); + + public override QsTypeKind OnTypeParameter(QsTypeParameter tp) => + base.OnTypeParameter(new QsTypeParameter(state.GetNewName(tp.Origin), tp.TypeName, tp.Range)); + } + + private class ExpressionKindTransformation : Core.ExpressionKindTransformation + { + private readonly TransformationState state; + + public ExpressionKindTransformation(RenameReferences parent, TransformationState state) : base(parent) => + this.state = state; + + public override QsExpressionKind OnIdentifier(Identifier id, QsNullable> typeArgs) + { + if (id is Identifier.GlobalCallable global) + { + id = Identifier.NewGlobalCallable(state.GetNewName(global.Item)); + } + return base.OnIdentifier(id, typeArgs); + } + } + + private class NamespaceTransformation : Core.NamespaceTransformation + { + private readonly TransformationState state; + + public NamespaceTransformation(RenameReferences parent, TransformationState state) : base(parent) => + this.state = state; + + public override QsDeclarationAttribute OnAttribute(QsDeclarationAttribute attribute) + { + var argument = Transformation.Expressions.OnTypedExpression(attribute.Argument); + var typeId = attribute.TypeId.IsValue + ? QsNullable.NewValue(state.RenameUdt(attribute.TypeId.Item)) + : attribute.TypeId; + return base.OnAttribute( + new QsDeclarationAttribute(typeId, argument, attribute.Offset, attribute.Comments)); + } + + public override QsCallable OnCallableDeclaration(QsCallable callable) => + base.OnCallableDeclaration(callable.WithFullName(state.GetNewName)); + + public override QsCustomType OnTypeDeclaration(QsCustomType type) => + base.OnTypeDeclaration(type.WithFullName(state.GetNewName)); + + public override QsSpecialization OnSpecializationDeclaration(QsSpecialization spec) => + base.OnSpecializationDeclaration(spec.WithParent(state.GetNewName)); + } + } + // general purpose helpers From 7d31d30714f54fd9a5fb833241335e275c3d451f Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 09:03:56 -0700 Subject: [PATCH 089/135] Add TODO about reversing name mangling --- src/QsCompiler/CompilationManager/CompilationUnit.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index f16096a4d1..c73c7bdedc 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -716,6 +716,7 @@ private static (IEnumerable, IEnumerable) .ToImmutableDictionary(item => item.key, item => item.value); QsQualifiedName GetNewName(QsQualifiedName name, string source) => + // TODO: We might need to change the name format to make it easier to reverse later. new QsQualifiedName(name.Namespace, NonNullable.New($"__{name.Name.Value}_{tags[source]}__")); ImmutableDictionary GetMappingForSourceGroup( From 93190f45f933e855fc3e457c51ee31f418df0dd7 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 09:14:36 -0700 Subject: [PATCH 090/135] Update comments about accessibility in Namespace class --- src/QsCompiler/Core/SymbolTable.fs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 8ac3f12d6c..f87a651e49 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -290,7 +290,10 @@ type private PartialNamespace private /// Represents a namespace and all of its declarations. /// -/// Note that this class is *not* thread-safe, and access modifiers are always ignored when looking up declarations. +/// This class is *not* thread-safe. +/// +/// Access modifiers are taken into consideration when resolving symbols. Some methods bypass this (e.g., when returning +/// a list of all declarations). Individual methods will mention if they adhere to symbol accessibility. and Namespace private (name, parts : IEnumerable,PartialNamespace>>, CallablesInReferences : ImmutableDictionary, CallableDeclarationHeader>, @@ -521,6 +524,7 @@ and Namespace private /// Returns a resolution result for the callable with the given name containing the name of the source file or /// referenced assembly in which it is declared, and a string indicating the redirection if it has been deprecated. + /// Resolution is based on accessibility to source files in this compilation unit. /// /// If the given callable corresponds to the (auto-generated) type constructor for a user defined type, returns the /// file in which that type is declared as the source. From 5ed2a27f0d6de292035e2a05e0b68f853c805a15 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 10:16:10 -0700 Subject: [PATCH 091/135] Check partial NS before ref in TryAddCallableSpecialization --- src/QsCompiler/Core/SymbolTable.fs | 33 +++++++++++++++++------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index f87a651e49..d4e72d281c 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -678,39 +678,44 @@ and Namespace private /// If a declaration for a callable of the given name exists within this namespace, /// verifies that no specialization of the given kind that clashes with the give specialization already exists, /// and adds the specialization defined by the given generator for the given kind to the dictionary of specializations in the given source. - /// The given location is associated with the given specialization and accessible via the record properties Position and HeaderRange. + /// The given location is associated with the given specialization and accessible via the record properties Position and HeaderRange. /// Returns an array with suitable diagnostics if a clashing specialization already exists, and/or - /// if the length of the type arguments in the given generator does not match the number of type parameters of the callable declaration. + /// if the length of the type arguments in the given generator does not match the number of type parameters of the callable declaration. /// If no declaration for the given callable name exists within this namespace, returns an array with suitable diagnostics. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. /// IMPORTANT: The verification of whether the given specialization kind (body, adjoint, controlled, or controlled adjoint) may exist - /// for the given callable is up to the calling routine. + /// for the given callable is up to the calling routine. member this.TryAddCallableSpecialization kind (source, location : QsLocation) ((cName, cRange), generator : QsSpecializationGenerator, attributes, documentation) = let getRelevantDeclInfo (declSource : NonNullable) = let unitOrInvalid fct = function - | Item item -> fct item |> function | UnitType | InvalidType -> true | _ -> false + | Item item -> match fct item with + | UnitType + | InvalidType -> true + | _ -> false | _ -> false - match CallablesInReferences.TryGetValue cName with - | true, cDecl -> - let unitReturn = cDecl.Signature.ReturnType |> unitOrInvalid (fun (t : ResolvedType) -> t.Resolution) - unitReturn, cDecl.Signature.TypeParameters.Length - | false, _ -> - let _, cDecl = Parts.[declSource].GetCallable cName // ok only because/if we have covered that the callable is not in a reference! + + match Parts.TryGetValue declSource with + | true, partial -> + let _, cDecl = partial.GetCallable cName let unitReturn = cDecl.Defined.ReturnType |> unitOrInvalid (fun (t : QsType) -> t.Type) unitReturn, cDecl.Defined.TypeParameters.Length + | false, _ -> + let cDecl = CallablesInReferences.[cName] + let unitReturn = cDecl.Signature.ReturnType |> unitOrInvalid (fun (t : ResolvedType) -> t.Resolution) + unitReturn, cDecl.Signature.TypeParameters.Length - match Parts.TryGetValue source with + match Parts.TryGetValue source with | true, partial -> match this.TryFindCallable cName with | Found (declSource, _) -> - let AddAndClearCache () = + let AddAndClearCache () = CallablesDefinedInAllSourcesCache <- null partial.AddCallableSpecialization location kind (cName, generator, attributes, documentation) // verify that the given specializations are indeed compatible with the defined type parameters let qFunctorSupport, nrTypeParams = getRelevantDeclInfo declSource - let givenNrTypeParams = generator.TypeArguments |> function | Value args -> Some args.Length | Null -> None + let givenNrTypeParams = generator.TypeArguments |> function | Value args -> Some args.Length | Null -> None if givenNrTypeParams.IsSome && givenNrTypeParams.Value <> nrTypeParams then - [| location.Range |> QsCompilerDiagnostic.Error (ErrorCode.TypeSpecializationMismatch, [nrTypeParams.ToString()]) |] + [| location.Range |> QsCompilerDiagnostic.Error (ErrorCode.TypeSpecializationMismatch, [nrTypeParams.ToString()]) |] // verify if a unit return value is required for the given specialization kind elif not qFunctorSupport then kind |> function | QsBody -> AddAndClearCache(); [||] From 4de466dea50542385eb386730205b363e04da692 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 10:45:32 -0700 Subject: [PATCH 092/135] Skip calling FromSingleSource in NS.TryFindCallable --- src/QsCompiler/Core/SymbolTable.fs | 40 +++++++++++++++++------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index d4e72d281c..0084f24d83 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -52,7 +52,7 @@ module private ResolutionResult = /// Returns the first result matching Found, Inaccessible or NotFound, in decreasing order of priority. If the /// sequence contains a Found result, the rest of the sequence after it is not evaluated. - let internal firstResolution results = + let internal tryFirst results = results |> Seq.tryPick (function | Found callable -> Some (Found callable) | _ -> None) |> Option.orElseWith (fun () -> @@ -60,8 +60,8 @@ module private ResolutionResult = |> Option.defaultValue NotFound /// Returns a Found result if there is only one in the sequence. If there is more than one, returns an Ambiguous - /// result. Otherwise, returns the same value as ResolutionResult.firstResolution. - let internal uniqueResolution nsGetter results = + /// result. Otherwise, returns the same value as ResolutionResult.tryFirst. + let internal tryExactlyOne nsGetter results = let found = results |> Seq.filter (function | Found _ -> true | _ -> false) if Seq.length found > 1 then found @@ -70,7 +70,16 @@ module private ResolutionResult = |> Ambiguous else found |> Seq.tryExactlyOne - |> Option.defaultWith (fun () -> firstResolution results) + |> Option.defaultWith (fun () -> tryFirst results) + + /// Returns a Found result if there is only one in the sequence. If there is more than one, raises an exception. + /// Otherwise, returns the same value as ResolutionResult.tryFirst. + let internal exactlyOne = tryExactlyOne (fun _ -> NonNullable<_>.New "") >> function + | Found value -> Found value + | Ambiguous _ -> QsCompilerError.Raise "Resolution is ambiguous" + Exception () |> raise + | Inaccessible -> Inaccessible + | NotFound -> NotFound /// Represents the partial declaration of a namespace in a single file. @@ -543,10 +552,11 @@ and Namespace private let findInPartial (partial : PartialNamespace) = match partial.TryGetCallable cName with | true, (_, callable) -> - Some (partial.Source, - SymbolResolution.TryFindRedirectInUnresolved checkDeprecation callable.DefinedAttributes, - callable.Modifiers.Access) - | false, _ -> None + if Namespace.IsDeclarationAccessible true callable.Modifiers.Access + then Found (partial.Source, + SymbolResolution.TryFindRedirectInUnresolved checkDeprecation callable.DefinedAttributes) + else Inaccessible + | false, _ -> NotFound seq { yield match CallablesInReferences.TryGetValue cName with @@ -556,14 +566,9 @@ and Namespace private else Inaccessible | false, _ -> NotFound - yield match FromSingleSource findInPartial with - | Value (source, deprecation, access) -> - if Namespace.IsDeclarationAccessible true access - then Found (source, deprecation) - else Inaccessible - | Null -> NotFound + yield Seq.map findInPartial Parts.Values |> ResolutionResult.exactlyOne } - |> ResolutionResult.firstResolution + |> ResolutionResult.tryFirst /// Sets the resolution for the type with the given name in the given source file to the given type, /// and replaces the resolved attributes with the given values. @@ -694,6 +699,7 @@ and Namespace private | _ -> false | _ -> false + // Check if the declaration's source file is local first, then look in references. match Parts.TryGetValue declSource with | true, partial -> let _, cDecl = partial.GetCallable cName @@ -828,7 +834,7 @@ and NamespaceManager List.Cons (currentNs, importedNs) |> List.distinctBy (fun ns -> ns.Name) |> Seq.map resolveWithNsName - |> ResolutionResult.uniqueResolution fst + |> ResolutionResult.tryExactlyOne fst /// Given a qualifier for a symbol name, returns the corresponding namespace as Some /// if such a namespace or such a namespace short name within the given parent namespace and source file exists. @@ -1540,7 +1546,7 @@ and NamespaceManager yield findInSources ns declSource } - |> ResolutionResult.firstResolution + |> ResolutionResult.tryFirst finally syncRoot.ExitReadLock() /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader in a ResolutionResult if From 30d3b4ff9673650b538af6f9134aed04195350ad Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 13 Mar 2020 11:13:20 -0700 Subject: [PATCH 093/135] Updated references to built-in objects. --- .../EditorSupport/CodeActions.cs | 105 +-- .../RewriteSteps/ClassicallyControlled.cs | 28 +- src/QsCompiler/Core/SymbolResolution.fs | 452 +++++----- src/QsCompiler/Core/SymbolTable.fs | 846 +++++++++--------- src/QsCompiler/Core/SyntaxGenerator.fs | 208 ++--- .../Tests.Compiler/ClassicalControlTests.fs | 52 +- .../Transformations/ClassicallyControlled.cs | 12 +- 7 files changed, 854 insertions(+), 849 deletions(-) diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index aee6fbebf4..3cb4f0eed4 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -22,9 +22,9 @@ internal static class SuggestedEdits { /// /// Returns the given edit for the specified file as WorkspaceEdit. - /// Throws an ArgumentNullException if the given file or any of the given edits is null. + /// Throws an ArgumentNullException if the given file or any of the given edits is null. /// - private static WorkspaceEdit GetWorkspaceEdit(this FileContentManager file, params TextEdit[] edits) + private static WorkspaceEdit GetWorkspaceEdit(this FileContentManager file, params TextEdit[] edits) { if (file == null) throw new ArgumentNullException(nameof(file)); if (edits == null || edits.Any(edit => edit == null)) throw new ArgumentNullException(nameof(edits)); @@ -39,7 +39,7 @@ private static WorkspaceEdit GetWorkspaceEdit(this FileContentManager file, para /// /// Returns all namespaces in which a callable with the name of the symbol at the given position in the given file belongs to. - /// Returns an empty collection if any of the arguments is null or if no unqualified symbol exists at that location. + /// Returns an empty collection if any of the arguments is null or if no unqualified symbol exists at that location. /// Returns the name of the identifier as out parameter if an unqualified symbol exists at that location. /// private static IEnumerable> NamespaceSuggestionsForIdAtPosition @@ -54,7 +54,7 @@ private static IEnumerable> NamespaceSuggestionsForIdAtPosit /// /// Returns all namespaces in which a type with the name of the symbol at the given position in the given file belongs to. - /// Returns an empty collection if any of the arguments is null or if no unqualified symbol exists at that location. + /// Returns an empty collection if any of the arguments is null or if no unqualified symbol exists at that location. /// Returns the name of the type as out parameter if an unqualified symbol exists at that location. /// private static IEnumerable> NamespaceSuggestionsForTypeAtPosition @@ -70,8 +70,8 @@ private static IEnumerable> NamespaceSuggestionsForTypeAtPos } /// - /// Returns all code fragments in the specified file that overlap with the given range. - /// Returns an empty sequence if any of the given arguments is null. + /// Returns all code fragments in the specified file that overlap with the given range. + /// Returns an empty sequence if any of the given arguments is null. /// private static IEnumerable FragmentsOverlappingWithRange(this FileContentManager file, LSP.Range range) { @@ -92,9 +92,9 @@ private static IEnumerable FragmentsOverlappingWithRange(this File } /// - /// Return an enumerable of suitable edits to add open directives for all given namespaces for which no open directive already exists. - /// Returns an edit for opening a given namespace even if an alias is already defined for that namespace. - /// Returns an empty enumerable if suitable edits could not be determined. + /// Return an enumerable of suitable edits to add open directives for all given namespaces for which no open directive already exists. + /// Returns an edit for opening a given namespace even if an alias is already defined for that namespace. + /// Returns an empty enumerable if suitable edits could not be determined. /// private static IEnumerable OpenDirectiveSuggestions(this FileContentManager file, int lineNr, params NonNullable[] namespaces) { @@ -131,9 +131,9 @@ private static IEnumerable OpenDirectiveSuggestions(this FileContentMa } /// - /// Returns a sequence of suggestions on how errors for ambiguous types and callable in the given diagnostics can be fixed, - /// given the file for which those diagnostics were generated and the corresponding compilation. - /// Returns an empty enumerable if any of the given arguments is null. + /// Returns a sequence of suggestions on how errors for ambiguous types and callable in the given diagnostics can be fixed, + /// given the file for which those diagnostics were generated and the corresponding compilation. + /// Returns an empty enumerable if any of the given arguments is null. /// internal static IEnumerable<(string, WorkspaceEdit)> SuggestionsForAmbiguousIdentifiers (this FileContentManager file, CompilationUnit compilation, IEnumerable diagnostics) @@ -159,10 +159,10 @@ private static IEnumerable OpenDirectiveSuggestions(this FileContentMa } /// - /// Returns a sequence of suggestions on how errors for unknown types and callable in the given diagnostics can be fixed, - /// given the file for which those diagnostics were generated and the corresponding compilation. - /// The given line number is used to determine the containing namespace. - /// Returns an empty enumerable if any of the given arguments is null. + /// Returns a sequence of suggestions on how errors for unknown types and callable in the given diagnostics can be fixed, + /// given the file for which those diagnostics were generated and the corresponding compilation. + /// The given line number is used to determine the containing namespace. + /// Returns an empty enumerable if any of the given arguments is null. /// internal static IEnumerable<(string, WorkspaceEdit)> SuggestionsForUnknownIdentifiers (this FileContentManager file, CompilationUnit compilation, int lineNr, IEnumerable diagnostics) @@ -181,9 +181,9 @@ private static IEnumerable OpenDirectiveSuggestions(this FileContentMa } /// - /// Returns a sequence of suggestions on how deprecated syntax can be updated based on the generated diagnostics, - /// and given the file for which those diagnostics were generated. - /// Returns an empty enumerable if any of the given arguments is null. + /// Returns a sequence of suggestions on how deprecated syntax can be updated based on the generated diagnostics, + /// and given the file for which those diagnostics were generated. + /// Returns an empty enumerable if any of the given arguments is null. /// internal static IEnumerable<(string, WorkspaceEdit)> SuggestionsForDeprecatedSyntax (this FileContentManager file, IEnumerable diagnostics) @@ -197,7 +197,7 @@ private static IEnumerable OpenDirectiveSuggestions(this FileContentMa (string, WorkspaceEdit) ReplaceWith(string text, LSP.Range range) { - bool NeedsWs(Char ch) => Char.IsLetterOrDigit(ch) || ch == '_'; + static bool NeedsWs(Char ch) => Char.IsLetterOrDigit(ch) || ch == '_'; if (range?.Start != null && range.End != null) { var beforeEdit = file.GetLine(range.Start.Line).Text.Substring(0, range.Start.Character); @@ -218,7 +218,7 @@ private static IEnumerable OpenDirectiveSuggestions(this FileContentMa // update deprecated operation characteristics syntax - string CharacteristicsAnnotation(Characteristics c) + static string CharacteristicsAnnotation(Characteristics c) { var charEx = SyntaxTreeToQsharp.CharacteristicsExpression(SymbolResolution.ResolveCharacteristics(c)); return charEx == null ? "" : $"{Keywords.qsCharacteristics.id} {charEx}"; @@ -226,10 +226,11 @@ string CharacteristicsAnnotation(Characteristics c) var suggestionsForOpCharacteristics = deprecatedOpCharacteristics.SelectMany(d => { - // TODO: TryGetQsSymbolInfo currently only returns information about the inner most leafs rather than all types etc. - // Once it returns indeed all types in the fragment, the following code block should be replaced by the commented out code below. + // TODO: TryGetQsSymbolInfo currently only returns information about the inner most leafs rather than all types etc. + // Once it returns indeed all types in the fragment, the following code block should be replaced by the commented out code below. var fragment = file.TryGetFragmentAt(d.Range.Start, out var _); - IEnumerable GetCharacteristics(QsTuple> argTuple) => + + static IEnumerable GetCharacteristics(QsTuple> argTuple) => SyntaxGenerator.ExtractItems(argTuple).SelectMany(item => item.Item2.ExtractCharacteristics()).Distinct(); var characteristicsInFragment = fragment?.Kind is QsFragmentKind.FunctionDeclaration function ? GetCharacteristics(function.Item2.Argument) : @@ -254,9 +255,9 @@ IEnumerable GetCharacteristics(QsTuple> } /// - /// Returns a sequence of suggestions for update-and-reassign statements based on the generated diagnostics, - /// and given the file for which those diagnostics were generated. - /// Returns an empty enumerable if any of the given arguments is null. + /// Returns a sequence of suggestions for update-and-reassign statements based on the generated diagnostics, + /// and given the file for which those diagnostics were generated. + /// Returns an empty enumerable if any of the given arguments is null. /// internal static IEnumerable<(string, WorkspaceEdit)> SuggestionsForUpdateAndReassignStatements (this FileContentManager file, IEnumerable diagnostics) @@ -286,29 +287,29 @@ IEnumerable GetCharacteristics(QsTuple> } /// - /// Returns a sequence of suggestions for replacing ranges over array indices with the corresponding library call, - /// provided the corresponding library is referenced. - /// Returns an empty enumerable if this is not the case or any of the given arguments is null. + /// Returns a sequence of suggestions for replacing ranges over array indices with the corresponding library call, + /// provided the corresponding library is referenced. + /// Returns an empty enumerable if this is not the case or any of the given arguments is null. /// internal static IEnumerable<(string, WorkspaceEdit)> SuggestionsForIndexRange (this FileContentManager file, CompilationUnit compilation, LSP.Range range) { if (file == null || compilation == null || range?.Start == null) return Enumerable.Empty<(string, WorkspaceEdit)>(); - var indexRangeNamespaces = compilation.GlobalSymbols.NamespacesContainingCallable(BuiltIn.IndexRange.Name); - if (!indexRangeNamespaces.Contains(BuiltIn.IndexRange.Namespace)) return Enumerable.Empty<(string, WorkspaceEdit)>(); - var suggestedOpenDir = file.OpenDirectiveSuggestions(range.Start.Line, BuiltIn.IndexRange.Namespace); + var indexRangeNamespaces = compilation.GlobalSymbols.NamespacesContainingCallable(BuiltIn.IndexRange.FullName.Name); + if (!indexRangeNamespaces.Contains(BuiltIn.IndexRange.FullName.Namespace)) return Enumerable.Empty<(string, WorkspaceEdit)>(); + var suggestedOpenDir = file.OpenDirectiveSuggestions(range.Start.Line, BuiltIn.IndexRange.FullName.Namespace); - /// Returns true the given expression is of the form "0 .. Length(args) - 1", - /// as well as the range of the entire expression and the argument tuple "(args)" as out parameters. - bool IsIndexRange(QsExpression iterExpr, Position offset, out LSP.Range exprRange, out LSP.Range argRange) + /// Returns true the given expression is of the form "0 .. Length(args) - 1", + /// as well as the range of the entire expression and the argument tuple "(args)" as out parameters. + static bool IsIndexRange(QsExpression iterExpr, Position offset, out LSP.Range exprRange, out LSP.Range argRange) { if (iterExpr.Expression is QsExpressionKind.RangeLiteral rangeExpression && iterExpr.Range.IsValue && // iterable expression is a valid range literal rangeExpression.Item1.Expression is QsExpressionKind.IntLiteral intLiteralExpression && intLiteralExpression.Item == 0L && // .. starting at 0 .. - rangeExpression.Item2.Expression is QsExpressionKind.SUB SUBExpression && // .. and ending in subracting .. + rangeExpression.Item2.Expression is QsExpressionKind.SUB SUBExpression && // .. and ending in subtracting .. SUBExpression.Item2.Expression is QsExpressionKind.IntLiteral subIntLiteralExpression && subIntLiteralExpression.Item == 1L && // .. 1 from .. SUBExpression.Item1.Expression is QsExpressionKind.CallLikeExpression callLikeExression && // .. a call .. callLikeExression.Item1.Expression is QsExpressionKind.Identifier identifier && // .. to and identifier .. - identifier.Item1.Symbol is QsSymbolKind.Symbol symName && symName.Item.Value == BuiltIn.Length.Name.Value && // .. "Length" called with .. + identifier.Item1.Symbol is QsSymbolKind.Symbol symName && symName.Item.Value == BuiltIn.Length.FullName.Name.Value && // .. "Length" called with .. callLikeExression.Item2.Expression is QsExpressionKind.ValueTuple valueTuple && callLikeExression.Item2.Range.IsValue) // .. a valid argument tuple { exprRange = DiagnosticTools.GetAbsoluteRange(offset, iterExpr.Range.Item); @@ -321,8 +322,8 @@ callLikeExression.Item1.Expression is QsExpressionKind IndexRangeEdits(CodeFragment fragment) + /// The returned edits do *not* include an edit for adding the corresponding open-directive if necessary. + static IEnumerable IndexRangeEdits(CodeFragment fragment) { if (fragment.Kind is QsFragmentKind.ForLoopIntro forLoopIntro && // todo: in principle we could give these suggestions for any index range IsIndexRange(forLoopIntro.Item2, fragment.GetRange().Start, out var iterExprRange, out var argTupleRange)) @@ -330,7 +331,7 @@ IEnumerable IndexRangeEdits(CodeFragment fragment) yield return new TextEdit() { Range = new LSP.Range() { Start = iterExprRange.Start, End = argTupleRange.Start }, - NewText = BuiltIn.IndexRange.Name.Value + NewText = BuiltIn.IndexRange.FullName.Name.Value }; yield return new TextEdit() { @@ -342,15 +343,15 @@ IEnumerable IndexRangeEdits(CodeFragment fragment) var fragments = file.FragmentsOverlappingWithRange(range); var edits = fragments.SelectMany(IndexRangeEdits); - return edits.Any() - ? new[] { ("Use IndexRange to iterate over indices.", file.GetWorkspaceEdit(suggestedOpenDir.Concat(edits).ToArray())) } + return edits.Any() + ? new[] { ("Use IndexRange to iterate over indices.", file.GetWorkspaceEdit(suggestedOpenDir.Concat(edits).ToArray())) } : Enumerable.Empty<(string, WorkspaceEdit)>(); } /// - /// Returns a sequence of suggestions for removing code that is never executed based on the generated diagnostics, - /// and given the file for which those diagnostics were generated. - /// Returns an empty enumerable if any of the given arguments is null. + /// Returns a sequence of suggestions for removing code that is never executed based on the generated diagnostics, + /// and given the file for which those diagnostics were generated. + /// Returns an empty enumerable if any of the given arguments is null. /// internal static IEnumerable<(string, WorkspaceEdit)> SuggestionsForUnreachableCode (this FileContentManager file, IEnumerable diagnostics) @@ -380,7 +381,7 @@ WorkspaceEdit SuggestedRemoval(Position pos) // determine the whitespace for the replacement string var lastLine = file.GetLine(lastFragToken.Line).Text.Substring(0, lastInScope.GetRange().Start.Character); var trimmedLastLine = lastLine.TrimEnd(); - var whitespace = lastLine.Substring(trimmedLastLine.Length, lastLine.Length - trimmedLastLine.Length); + var whitespace = lastLine[trimmedLastLine.Length..]; // build the replacement string var replaceString = lastBeforeErase.FollowedBy == CodeFragment.MissingDelimiter ? "" : $"{lastBeforeErase.FollowedBy}"; @@ -398,11 +399,11 @@ WorkspaceEdit SuggestedRemoval(Position pos) } /// - /// Returns a sequence of suggestions to insert doc comments for an undocumented declaration that overlap with the given range in the given file. + /// Returns a sequence of suggestions to insert doc comments for an undocumented declaration that overlap with the given range in the given file. /// Returns an empty enumerable if more than one code fragment overlaps with the given range, /// or the overlapping fragment does not contain a declaration, /// or the overlapping fragment contains a declaration that is already documented, - /// or if any of the given arguments is null. + /// or if any of the given arguments is null. /// internal static IEnumerable<(string, WorkspaceEdit)> DocCommentSuggestions(this FileContentManager file, LSP.Range range) { @@ -411,14 +412,14 @@ WorkspaceEdit SuggestedRemoval(Position pos) if (fragment?.Kind == null || overlapping.Count() != 1) return Enumerable.Empty<(string, WorkspaceEdit)>(); // only suggest doc comment directly on the declaration var (nsDecl, callableDecl, typeDecl) = (fragment.Kind.DeclaredNamespace(), fragment.Kind.DeclaredCallable(), fragment.Kind.DeclaredType()); - var declSymbol = nsDecl.IsValue ? nsDecl.Item.Item1.Symbol + var declSymbol = nsDecl.IsValue ? nsDecl.Item.Item1.Symbol : callableDecl.IsValue ? callableDecl.Item.Item1.Symbol : typeDecl.IsValue ? typeDecl.Item.Item1.Symbol : null; var declStart = fragment.GetRange().Start; if (declSymbol == null || file.DocumentingComments(declStart).Any()) return Enumerable.Empty<(string, WorkspaceEdit)>(); // set declStart to the position of the first attribute attached to the declaration - bool EmptyOrFirstAttribute(IEnumerable line, out CodeFragment att) + static bool EmptyOrFirstAttribute(IEnumerable line, out CodeFragment att) { att = line?.Reverse().TakeWhile(t => t.Kind is QsFragmentKind.DeclarationAttribute).LastOrDefault(); return att != null || (line != null && !line.Any()); @@ -447,7 +448,7 @@ bool EmptyOrFirstAttribute(IEnumerable line, out CodeFragment att) // Document Input Parameters args.Any() ? $"{docPrefix}# Input{endLine}" : String.Empty, String.Concat(args.Select(x => $"{docPrefix}## {x.Item1.Symbol.AsDeclarationName(null)}{endLine}{docPrefix}{endLine}")), - // Document Output + // Document Output hasOutput ? $"{docPrefix}# Output{endLine}{docPrefix}{endLine}" : String.Empty, // Document Type Parameters typeParams.Any() ? $"{docPrefix}# Type Parameters{endLine}" : String.Empty, diff --git a/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs b/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs index 01410bf9ff..9e62c97956 100644 --- a/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs +++ b/src/QsCompiler/Compiler/RewriteSteps/ClassicallyControlled.cs @@ -2,8 +2,8 @@ // Licensed under the MIT License. using System.Collections.Generic; -using System.Linq; -using Microsoft.Quantum.QsCompiler.DataTypes; +using System.Linq; +using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.SyntaxTree; using Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlled; @@ -45,20 +45,20 @@ public bool PreconditionVerification(QsCompilation compilation) var providedOperations = new QsNamespace[] { controlNs }.Callables().Select(c => c.FullName.Name); var requiredBuiltIns = new List>() { - BuiltIn.ApplyIfZero.Name, - BuiltIn.ApplyIfZeroA.Name, - BuiltIn.ApplyIfZeroC.Name, - BuiltIn.ApplyIfZeroCA.Name, + BuiltIn.ApplyIfZero.FullName.Name, + BuiltIn.ApplyIfZeroA.FullName.Name, + BuiltIn.ApplyIfZeroC.FullName.Name, + BuiltIn.ApplyIfZeroCA.FullName.Name, - BuiltIn.ApplyIfOne.Name, - BuiltIn.ApplyIfOneA.Name, - BuiltIn.ApplyIfOneC.Name, - BuiltIn.ApplyIfOneCA.Name, + BuiltIn.ApplyIfOne.FullName.Name, + BuiltIn.ApplyIfOneA.FullName.Name, + BuiltIn.ApplyIfOneC.FullName.Name, + BuiltIn.ApplyIfOneCA.FullName.Name, - BuiltIn.ApplyIfElseR.Name, - BuiltIn.ApplyIfElseRA.Name, - BuiltIn.ApplyIfElseRC.Name, - BuiltIn.ApplyIfElseRCA.Name + BuiltIn.ApplyIfElseR.FullName.Name, + BuiltIn.ApplyIfElseRA.FullName.Name, + BuiltIn.ApplyIfElseRC.FullName.Name, + BuiltIn.ApplyIfElseRCA.FullName.Name }; return requiredBuiltIns.All(builtIn => providedOperations.Any(provided => provided.Equals(builtIn))); diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index 921ecd96f5..66e4eebc19 100644 --- a/src/QsCompiler/Core/SymbolResolution.fs +++ b/src/QsCompiler/Core/SymbolResolution.fs @@ -22,8 +22,8 @@ type AttributeAnnotation = { Comments : QsComments } with - static member internal NonInterpolatedStringArgument inner = function - | Item arg -> inner arg |> function + static member internal NonInterpolatedStringArgument inner = function + | Item arg -> inner arg |> function | StringLiteral (str, interpol) when interpol.Length = 0 -> str.Value | _ -> null | _ -> null @@ -47,92 +47,92 @@ type internal ResolvedGenerator = internal { Directive : QsNullable } -/// used to group the relevant sets of specializations (i.e. group them according to type- and set-arguments) +/// used to group the relevant sets of specializations (i.e. group them according to type- and set-arguments) type SpecializationBundleProperties = internal { BundleInfo : CallableInformation - DefinedGenerators : ImmutableDictionary -} with + DefinedGenerators : ImmutableDictionary +} with - /// Given the type- and set-arguments associated with a certain specialization, + /// Given the type- and set-arguments associated with a certain specialization, /// determines the corresponding unique identifier for all specializations with the same type- and set-arguments. - static member public BundleId (typeArgs : QsNullable>) = + static member public BundleId (typeArgs : QsNullable>) = typeArgs |> QsNullable<_>.Map (fun args -> (args |> Seq.map (fun t -> t.WithoutRangeInfo)).ToImmutableArray()) - /// Returns an identifier for the bundle to which the given specialization declaration belongs to. - /// Throws an InvalidOperationException if no (partial) resolution is defined for the given specialization. - static member internal BundleId (spec : Resolution<_,_>) = + /// Returns an identifier for the bundle to which the given specialization declaration belongs to. + /// Throws an InvalidOperationException if no (partial) resolution is defined for the given specialization. + static member internal BundleId (spec : Resolution<_,_>) = match spec.Resolved with | Null -> InvalidOperationException "cannot determine id for unresolved specialization" |> raise - | Value (gen : ResolvedGenerator) -> SpecializationBundleProperties.BundleId gen.TypeArguments + | Value (gen : ResolvedGenerator) -> SpecializationBundleProperties.BundleId gen.TypeArguments - /// Given a function that associates an item of the given array with a particular set of type- and set-arguments, - /// as well as a function that associates it with a certain specialization kind, - /// returns a dictionary that contains a dictionary mapping the specialization kind to the corresponding item for each set of type arguments. - /// The keys for the returned dictionary are the BundleIds for particular sets of type- and characteristics-arguments. - static member public Bundle (getTypeArgs : Func<_,_>, getKind : Func<_,QsSpecializationKind>) (specs : IEnumerable<'T>) = + /// Given a function that associates an item of the given array with a particular set of type- and set-arguments, + /// as well as a function that associates it with a certain specialization kind, + /// returns a dictionary that contains a dictionary mapping the specialization kind to the corresponding item for each set of type arguments. + /// The keys for the returned dictionary are the BundleIds for particular sets of type- and characteristics-arguments. + static member public Bundle (getTypeArgs : Func<_,_>, getKind : Func<_,QsSpecializationKind>) (specs : IEnumerable<'T>) = specs.ToLookup(new Func<_,_>(getTypeArgs.Invoke >> SpecializationBundleProperties.BundleId)).ToDictionary( - (fun group -> group.Key), + (fun group -> group.Key), (fun group -> group.ToImmutableDictionary(getKind))) -module SymbolResolution = +module SymbolResolution = // routines for giving deprecation warnings - /// Returns true if any one of the given unresolved attributes indicates a deprecation. - let internal IndicatesDeprecation checkQualification attribute = attribute.Id.Symbol |> function - | Symbol sym -> sym.Value = BuiltIn.Deprecated.Name.Value && checkQualification "" - | QualifiedSymbol (ns, sym) -> sym.Value = BuiltIn.Deprecated.Name.Value && (ns.Value = BuiltIn.Deprecated.Namespace.Value || checkQualification ns.Value) + /// Returns true if any one of the given unresolved attributes indicates a deprecation. + let internal IndicatesDeprecation checkQualification attribute = attribute.Id.Symbol |> function + | Symbol sym -> sym.Value = BuiltIn.Deprecated.FullName.Name.Value && checkQualification "" + | QualifiedSymbol (ns, sym) -> sym.Value = BuiltIn.Deprecated.FullName.Name.Value && (ns.Value = BuiltIn.Deprecated.FullName.Namespace.Value || checkQualification ns.Value) | _ -> false - /// Given the redirection extracted by TryFindRedirect, - /// returns an array with diagnostics for using a type or callable with the given name at the given range. - /// Returns an empty array if no redirection was determined, i.e. the given redirection was Null. + /// Given the redirection extracted by TryFindRedirect, + /// returns an array with diagnostics for using a type or callable with the given name at the given range. + /// Returns an empty array if no redirection was determined, i.e. the given redirection was Null. let GenerateDeprecationWarning (fullName : QsQualifiedName, range) redirect = redirect |> function - | Value redirect -> + | Value redirect -> let usedName = sprintf "%s.%s" fullName.Namespace.Value fullName.Name.Value if String.IsNullOrWhiteSpace redirect then [| range |> QsCompilerDiagnostic.Warning (WarningCode.DeprecationWithoutRedirect, [usedName]) |] else [| range |> QsCompilerDiagnostic.Warning (WarningCode.DeprecationWithRedirect, [usedName; redirect]) |] | Null -> [| |] - /// Applies the given getArgument function to the given attributes, - /// and extracts and returns the non-interpolated string argument for all attributes for which getArgument returned Some. - /// The returned sequence may contain null if the argument of an attribute for which getArgument returned Some was not a non-interpolated string. - let private StringArgument (getArgument, getInner) attributes = + /// Applies the given getArgument function to the given attributes, + /// and extracts and returns the non-interpolated string argument for all attributes for which getArgument returned Some. + /// The returned sequence may contain null if the argument of an attribute for which getArgument returned Some was not a non-interpolated string. + let private StringArgument (getArgument, getInner) attributes = let argument = getArgument >> function | Some redirect -> redirect |> AttributeAnnotation.NonInterpolatedStringArgument getInner |> Some | None -> None - attributes |> Seq.choose argument + attributes |> Seq.choose argument - /// Checks whether the given attributes indicate that the corresponding declaration has been deprecated. - /// Returns a string containing the name of the type or callable to use instead as Value if this is the case, or Null otherwise. - /// The returned string is empty or null if the declaration has been deprecated but an alternative is not specified or could not be determined. - /// If several attributes indicate deprecation, a redirection is suggested based on the first deprecation attribute. - let internal TryFindRedirectInUnresolved checkQualification attributes = + /// Checks whether the given attributes indicate that the corresponding declaration has been deprecated. + /// Returns a string containing the name of the type or callable to use instead as Value if this is the case, or Null otherwise. + /// The returned string is empty or null if the declaration has been deprecated but an alternative is not specified or could not be determined. + /// If several attributes indicate deprecation, a redirection is suggested based on the first deprecation attribute. + let internal TryFindRedirectInUnresolved checkQualification attributes = let getRedirect (att : AttributeAnnotation) = if att |> IndicatesDeprecation checkQualification then Some att.Argument else None StringArgument (getRedirect, fun ex -> ex.Expression) attributes |> Seq.tryHead |> QsNullable<_>.FromOption - /// Checks whether the given attributes indicate that the corresponding declaration has been deprecated. - /// Returns a string containing the name of the type or callable to use instead as Value if this is the case, or Null otherwise. - /// The returned string is empty or null if the declaration has been deprecated but an alternative is not specified or could not be determined. - /// If several attributes indicate deprecation, a redirection is suggested based on the first deprecation attribute. - let TryFindRedirect attributes = + /// Checks whether the given attributes indicate that the corresponding declaration has been deprecated. + /// Returns a string containing the name of the type or callable to use instead as Value if this is the case, or Null otherwise. + /// The returned string is empty or null if the declaration has been deprecated but an alternative is not specified or could not be determined. + /// If several attributes indicate deprecation, a redirection is suggested based on the first deprecation attribute. + let TryFindRedirect attributes = let getRedirect (att : QsDeclarationAttribute) = if att |> BuiltIn.MarksDeprecation then Some att.Argument else None StringArgument (getRedirect, fun ex -> ex.Expression) attributes |> Seq.tryHead |> QsNullable<_>.FromOption - /// Checks whether the given attributes indicate that the corresponding declaration contains a unit test. - /// Returns a sequence of strings defining all execution targets on which the test should be run. Invalid execution targets are set to null. - /// The returned sequence is empty if the declaration does not contain a unit test. - let TryFindTestTargets attributes = + /// Checks whether the given attributes indicate that the corresponding declaration contains a unit test. + /// Returns a sequence of strings defining all execution targets on which the test should be run. Invalid execution targets are set to null. + /// 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 targetName (target : string) = + let targetName (target : string) = if target = null then null elif SyntaxGenerator.FullyQualifiedName.IsMatch target then target else target.ToLowerInvariant() |> validTargets.TryGetValue |> function | true, valid -> valid | false, _ -> null - StringArgument (getTarget, fun ex -> ex.Expression) attributes + StringArgument (getTarget, fun ex -> ex.Expression) attributes |> Seq.map targetName |> ImmutableHashSet.CreateRange @@ -142,7 +142,7 @@ module SymbolResolution = let rec ResolveCharacteristics (ex : Characteristics) = // needs to preserve set parameters match ex.Characteristics with | EmptySet -> ResolvedCharacteristics.Empty - | SimpleSet s -> SimpleSet s |> ResolvedCharacteristics.New + | SimpleSet s -> SimpleSet s |> ResolvedCharacteristics.New | Intersection (s1, s2) -> Intersection (ResolveCharacteristics s1, ResolveCharacteristics s2) |> ResolvedCharacteristics.New | Union (s1, s2) -> Union (ResolveCharacteristics s1, ResolveCharacteristics s2) |> ResolvedCharacteristics.New | InvalidSetExpr -> InvalidSetExpr |> ResolvedCharacteristics.New @@ -153,35 +153,35 @@ module SymbolResolution = let ts, errs = (inner.Select fst).ToImmutableArray(), inner.Select snd |> Array.concat build ts, errs - /// Helper function for ResolveCallableSignature that verifies whether the given type parameters and the given return type - /// are fully resolved by an argument of the given the argument type. Generates and returns suitable warnings if this is not the case. - let private TypeParameterResolutionWarnings (argumentType : ResolvedType) (returnType : ResolvedType, range) typeParams = + /// Helper function for ResolveCallableSignature that verifies whether the given type parameters and the given return type + /// are fully resolved by an argument of the given the argument type. Generates and returns suitable warnings if this is not the case. + let private TypeParameterResolutionWarnings (argumentType : ResolvedType) (returnType : ResolvedType, range) typeParams = // FIXME: this verification needs to be done for each specialization individually once type specializations are fully supported - let typeParamsResolvedByArg = + let typeParamsResolvedByArg = let getTypeParams (t : ResolvedType) = t.Resolution |> function - | QsTypeKind.TypeParameter (tp : QsTypeParameter) -> [tp.TypeName].AsEnumerable() + | QsTypeKind.TypeParameter (tp : QsTypeParameter) -> [tp.TypeName].AsEnumerable() | _ -> Enumerable.Empty() argumentType.ExtractAll getTypeParams |> Seq.toList - let excessTypeParamWarn = + let excessTypeParamWarn = let isUnresolvedByArg = function | (ValidName name, range) -> if typeParamsResolvedByArg.Contains name then None else range |> QsCompilerDiagnostic.Warning (WarningCode.TypeParameterNotResolvedByArgument, []) |> Some | _ -> None typeParams |> List.choose isUnresolvedByArg - let unresolvableReturnType = - let isUnresolved = function - | QsTypeKind.TypeParameter (tp : QsTypeParameter) -> not (typeParamsResolvedByArg |> List.contains tp.TypeName) + let unresolvableReturnType = + let isUnresolved = function + | QsTypeKind.TypeParameter (tp : QsTypeParameter) -> not (typeParamsResolvedByArg |> List.contains tp.TypeName) | _ -> false returnType.Exists isUnresolved let returnTypeErr = range |> QsCompilerDiagnostic.Warning (WarningCode.ReturnTypeNotResolvedByArgument, []) if unresolvableReturnType then returnTypeErr :: excessTypeParamWarn |> List.toArray else excessTypeParamWarn |> List.toArray - /// Helper function for ResolveCallableSignature that resolves the given argument tuple - /// using the given routine to resolve the declared item types. - /// Throws an ArgumentException if the given argument is a QsTupleItem opposed to a QsTuple. - let private ResolveArgumentTuple (resolveSymbol, resolveType) arg = - let resolveArg (qsSym : QsSymbol, symType) = + /// Helper function for ResolveCallableSignature that resolves the given argument tuple + /// using the given routine to resolve the declared item types. + /// Throws an ArgumentException if the given argument is a QsTupleItem opposed to a QsTuple. + let private ResolveArgumentTuple (resolveSymbol, resolveType) arg = + let resolveArg (qsSym : QsSymbol, symType) = let range = qsSym.Range.ValueOr QsCompilerDiagnostic.DefaultRange let t, tErrs = symType |> resolveType let variable, symErrs = resolveSymbol (qsSym.Symbol, range) t @@ -196,26 +196,26 @@ module SymbolResolution = | _ -> ArgumentException "the argument to a callable needs to be a QsTuple" |> raise /// Returns the LocalVariableDeclaration with the given name and type for an item within a type or callable declaration. - /// Correspondingly, the item is immutable, has no quantum dependencies, - /// the position information is set to null, and the range is set to the given one. + /// Correspondingly, the item is immutable, has no quantum dependencies, + /// the position information is set to null, and the range is set to the given one. let private DeclarationArgument range (name, t) = let info = {IsMutable = false; HasLocalQuantumDependency = false} {VariableName = name; Type = t; InferredInformation = info; Position = Null; Range = range} - /// Give a list with the characteristics of all specializations as well as a routine for type resolution, + /// Give a list with the characteristics of all specializations as well as a routine for type resolution, /// fully resolves the given callable signature as well as its argument tuple. - /// The position offset information for variables declared in the argument tuple will be set to Null. + /// The position offset information for variables declared in the argument tuple will be set to Null. /// Returns the resolved signature and argument tuple, as well as an array with the diagnostics created during resolution. - /// Throws an ArgumentException if the given list of specialization characteristics is empty. + /// Throws an ArgumentException if the given list of specialization characteristics is empty. let internal ResolveCallableSignature (resolveType, specBundleInfos : CallableInformation list) (signature : CallableSignature) = - let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange - let typeParams, tpErrs = - signature.TypeParameters |> Seq.fold (fun (tps, errs) qsSym -> + let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange + let typeParams, tpErrs = + signature.TypeParameters |> Seq.fold (fun (tps, errs) qsSym -> let range = qsSym.Range |> orDefault let invalidTp = InvalidName, range - match qsSym.Symbol with + match qsSym.Symbol with | QsSymbolKind.InvalidSymbol -> invalidTp :: tps, errs - | QsSymbolKind.Symbol sym -> + | QsSymbolKind.Symbol sym -> if not (tps |> List.exists (fst >> (=)(ValidName sym))) then (ValidName sym, range) :: tps, errs else invalidTp :: tps, (range |> QsCompilerDiagnostic.Error (ErrorCode.TypeParameterRedeclaration, [sym.Value])) :: errs | _ -> invalidTp :: tps, (range |> QsCompilerDiagnostic.Error (ErrorCode.InvalidTypeParameterDeclaration, [])) :: errs @@ -231,7 +231,7 @@ module SymbolResolution = let argTuple, inErr = signature.Argument |> ResolveArgumentTuple (resolveArg, resolveType) let argType = argTuple.ResolveWith (fun x -> x.Type.WithoutRangeInfo) let returnType, outErr = signature.ReturnType |> resolveType - let resolvedParams, resErrs = + let resolvedParams, resErrs = let errs = TypeParameterResolutionWarnings argType (returnType, signature.ReturnType.Range |> orDefault) typeParams (typeParams |> Seq.map fst).ToImmutableArray(), errs let callableInfo = CallableInformation.Common specBundleInfos @@ -239,28 +239,28 @@ module SymbolResolution = (resolvedSig, argTuple), [inErr; outErr; resErrs; tpErrs] |> Array.concat /// Give a routine for type resolution, fully resolves the given user defined type as well as its items. - /// The position offset information for the declared named items will be set to Null. + /// The position offset information for the declared named items will be set to Null. /// Returns the underlying type as well as the item tuple, along with an array with the diagnostics created during resolution. - /// Throws an ArgumentException if the given type tuple is an empty QsTuple. - let internal ResolveTypeDeclaration resolveType (udtTuple : QsTuple) = + /// Throws an ArgumentException if the given type tuple is an empty QsTuple. + let internal ResolveTypeDeclaration resolveType (udtTuple : QsTuple) = let itemDeclarations = new List>>() let resolveItem (sym, range) t = sym |> function - | QsSymbolKind.MissingSymbol + | QsSymbolKind.MissingSymbol | QsSymbolKind.InvalidSymbol -> Anonymous t, [||] - | QsSymbolKind.Symbol sym when itemDeclarations.Exists (fun item -> item.VariableName.Value = sym.Value) -> + | QsSymbolKind.Symbol sym when itemDeclarations.Exists (fun item -> item.VariableName.Value = sym.Value) -> Anonymous t, [| range |> QsCompilerDiagnostic.Error (ErrorCode.NamedItemAlreadyExists, [sym.Value]) |] - | QsSymbolKind.Symbol sym -> + | QsSymbolKind.Symbol sym -> let info = {IsMutable = false; HasLocalQuantumDependency = false} itemDeclarations.Add { VariableName = sym; Type = t; InferredInformation = info; Position = Null; Range = range } - (sym, t) |> DeclarationArgument range |> Named, [||] + (sym, t) |> DeclarationArgument range |> Named, [||] | _ -> Anonymous t, [| range |> QsCompilerDiagnostic.Error (ErrorCode.ExpectingUnqualifiedSymbol, []) |] - let argTuple, errs = udtTuple |> function + let argTuple, errs = udtTuple |> function | QsTuple items when items.Length = 0 -> ArgumentException "underlying type in type declaration cannot be an empty tuple" |> raise | QsTuple _ -> udtTuple |> ResolveArgumentTuple (resolveItem, resolveType) | QsTupleItem _ -> ImmutableArray.Create udtTuple |> QsTuple |> ResolveArgumentTuple (resolveItem, resolveType) - let underlyingType = argTuple.ResolveWith (function + let underlyingType = argTuple.ResolveWith (function | Anonymous t -> t.WithoutRangeInfo - | Named x -> x.Type.WithoutRangeInfo) + | Named x -> x.Type.WithoutRangeInfo) (underlyingType, argTuple), errs /// Fully (i.e. recursively) resolves the given Q# type used within the given parent in the given source file. @@ -270,24 +270,24 @@ module SymbolResolution = /// Verifies that all used type parameters are defined in the given list of type parameters, /// and generates suitable diagnostics if they are not, replacing them by the Q# type denoting an invalid type. /// Returns the resolved type as well as an array with diagnostics. - /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is inconsistent with the defined callables. - /// May throw an ArgumentException if no namespace with the given name exists, or the given source file is not listed as source of that namespace. - /// Throws a NonSupportedException if the QsType to resolve contains a MissingType. - let rec internal ResolveType (processUDT, processTypeParameter) (qsType : QsType) = + /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is inconsistent with the defined callables. + /// May throw an ArgumentException if no namespace with the given name exists, or the given source file is not listed as source of that namespace. + /// Throws a NonSupportedException if the QsType to resolve contains a MissingType. + let rec internal ResolveType (processUDT, processTypeParameter) (qsType : QsType) = let resolve = ResolveType (processUDT, processTypeParameter) let asResolvedType t = ResolvedType.New (true, t) let buildWith builder ts = builder ts |> asResolvedType let invalid = InvalidType |> asResolvedType let range = qsType.Range.ValueOr QsCompilerDiagnostic.DefaultRange - match qsType.Type with + match qsType.Type with | ArrayType baseType -> [baseType] |> AccumulateInner resolve (buildWith (fun ts -> ArrayType ts.[0])) - | TupleType items -> items |> AccumulateInner resolve (buildWith TupleType) - | QsTypeKind.TypeParameter sym -> sym.Symbol |> function + | TupleType items -> items |> AccumulateInner resolve (buildWith TupleType) + | QsTypeKind.TypeParameter sym -> sym.Symbol |> function | Symbol name -> processTypeParameter (name, sym.Range) |> fun (k, errs) -> k |> asResolvedType, errs | InvalidSymbol -> invalid, [||] | _ -> invalid, [| range |> QsCompilerDiagnostic.Error (ErrorCode.ExpectingUnqualifiedSymbol, []) |] - | QsTypeKind.Operation ((arg,res), characteristics) -> + | QsTypeKind.Operation ((arg,res), characteristics) -> let opInfo = {Characteristics = characteristics |> ResolveCharacteristics; InferredInformation = InferredCallableInformation.NoInformation} let builder (ts : ImmutableArray<_>) = QsTypeKind.Operation ((ts.[0], ts.[1]), opInfo) [arg; res] |> AccumulateInner resolve (buildWith builder) @@ -297,25 +297,25 @@ module SymbolResolution = | QualifiedSymbol (ns, sym) -> processUDT ((Some ns, sym), name.Range) |> fun (k, errs) -> k |> asResolvedType, errs | InvalidSymbol -> invalid, [||] | MissingSymbol | OmittedSymbols | SymbolTuple _ -> invalid, [| range |> QsCompilerDiagnostic.Error (ErrorCode.ExpectingIdentifier, []) |] - | UnitType -> QsTypeKind.UnitType |> asResolvedType, [||] - | Int -> QsTypeKind.Int |> asResolvedType, [||] - | BigInt -> QsTypeKind.BigInt |> asResolvedType, [||] - | Double -> QsTypeKind.Double |> asResolvedType, [||] - | Bool -> QsTypeKind.Bool |> asResolvedType, [||] - | String -> QsTypeKind.String |> asResolvedType, [||] - | Qubit -> QsTypeKind.Qubit |> asResolvedType, [||] - | Result -> QsTypeKind.Result |> asResolvedType, [||] - | Pauli -> QsTypeKind.Pauli |> asResolvedType, [||] - | Range -> QsTypeKind.Range |> asResolvedType, [||] - | InvalidType -> QsTypeKind.InvalidType |> asResolvedType, [||] - | MissingType -> NotSupportedException "missing type cannot be resolved" |> raise - - /// Resolves the given attribute using the given function getAttribute to resolve the type id and expected argument type. - /// Generates suitable diagnostics if a suitable attribute cannot be determined, - /// or if the attribute argument contains expressions that are not supported, - /// or if the resolved argument type does not match the expected argument type. - /// The TypeId in the resolved attribute is set to Null if the unresolved Id is not a valid identifier - /// or if the correct attribute cannot be determined, and is set to the corresponding type identifier otherwise. + | UnitType -> QsTypeKind.UnitType |> asResolvedType, [||] + | Int -> QsTypeKind.Int |> asResolvedType, [||] + | BigInt -> QsTypeKind.BigInt |> asResolvedType, [||] + | Double -> QsTypeKind.Double |> asResolvedType, [||] + | Bool -> QsTypeKind.Bool |> asResolvedType, [||] + | String -> QsTypeKind.String |> asResolvedType, [||] + | Qubit -> QsTypeKind.Qubit |> asResolvedType, [||] + | Result -> QsTypeKind.Result |> asResolvedType, [||] + | Pauli -> QsTypeKind.Pauli |> asResolvedType, [||] + | Range -> QsTypeKind.Range |> asResolvedType, [||] + | InvalidType -> QsTypeKind.InvalidType |> asResolvedType, [||] + | MissingType -> NotSupportedException "missing type cannot be resolved" |> raise + + /// Resolves the given attribute using the given function getAttribute to resolve the type id and expected argument type. + /// Generates suitable diagnostics if a suitable attribute cannot be determined, + /// or if the attribute argument contains expressions that are not supported, + /// or if the resolved argument type does not match the expected argument type. + /// The TypeId in the resolved attribute is set to Null if the unresolved Id is not a valid identifier + /// or if the correct attribute cannot be determined, and is set to the corresponding type identifier otherwise. let internal ResolveAttribute getAttribute (attribute : AttributeAnnotation) = let asTypedExression range (exKind, exType) = { Expression = exKind @@ -323,16 +323,16 @@ module SymbolResolution = ResolvedType = exType |> ResolvedType.New InferredInformation = {IsMutable = false; HasLocalQuantumDependency = false} Range = range} - let invalidExpr range = (InvalidExpr, InvalidType) |> asTypedExression range + let invalidExpr range = (InvalidExpr, InvalidType) |> asTypedExression range let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange - // We may in the future decide to support arbitary expressions as long as they can be evaluated at compile time. - // At that point it may make sense to replace this with the standard resolution routine for typed expressions. - // For now we support only a restrictive set of valid arguments. - let rec ArgExression (ex : QsExpression) : TypedExpression * QsCompilerDiagnostic[] = + // We may in the future decide to support arbitary expressions as long as they can be evaluated at compile time. + // At that point it may make sense to replace this with the standard resolution routine for typed expressions. + // For now we support only a restrictive set of valid arguments. + let rec ArgExression (ex : QsExpression) : TypedExpression * QsCompilerDiagnostic[] = let diagnostic code range = range |> orDefault |> QsCompilerDiagnostic.Error (code, []) // NOTE: if this is adapted, adapt the header hash as well - match ex.Expression with + match ex.Expression with | UnitValue -> (UnitValue, UnitType) |> asTypedExression ex.Range, [||] | DoubleLiteral d -> (DoubleLiteral d, Double) |> asTypedExression ex.Range, [||] | IntLiteral i -> (IntLiteral i, Int) |> asTypedExression ex.Range, [||] @@ -343,109 +343,109 @@ module SymbolResolution = | StringLiteral (s, exs) -> if exs.Length <> 0 then invalidExpr ex.Range, [| ex.Range |> diagnostic ErrorCode.InterpolatedStringInAttribute |] else (StringLiteral (s, ImmutableArray.Empty), String) |> asTypedExression ex.Range, [||] - | ValueTuple vs -> + | ValueTuple vs -> let innerExs, errs = aggregateInner vs let types = (innerExs |> Seq.map (fun ex -> ex.ResolvedType)).ToImmutableArray() (ValueTuple innerExs, TupleType types) |> asTypedExression ex.Range, errs | ValueArray vs -> let innerExs, errs = aggregateInner vs - // we can make the following simple check since / as long as there is no variance behavior + // we can make the following simple check since / as long as there is no variance behavior // for any of the supported attribute argument types - let typeIfValid (ex : TypedExpression) = + let typeIfValid (ex : TypedExpression) = match ex.ResolvedType.Resolution with | InvalidType -> None | _ -> Some ex.ResolvedType - match innerExs |> Seq.choose typeIfValid |> Seq.distinct |> Seq.toList with + match innerExs |> Seq.choose typeIfValid |> Seq.distinct |> Seq.toList with | [bt] -> (ValueArray innerExs, ArrayType bt) |> asTypedExression ex.Range, errs | [] when innerExs.Length <> 0 -> (ValueArray innerExs, ResolvedType.New InvalidType |> ArrayType) |> asTypedExression ex.Range, errs | [] -> invalidExpr ex.Range, errs |> Array.append [| ex.Range |> diagnostic ErrorCode.EmptyValueArray |] - | _ -> invalidExpr ex.Range, errs |> Array.append [| ex.Range |> diagnostic ErrorCode.ArrayBaseTypeMismatch |] - | NewArray (bt, idx) -> + | _ -> invalidExpr ex.Range, errs |> Array.append [| ex.Range |> diagnostic ErrorCode.ArrayBaseTypeMismatch |] + | NewArray (bt, idx) -> let onUdt (_, udtRange) = InvalidType, [| udtRange |> diagnostic ErrorCode.ArgumentOfUserDefinedTypeInAttribute |] - let onTypeParam (_, tpRange) = InvalidType, [| tpRange |> diagnostic ErrorCode.TypeParameterizedArgumentInAttribute |] - let resBaseType, typeErrs = ResolveType (onUdt, onTypeParam) bt + let onTypeParam (_, tpRange) = InvalidType, [| tpRange |> diagnostic ErrorCode.TypeParameterizedArgumentInAttribute |] + let resBaseType, typeErrs = ResolveType (onUdt, onTypeParam) bt let resIdx, idxErrs = ArgExression idx (NewArray (resBaseType, resIdx), ArrayType resBaseType) |> asTypedExression ex.Range, Array.concat [typeErrs; idxErrs] // TODO: detect constructor calls | InvalidExpr -> invalidExpr ex.Range, [||] - | _ -> invalidExpr ex.Range, [| ex.Range |> diagnostic ErrorCode.InvalidAttributeArgument |] - and aggregateInner vs = + | _ -> invalidExpr ex.Range, [| ex.Range |> diagnostic ErrorCode.InvalidAttributeArgument |] + and aggregateInner vs = let innerExs, errs = vs |> Seq.map ArgExression |> Seq.toList |> List.unzip innerExs.ToImmutableArray(), Array.concat errs - // Any user defined type that has been decorated with the attribute + // Any user defined type that has been decorated with the attribute // "Attribute" defined in Microsoft.Quantum.Core may be used as attribute. let resArg, argErrs = ArgExression attribute.Argument let buildAttribute id = {TypeId = id; Argument = resArg; Offset = attribute.Position; Comments = attribute.Comments} let getAttribute (ns, sym) = getAttribute ((ns, sym), attribute.Id.Range) |> function | None, errs -> Null |> buildAttribute, errs |> Array.append argErrs | Some (name, argType : ResolvedType), errs -> - // we can make the following simple check since / as long as there is no variance behavior + // we can make the following simple check since / as long as there is no variance behavior // for any of the supported attribute argument types let isError (msg : QsCompilerDiagnostic) = msg.Diagnostic |> function | Error _ -> true | _ -> false let argIsInvalid = resArg.ResolvedType.Resolution = InvalidType || argErrs |> Array.exists isError if resArg.ResolvedType.WithoutRangeInfo <> argType.WithoutRangeInfo && not argIsInvalid then let mismatchErr = attribute.Argument.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AttributeArgumentTypeMismatch, []) - Value name |> buildAttribute, Array.concat [errs; argErrs; [| mismatchErr |]] + Value name |> buildAttribute, Array.concat [errs; argErrs; [| mismatchErr |]] else Value name |> buildAttribute, errs |> Array.append argErrs - match attribute.Id.Symbol with - | Symbol sym -> getAttribute (None, sym) + match attribute.Id.Symbol with + | Symbol sym -> getAttribute (None, sym) | QualifiedSymbol (ns, sym) -> getAttribute (Some ns, sym) | InvalidSymbol -> Null |> buildAttribute, argErrs - | MissingSymbol | OmittedSymbols | SymbolTuple _ -> - Null |> buildAttribute, [| attribute.Id.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidAttributeIdentifier, []) |] + | MissingSymbol | OmittedSymbols | SymbolTuple _ -> + Null |> buildAttribute, [| attribute.Id.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidAttributeIdentifier, []) |] // private routines for resolving specialization generation directives /// Resolves the given specialization generator to a suitable implementation under the assumption - /// that at least one specialization for the same type- and set-arguments has been declared as intrinsic. - /// In particular, resolves the given generator to either and intrinsic implementation, + /// that at least one specialization for the same type- and set-arguments has been declared as intrinsic. + /// In particular, resolves the given generator to either and intrinsic implementation, /// or to the generator directive "self" if allowSelf is set to true. - /// Returns the generated implementation as Value, along with an array of diagnostics. - /// Does *not* generate diagnostics for things that do not require semantic information to detect - /// (these should be detected and raised upon context verification). - let private NeedsToBeIntrinsic (gen : QsSpecializationGenerator, allowSelf) = + /// Returns the generated implementation as Value, along with an array of diagnostics. + /// Does *not* generate diagnostics for things that do not require semantic information to detect + /// (these should be detected and raised upon context verification). + let private NeedsToBeIntrinsic (gen : QsSpecializationGenerator, allowSelf) = let genRange = gen.Range.ValueOr QsCompilerDiagnostic.DefaultRange let isSelfInverse = function | SelfInverse -> true | _ -> false let isInvalid = function | InvalidGenerator -> true | _ -> false - match gen.Generator with + match gen.Generator with | QsSpecializationGeneratorKind.Intrinsic | QsSpecializationGeneratorKind.AutoGenerated -> Intrinsic |> Value, [||] - | QsSpecializationGeneratorKind.UserDefinedImplementation _ -> - Intrinsic |> Value, [| genRange |> QsCompilerDiagnostic.Error (ErrorCode.UserDefinedImplementationForIntrinsic, []) |] - | QsSpecializationGeneratorKind.FunctorGenerationDirective dir -> + | QsSpecializationGeneratorKind.UserDefinedImplementation _ -> + Intrinsic |> Value, [| genRange |> QsCompilerDiagnostic.Error (ErrorCode.UserDefinedImplementationForIntrinsic, []) |] + | QsSpecializationGeneratorKind.FunctorGenerationDirective dir -> if isSelfInverse dir then (if allowSelf then Generated SelfInverse else Intrinsic) |> Value, [||] // a context error is raised if self is not valid elif isInvalid dir then Intrinsic |> Value, [||] else Intrinsic |> Value, [| genRange |> QsCompilerDiagnostic.Warning (WarningCode.GeneratorDirectiveWillBeIgnored, []) |] - /// Resolves the given specialization generator to a "self" generator directive, - /// and returns it as Value along with an array of diagnostics. - /// Does *not* generate diagnostics for things that do not require semantic information to detect - /// (these should be detected and raised upon context verification). - let private NeedsToBeSelfInverse (gen : QsSpecializationGenerator) = + /// Resolves the given specialization generator to a "self" generator directive, + /// and returns it as Value along with an array of diagnostics. + /// Does *not* generate diagnostics for things that do not require semantic information to detect + /// (these should be detected and raised upon context verification). + let private NeedsToBeSelfInverse (gen : QsSpecializationGenerator) = let genRange = gen.Range.ValueOr QsCompilerDiagnostic.DefaultRange let isSelfInverse = function | SelfInverse -> true | _ -> false let isInvalid = function | InvalidGenerator -> true | _ -> false let diagnostics = gen.Generator |> function | QsSpecializationGeneratorKind.Intrinsic | QsSpecializationGeneratorKind.AutoGenerated -> [||] - | QsSpecializationGeneratorKind.UserDefinedImplementation _ -> - [| genRange |> QsCompilerDiagnostic.Error (ErrorCode.NonSelfGeneratorForSelfadjoint, []) |] - | QsSpecializationGeneratorKind.FunctorGenerationDirective dir -> - if isSelfInverse dir || isInvalid dir then [||] + | QsSpecializationGeneratorKind.UserDefinedImplementation _ -> + [| genRange |> QsCompilerDiagnostic.Error (ErrorCode.NonSelfGeneratorForSelfadjoint, []) |] + | QsSpecializationGeneratorKind.FunctorGenerationDirective dir -> + if isSelfInverse dir || isInvalid dir then [||] else [| genRange |> QsCompilerDiagnostic.Error (ErrorCode.NonSelfGeneratorForSelfadjoint, []) |] Generated SelfInverse |> Value, diagnostics - /// Given the generator of a body specialization declaration, - /// returns Null if the generator indicates a user defined specialization, and returns the resolved implementation as Value otherwise. - /// Generates and returns an array of diagnostics. - /// If containsIntrinsic is set to true, generates a suitable error if the generator to resolve is incompatible, and resolves it to "intrinsic". - /// The resolution corresponds to an invalid generator directive unless the generator is either intrinsic, user defined or "auto". - /// Does *not* generate diagnostics for things that do not require semantic information to detect - /// (these should be detected and raised upon context verification). - let private ResolveBodyGeneratorDirective (opInfo : InferredCallableInformation) (gen : QsSpecializationGenerator) = + /// Given the generator of a body specialization declaration, + /// returns Null if the generator indicates a user defined specialization, and returns the resolved implementation as Value otherwise. + /// Generates and returns an array of diagnostics. + /// If containsIntrinsic is set to true, generates a suitable error if the generator to resolve is incompatible, and resolves it to "intrinsic". + /// The resolution corresponds to an invalid generator directive unless the generator is either intrinsic, user defined or "auto". + /// Does *not* generate diagnostics for things that do not require semantic information to detect + /// (these should be detected and raised upon context verification). + let private ResolveBodyGeneratorDirective (opInfo : InferredCallableInformation) (gen : QsSpecializationGenerator) = if opInfo.IsIntrinsic then NeedsToBeIntrinsic (gen, false) else gen.Generator |> function | QsSpecializationGeneratorKind.Intrinsic -> Intrinsic |> Value, [||] @@ -454,15 +454,15 @@ module SymbolResolution = | Distribute | SelfInverse | Invert | InvalidGenerator -> Generated InvalidGenerator |> Value, [||] // a context error is raised in this case | QsSpecializationGeneratorKind.AutoGenerated -> Intrinsic |> Value, [||] // todo: generate based on controlled if possible? - /// Given the generator of an adjoint specialization declaration, - /// returns Null if the generator indicates a user defined specialization, and returns the resolved implementation as Value otherwise. - /// Generates and returns an array of diagnostics. + /// Given the generator of an adjoint specialization declaration, + /// returns Null if the generator indicates a user defined specialization, and returns the resolved implementation as Value otherwise. + /// Generates and returns an array of diagnostics. /// If containsIntrinsic is set to true, generates a suitable error if the generator to resolve is incompatible, - /// and resolves it to either "intrinsic" or - if containsSelfInverse is set to true - to "self". + /// and resolves it to either "intrinsic" or - if containsSelfInverse is set to true - to "self". /// Otherwise it resolves any valid directive to either an "invert" or a "self" directive depending on whether containsSelfInverse is set to true. - /// Does *not* generate diagnostics for things that do not require semantic information to detect - /// (these should be detected and raised upon context verification). - let private ResolveAdjointGeneratorDirective (info : InferredCallableInformation) (gen : QsSpecializationGenerator) = + /// Does *not* generate diagnostics for things that do not require semantic information to detect + /// (these should be detected and raised upon context verification). + let private ResolveAdjointGeneratorDirective (info : InferredCallableInformation) (gen : QsSpecializationGenerator) = if info.IsSelfAdjoint then NeedsToBeSelfInverse gen elif info.IsIntrinsic then NeedsToBeIntrinsic (gen, true) else gen.Generator |> function @@ -470,16 +470,16 @@ module SymbolResolution = | QsSpecializationGeneratorKind.UserDefinedImplementation _ -> Null, [||] | QsSpecializationGeneratorKind.FunctorGenerationDirective dir -> dir |> function | Distribute -> Generated InvalidGenerator |> Value, [||] // a context error is raised in this case - | SelfInverse | Invert | InvalidGenerator -> Generated dir |> Value, [||] + | SelfInverse | Invert | InvalidGenerator -> Generated dir |> Value, [||] | QsSpecializationGeneratorKind.AutoGenerated -> Generated Invert |> Value, [||] - /// Given the generator of a controlled specialization declaration, - /// returns Null if the generator indicates a user defined specialization, and returns the resolved implementation as Value otherwise. - /// Generates and returns an array of diagnostics. - /// If containsIntrinsic is set to true, generates a suitable error if the generator to resolve is incompatible, and resolves it to "intrinsic". - /// Otherwise resolves any valid directive to a "distribute" directive. - /// Does *not* generate diagnostics for things that do not require semantic information to detect - /// (these should be detected and raised upon context verification). + /// Given the generator of a controlled specialization declaration, + /// returns Null if the generator indicates a user defined specialization, and returns the resolved implementation as Value otherwise. + /// Generates and returns an array of diagnostics. + /// If containsIntrinsic is set to true, generates a suitable error if the generator to resolve is incompatible, and resolves it to "intrinsic". + /// Otherwise resolves any valid directive to a "distribute" directive. + /// Does *not* generate diagnostics for things that do not require semantic information to detect + /// (these should be detected and raised upon context verification). let private ResolveControlledGeneratorDirective (info : InferredCallableInformation) (gen : QsSpecializationGenerator) = if info.IsIntrinsic then NeedsToBeIntrinsic (gen, false) else gen.Generator |> function @@ -487,21 +487,21 @@ module SymbolResolution = | QsSpecializationGeneratorKind.UserDefinedImplementation _ -> Null, [||] | QsSpecializationGeneratorKind.FunctorGenerationDirective dir -> dir |> function | SelfInverse | Invert -> Generated InvalidGenerator |> Value, [||] // a context error is raised in this case - | Distribute | InvalidGenerator -> Generated dir |> Value, [||] + | Distribute | InvalidGenerator -> Generated dir |> Value, [||] | QsSpecializationGeneratorKind.AutoGenerated -> Generated Distribute |> Value, [||] - /// Given the generator of a controlled adjoint specialization declaration, - /// returns Null if the generator indicates a user defined specialization, and returns the resolved implementation as Value otherwise. - /// Generates and returns an array of diagnostics. + /// Given the generator of a controlled adjoint specialization declaration, + /// returns Null if the generator indicates a user defined specialization, and returns the resolved implementation as Value otherwise. + /// Generates and returns an array of diagnostics. /// If containsIntrinsic is set to true, generates a suitable error if the generator to resolve is incompatible, - /// and resolves it to either "intrinsic" or - if containsSelfInverse is set to true - to "self". - /// If this is not the case, all specialization generator directives are resolved to themselves unless they are specified as "auto". - /// If an automatic determination of a suitable directive is requested (as indicated by "auto"), then it is resolved to + /// and resolves it to either "intrinsic" or - if containsSelfInverse is set to true - to "self". + /// If this is not the case, all specialization generator directives are resolved to themselves unless they are specified as "auto". + /// If an automatic determination of a suitable directive is requested (as indicated by "auto"), then it is resolved to /// a) a self inverse directive, if the corresponding adjoint specialization is self inverse, and - /// b) to "invert" if the corresponding adjoint specialization is compiler generated but the controlled version is user defined, and - /// b) to a "distribute" directive to be applied to the adjoint version otherwise. - /// Does *not* generate diagnostics for things that do not require semantic information to detect - /// (these should be detected and raised upon context verification). + /// b) to "invert" if the corresponding adjoint specialization is compiler generated but the controlled version is user defined, and + /// b) to a "distribute" directive to be applied to the adjoint version otherwise. + /// Does *not* generate diagnostics for things that do not require semantic information to detect + /// (these should be detected and raised upon context verification). let private ResolveControlledAdjointGeneratorDirective (adjGenKind, ctlGenKind) (info : InferredCallableInformation) (gen : QsSpecializationGenerator) = if info.IsSelfAdjoint then NeedsToBeSelfInverse gen elif info.IsIntrinsic then NeedsToBeIntrinsic (gen, true) @@ -509,53 +509,53 @@ module SymbolResolution = | QsSpecializationGeneratorKind.Intrinsic -> Intrinsic |> Value, [||] | QsSpecializationGeneratorKind.UserDefinedImplementation _ -> Null, [||] | QsSpecializationGeneratorKind.FunctorGenerationDirective dir -> dir |> function - | Distribute | SelfInverse | Invert | InvalidGenerator -> Generated dir |> Value, [||] + | Distribute | SelfInverse | Invert | InvalidGenerator -> Generated dir |> Value, [||] | QsSpecializationGeneratorKind.AutoGenerated -> (ctlGenKind, adjGenKind) |> function - | UserDefinedImplementation _, FunctorGenerationDirective _ + | UserDefinedImplementation _, FunctorGenerationDirective _ | UserDefinedImplementation _, AutoGenerated -> Generated Invert |> Value, [||] | _ -> Generated Distribute |> Value, [||] // routines for resolving specialization declarations - /// Resolves the type- and set-arguments of the given specialization using the given function. - /// Returns the resolved arguments as well as an array of diagnostics. - /// Does nothing and simply returns the resolution of the given specialization if a resolution has already been set. - let internal ResolveTypeArgument typeResolution (_, spec : Resolution) = - let resolveGenerator () = + /// Resolves the type- and set-arguments of the given specialization using the given function. + /// Returns the resolved arguments as well as an array of diagnostics. + /// Does nothing and simply returns the resolution of the given specialization if a resolution has already been set. + let internal ResolveTypeArgument typeResolution (_, spec : Resolution) = + let resolveGenerator () = let typeArgs, tErrs = spec.Defined.TypeArguments |> function | Null -> Null, [||] - | Value targs -> - let resolved, errs = targs |> Seq.map typeResolution |> Seq.toList |> List.unzip + | Value targs -> + let resolved, errs = targs |> Seq.map typeResolution |> Seq.toList |> List.unzip resolved.ToImmutableArray() |> Value, errs |> Array.concat - let resolvedGen = {TypeArguments = typeArgs; Information = CallableInformation.Invalid; Directive = Null} - Value resolvedGen, tErrs |> Array.map (fun msg -> spec.Position, msg) - match spec.Resolved with + let resolvedGen = {TypeArguments = typeArgs; Information = CallableInformation.Invalid; Directive = Null} + Value resolvedGen, tErrs |> Array.map (fun msg -> spec.Position, msg) + match spec.Resolved with | Value resolvedGen -> Value resolvedGen, [||] | Null -> resolveGenerator() - /// Given a dictionary of all existing specializations for a particular set of type- and set-arguments - /// that maps the specialization kind to the corresponding generator, as well as the characteristics and location of the callable declaration, - /// determines the resolved characteristics of the specializations for these type- and set-arguments. - /// Calls generateSpecialization for each missing specialization kind that can be inferred. - /// Returns the resolved characteristics as well as an array of diagnostics. - let private InferCharacteristicsAndMetadata generateSpecialization (specKinds : ImmutableDictionary<_,_>) (characteristics : Characteristics, declLocation : QsLocation) = + /// Given a dictionary of all existing specializations for a particular set of type- and set-arguments + /// that maps the specialization kind to the corresponding generator, as well as the characteristics and location of the callable declaration, + /// determines the resolved characteristics of the specializations for these type- and set-arguments. + /// Calls generateSpecialization for each missing specialization kind that can be inferred. + /// Returns the resolved characteristics as well as an array of diagnostics. + let private InferCharacteristicsAndMetadata generateSpecialization (specKinds : ImmutableDictionary<_,_>) (characteristics : Characteristics, declLocation : QsLocation) = let adjExists, ctlExists = specKinds.ContainsKey QsAdjoint, specKinds.ContainsKey QsControlled let bodyExists, ctlAdjExists = specKinds.ContainsKey QsBody, specKinds.ContainsKey QsControlledAdjoint let annotRange cond = if cond then characteristics.Range else Null - let resolved, (supportsAdj, adjRange), (supportsCtl, ctlRange) = + let resolved, (supportsAdj, adjRange), (supportsCtl, ctlRange) = let declCharacteristics = ResolveCharacteristics characteristics - if not declCharacteristics.AreInvalid then + if not declCharacteristics.AreInvalid then let supported = declCharacteristics.GetProperties() - let adjSup, ctlSup = supported.Contains Adjointable, supported.Contains Controllable + let adjSup, ctlSup = supported.Contains Adjointable, supported.Contains Controllable let additional = ResolvedCharacteristics.FromProperties (seq { if (adjExists || ctlAdjExists) && not adjSup then yield Adjointable if (ctlExists || ctlAdjExists) && not ctlSup then yield Controllable }) let adj, ctl = (adjExists || ctlAdjExists || adjSup, annotRange adjSup), (ctlExists || ctlAdjExists || ctlSup, annotRange ctlSup) Union (declCharacteristics, additional) |> ResolvedCharacteristics.New, adj, ctl else declCharacteristics, (adjExists || ctlAdjExists, Null), (ctlExists || ctlAdjExists, Null) - let metadata = + let metadata = let isIntrinsic = function | QsSpecializationGeneratorKind.Intrinsic -> true | _ -> false let intrinsic = specKinds.Values |> Seq.map (fun g -> g.Generator) |> Seq.exists isIntrinsic let selfGenerator = specKinds.Values |> Seq.exists (fun gen -> gen.Generator = FunctorGenerationDirective SelfInverse) @@ -566,32 +566,32 @@ module SymbolResolution = if supportsAdj && not adjExists then yield generateSpecialization QsAdjoint (declLocation, adjRange) if supportsCtl && not ctlExists then yield generateSpecialization QsControlled (declLocation, ctlRange) if supportsAdj && supportsCtl && not ctlAdjExists then yield generateSpecialization QsControlledAdjoint (declLocation, ctlAdjRange) - if not bodyExists then + if not bodyExists then yield generateSpecialization QsBody (declLocation, Null) yield [| declLocation.Range |> QsCompilerDiagnostic.Warning (WarningCode.MissingBodyDeclaration, []) |] ] let isError (m : QsCompilerDiagnostic) = m.Diagnostic |> function | Error _ -> true | _ -> false - if errs |> Array.exists isError then InvalidSetExpr |> ResolvedCharacteristics.New, metadata, errs + if errs |> Array.exists isError then InvalidSetExpr |> ResolvedCharacteristics.New, metadata, errs else resolved, metadata, errs - /// Given the signature and source file of a callable as well as all specializations defined for it, constructs + /// Given the signature and source file of a callable as well as all specializations defined for it, constructs /// a dictionary that contains the bundle properties for each set of type- and set-arguments for which the callable has been specialized. /// The keys of the dictionary are given by the BundleIds obtained for the type- and set-arguments in question. /// Calls generateSpecialization for each specialization that is not listed in the given collection of specializations but can be inferred, - /// either based on the declared characteristics of the parent callable or based on other existing specializations. - /// Returns the constructed dictionary as well as an array of diagnostics. - /// Throws an InvalidOperationException if no (partial) resolution is defined for any one of the given specializations. - let internal GetBundleProperties generateSpecialization (parentSignature : Resolution, source) (definedSpecs : IEnumerable<_>) = + /// either based on the declared characteristics of the parent callable or based on other existing specializations. + /// Returns the constructed dictionary as well as an array of diagnostics. + /// Throws an InvalidOperationException if no (partial) resolution is defined for any one of the given specializations. + let internal GetBundleProperties generateSpecialization (parentSignature : Resolution, source) (definedSpecs : IEnumerable<_>) = let declCharacteristics = parentSignature.Defined.Characteristics // if we allow to specialize for certain set parameters, then these need to be resolved in parent let declLocation = {Offset = parentSignature.Position; Range = parentSignature.Range} let definedSpecs = definedSpecs.ToLookup (snd >> snd >> (SpecializationBundleProperties.BundleId : Resolution<_,_> -> _), id) let mutable errs = [] - let bundleProps (relevantSpecs : IEnumerable<_>, definedArgs) = + let bundleProps (relevantSpecs : IEnumerable<_>, definedArgs) = let gens, bundleErrs = let relevantSpecs = relevantSpecs.ToLookup(fst, snd) let positionedErr errCode (specSource, res : Resolution<_,_>) = specSource, (res.Position, res.Range |> QsCompilerDiagnostic.Error (errCode, [])) - let errs = - (relevantSpecs.[QsBody].Skip(1) |> Seq.map (positionedErr ErrorCode.RedefinitionOfBody)) + let errs = + (relevantSpecs.[QsBody].Skip(1) |> Seq.map (positionedErr ErrorCode.RedefinitionOfBody)) |> Seq.append (relevantSpecs.[QsAdjoint].Skip(1) |> Seq.map (positionedErr ErrorCode.RedefinitionOfAdjoint)) |> Seq.append (relevantSpecs.[QsControlled].Skip(1) |> Seq.map (positionedErr ErrorCode.RedefinitionOfControlled)) |> Seq.append (relevantSpecs.[QsControlledAdjoint].Skip(1) |> Seq.map (positionedErr ErrorCode.RedefinitionOfControlledAdjoint)) @@ -605,25 +605,25 @@ module SymbolResolution = for group in definedSpecs do props.Add(group.Key, bundleProps(group, (group.First() |> snd |> snd).Defined.TypeArguments)) props.ToImmutableDictionary(), errs - /// Given a dictionary that maps the BundleId for each set of type- and set-arguments for which the callable has been specialized + /// Given a dictionary that maps the BundleId for each set of type- and set-arguments for which the callable has been specialized /// to the corresponding bundle properties determines the resolution for the given specialization of the given kind. - /// Returns the resolved generator as well as an array of diagnostics generated during resolution. - /// Throws an InvalidOperationException if no (partial) resolution is defined for the given specialization. - /// Fails with the standard KeyNotFoundException if the given specialization is not part of a specialization bundle in the given properties dictionary. - let internal ResolveGenerator (properties : ImmutableDictionary<_,_>) (kind, spec : Resolution) = + /// Returns the resolved generator as well as an array of diagnostics generated during resolution. + /// Throws an InvalidOperationException if no (partial) resolution is defined for the given specialization. + /// Fails with the standard KeyNotFoundException if the given specialization is not part of a specialization bundle in the given properties dictionary. + let internal ResolveGenerator (properties : ImmutableDictionary<_,_>) (kind, spec : Resolution) = let bundle : SpecializationBundleProperties = properties.[SpecializationBundleProperties.BundleId spec] let impl, err = kind |> function | QsBody -> ResolveBodyGeneratorDirective bundle.BundleInfo.InferredInformation spec.Defined | QsAdjoint -> ResolveAdjointGeneratorDirective bundle.BundleInfo.InferredInformation spec.Defined | QsControlled -> ResolveControlledGeneratorDirective bundle.BundleInfo.InferredInformation spec.Defined - | QsControlledAdjoint -> + | QsControlledAdjoint -> let getGenKindOrAuto kind = bundle.DefinedGenerators.TryGetValue kind |> function | true, (gen : QsSpecializationGenerator) -> gen.Generator - | false, _ -> AutoGenerated // automatically inserted specializations won't be part of the bundle + | false, _ -> AutoGenerated // automatically inserted specializations won't be part of the bundle let adjGen, ctlGen = getGenKindOrAuto QsAdjoint, getGenKindOrAuto QsControlled ResolveControlledAdjointGeneratorDirective (adjGen, ctlGen) bundle.BundleInfo.InferredInformation spec.Defined let dir = impl |> function | Value (Generated dir) -> Value dir | _ -> Null let resolvedGen = spec.Resolved |> QsNullable<_>.Map (fun resolution -> {resolution with Information = bundle.BundleInfo; Directive = dir}) - resolvedGen, err |> Array.map (fun msg -> spec.Position, msg) + resolvedGen, err |> Array.map (fun msg -> spec.Position, msg) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 251c45ad80..86e1ba026d 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -13,7 +13,7 @@ open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.SymbolResolution open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxGenerator -open Microsoft.Quantum.QsCompiler.SyntaxTokens +open Microsoft.Quantum.QsCompiler.SyntaxTokens open Microsoft.Quantum.QsCompiler.SyntaxTree open Newtonsoft.Json @@ -23,20 +23,20 @@ type private PartialNamespace private (name : NonNullable, source : NonNullable, documentation : IEnumerable>, - openNS : IEnumerable, string>>, + openNS : IEnumerable, string>>, typeDecl : IEnumerable, Resolution, ResolvedType * QsTuple<_>>>>, callableDecl : IEnumerable, QsCallableKind * Resolution>>>, - specializations : IEnumerable, List>>>) = + specializations : IEnumerable, List>>>) = let keySelector (item : KeyValuePair<'k,'v>) = item.Key let valueSelector (item : KeyValuePair<'k,'v>) = item.Value let unresolved (location : QsLocation) (definition, attributes, doc) = { - Defined = definition; + Defined = definition; DefinedAttributes = attributes - Resolved = Null; + Resolved = Null; ResolvedAttributes = ImmutableArray.Empty - Position = location.Offset; - Range = location.Range; + Position = location.Offset; + Range = location.Range; Documentation = doc } @@ -49,24 +49,24 @@ type private PartialNamespace private /// the key is the name of the type let TypeDeclarations = typeDecl.ToDictionary(keySelector, valueSelector) /// dictionary of callables declared within this namespace and file - /// includes functions, operations, *and* (auto-gnerated) type constructors + /// includes functions, operations, *and* (auto-generated) type constructors /// the key is the name of the callable let CallableDeclarations = callableDecl.ToDictionary(keySelector, valueSelector) /// dictionary of callable specializations declared within this namespace and file /// -> note that all specializations that are declared in a namespace *have* to extend a declarations in the same namespace, /// -> however, they may be declared in a source file (or even compilation unit) that is different from the one of the original declaration /// the key is the name of the callable, and the key to the returned map is the specialization kind (body, adjoint, controlled, or controlled adjoint) - let CallableSpecializations = + let CallableSpecializations = let specs = specializations |> Seq.map (fun entry -> entry.Key, (entry.Value.ToList())) specs.ToDictionary(fst, snd) /// constructor taking the name of the namespace as well as the name of the file it is declared in as arguments - internal new (name, source) = new PartialNamespace(name, source, [], [new KeyValuePair<_,_>(name, null)], [], [], []) + internal new (name, source) = new PartialNamespace(name, source, [], [new KeyValuePair<_,_>(name, null)], [], [], []) /// returns a new PartialNamespace that is an exact copy of this one /// -> any modification of the returned PartialNamespace is not reflected in this one member this.Copy() = - let openNS = OpenNamespaces + let openNS = OpenNamespaces let doc = AssociatedDocumentation let typeDecl = TypeDeclarations let callableDecl = CallableDeclarations @@ -89,16 +89,16 @@ type private PartialNamespace private /// callables defined within this (part of) the namespace (includes auto-generated type constructors) /// -> NOTE: the returned enumerable is *not* immutable and may change over time! member internal this.DefinedCallables = CallableDeclarations.Select(fun item -> item.Key, item.Value) - + /// returns a dictionary with all currently known namespace short names and which namespace they represent - member internal this.NamespaceShortNames = + member internal this.NamespaceShortNames = let shortNames = this.ImportedNamespaces |> Seq.filter (fun kv -> kv.Value <> null) shortNames.ToImmutableDictionary((fun kv -> NonNullable.New kv.Value), (fun kv -> kv.Key)) /// Gets the type with the given name from the dictionary of declared types. /// Fails with the standard key does not exist error if no such declaration exists. member internal this.GetType tName = TypeDeclarations.[tName] - member internal this.ContainsType = TypeDeclarations.ContainsKey + member internal this.ContainsType = TypeDeclarations.ContainsKey member internal this.TryGetType = TypeDeclarations.TryGetValue /// Gets the callable with the given name from the dictionary of declared callable. @@ -107,85 +107,85 @@ type private PartialNamespace private member internal this.ContainsCallable = CallableDeclarations.ContainsKey member internal this.TryGetCallable = CallableDeclarations.TryGetValue - /// Given a callable name, returns all specializations for it defined within this part of the namespace. + /// Given a callable name, returns all specializations for it defined within this part of the namespace. /// NOTE: The verification of whether a callable with that name has been declared in this namespace needs to be done by the calling routine. - member internal this.GetSpecializations cName = - match CallableSpecializations.TryGetValue cName with + member internal this.GetSpecializations cName = + match CallableSpecializations.TryGetValue cName with | true, specs -> specs.ToImmutableArray() | false, _ -> ImmutableArray.Empty // mustn't fail, since the query is valid even if the callable is declared in another file - /// Adds the given lines of documentation to the list of documenting sections - /// associated with this namespace within this source file. - member this.AddDocumentation (doc : IEnumerable<_>) = + /// Adds the given lines of documentation to the list of documenting sections + /// associated with this namespace within this source file. + member this.AddDocumentation (doc : IEnumerable<_>) = AssociatedDocumentation.Add(doc.ToImmutableArray()) /// If the given namespace name is not already listened as imported, adds the given namespace name to the list of open namespaces. - /// -> Note that this routine will fail with the standard dictionary.Add error if an open directive for the given namespace name already exists. - /// -> The verification of whether a namespace with the given name exists in the first place needs to be done by the calling routine. - member this.AddOpenDirective (openedNS, alias) = - OpenNamespaces.Add(openedNS, alias) + /// -> Note that this routine will fail with the standard dictionary.Add error if an open directive for the given namespace name already exists. + /// -> The verification of whether a namespace with the given name exists in the first place needs to be done by the calling routine. + member this.AddOpenDirective (openedNS, alias) = + OpenNamespaces.Add(openedNS, alias) /// Adds the given type declaration for the given type name to the dictionary of declared types. - /// Adds the corresponding type constructor to the dictionary of declared callables. - /// The given location is associated with both the type constructur and the type itself and accessible via the record properties Position and SymbolRange. - /// -> Note that this routine will fail with the standard dictionary.Add error if either a type or a callable with that name already exists. - member this.AddType (location : QsLocation) (tName, typeTuple, attributes, documentation) = + /// Adds the corresponding type constructor to the dictionary of declared callables. + /// The given location is associated with both the type constructor and the type itself and accessible via the record properties Position and SymbolRange. + /// -> Note that this routine will fail with the standard dictionary.Add error if either a type or a callable with that name already exists. + member this.AddType (location : QsLocation) (tName, typeTuple, attributes, documentation) = let mutable anonItemId = 0 let withoutRange sym = {Symbol = sym; Range = Null} - let replaceAnonymous (itemName : QsSymbol, itemType) = // positional info for types in type constructors is removed upon resolution - let anonItemName () = + let replaceAnonymous (itemName : QsSymbol, itemType) = // positional info for types in type constructors is removed upon resolution + let anonItemName () = anonItemId <- anonItemId + 1 sprintf "__Item%i__" anonItemId |> NonNullable.New match itemName.Symbol with | MissingSymbol -> QsTupleItem (Symbol (anonItemName()) |> withoutRange, itemType) | _ -> QsTupleItem (itemName.Symbol |> withoutRange, itemType) // no range info in auto-generated constructor - let constructorSignature = - let constructorArgument = - let rec buildItem = function + let constructorSignature = + let constructorArgument = + let rec buildItem = function | QsTuple args -> (args |> Seq.map buildItem).ToImmutableArray() |> QsTuple | QsTupleItem (n, t) -> replaceAnonymous (n, t) - match typeTuple with + match typeTuple with | QsTupleItem (n, t) -> ImmutableArray.Create (replaceAnonymous (n, t)) |> QsTuple | QsTuple _ -> buildItem typeTuple let returnType = {Type = UserDefinedType (QualifiedSymbol (this.Name, tName) |> withoutRange); Range = Null} {TypeParameters = ImmutableArray.Empty; Argument = constructorArgument; ReturnType = returnType; Characteristics = {Characteristics = EmptySet; Range = Null}} // There are a couple of reasons not just blindly attach all attributes associated with the type to the constructor: - // For one, we would need to make sure that the range information for duplications is stripped such that e.g. rename commands are not executed multiple times. + // For one, we would need to make sure that the range information for duplications is stripped such that e.g. rename commands are not executed multiple times. // We would furthermore have to adapt the entry point verification logic below, since type constructors are not valid entry points. let deprecationWithoutRedirect = { - Id = {Symbol = Symbol BuiltIn.Deprecated.Name; Range = Null} + Id = {Symbol = Symbol BuiltIn.Deprecated.FullName.Name; Range = Null} Argument = {Expression = StringLiteral (NonNullable.New "", ImmutableArray.Empty); Range = Null} Position = location.Offset - Comments = QsComments.Empty} + Comments = QsComments.Empty} let constructorAttr = // we will attach any attribute that likely indicates a deprecation to the type constructor as well - let validDeprecatedQualification qual = String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value - if attributes |> Seq.exists (SymbolResolution.IndicatesDeprecation validDeprecatedQualification) then ImmutableArray.Create deprecationWithoutRedirect + let validDeprecatedQualification qual = String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.FullName.Namespace.Value + if attributes |> Seq.exists (SymbolResolution.IndicatesDeprecation validDeprecatedQualification) then ImmutableArray.Create deprecationWithoutRedirect else ImmutableArray.Empty TypeDeclarations.Add(tName, (typeTuple, attributes, documentation) |> unresolved location) - this.AddCallableDeclaration location (tName, (TypeConstructor, constructorSignature), constructorAttr, ImmutableArray.Empty) + this.AddCallableDeclaration location (tName, (TypeConstructor, constructorSignature), constructorAttr, ImmutableArray.Empty) let bodyGen = {TypeArguments = Null; Generator = QsSpecializationGeneratorKind.Intrinsic; Range = Value location.Range} - this.AddCallableSpecialization location QsBody (tName, bodyGen, ImmutableArray.Empty, ImmutableArray.Empty) + this.AddCallableSpecialization location QsBody (tName, bodyGen, ImmutableArray.Empty, ImmutableArray.Empty) - /// Adds a callable declaration of the given kind (operation or function) + /// Adds a callable declaration of the given kind (operation or function) /// with the given callable name and signature to the dictionary of declared callables. - /// The given location is associated with the callable declaration and accessible via the record properties Position and SymbolRange. - /// -> Note that this routine will fail with the standard dictionary.Add error if a callable with that name already exists. - member this.AddCallableDeclaration location (cName, (kind, signature), attributes, documentation) = + /// The given location is associated with the callable declaration and accessible via the record properties Position and SymbolRange. + /// -> Note that this routine will fail with the standard dictionary.Add error if a callable with that name already exists. + member this.AddCallableDeclaration location (cName, (kind, signature), attributes, documentation) = CallableDeclarations.Add(cName, (kind, (signature, attributes, documentation) |> unresolved location)) - /// Adds the callable specialization defined by the given kind and generator for the callable of the given name to the dictionary of declared specializations. - /// The given location is associated with the given specialization and accessible via the record properties Position and HeaderRange. - /// -> Note that the verification of whether the corresponding callable declaration exists within the namespace is up to the calling routine. - /// *IMPORTANT*: both the verification of whether the length of the given array of type specialization + /// Adds the callable specialization defined by the given kind and generator for the callable of the given name to the dictionary of declared specializations. + /// The given location is associated with the given specialization and accessible via the record properties Position and HeaderRange. + /// -> Note that the verification of whether the corresponding callable declaration exists within the namespace is up to the calling routine. + /// *IMPORTANT*: both the verification of whether the length of the given array of type specialization /// matches the number of type parameters in the callable declaration, and whether a specialization that clashes with this one /// already exists is up to the calling routine! - member this.AddCallableSpecialization location kind (cName, generator : QsSpecializationGenerator, attributes, documentation) = - // NOTE: all types that are not specialized need to be resolved according to the file in which the callable is declared, - // but all specialized types need to be resolved according to *this* file + member this.AddCallableSpecialization location kind (cName, generator : QsSpecializationGenerator, attributes, documentation) = + // NOTE: all types that are not specialized need to be resolved according to the file in which the callable is declared, + // but all specialized types need to be resolved according to *this* file let spec = kind, (generator, attributes, documentation) |> unresolved location match CallableSpecializations.TryGetValue cName with | true, specs -> specs.Add spec // it is up to the namespace to verify the type specializations @@ -193,34 +193,34 @@ type private PartialNamespace private /// Deletes the *explicitly* defined specialization at the specified location for the callable with the given name. /// Does not delete specializations that have been inserted by the compiler, i.e. specializations whose location matches the callable declaration location. - /// Returns the number of removed specializations. + /// Returns the number of removed specializations. /// Throws the standard key does not exist exception if no specialization for the callable with that name exists. - member internal this.RemoveCallableSpecialization (location : QsLocation) cName = - match CallableDeclarations.TryGetValue cName with - | true, (_, decl) when decl.Position = location.Offset && decl.Range = location.Range -> 0 + member internal this.RemoveCallableSpecialization (location : QsLocation) cName = + match CallableDeclarations.TryGetValue cName with + | true, (_, decl) when decl.Position = location.Offset && decl.Range = location.Range -> 0 | _ -> CallableSpecializations.[cName].RemoveAll (fun (_, res) -> location.Offset = res.Position && location.Range = res.Range) /// Sets the resolution for the type with the given name to the given type, and replaces the resolved attributes with the given values. /// Throws the standard key does not exist exception if no type with that name exists. - member internal this.SetTypeResolution (tName, resolvedType, resAttributes) = + member internal this.SetTypeResolution (tName, resolvedType, resAttributes) = let qsType = TypeDeclarations.[tName] TypeDeclarations.[tName] <- {qsType with Resolved = resolvedType; ResolvedAttributes = resAttributes} /// Sets the resolution for the signature of the callable with the given name to the given signature, /// and replaces the resolved attributes with the given values. /// Throws the standard key does not exist exception if no callable with that name exists. - member internal this.SetCallableResolution (cName, resolvedSignature, resAttributes) = + member internal this.SetCallableResolution (cName, resolvedSignature, resAttributes) = let (kind, signature) = CallableDeclarations.[cName] CallableDeclarations.[cName] <- (kind, {signature with Resolved = resolvedSignature; ResolvedAttributes = resAttributes}) - /// Applies the given functions computing the resolution of attributes and the generation directive - /// to all defined specializations of the callable with the given name, - /// and sets its resolution and resolved attributes to the computed values. - /// Collects and returns an array of all diagnostics generated by computeResolution. + /// Applies the given functions computing the resolution of attributes and the generation directive + /// to all defined specializations of the callable with the given name, + /// and sets its resolution and resolved attributes to the computed values. + /// Collects and returns an array of all diagnostics generated by computeResolution. /// Does nothing and returns an empty array if no specialization for a callable with the given name exists within this partial namespace. - member internal this.SetSpecializationResolutions (cName, computeResolution, getResAttributes) = - match CallableSpecializations.TryGetValue cName with - | true, specs -> + member internal this.SetSpecializationResolutions (cName, computeResolution, getResAttributes) = + match CallableSpecializations.TryGetValue cName with + | true, specs -> [|0 .. specs.Count - 1|] |> Array.collect (fun index -> let kind, spec = specs.[index] let resAttr, attErrs = getResAttributes this.Source spec @@ -245,15 +245,15 @@ and Namespace private /// Given a non-nullable string, returns true if either a callable or a type with that name already exists /// either in a source file, or in a referenced assembly. - let IsDefined arg = - CallablesInReferences.ContainsKey arg || TypesInReferences.ContainsKey arg || + let IsDefined arg = + CallablesInReferences.ContainsKey arg || TypesInReferences.ContainsKey arg || Parts.Values.Any (fun partial -> partial.ContainsCallable arg || partial.ContainsType arg) /// Given a selector, determines the source files for which the given selector returns a Value. /// Returns that Value if exactly one such source file exists, or Null if no such file exists. /// Note that files contained in referenced assemblies are *not* considered to be source files for the namespace! - /// Throws an ArgumentException if the selector returns a Value for more than one source file. - let FromSingleSource (selector : PartialNamespace -> _ Option) = + /// Throws an ArgumentException if the selector returns a Value for more than one source file. + let FromSingleSource (selector : PartialNamespace -> _ Option) = let sources = Parts.Values |> Seq.choose selector if sources.Count() > 1 then ArgumentException "given selector selects more than one partial namespace" |> raise if sources.Any() then Value (sources.Single()) else Null @@ -262,7 +262,7 @@ and Namespace private /// name of the namespace member this.Name = name /// Immutable array with the names of all source files that contain (a part of) the namespace. - /// Note that files contained in referenced assemblies that implement part of the namespace + /// Note that files contained in referenced assemblies that implement part of the namespace /// are *not* considered to be source files within the context of this Namespace instance! member internal this.Sources = Parts.Keys.ToImmutableHashSet() /// contains all types declared within one of the referenced assemblies as part of this namespace @@ -274,14 +274,14 @@ and Namespace private /// constructor taking the name of the namespace as well the name of the files in which (part of) it is declared in as arguments, /// as well as the information about all types and callables declared in referenced assemblies that belong to this namespace - internal new (name, sources, callablesInRefs : IEnumerable<_>, specializationsInRefs : IEnumerable<_>, typesInRefs : IEnumerable<_>) = + internal new (name, sources, callablesInRefs : IEnumerable<_>, specializationsInRefs : IEnumerable<_>, typesInRefs : IEnumerable<_>) = let initialSources = sources |> Seq.distinct |> Seq.map (fun source -> new KeyValuePair<_,_>(source, new PartialNamespace(name, source))) let typesInRefs = typesInRefs.Where (fun (header : TypeDeclarationHeader) -> header.QualifiedName.Namespace = name) let callablesInRefs = callablesInRefs.Where(fun (header : CallableDeclarationHeader) -> header.QualifiedName.Namespace = name) let specializationsInRefs = specializationsInRefs.Where(fun (header : SpecializationDeclarationHeader, _) -> header.Parent.Namespace = name) // ignore ambiguous/clashing references - let FilterUnique (g : IGrouping<_,_>) = + let FilterUnique (g : IGrouping<_,_>) = if g.Count() > 1 then None else g.Single() |> Some let typesInRefs = typesInRefs.GroupBy(fun t -> t.QualifiedName.Name) |> Seq.choose FilterUnique @@ -293,70 +293,70 @@ and Namespace private new Namespace(name, initialSources, callables, specializations, types) - /// returns true if the namespace currently contains no source files or referenced content - member this.IsEmpty = - not (this.Sources.Any() || this.TypesInReferencedAssemblies.Any() || + /// returns true if the namespace currently contains no source files or referenced content + member this.IsEmpty = + not (this.Sources.Any() || this.TypesInReferencedAssemblies.Any() || this.CallablesInReferencedAssemblies.Any() || this.SpecializationsInReferencedAssemblies.Any()) /// returns a new Namespace that is an exact (deep) copy of this one /// -> any modification of the returned Namespace is not reflected in this one - member this.Copy() = + member this.Copy() = let partials = Parts |> Seq.map (fun part -> new KeyValuePair<_,_>(part.Key, part.Value.Copy())) new Namespace(name, partials, CallablesInReferences, SpecializationsInReferences, TypesInReferences) - /// Returns a lookup that given the name of a source file, + /// Returns a lookup that given the name of a source file, /// returns all documentation associated with this namespace defined in that file. - member internal this.Documentation = - Parts.Values.SelectMany(fun partial -> + member internal this.Documentation = + Parts.Values.SelectMany(fun partial -> partial.Documentation |> Seq.map (fun doc -> partial.Source, doc)).ToLookup(fst, snd) /// Returns all namespaces that are open or aliased in the given source file for this namespace. - /// The returned dictionary maps the names of the opened or aliased namespace to its alias if such an alias exists, - /// and in particular also contains an entry for the namespace itself. + /// The returned dictionary maps the names of the opened or aliased namespace to its alias if such an alias exists, + /// and in particular also contains an entry for the namespace itself. /// Throws an ArgumentException if the given source file is not listed as source file for (part of) the namespace. - member internal this.ImportedNamespaces source = - match Parts.TryGetValue source with + member internal this.ImportedNamespaces source = + match Parts.TryGetValue source with | true, partial -> partial.ImportedNamespaces | false, _ -> ArgumentException "given source file is not listed as a source file for this namespace" |> raise /// Returns a dictionary with all currently known namespace short names within the given source file and which namespace they represent. /// Throws an ArgumentException if the given source file is not listed as source file for (part of) the namespace. member internal this.NamespaceShortNames source = - match Parts.TryGetValue source with + match Parts.TryGetValue source with | true, partial -> partial.NamespaceShortNames | false, _ -> ArgumentException "given source file is not listed as a source file for this namespace" |> raise - /// If a type with the given name is defined in the specified source file or reference, - /// checks if that type has been marked as attribute and returns its underlying type if it has. - /// A type is considered to be marked as attribute if the list of defined attributes contains an attribute - /// with name "Attribute" that is qualified by any of the given possible qualifications. + /// If a type with the given name is defined in the specified source file or reference, + /// checks if that type has been marked as attribute and returns its underlying type if it has. + /// A type is considered to be marked as attribute if the list of defined attributes contains an attribute + /// with name "Attribute" that is qualified by any of the given possible qualifications. /// If the list of possible qualifications contains an empty string, then the "Attribute" may be unqualified. /// Throws an ArgumentException if no such type exists in any of the references and the source file is not listed as source file of the namespace. - /// Throws an InvalidOperationExeception if the corresponding type has not been resolved. - member internal this.TryGetAttributeDeclaredIn source (attName, possibleQualifications : _ seq) = - let marksAttribute (t : QsDeclarationAttribute) = t.TypeId |> function - | Value id -> id.Namespace.Value = BuiltIn.Attribute.Namespace.Value && id.Name.Value = BuiltIn.Attribute.Name.Value + /// Throws an InvalidOperationExeception if the corresponding type has not been resolved. + member internal this.TryGetAttributeDeclaredIn source (attName, possibleQualifications : _ seq) = + let marksAttribute (t : QsDeclarationAttribute) = t.TypeId |> function + | Value id -> id.Namespace.Value = BuiltIn.Attribute.FullName.Namespace.Value && id.Name.Value = BuiltIn.Attribute.FullName.Name.Value | Null -> false - let missingResolutionException () = InvalidOperationException "cannot get unresolved attribute" |> raise - let compareAttributeName (att : AttributeAnnotation) = att.Id.Symbol |> function - | Symbol sym when sym.Value = BuiltIn.Attribute.Name.Value && possibleQualifications.Contains "" -> true - | QualifiedSymbol (ns, sym) when sym.Value = BuiltIn.Attribute.Name.Value && possibleQualifications.Contains ns.Value -> true + let missingResolutionException () = InvalidOperationException "cannot get unresolved attribute" |> raise + let compareAttributeName (att : AttributeAnnotation) = att.Id.Symbol |> function + | Symbol sym when sym.Value = BuiltIn.Attribute.FullName.Name.Value && possibleQualifications.Contains "" -> true + | QualifiedSymbol (ns, sym) when sym.Value = BuiltIn.Attribute.FullName.Name.Value && possibleQualifications.Contains ns.Value -> true | _ -> false - match TypesInReferences.TryGetValue attName with + match TypesInReferences.TryGetValue attName with | true, tDecl -> if tDecl.Attributes |> Seq.exists marksAttribute then Some tDecl.Type else None | false, _ -> Parts.TryGetValue source |> function // ok only because/if we have covered that the type is not in a reference! | false, _ -> ArgumentException "given source file is not listed as source of the namespace" |> raise - | true, partialNS -> partialNS.TryGetType attName |> function - | true, resolution when resolution.DefinedAttributes |> Seq.exists compareAttributeName -> + | true, partialNS -> partialNS.TryGetType attName |> function + | true, resolution when resolution.DefinedAttributes |> Seq.exists compareAttributeName -> resolution.Resolved.ValueOrApply missingResolutionException |> fst |> Some | _ -> None /// Returns the type with the given name defined in the given source file within this namespace. /// Note that files contained in referenced assemblies are *not* considered to be source files for the namespace! - /// Throws an ArgumentException if the source file is not listed as source file of the namespace, - /// or if no type with the given name exists within this namespace in that source file. + /// Throws an ArgumentException if the source file is not listed as source file of the namespace, + /// or if no type with the given name exists within this namespace in that source file. member internal this.TypeInSource source tName = - match Parts.TryGetValue source with + match Parts.TryGetValue source with | true, partial -> partial.TryGetType tName |> function | true, typeDecl -> typeDecl | false, _ -> ArgumentException "no type with that name exist in the given source" |> raise @@ -365,88 +365,88 @@ and Namespace private /// Returns all types defined in the given source file within this namespace. /// Note that files contained in referenced assemblies are *not* considered to be source files for the namespace! /// Throws an ArgumentException if the source file is not listed as source file of the namespace. - member internal this.TypesDefinedInSource source = - match Parts.TryGetValue source with + member internal this.TypesDefinedInSource source = + match Parts.TryGetValue source with | true, partial -> partial.DefinedTypes.ToImmutableArray() | false, _ -> ArgumentException "given source file is not listed as source of the namespace" |> raise /// Returns all types defined in a source file associated with this namespace. /// This excludes types that are defined in files contained in referenced assemblies. - member internal this.TypesDefinedInAllSources () = + member internal this.TypesDefinedInAllSources () = if TypesDefinedInAllSourcesCache = null then - let getInfos (partial : PartialNamespace) = + let getInfos (partial : PartialNamespace) = partial.DefinedTypes |> Seq.map (fun (tName, decl) -> tName, (partial.Source, decl)) TypesDefinedInAllSourcesCache <- (Parts.Values.SelectMany getInfos).ToImmutableDictionary(fst, snd) TypesDefinedInAllSourcesCache /// Returns the callable with the given name defined in the given source file within this namespace. /// Note that files contained in referenced assemblies are *not* considered to be source files for the namespace! - /// Throws an ArgumentException if the source file is not listed as source file of the namespace, - /// or if no callable with the given name exists within this namespace in that source file. - member internal this.CallableInSource source cName = - match Parts.TryGetValue source with + /// Throws an ArgumentException if the source file is not listed as source file of the namespace, + /// or if no callable with the given name exists within this namespace in that source file. + member internal this.CallableInSource source cName = + match Parts.TryGetValue source with | true, partial -> partial.TryGetCallable cName |> function | true, callable -> callable | false, _ -> ArgumentException "no callable with that name exist in the given source" |> raise | false, _ -> ArgumentException "given source file is not listed as source of the namespace" |> raise /// Returns all callables defined in the given source file within this namespace. - /// Callables include operations, functions, and auto-genrated type constructors for declared types. + /// Callables include operations, functions, and auto-generated type constructors for declared types. /// Note that files contained in referenced assemblies are *not* considered to be source files for the namespace! /// Throws an ArgumentException if the source file is not listed as source file of the namespace. - member internal this.CallablesDefinedInSource source = - match Parts.TryGetValue source with + member internal this.CallablesDefinedInSource source = + match Parts.TryGetValue source with | true, partial -> partial.DefinedCallables.ToImmutableArray() | false, _ -> ArgumentException "given source file is not listed as source of the namespace" |> raise /// Returns all callables defined in a source file associated with this namespace. /// This excludes callables that are defined in files contained in referenced assemblies. - /// Callables include operations, functions, and auto-genrated type constructors for declared types. - member internal this.CallablesDefinedInAllSources () = + /// Callables include operations, functions, and auto-generated type constructors for declared types. + member internal this.CallablesDefinedInAllSources () = if CallablesDefinedInAllSourcesCache = null then - let getInfos (partial : PartialNamespace) = + let getInfos (partial : PartialNamespace) = partial.DefinedCallables |> Seq.map (fun (cName, decl) -> cName, (partial.Source, decl)) CallablesDefinedInAllSourcesCache <- (Parts.Values.SelectMany getInfos).ToImmutableDictionary(fst, snd) CallablesDefinedInAllSourcesCache /// Returns all specializations for the callable with the given name defined in a source file associated with this namespace, /// This excludes specializations that are defined in files contained in referenced assemblies. - /// Throws an ArgumentException if no callable with the given name is defined in this namespace. - member internal this.SpecializationsDefinedInAllSources cName = + /// Throws an ArgumentException if no callable with the given name is defined in this namespace. + member internal this.SpecializationsDefinedInAllSources cName = match this.ContainsCallable cName with - | Value _ -> (Parts.Values.SelectMany (fun partial -> - partial.GetSpecializations cName |> Seq.map (fun (kind, decl) -> kind, (partial.Source, decl)))).ToImmutableArray() + | Value _ -> (Parts.Values.SelectMany (fun partial -> + partial.GetSpecializations cName |> Seq.map (fun (kind, decl) -> kind, (partial.Source, decl)))).ToImmutableArray() | Null -> ArgumentException "no callable with the given name exist within the namespace" |> raise - /// If this namespace contains a declaration for the given type name, + /// If this namespace contains a declaration for the given type name, /// returns a Value with the name of the source file or the name of the file within a referenced assembly - /// in which it is declared as well as a string option indicating the redirection for the type if it has been deprecated. + /// in which it is declared as well as a string option indicating the redirection for the type if it has been deprecated. /// Returns Null otherwise. - /// Whether the type has been deprecated is determined by checking the associated attributes for an attribute with the corresponding name. + /// Whether the type has been deprecated is determined by checking the associated attributes for an attribute with the corresponding name. /// Note that if the type is declared in a source files, the *unresolved* attributes will be checked. - /// In that case checkDeprecation is used to validate the namespace qualification of the attribute. + /// In that case checkDeprecation is used to validate the namespace qualification of the attribute. /// If checkDeprecation is not specified, it is assumed that no qualification is needed in the relevant namespace and source file. - member this.ContainsType (tName, ?checkDeprecation : (string -> bool)) = - let checkDeprecation = defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) - match TypesInReferences.TryGetValue tName with + member this.ContainsType (tName, ?checkDeprecation : (string -> bool)) = + let checkDeprecation = defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.FullName.Namespace.Value) + match TypesInReferences.TryGetValue tName with | true, tDecl -> Value (tDecl.SourceFile, tDecl.Attributes |> SymbolResolution.TryFindRedirect) - | false, _ -> FromSingleSource (fun partialNS -> partialNS.TryGetType tName |> function + | false, _ -> FromSingleSource (fun partialNS -> partialNS.TryGetType tName |> function | true, tDecl -> Some (partialNS.Source, tDecl.DefinedAttributes |> SymbolResolution.TryFindRedirectInUnresolved checkDeprecation) | false, _ -> None) - /// If this namespace contains the declaration for the given callable name, + /// If this namespace contains the declaration for the given callable name, /// returns a Value with the name of the source file or the name of the file within a referenced assembly - /// in which it is declared as well as a string option indicating the redirection for the callable if it has been deprecated. + /// in which it is declared as well as a string option indicating the redirection for the callable if it has been deprecated. /// Returns Null otherwise. /// If the given callable corresponds to the (auto-generated) type constructor for a user defined type, /// returns the file in which that type is declared as source. - /// Whether the callable has been deprecated is determined by checking the associated attributes for an attribute with the corresponding name. + /// Whether the callable has been deprecated is determined by checking the associated attributes for an attribute with the corresponding name. /// Note that if the type is declared in a source files, the *unresolved* attributes will be checked. - /// In that case checkDeprecation is used to validate the namespace qualification of the attribute. + /// In that case checkDeprecation is used to validate the namespace qualification of the attribute. /// If checkDeprecation is not specified, it is assumed that no qualification is needed in the relevant namespace and source file. - member this.ContainsCallable (cName, ?checkDeprecation : (string -> bool)) = - let checkDeprecation = defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) - match CallablesInReferences.TryGetValue cName with + member this.ContainsCallable (cName, ?checkDeprecation : (string -> bool)) = + let checkDeprecation = defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.FullName.Namespace.Value) + match CallablesInReferences.TryGetValue cName with | true, cDecl -> Value (cDecl.SourceFile, cDecl.Attributes |> SymbolResolution.TryFindRedirect) | false, _ -> FromSingleSource (fun partialNS -> partialNS.TryGetCallable cName |> function | true, (_, cDecl) -> Some (partialNS.Source, cDecl.DefinedAttributes |> SymbolResolution.TryFindRedirectInUnresolved checkDeprecation) @@ -455,35 +455,35 @@ and Namespace private /// Sets the resolution for the type with the given name in the given source file to the given type, /// and replaces the resolved attributes with the given values. /// Fails with the standard key does not exist error if no such source file exists or no type with that name exists in that file. - member internal this.SetTypeResolution source (tName, resolution, resAttributes) = + member internal this.SetTypeResolution source (tName, resolution, resAttributes) = TypesDefinedInAllSourcesCache <- null CallablesDefinedInAllSourcesCache <- null Parts.[source].SetTypeResolution (tName, resolution, resAttributes) - /// Sets the resolution for the signature of the callable with the given name in the given source file + /// Sets the resolution for the signature of the callable with the given name in the given source file /// to the given signature, and replaces the resolved attributes with the given values. /// Fails with the standard key does not exist error if no such source file exists or no callable with that name exists in that file. - member internal this.SetCallableResolution source (cName, resolution, resAttributes) = + member internal this.SetCallableResolution source (cName, resolution, resAttributes) = CallablesDefinedInAllSourcesCache <- null Parts.[source].SetCallableResolution (cName, resolution, resAttributes) - /// Applies the given functions computing the resolution of attributes and the generation directive - /// to all defined specializations of the callable with the given name, - /// and sets its resolution and resolved attributes to the computed values. + /// Applies the given functions computing the resolution of attributes and the generation directive + /// to all defined specializations of the callable with the given name, + /// and sets its resolution and resolved attributes to the computed values. /// Returns a list with the name of the source file and each generated diagnostic. /// Fails with the standard key does not exist error if no callable with that name exists. - member internal this.SetSpecializationResolutions (cName, computeResolution, getResAttributes) = + member internal this.SetSpecializationResolutions (cName, computeResolution, getResAttributes) = CallablesDefinedInAllSourcesCache <- null - let setResolutions (partial : PartialNamespace) = - partial.SetSpecializationResolutions (cName, computeResolution, getResAttributes) + let setResolutions (partial : PartialNamespace) = + partial.SetSpecializationResolutions (cName, computeResolution, getResAttributes) |> Array.map (fun err -> partial.Source, err) Parts.Values |> Seq.map setResolutions |> Seq.toList - /// If the given source is not currently listed as source file for (part of) the namespace, + /// If the given source is not currently listed as source file for (part of) the namespace, /// adds the given file name to the list of sources and returns true. - /// Returns false otherwise. - member internal this.TryAddSource source = - if not (Parts.ContainsKey source) then + /// Returns false otherwise. + member internal this.TryAddSource source = + if not (Parts.ContainsKey source) then TypesDefinedInAllSourcesCache <- null CallablesDefinedInAllSourcesCache <- null Parts.Add(source, new PartialNamespace(this.Name, source)) @@ -493,30 +493,30 @@ and Namespace private /// If the given source is currently listed as source file for (part of) the namespace, /// removes it from that list (and all declarations along with it) and returns true. /// Returns false otherwise. - member internal this.TryRemoveSource source = + member internal this.TryRemoveSource source = if Parts.Remove source then TypesDefinedInAllSourcesCache <- null CallablesDefinedInAllSourcesCache <- null true else false - /// Adds the given lines of documentation to the list of documenting sections - /// associated with this namespace within the given source file. + /// Adds the given lines of documentation to the list of documenting sections + /// associated with this namespace within the given source file. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. member this.AddDocumentation source doc = - match Parts.TryGetValue source with + match Parts.TryGetValue source with | true, partial -> partial.AddDocumentation doc | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise /// Adds the given namespace name to the list of opened namespaces for the part of the namespace defined in the given source file. - /// Generates suitable diagnostics at the given range if the given namespace has already been opened and/or opened under a different alias, - /// or if the given alias is already in use for a different namespace. + /// Generates suitable diagnostics at the given range if the given namespace has already been opened and/or opened under a different alias, + /// or if the given alias is already in use for a different namespace. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. - member internal this.TryAddOpenDirective source (openedNS, nsRange) (alias, aliasRange) = + member internal this.TryAddOpenDirective source (openedNS, nsRange) (alias, aliasRange) = let alias = if String.IsNullOrWhiteSpace alias then null else alias.Trim() let aliasIsSameAs str = (str = null && alias = null) || (str <> null && alias <> null && str = alias) - match Parts.TryGetValue source with - | true, partial -> + match Parts.TryGetValue source with + | true, partial -> let imported = partial.ImportedNamespaces match imported.TryGetValue openedNS with | true, existing when aliasIsSameAs existing && existing = null -> [| nsRange |> QsCompilerDiagnostic.Warning (WarningCode.NamespaceAleadyOpen, []) |] @@ -524,40 +524,40 @@ and Namespace private | true, existing when existing <> null -> [| nsRange |> QsCompilerDiagnostic.Error (ErrorCode.AliasForNamespaceAlreadyExists, [existing]) |] | true, _ -> [| nsRange |> QsCompilerDiagnostic.Error (ErrorCode.AliasForOpenedNamespace, []) |] | false, _ when alias <> null && imported.ContainsValue alias -> [| aliasRange |> QsCompilerDiagnostic.Error (ErrorCode.InvalidNamespaceAliasName, [alias]) |] - | false, _ -> + | false, _ -> TypesDefinedInAllSourcesCache <- null CallablesDefinedInAllSourcesCache <- null partial.AddOpenDirective(openedNS, alias); [||] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise - /// If no type with the given name exists in this namespace, adds the given type declaration - /// as well as the corresponding constructor declaration to the given source, and returns an empty array. - /// The given location is associated with both the type constructur and the type itself and accessible via the record properties Position and SymbolRange. + /// If no type with the given name exists in this namespace, adds the given type declaration + /// as well as the corresponding constructor declaration to the given source, and returns an empty array. + /// The given location is associated with both the type constructor and the type itself and accessible via the record properties Position and SymbolRange. /// If a type or callable with that name already exists, returns an array of suitable diagnostics. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. - member this.TryAddType (source, location) ((tName, tRange), typeTuple, attributes, documentation) : QsCompilerDiagnostic[] = - match Parts.TryGetValue source with - | true, partial when not (IsDefined tName) -> + member this.TryAddType (source, location) ((tName, tRange), typeTuple, attributes, documentation) : QsCompilerDiagnostic[] = + match Parts.TryGetValue source with + | true, partial when not (IsDefined tName) -> TypesDefinedInAllSourcesCache <- null CallablesDefinedInAllSourcesCache <- null partial.AddType location (tName, typeTuple, attributes, documentation); [||] | true, _ -> this.ContainsType tName |> function | Value _ -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeRedefinition, [tName.Value]) |] - | Null -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeConstructorOverlapWithCallable, [tName.Value]) |] + | Null -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeConstructorOverlapWithCallable, [tName.Value]) |] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise - /// If no callable (function, operation, or type constructor) with the given name exists in this namespace, - /// adds a declaration for the callable of the given kind (operation or function) with the given name and signature - /// to the given source, and returns an empty array. - /// The given location is associated with the callable declaration and accessible via the record properties Position and SymbolRange. + /// If no callable (function, operation, or type constructor) with the given name exists in this namespace, + /// adds a declaration for the callable of the given kind (operation or function) with the given name and signature + /// to the given source, and returns an empty array. + /// The given location is associated with the callable declaration and accessible via the record properties Position and SymbolRange. /// If a callable with that name already exists, returns an array of suitable diagnostics. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. member this.TryAddCallableDeclaration (source, location) ((cName, cRange), (kind, signature), attributes, documentation) = - match Parts.TryGetValue source with - | true, partial when not (IsDefined cName) -> + match Parts.TryGetValue source with + | true, partial when not (IsDefined cName) -> CallablesDefinedInAllSourcesCache <- null partial.AddCallableDeclaration location (cName, (kind, signature), attributes, documentation); [||] - | true, _ -> this.ContainsType cName |> function + | true, _ -> this.ContainsType cName |> function | Value _ -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableOverlapWithTypeConstructor, [cName.Value]) |] | Null -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableRedefinition, [cName.Value]) |] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise @@ -565,14 +565,14 @@ and Namespace private /// If a declaration for a callable of the given name exists within this namespace, /// verifies that no specialization of the given kind that clashes with the give specialization already exists, /// and adds the specialization defined by the given generator for the given kind to the dictionary of specializations in the given source. - /// The given location is associated with the given specialization and accessible via the record properties Position and HeaderRange. + /// The given location is associated with the given specialization and accessible via the record properties Position and HeaderRange. /// Returns an array with suitable diagnostics if a clashing specialization already exists, and/or - /// if the length of the type arguments in the given generator does not match the number of type parameters of the callable declaration. + /// if the length of the type arguments in the given generator does not match the number of type parameters of the callable declaration. /// If no declaration for the given callable name exists within this namespace, returns an array with suitable diagnostics. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. /// IMPORTANT: The verification of whether the given specialization kind (body, adjoint, controlled, or controlled adjoint) may exist - /// for the given callable is up to the calling routine. - member this.TryAddCallableSpecialization kind (source, location : QsLocation) ((cName, cRange), generator : QsSpecializationGenerator, attributes, documentation) = + /// for the given callable is up to the calling routine. + member this.TryAddCallableSpecialization kind (source, location : QsLocation) ((cName, cRange), generator : QsSpecializationGenerator, attributes, documentation) = let getRelevantDeclInfo (declSource : NonNullable) = let unitOrInvalid fct = function | Item item -> fct item |> function | UnitType | InvalidType -> true | _ -> false @@ -586,18 +586,18 @@ and Namespace private let unitReturn = cDecl.Defined.ReturnType |> unitOrInvalid (fun (t : QsType) -> t.Type) unitReturn, cDecl.Defined.TypeParameters.Length - match Parts.TryGetValue source with + match Parts.TryGetValue source with | true, partial -> match this.ContainsCallable cName with | Value (declSource, _) -> - let AddAndClearCache () = + let AddAndClearCache () = CallablesDefinedInAllSourcesCache <- null partial.AddCallableSpecialization location kind (cName, generator, attributes, documentation) // verify that the given specializations are indeed compatible with the defined type parameters let qFunctorSupport, nrTypeParams = getRelevantDeclInfo declSource - let givenNrTypeParams = generator.TypeArguments |> function | Value args -> Some args.Length | Null -> None + let givenNrTypeParams = generator.TypeArguments |> function | Value args -> Some args.Length | Null -> None if givenNrTypeParams.IsSome && givenNrTypeParams.Value <> nrTypeParams then - [| location.Range |> QsCompilerDiagnostic.Error (ErrorCode.TypeSpecializationMismatch, [nrTypeParams.ToString()]) |] + [| location.Range |> QsCompilerDiagnostic.Error (ErrorCode.TypeSpecializationMismatch, [nrTypeParams.ToString()]) |] // verify if a unit return value is required for the given specialization kind elif not qFunctorSupport then kind |> function | QsBody -> AddAndClearCache(); [||] @@ -608,36 +608,36 @@ and Namespace private | Null -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.SpecializationForUnknownCallable, [cName.Value]) |] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise - /// Adds an auto-generated specialization of the given kind to the callable with the given name and declaration in the specified source file. - /// Sets the location to the same location as the callable declaration, with the range set to the message range if the given message range is not Null. - /// Return the diagnostics generated upon adding the specialization. + /// Adds an auto-generated specialization of the given kind to the callable with the given name and declaration in the specified source file. + /// Sets the location to the same location as the callable declaration, with the range set to the message range if the given message range is not Null. + /// Return the diagnostics generated upon adding the specialization. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. member internal this.InsertSpecialization (kind, typeArgs) (parentName : NonNullable, source) (declLocation : QsLocation, msgRange : QsRangeInfo) = let location = {Offset = declLocation.Offset; Range = msgRange.ValueOr declLocation.Range} let generator = {TypeArguments = typeArgs; Generator = AutoGenerated; Range = msgRange} let doc = ImmutableArray.Create(sprintf "automatically generated %A specialization for %s.%s" kind this.Name.Value parentName.Value) - this.TryAddCallableSpecialization kind (source, location) ((parentName, declLocation.Range), generator, ImmutableArray.Empty, doc) + this.TryAddCallableSpecialization kind (source, location) ((parentName, declLocation.Range), generator, ImmutableArray.Empty, doc) /// Deletes the specialization(s) defined at the specified location and source file for the callable with the given name. /// Returns the number of removed specializations. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. /// Throws the standard key does not exist exception if no callable with that name exists. member internal this.RemoveSpecialization (source, location) cName = - match Parts.TryGetValue source with + match Parts.TryGetValue source with | true, partial -> partial.RemoveCallableSpecialization location cName | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise /// Threadsafe class for global symbol management. -/// Takes a lookup for all callables and for all types declared within one of the assemblies +/// Takes a lookup for all callables and for all types declared within one of the assemblies /// referenced by the compilation unit this namespace manager belongs to. /// The key for the given lookups is the name of the namespace the declarations belongs to. -and NamespaceManager +and NamespaceManager (syncRoot : IReaderWriterLock, - callablesInRefs : IEnumerable, + callablesInRefs : IEnumerable, specializationsInRefs : IEnumerable, - typesInRefs : IEnumerable) = - // This class itself does not use any concurrency, + typesInRefs : IEnumerable) = + // 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 // access to that lock needs to be coordinated by whatever coordinates the mutations. @@ -649,7 +649,7 @@ and NamespaceManager /// dictionary with all declared namespaces /// the key is the name of the namespace - let Namespaces = + let Namespaces = let namespaces = new Dictionary, Namespace>() let callables = callablesInRefs.ToLookup(fun header -> header.QualifiedName.Namespace) let specializations = specializationsInRefs.ToLookup(fun (header,_) -> header.Parent.Namespace) @@ -660,46 +660,46 @@ and NamespaceManager namespaces.Add (nsName, new Namespace(nsName, [], callables.[nsName], specializations.[nsName], types.[nsName])) namespaces - /// Returns the full name of all entry points currently resolved in any of the tracked source files. - let GetEntryPoints () = - let entryPoints = Namespaces.Values |> Seq.collect (fun ns -> + /// Returns the full name of all entry points currently resolved in any of the tracked source files. + let GetEntryPoints () = + let entryPoints = Namespaces.Values |> Seq.collect (fun ns -> ns.CallablesDefinedInAllSources() |> Seq.choose (fun kvPair -> let cName, (source, (_, decl)) = kvPair.Key, kvPair.Value if decl.ResolvedAttributes |> Seq.exists BuiltIn.MarksEntryPoint then Some ({Namespace = ns.Name; Name = cName}, source) else None)) entryPoints.ToImmutableArray() - /// If a namespace with the given name exists, returns that namespace + /// If a namespace with the given name exists, returns that namespace /// as well as all imported namespaces for that namespace in the given source file. /// Filters namespaces that have been imported under a different name. /// Filters all unknown namespaces, i.e. imported namespaces that are not managed by this namespace manager. - /// Throws an ArgumentException if no namespace with the given name exists, - /// or the given source file is not listed as source of that namespace. - let OpenNamespaces (nsName, source) = - let isKnownAndNotAliased (kv : KeyValuePair<_,_>) = + /// Throws an ArgumentException if no namespace with the given name exists, + /// or the given source file is not listed as source of that namespace. + let OpenNamespaces (nsName, source) = + let isKnownAndNotAliased (kv : KeyValuePair<_,_>) = if kv.Value <> null then None else Namespaces.TryGetValue kv.Key |> function | true, ns -> Some ns | false, _ -> None - match Namespaces.TryGetValue nsName with - | true, ns -> ns, ns.ImportedNamespaces source |> Seq.choose isKnownAndNotAliased |> Seq.toList + match Namespaces.TryGetValue nsName with + | true, ns -> ns, ns.ImportedNamespaces source |> Seq.choose isKnownAndNotAliased |> Seq.toList | false, _ -> ArgumentException("no namespace with the given name exists") |> raise - /// If the given function containsSymbol returns a Value for the namespace with the given name, - /// returns a list containing only a single tuple with the given namespace name and the returned value. - /// Otherwise returns a list with all possible (namespaceName, returnedValue) tuples that yielded non-Null values + /// If the given function containsSymbol returns a Value for the namespace with the given name, + /// returns a list containing only a single tuple with the given namespace name and the returned value. + /// Otherwise returns a list with all possible (namespaceName, returnedValue) tuples that yielded non-Null values /// for namespaces opened within the given namespace and source file. - /// Throws an ArgumentException if no namespace with the given name exists, - /// or the given source file is not listed as source of that namespace. - let PossibleResolutions containsSymbol (nsName, source) = - let containsSource (arg : Namespace) = - containsSymbol arg |> QsNullable<_>.Map (fun source -> (arg.Name, source)) + /// Throws an ArgumentException if no namespace with the given name exists, + /// or the given source file is not listed as source of that namespace. + let PossibleResolutions containsSymbol (nsName, source) = + let containsSource (arg : Namespace) = + containsSymbol arg |> QsNullable<_>.Map (fun source -> (arg.Name, source)) let currentNS, importedNS = OpenNamespaces (nsName, source) currentNS |> containsSource |> function | Value (nsName, source) -> [(nsName, source)] | Null -> importedNS |> List.choose (containsSource >> function | Value v -> Some v | Null -> None) /// Given a qualifier for a symbol name, returns the corresponding namespace as Some - /// if such a namespace or such a namespace short name within the given parent namespace and source file exists. + /// if such a namespace or such a namespace short name within the given parent namespace and source file exists. /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. - let TryResolveQualifier qualifier (nsName, source) = + let TryResolveQualifier qualifier (nsName, source) = match Namespaces.TryGetValue qualifier with | false, _ -> Namespaces.TryGetValue nsName |> function // check if qualifier is a namespace short name | true, parentNS -> (parentNS.NamespaceShortNames source).TryGetValue qualifier |> function @@ -711,147 +711,147 @@ and NamespaceManager | true, ns -> Some ns /// Returns the possible qualifications for the built-in type or callable used in the given namespace and source. - /// where the given source may either be the name of a source file or of a referenced assembly. - /// If the given source is not listed as source file of the namespace, assumes that the source if one of the references - /// and returns the namespace name of the given built in type or callable as only possible qualification. - /// Throws an ArgumentException if no namespace with the given name exists. - let PossibleQualifications (nsName, source) (builtIn : BuiltIn) = - match Namespaces.TryGetValue nsName with - | true, ns when ns.Sources.Contains source -> (ns.ImportedNamespaces source).TryGetValue builtIn.Namespace |> function - | true, null when ns.ContainsType builtIn.Name = Null || nsName.Value = builtIn.Namespace.Value -> [""; builtIn.Namespace.Value] - | true, null -> [builtIn.Namespace.Value] // the built-in type or callable is shadowed - | true, alias -> [alias; builtIn.Namespace.Value] - | false, _ -> [builtIn.Namespace.Value] - | true, _ -> [builtIn.Namespace.Value]; + /// where the given source may either be the name of a source file or of a referenced assembly. + /// If the given source is not listed as source file of the namespace, assumes that the source if one of the references + /// and returns the namespace name of the given built in type or callable as only possible qualification. + /// Throws an ArgumentException if no namespace with the given name exists. + let PossibleQualifications (nsName, source) (builtIn : BuiltIn) = + match Namespaces.TryGetValue nsName with + | true, ns when ns.Sources.Contains source -> (ns.ImportedNamespaces source).TryGetValue builtIn.FullName.Namespace |> function + | true, null when ns.ContainsType builtIn.FullName.Name = Null || nsName.Value = builtIn.FullName.Namespace.Value -> [""; builtIn.FullName.Namespace.Value] + | true, null -> [builtIn.FullName.Namespace.Value] // the built-in type or callable is shadowed + | true, alias -> [alias; builtIn.FullName.Namespace.Value] + | false, _ -> [builtIn.FullName.Namespace.Value] + | true, _ -> [builtIn.FullName.Namespace.Value]; | false, _ -> ArgumentException "no namespace with the given name exists" |> raise - /// Given the qualified or unqualfied name of a type used within the given parent namespace and source file, determines if such a type exists - /// and returns its full name and the source file or referenced assembly in which it is defined as Some if it does. + /// Given the qualified or unqualified name of a type used within the given parent namespace and source file, determines if such a type exists + /// and returns its full name and the source file or referenced assembly in which it is defined as Some if it does. /// Returns None if no such type exist, or if the type name is unqualified and ambiguous. /// Generates and returns an array with suitable diagnostics. /// Throws an ArgumentException if the given parent namespace does not exist, - /// or if no source file with the given name is listed as source of that namespace. - let TryResolveTypeName (parentNS, source) ((nsName, symName), symRange) = + /// or if no source file with the given name is listed as source of that namespace. + let TryResolveTypeName (parentNS, source) ((nsName, symName), symRange) = let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange let checkQualificationForDeprecation qual = BuiltIn.Deprecated |> PossibleQualifications (parentNS, source) |> Seq.contains qual - let buildAndReturn (ns, declSource, deprecation, errs) = + let buildAndReturn (ns, declSource, deprecation, errs) = let deprecatedWarnings = deprecation |> SymbolResolution.GenerateDeprecationWarning ({Namespace = ns; Name = symName}, symRange |> orDefault) Some ({Namespace = ns; Name = symName; Range = symRange}, declSource), deprecatedWarnings |> Array.append errs - let tryFind (parentNS, source) (tName, tRange) = - match (parentNS, source) |> PossibleResolutions (fun ns -> ns.ContainsType (tName, checkQualificationForDeprecation)) with + let tryFind (parentNS, source) (tName, tRange) = + match (parentNS, source) |> PossibleResolutions (fun ns -> ns.ContainsType (tName, checkQualificationForDeprecation)) with | [] -> Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownType, [tName.Value]) |] | [(nsName, (declSource, deprecated))] -> Value (nsName, declSource, deprecated), [||] - | resolutions -> + | resolutions -> let diagArg = String.Join(", ", resolutions.Select (fun (ns,_) -> ns.Value)) Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AmbiguousType, [tName.Value; diagArg]) |] - match nsName with + match nsName with | None -> tryFind (parentNS, source) (symName, symRange) |> function | Value (ns, declSource, deprecation), errs -> buildAndReturn (ns, declSource, deprecation, errs) | Null, errs -> None, errs | Some qualifier -> (parentNS, source) |> TryResolveQualifier qualifier |> function - | None -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownNamespace, [qualifier.Value]) |] - | Some ns -> + | None -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownNamespace, [qualifier.Value]) |] + | Some ns -> ns.ContainsType (symName, checkQualificationForDeprecation) |> function - | Value (declSource, deprecation) -> buildAndReturn (ns.Name, declSource, deprecation, [||]) + | Value (declSource, deprecation) -> buildAndReturn (ns.Name, declSource, deprecation, [||]) | Null -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeInNamespace, [symName.Value; qualifier.Value]) |] /// Given the name of the namespace as well as the source file in which the attribute occurs, resolves the given attribute. - /// Generates suitable diagnostics if a suitable attribute cannot be determined, - /// or if the attribute argument contains expressions that are not supported, - /// or if the resolved argument type does not match the expected argument type. + /// Generates suitable diagnostics if a suitable attribute cannot be determined, + /// or if the attribute argument contains expressions that are not supported, + /// or if the resolved argument type does not match the expected argument type. /// Returns the resolved attribute as well as the generated diagnostics. - /// The TypeId in the resolved attribute is set to Null if the unresolved Id is not a valid identifier - /// or if the correct attribute cannot be determined, and is set to the corresponding type identifier otherwise. + /// The TypeId in the resolved attribute is set to Null if the unresolved Id is not a valid identifier + /// or if the correct attribute cannot be determined, and is set to the corresponding type identifier otherwise. /// May throw an ArgumentException if the given parent namespace does not exist, - /// or if no source file with the given name is listed as source of that namespace. - member private this.ResolveAttribute (parentNS, source) attribute = - let getAttribute ((nsName, symName), symRange) = + /// or if no source file with the given name is listed as source of that namespace. + member private this.ResolveAttribute (parentNS, source) attribute = + let getAttribute ((nsName, symName), symRange) = match TryResolveTypeName (parentNS, source) ((nsName, symName), symRange) with | Some (udt, declSource), errs -> // declSource may be the name of an assembly! let fullName = sprintf "%s.%s" udt.Namespace.Value udt.Name.Value let validQualifications = BuiltIn.Attribute |> PossibleQualifications (udt.Namespace, declSource) - match Namespaces.TryGetValue udt.Namespace with - | true, ns -> ns.TryGetAttributeDeclaredIn declSource (udt.Name, validQualifications) |> function - | None -> None, [| symRange.ValueOr QsCompilerDiagnostic.DefaultRange |> QsCompilerDiagnostic.Error (ErrorCode.NotMarkedAsAttribute, [fullName]) |] + match Namespaces.TryGetValue udt.Namespace with + | true, ns -> ns.TryGetAttributeDeclaredIn declSource (udt.Name, validQualifications) |> function + | None -> None, [| symRange.ValueOr QsCompilerDiagnostic.DefaultRange |> QsCompilerDiagnostic.Error (ErrorCode.NotMarkedAsAttribute, [fullName]) |] | Some argType -> Some (udt, argType), errs | false, _ -> QsCompilerError.Raise "namespace for defined type not found"; None, errs | None, errs -> None, errs let resolved, msgs = SymbolResolution.ResolveAttribute getAttribute attribute resolved, msgs |> Array.map (fun m -> attribute.Position, m) - /// Resolves the DefinedAttributes of the given declaration using ResolveAttribute and validates any entry points, if any. - /// Returns the resolved attributes as well as an array with diagnostics along with the declaration position. - /// Each entry in the returned array of attributes is the resolution for the corresponding entry in the array of defined attributes. - /// May throw an ArgumentException if no parent callable with the given name exists. - member private this.ResolveAttributes (parent : QsQualifiedName, source) (decl : Resolution<'T,_>) = + /// Resolves the DefinedAttributes of the given declaration using ResolveAttribute and validates any entry points, if any. + /// Returns the resolved attributes as well as an array with diagnostics along with the declaration position. + /// Each entry in the returned array of attributes is the resolution for the corresponding entry in the array of defined attributes. + /// May throw an ArgumentException if no parent callable with the given name exists. + member private this.ResolveAttributes (parent : QsQualifiedName, source) (decl : Resolution<'T,_>) = 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 validateAttributes (alreadyDefined : int list, resAttr) (att : QsDeclarationAttribute) = - let returnInvalid msg = + let validateAttributes (alreadyDefined : int list, resAttr) (att : QsDeclarationAttribute) = + let returnInvalid msg = errs.AddRange msg alreadyDefined, {att with TypeId = Null} :: resAttr match att.TypeId with // known attribute - | Value tId -> + | Value tId -> let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange - let attributeHash = - if tId.Namespace.Value = BuiltIn.Deprecated.Namespace.Value && tId.Name.Value = BuiltIn.Deprecated.Name.Value then hash (tId.Namespace.Value, tId.Name.Value) + let attributeHash = + if tId.Namespace.Value = BuiltIn.Deprecated.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Deprecated.FullName.Name.Value then hash (tId.Namespace.Value, tId.Name.Value) else hash (tId.Namespace.Value, tId.Name.Value, NamespaceManager.ExpressionHash att.Argument) // the attribute is a duplication of another attribute on this declaration - if alreadyDefined.Contains attributeHash then - (att.Offset, tId.Range |> orDefault - |> QsCompilerDiagnostic.Warning (WarningCode.DuplicateAttribute, [tId.Name.Value])) + if alreadyDefined.Contains attributeHash then + (att.Offset, tId.Range |> orDefault + |> QsCompilerDiagnostic.Warning (WarningCode.DuplicateAttribute, [tId.Name.Value])) |> Seq.singleton |> returnInvalid - // the attribute marks an entry point - elif tId.Namespace.Value = BuiltIn.EntryPoint.Namespace.Value && tId.Name.Value = BuiltIn.EntryPoint.Name.Value then + // the attribute marks an entry point + elif tId.Namespace.Value = BuiltIn.EntryPoint.FullName.Namespace.Value && tId.Name.Value = BuiltIn.EntryPoint.FullName.Name.Value then match box decl.Defined with - | :? CallableSignature as signature when not (signature.TypeParameters.Any()) -> - let validateArgAndReturnTypes (qsType : QsType) = + | :? CallableSignature as signature when not (signature.TypeParameters.Any()) -> + let validateArgAndReturnTypes (qsType : QsType) = 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 + | 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 | _ -> Seq.empty) - let argErrs = signature.Argument.Items.Select(snd).Append signature.ReturnType |> Seq.collect validateArgAndReturnTypes + let argErrs = signature.Argument.Items.Select(snd).Append signature.ReturnType |> Seq.collect validateArgAndReturnTypes let hasCharacteristics = signature.Characteristics.Characteristics |> function | EmptySet | InvalidSetExpr -> false | _ -> true - match Namespaces.TryGetValue parent.Namespace with + 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, [])) - if argErrs.Any() then returnInvalid argErrs + if argErrs.Any() then returnInvalid argErrs else GetEntryPoints() |> Seq.tryHead |> function | None -> attributeHash :: alreadyDefined, att :: resAttr - | Some (epName, epSource) -> + | 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.MultipleEntryPoints, msgArgs)) |> Seq.singleton |> returnInvalid | _ -> (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidEntryPointPlacement, [])) |> Seq.singleton |> returnInvalid - + // the attribute marks a unit test - elif tId.Namespace.Value = BuiltIn.Test.Namespace.Value && tId.Name.Value = BuiltIn.Test.Name.Value then - let isUnitToUnit (signature : CallableSignature) = - let isUnitType = function + elif tId.Namespace.Value = BuiltIn.Test.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Test.FullName.Name.Value then + let isUnitToUnit (signature : CallableSignature) = + let isUnitType = function | Tuple _ | Missing -> false | Item (itemType : QsType) -> itemType.Type = UnitType | _ -> true // invalid type - match signature.Argument.Items |> Seq.toList with + match signature.Argument.Items |> Seq.toList with | [] -> 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()) -> + 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()) 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 + attributeHash :: alreadyDefined, att :: resAttr + else (att.Offset, att.Argument.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidExecutionTargetForTest, [])) |> Seq.singleton |> returnInvalid | _ -> (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidTestAttributePlacement, [])) |> Seq.singleton |> returnInvalid // the attribute is another kind of attribute that requires no further verification at this point - else attributeHash :: alreadyDefined, att :: resAttr + else attributeHash :: alreadyDefined, att :: resAttr // unknown attribute, and an error has already been generated | _ -> alreadyDefined, att :: resAttr @@ -865,25 +865,25 @@ and NamespaceManager /// Verifies that all used type parameters are defined in the given list of type parameters, /// and generates suitable diagnostics if they are not, replacing them by the Q# type denoting an invalid type. /// Returns the resolved type as well as an array with diagnostics. - /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is consistent with the defined callables. - /// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations. - /// Throws a NonSupportedException if the QsType to resolve contains a MissingType. - member this.ResolveType (parent : QsQualifiedName, tpNames : ImmutableArray<_>, source : NonNullable) (qsType : QsType) : ResolvedType * QsCompilerDiagnostic[] = - let processUDT = TryResolveTypeName (parent.Namespace, source) >> function + /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is consistent with the defined callables. + /// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations. + /// Throws a NonSupportedException if the QsType to resolve contains a MissingType. + member this.ResolveType (parent : QsQualifiedName, tpNames : ImmutableArray<_>, source : NonNullable) (qsType : QsType) : ResolvedType * QsCompilerDiagnostic[] = + let processUDT = TryResolveTypeName (parent.Namespace, source) >> function | Some (udt, _), errs -> UserDefinedType udt, errs - | None, errs -> InvalidType, errs - let processTP (symName, symRange) = + | None, errs -> InvalidType, errs + let processTP (symName, symRange) = if tpNames |> Seq.contains symName then TypeParameter {Origin = parent; TypeName = symName; Range = symRange}, [||] else InvalidType, [| symRange.ValueOr QsCompilerDiagnostic.DefaultRange |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeParameterName, [symName.Value]) |] syncRoot.EnterReadLock() try SymbolResolution.ResolveType (processUDT, processTP) qsType finally syncRoot.ExitReadLock() - /// Resolves the underlying type as well as all named and unnamed items for the given type declaration in the specified source file. - /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is consistent with the defined types. - /// May throw an exception if the given parent and/or source file is inconsistent with the defined types. - /// Throws an ArgumentException if the given type tuple is an empty QsTuple. - member private this.ResolveTypeDeclaration (fullName : QsQualifiedName, source) typeTuple = + /// Resolves the underlying type as well as all named and unnamed items for the given type declaration in the specified source file. + /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is consistent with the defined types. + /// May throw an exception if the given parent and/or source file is inconsistent with the defined types. + /// Throws an ArgumentException if the given type tuple is an empty QsTuple. + member private this.ResolveTypeDeclaration (fullName : QsQualifiedName, source) typeTuple = let resolveType = this.ResolveType (fullName, ImmutableArray<_>.Empty, source) // currently type parameters for udts are not supported SymbolResolution.ResolveTypeDeclaration resolveType typeTuple @@ -891,12 +891,12 @@ and NamespaceManager /// fully resolves all Q# types in the signature using ResolveType. /// Returns a new signature with the resolved types, the resolved argument tuple, as well as the array of diagnostics created during type resolution. /// The position offset information for the variables declared in the argument tuple will be set to Null. - /// Positional information within types is set to Null if the parent callable is a type constructor. - /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is consistent with the defined callables. - /// May throw an exception if the given parent and/or source file is inconsistent with the defined callables. + /// Positional information within types is set to Null if the parent callable is a type constructor. + /// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is consistent with the defined callables. + /// May throw an exception if the given parent and/or source file is inconsistent with the defined callables. /// Throws an ArgumentException if the given list of characteristics is empty. member private this.ResolveCallableSignature (parentKind, parentName : QsQualifiedName, source) (signature : CallableSignature, specBundleCharacteristics) = - let resolveType tpNames t = + let resolveType tpNames t = let res, errs = this.ResolveType (parentName, tpNames, source) t if parentKind <> TypeConstructor then res, errs else res.WithoutRangeInfo, errs // strip positional info for auto-generated type constructors @@ -905,10 +905,10 @@ and NamespaceManager /// Sets the Resolved property for all type and callable declarations to Null, and the ResolvedAttributes to an empty array. /// Unless the clearing is forced, does nothing if the symbols are not currently resolved. - member private this.ClearResolutions ?force = + member private this.ClearResolutions ?force = let force = defaultArg force false if this.ContainsResolutions || force then - for ns in Namespaces.Values do + for ns in Namespaces.Values do for kvPair in ns.TypesDefinedInAllSources() do ns.SetTypeResolution (fst kvPair.Value) (kvPair.Key, Null, ImmutableArray.Empty) for kvPair in ns.CallablesDefinedInAllSources() do @@ -918,41 +918,41 @@ and NamespaceManager /// Resolves and caches the attached attributes and underlying type of the types declared in all source files of each namespace. /// Returns the diagnostics generated upon resolution as well as the root position and file for each diagnostic as tuple. - member private this.CacheTypeResolution () = + member private this.CacheTypeResolution () = let sortedNamespaces = Namespaces.Values |> Seq.sortBy (fun ns -> ns.Name.Value) |> Seq.toList // Since attributes are declared as types, we first need to resolve all types ... let resolutionDiagnostics = sortedNamespaces |> Seq.collect (fun ns -> ns.TypesDefinedInAllSources() |> Seq.collect (fun kvPair -> let tName, (source, qsType) = kvPair.Key, kvPair.Value let fullName = {Namespace = ns.Name; Name = tName} - let resolved, msgs = qsType.Defined |> this.ResolveTypeDeclaration (fullName, source) - ns.SetTypeResolution source (tName, resolved |> Value, ImmutableArray.Empty) + let resolved, msgs = qsType.Defined |> this.ResolveTypeDeclaration (fullName, source) + ns.SetTypeResolution source (tName, resolved |> Value, ImmutableArray.Empty) msgs |> Array.map (fun msg -> source, (qsType.Position, msg)))) - // ... before we can resolve the corresponding attributes. + // ... before we can resolve the corresponding attributes. let attributeDiagnostics = sortedNamespaces |> Seq.collect (fun ns -> ns.TypesDefinedInAllSources() |> Seq.collect (fun kvPair -> let tName, (source, qsType) = kvPair.Key, kvPair.Value let parentName = {Namespace = ns.Name; Name = tName} - let resolvedAttributes, msgs = this.ResolveAttributes (parentName, source) qsType - ns.SetTypeResolution source (tName, qsType.Resolved, resolvedAttributes) + let resolvedAttributes, msgs = this.ResolveAttributes (parentName, source) qsType + ns.SetTypeResolution source (tName, qsType.Resolved, resolvedAttributes) msgs |> Array.map (fun msg -> source, msg))) resolutionDiagnostics.Concat(attributeDiagnostics).ToArray() - /// Resolves and caches all attached attributes and specialization generation directives for all callables - /// declared in all source files of each namespace, inserting inferred specializations if necessary and removing invalid specializations. - /// Then resolves and caches the signature of the callables themselves. + /// Resolves and caches all attached attributes and specialization generation directives for all callables + /// declared in all source files of each namespace, inserting inferred specializations if necessary and removing invalid specializations. + /// Then resolves and caches the signature of the callables themselves. /// Returns the diagnostics generated upon resolution as well as the root position and file for each diagnostic as tuple. /// IMPORTANT: does *not* return diagnostics generated for type constructors - suitable diagnostics need to be generated upon type resolution. - /// Throws an InvalidOperationException if the types corresponding to the attributes to resolve have not been resolved. - member private this.CacheCallableResolutions () = + /// Throws an InvalidOperationException if the types corresponding to the attributes to resolve have not been resolved. + member private this.CacheCallableResolutions () = // TODO: this needs to be adapted if we support external specializations - let diagnostics = Namespaces.Values |> Seq.sortBy (fun ns -> ns.Name.Value) |> Seq.collect (fun ns -> + let diagnostics = Namespaces.Values |> Seq.sortBy (fun ns -> ns.Name.Value) |> Seq.collect (fun ns -> ns.CallablesDefinedInAllSources() |> Seq.sortBy (fun kv -> kv.Key) |> Seq.collect (fun kvPair -> let source, (kind, signature) = kvPair.Value let parent = {Namespace = ns.Name; Name = kvPair.Key} // we first need to resolve the type arguments to determine the right sets of specializations to consider - let typeArgsResolution specSource = + let typeArgsResolution specSource = let typeResolution = this.ResolveType (parent, ImmutableArray.Empty, specSource) // do not allow using type parameters within type specializations! SymbolResolution.ResolveTypeArgument typeResolution let mutable errs = ns.SetSpecializationResolutions (parent.Name, typeArgsResolution, fun _ _ -> ImmutableArray.Empty, [||]) @@ -966,16 +966,16 @@ and NamespaceManager // we remove the specializations which could not be bundled and resolve the newly inserted ones for (specSource, (errPos, d)) in bundleErrs do - match d.Diagnostic with + match d.Diagnostic with | Information _ | Warning _ -> () - | Error errCode -> + | Error errCode -> let removed = ns.RemoveSpecialization (specSource, {Offset = errPos; Range = d.Range}) parent.Name QsCompilerError.Verify ((removed <= 1), sprintf "removed %i specializations based on error code %s" removed (errCode.ToString())) let autoResErrs = ns.SetSpecializationResolutions (parent.Name, typeArgsResolution, fun _ _ -> ImmutableArray.Empty, [||]) // only then can we resolve the generators themselves, as well as the callable and specialization attributes let callableAttributes, attrErrs = this.ResolveAttributes (parent, source) signature - let resolution _ = SymbolResolution.ResolveGenerator props + let resolution _ = SymbolResolution.ResolveGenerator props let specErrs = ns.SetSpecializationResolutions (parent.Name, resolution, fun attSource -> this.ResolveAttributes (parent, attSource)) // and finally we resolve the overall signature (whose characteristics are the intersection of the one of all bundles) @@ -994,28 +994,28 @@ and NamespaceManager member this.VersionNumber = versionNumber /// set to true if all types have been fully resolved and false otherwise - member this.ContainsResolutions + member this.ContainsResolutions with get() = containsResolutions and private set value = containsResolutions <- value /// For each given namespace, automatically adds an open directive to all partial namespaces - /// in all source files, if a namespace with that name indeed exists and is part of this compilation. - /// Independent on whether the symbols have already been resolved, proceeds to resolves - /// all types and callables as well as their attributes defined throughout all namespaces and caches the resolution. - /// Returns the diagnostics generated during resolution + /// in all source files, if a namespace with that name indeed exists and is part of this compilation. + /// Independent on whether the symbols have already been resolved, proceeds to resolves + /// all types and callables as well as their attributes defined throughout all namespaces and caches the resolution. + /// Returns the diagnostics generated during resolution /// together with the Position of the declaration for which the diagnostics were generated. - member this.ResolveAll (autoOpen : ImmutableHashSet<_>) = + member this.ResolveAll (autoOpen : ImmutableHashSet<_>) = // TODO: this needs to be adapted if we support external specializations syncRoot.EnterWriteLock() versionNumber <- versionNumber + 1 try let autoOpen = if autoOpen <> null then autoOpen else ImmutableHashSet.Empty - let nsToAutoOpen = autoOpen.Intersect Namespaces.Keys + let nsToAutoOpen = autoOpen.Intersect Namespaces.Keys let zeroRange = (QsPositionInfo.Zero, QsPositionInfo.Zero) - for ns in Namespaces.Values do - for source in ns.Sources do + for ns in Namespaces.Values do + for source in ns.Sources do for opened in nsToAutoOpen do this.AddOpenDirective (opened, zeroRange) (null, Value zeroRange) (ns.Name, source) |> ignore - // We need to resolve types before we resolve callables, + // We need to resolve types before we resolve callables, // since the attribute resolution for callables relies on the corresponding types having been resolved. let typeDiagnostics = this.CacheTypeResolution() let callableDiagnostics = this.CacheCallableResolutions() @@ -1023,44 +1023,44 @@ and NamespaceManager callableDiagnostics.Concat(typeDiagnostics).ToLookup(fst, snd) finally syncRoot.ExitWriteLock() - /// Returns a dictionary that maps each namespace name to a look-up + /// Returns a dictionary that maps each namespace name to a look-up /// that for each source file name contains the names of all imported namespaces in that file and namespace. - member this.Documentation () = + member this.Documentation () = syncRoot.EnterReadLock() try let docs = Namespaces.Values |> Seq.map (fun ns -> ns.Name, ns.Documentation) docs.ToImmutableDictionary(fst, snd) finally syncRoot.ExitReadLock() /// Returns a look-up that contains the names of all namespaces imported within a certain source file for the given namespace. - /// Throws an ArgumentException if no namespace with the given name exists. - member this.OpenDirectives nsName = + /// Throws an ArgumentException if no namespace with the given name exists. + member this.OpenDirectives nsName = syncRoot.EnterReadLock() - try match Namespaces.TryGetValue nsName with - | true, ns -> - let imported = ns.Sources |> Seq.collect (fun source -> - ns.ImportedNamespaces source |> Seq.choose (fun imported -> + try match Namespaces.TryGetValue nsName with + | true, ns -> + let imported = ns.Sources |> Seq.collect (fun source -> + ns.ImportedNamespaces source |> Seq.choose (fun imported -> if imported.Key <> ns.Name then Some (source, new ValueTuple<_,_>(imported.Key, imported.Value)) else None)) imported.ToLookup(fst, snd) | false, _ -> ArgumentException "no namespace with the given name exists" |> raise finally syncRoot.ExitReadLock() - /// Returns the headers of all imported specializations for callable with the given name. + /// Returns the headers of all imported specializations for callable with the given name. /// Throws an ArgumentException if no namespace or no callable with the given name exists. - member this.ImportedSpecializations (parent : QsQualifiedName) = + member this.ImportedSpecializations (parent : QsQualifiedName) = // TODO: this may need to be adapted if we support external specializations syncRoot.EnterReadLock() - try let imported = Namespaces.TryGetValue parent.Namespace |> function + try let imported = Namespaces.TryGetValue parent.Namespace |> function | false, _ -> ArgumentException "no namespace with the given name exists" |> raise | true, ns -> ns.SpecializationsInReferencedAssemblies.[parent.Name].ToImmutableArray() - if imported.Length <> 0 then imported - else ArgumentException "no specializations for a callable with the given name have been imported" |> raise + if imported.Length <> 0 then imported + else ArgumentException "no specializations for a callable with the given name have been imported" |> raise finally syncRoot.ExitReadLock() - /// Returns the resolved generation directive (if any) as well as the specialization headers + /// Returns the resolved generation directive (if any) as well as the specialization headers /// for all specializations defined in source files for the callable with the given name. /// Throws an ArgumentException if no namespace or no callable with the given name exists. - /// Throws an InvalidOperationException if the symbols are not currently resolved. - member this.DefinedSpecializations (parent : QsQualifiedName) = + /// Throws an InvalidOperationException if the symbols are not currently resolved. + member this.DefinedSpecializations (parent : QsQualifiedName) = let notResolvedException = InvalidOperationException "specializations are not resolved" syncRoot.EnterReadLock() try if not this.ContainsResolutions then notResolvedException |> raise @@ -1084,7 +1084,7 @@ and NamespaceManager finally syncRoot.ExitReadLock() /// Returns the source file and CallableDeclarationHeader of all callables imported from referenced assemblies. - member this.ImportedCallables () = + member this.ImportedCallables () = // TODO: this needs to be adapted if we support external specializations syncRoot.EnterReadLock() try let imported = Namespaces.Values |> Seq.collect (fun ns -> ns.CallablesInReferencedAssemblies.Values) @@ -1092,12 +1092,12 @@ and NamespaceManager finally syncRoot.ExitReadLock() /// Returns the declaration headers for all callables defined in source files. - /// Throws an InvalidOperationException if the symbols are not currently resolved. - member this.DefinedCallables () = + /// Throws an InvalidOperationException if the symbols are not currently resolved. + member this.DefinedCallables () = let notResolvedException = InvalidOperationException "callables are not resolved" syncRoot.EnterReadLock() try if not this.ContainsResolutions then notResolvedException |> raise - let defined = Namespaces.Values |> Seq.collect (fun ns -> + let defined = Namespaces.Values |> Seq.collect (fun ns -> ns.CallablesDefinedInAllSources() |> Seq.choose (fun kvPair -> let cName, (source, (kind, declaration)) = kvPair.Key, kvPair.Value match declaration.Resolved with @@ -1117,19 +1117,19 @@ and NamespaceManager finally syncRoot.ExitReadLock() /// Returns the source file and TypeDeclarationHeader of all types imported from referenced assemblies. - member this.ImportedTypes() = + member this.ImportedTypes() = syncRoot.EnterReadLock() try let imported = Namespaces.Values |> Seq.collect (fun ns -> ns.TypesInReferencedAssemblies.Values) imported.ToImmutableArray() finally syncRoot.ExitReadLock() /// Returns the declaration headers for all types defined in source files. - /// Throws an InvalidOperationException if the symbols are not currently resolved. - member this.DefinedTypes () = + /// Throws an InvalidOperationException if the symbols are not currently resolved. + member this.DefinedTypes () = let notResolvedException = InvalidOperationException "types are not resolved" syncRoot.EnterReadLock() try if not this.ContainsResolutions then notResolvedException |> raise - let defined = Namespaces.Values |> Seq.collect (fun ns -> + let defined = Namespaces.Values |> Seq.collect (fun ns -> ns.TypesDefinedInAllSources() |> Seq.choose (fun kvPair -> let tName, (source, qsType) = kvPair.Key, kvPair.Value match qsType.Resolved with @@ -1139,7 +1139,7 @@ and NamespaceManager Attributes = qsType.ResolvedAttributes SourceFile = source Position = DeclarationHeader.Offset.Defined qsType.Position - SymbolRange = DeclarationHeader.Range.Defined qsType.Range + SymbolRange = DeclarationHeader.Range.Defined qsType.Range Type = underlyingType TypeItems = items Documentation = qsType.Documentation @@ -1147,7 +1147,7 @@ and NamespaceManager defined.ToImmutableArray() finally syncRoot.ExitReadLock() - /// removes the given source file and all its content from all namespaces + /// removes the given source file and all its content from all namespaces member this.RemoveSource source = syncRoot.EnterWriteLock() versionNumber <- versionNumber + 1 @@ -1155,34 +1155,34 @@ and NamespaceManager let keys = Namespaces.Keys |> List.ofSeq for key in keys do if Namespaces.[key].IsEmpty then Namespaces.Remove key |> ignore this.ClearResolutions() - finally syncRoot.ExitWriteLock() + finally syncRoot.ExitWriteLock() /// clears all content from the symbol table - member this.Clear() = + member this.Clear() = syncRoot.EnterWriteLock() versionNumber <- versionNumber + 1 try this.ContainsResolutions <- true Namespaces.Clear() - finally syncRoot.ExitWriteLock() - + finally syncRoot.ExitWriteLock() + /// Adds every namespace along with all its content to target. - /// IMPORTANT: if a namespace already exists in the target, replaces it! + /// IMPORTANT: if a namespace already exists in the target, replaces it! member this.CopyTo (target : NamespaceManager) = syncRoot.EnterReadLock() try for ns in Namespaces.Values do target.AddOrReplaceNamespace ns // ns will be deep copied finally syncRoot.ExitReadLock() - /// If a namespace with the given name exists, - /// makes a (deep) copy of that namespace and - if the given source file is not already listed as source for that namespace - + /// If a namespace with the given name exists, + /// makes a (deep) copy of that namespace and - if the given source file is not already listed as source for that namespace - /// adds the given source to the list of sources for the made copy, before returning the copy. /// If no namespace with the given name exists, returns a new Namespace with the given source file listed as source. - /// NOTE: This routine does *not* modify this symbol table, - /// and any modification to the returned namespace won't be reflected here - + /// NOTE: This routine does *not* modify this symbol table, + /// and any modification to the returned namespace won't be reflected here - /// use AddOrReplaceNamespace to push back the modifications into the symbol table. member this.CopyForExtension (nsName, source) = syncRoot.EnterReadLock() - try match Namespaces.TryGetValue nsName with + try match Namespaces.TryGetValue nsName with | true, NS -> let copy = NS.Copy() if copy.TryAddSource source then copy @@ -1191,7 +1191,7 @@ and NamespaceManager finally syncRoot.ExitReadLock() /// Given a Namespace, makes a (deep) copy of that Namespace and replaces the existing namespace with that name - /// by that copy, if such a namespace already exists, or adds the copy as a new namespace. + /// by that copy, if such a namespace already exists, or adds the copy as a new namespace. /// -> Any modification to the namespace after pushing it into the symbol table (i.e. calling this routine) won't be reflected here. member this.AddOrReplaceNamespace (ns : Namespace) = syncRoot.EnterWriteLock() @@ -1200,32 +1200,32 @@ and NamespaceManager this.ClearResolutions true // force the clearing, since otherwise the newly added namespace may not be cleared finally syncRoot.ExitWriteLock() - /// Adds the opened namespace to the list of imported namespaces for the given source and namespace. - /// If the namespace to list as imported does not exists, or if the given alias cannot be used as namespace short name, - /// adds the corresponding diagnostics to an array of diagnostics and returns them. - /// Returns an empty array otherwise. + /// Adds the opened namespace to the list of imported namespaces for the given source and namespace. + /// If the namespace to list as imported does not exists, or if the given alias cannot be used as namespace short name, + /// adds the corresponding diagnostics to an array of diagnostics and returns them. + /// Returns an empty array otherwise. /// Throws an Argument exception if the given namespace or source file for which to add the open directive does not exist. - member this.AddOpenDirective (opened, openedRange) (alias, aliasRange) (nsName, source) = + member this.AddOpenDirective (opened, openedRange) (alias, aliasRange) (nsName, source) = syncRoot.EnterWriteLock() versionNumber <- versionNumber + 1 try this.ClearResolutions() - match Namespaces.TryGetValue nsName with - | true, ns when ns.Sources.Contains source -> - let validAlias = String.IsNullOrWhiteSpace alias || NonNullable.New (alias.Trim()) |> Namespaces.ContainsKey |> not + match Namespaces.TryGetValue nsName with + | true, ns when ns.Sources.Contains source -> + let validAlias = String.IsNullOrWhiteSpace alias || NonNullable.New (alias.Trim()) |> Namespaces.ContainsKey |> not if validAlias && Namespaces.ContainsKey opened then ns.TryAddOpenDirective source (opened, openedRange) (alias, aliasRange.ValueOr openedRange) elif validAlias then [| openedRange |> QsCompilerDiagnostic.Error (ErrorCode.UnknownNamespace, [opened.Value]) |] else [| aliasRange.ValueOr openedRange |> QsCompilerDiagnostic.Error (ErrorCode.InvalidNamespaceAliasName, [alias]) |] | true, _ -> ArgumentException "given source file is not listed as source of the given namespace" |> raise - | false, _ -> ArgumentException "no such namespace exists" |> raise + | false, _ -> ArgumentException "no such namespace exists" |> raise finally syncRoot.ExitWriteLock() - /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader as Value, - /// if the qualifier can be resolved within the given parent namespace and source file, and such a callable indeed exists. - /// If the callable is not defined an any of the references and the source file containing the callable declaration is specified (i.e. declSource is Some), - /// throws the corresponding exception if no such callable exists in that file. + /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader as Value, + /// if the qualifier can be resolved within the given parent namespace and source file, and such a callable indeed exists. + /// If the callable is not defined an any of the references and the source file containing the callable declaration is specified (i.e. declSource is Some), + /// throws the corresponding exception if no such callable exists in that file. /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. member private this.TryGetCallableHeader (callableName : QsQualifiedName, declSource) (nsName, source) = - let BuildHeader fullName (source, kind, declaration : Resolution<_,_>) = + let BuildHeader fullName (source, kind, declaration : Resolution<_,_>) = let fallback () = (declaration.Defined, [CallableInformation.Invalid]) |> this.ResolveCallableSignature (kind, callableName, source) |> fst let resolvedSignature, argTuple = declaration.Resolved.ValueOrApply fallback Value { @@ -1234,7 +1234,7 @@ and NamespaceManager Attributes = declaration.ResolvedAttributes SourceFile = source Position = DeclarationHeader.Offset.Defined declaration.Position - SymbolRange = DeclarationHeader.Range.Defined declaration.Range + SymbolRange = DeclarationHeader.Range.Defined declaration.Range Signature = resolvedSignature ArgumentTuple = argTuple Documentation = declaration.Documentation @@ -1245,39 +1245,39 @@ and NamespaceManager | Some ns -> ns.CallablesInReferencedAssemblies.TryGetValue callableName.Name |> function | true, cDecl -> Value cDecl | false, _ -> declSource |> function - | Some source -> + | Some source -> let kind, decl = ns.CallableInSource source callableName.Name // ok only because/if we have covered that the callable is not in a reference! BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) - | None -> + | None -> ns.CallablesDefinedInAllSources().TryGetValue callableName.Name |> function | true, (source, (kind, decl)) -> BuildHeader {callableName with Namespace = ns.Name} (source, kind, decl) | false, _ -> Null finally syncRoot.ExitReadLock() - /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader as Value, - /// if the qualifier can be resolved within the given parent namespace and source file, and such a callable indeed exists. + /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader as Value, + /// if the qualifier can be resolved within the given parent namespace and source file, and such a callable indeed exists. /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. member this.TryGetCallable (callableName : QsQualifiedName) (nsName, source) = this.TryGetCallableHeader (callableName, None) (nsName, source) - /// If the given callable name can be uniquely resolved within the given namespace and source file, + /// If the given callable name can be uniquely resolved within the given namespace and source file, /// returns the CallableDeclarationHeader with the information on that callable as Value. - /// Returns Null as well as a list with namespaces containing a callable with that name if this is not the case. - member this.TryResolveAndGetCallable cName (nsName, source) = + /// Returns Null as well as a list with namespaces containing a callable with that name if this is not the case. + member this.TryResolveAndGetCallable cName (nsName, source) = syncRoot.EnterReadLock() - try match (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsCallable cName) with + try match (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsCallable cName) with | [(declNS, (declSource, _))] -> this.TryGetCallableHeader ({Namespace = declNS; Name = cName}, Some declSource) (nsName, source) |> function | Null -> QsCompilerError.Raise "failed to get the callable information about a resolved callable"; Null, Seq.empty | info -> info, seq {yield declNS} | resolutions -> Null, resolutions.Select fst finally syncRoot.ExitReadLock() - /// Given a qualified type name, returns the corresponding TypeDeclarationHeader as Value, - /// if the qualifier can be resolved within the given parent namespace and source file, and such a type indeed exists. - /// If the type is not defined an any of the references and the source file containing the type declaration is specified (i.e. declSource is Some), - /// throws the corresponding exception if no such type exists in that file. + /// Given a qualified type name, returns the corresponding TypeDeclarationHeader as Value, + /// if the qualifier can be resolved within the given parent namespace and source file, and such a type indeed exists. + /// If the type is not defined an any of the references and the source file containing the type declaration is specified (i.e. declSource is Some), + /// throws the corresponding exception if no such type exists in that file. /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. member private this.TryGetTypeHeader (typeName : QsQualifiedName, declSource) (nsName, source) = - let BuildHeader fullName (source, declaration) = + let BuildHeader fullName (source, declaration) = let fallback () = declaration.Defined |> this.ResolveTypeDeclaration (typeName, source) |> fst let underlyingType, items = declaration.Resolved.ValueOrApply fallback Value { @@ -1285,13 +1285,13 @@ and NamespaceManager Attributes = declaration.ResolvedAttributes SourceFile = source Position = DeclarationHeader.Offset.Defined declaration.Position - SymbolRange = DeclarationHeader.Range.Defined declaration.Range + SymbolRange = DeclarationHeader.Range.Defined declaration.Range Type = underlyingType TypeItems = items Documentation = declaration.Documentation } syncRoot.EnterReadLock() - try match (nsName, source) |> TryResolveQualifier typeName.Namespace with + try match (nsName, source) |> TryResolveQualifier typeName.Namespace with | None -> Null | Some ns -> ns.TypesInReferencedAssemblies.TryGetValue typeName.Name |> function | true, tDecl -> Value tDecl @@ -1299,23 +1299,23 @@ and NamespaceManager | Some source -> let decl = ns.TypeInSource source typeName.Name // ok only because/if we have covered that the type is not in a reference! BuildHeader {typeName with Namespace = ns.Name} (source, decl) - | None -> + | None -> ns.TypesDefinedInAllSources().TryGetValue typeName.Name |> function | true, (source, decl) -> BuildHeader {typeName with Namespace = ns.Name} (source, decl) | false, _ -> Null finally syncRoot.ExitReadLock() - /// Given a qualified type name, returns the corresponding TypeDeclarationHeader as Value, - /// if the qualifier can be resolved within the given parent namespace and source file, and such a type indeed exists. + /// Given a qualified type name, returns the corresponding TypeDeclarationHeader as Value, + /// if the qualifier can be resolved within the given parent namespace and source file, and such a type indeed exists. /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent namespace does not exist. member this.TryGetType (typeName : QsQualifiedName) (nsName, source) = this.TryGetTypeHeader (typeName, None) (nsName, source) - /// If the given type name can be uniquely resolved within the given namespace and source file, + /// If the given type name can be uniquely resolved within the given namespace and source file, /// returns the TypeDeclarationHeader with the information on that type as Value. - /// Returns Null as well as a list with namespaces containing a type with that name if this is not the case. - member this.TryResolveAndGetType tName (nsName, source) = + /// Returns Null as well as a list with namespaces containing a type with that name if this is not the case. + member this.TryResolveAndGetType tName (nsName, source) = syncRoot.EnterReadLock() - try match (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsType tName) with + try match (nsName, source) |> PossibleResolutions (fun ns -> ns.ContainsType tName) with | [(declNS, (declSource, _))] -> this.TryGetTypeHeader ({Namespace = declNS; Name = tName}, Some declSource) (nsName, source) |> function | Null -> QsCompilerError.Raise "failed to get the type information about a resolved type"; Null, Seq.empty | info -> info, seq {yield declNS} @@ -1323,7 +1323,7 @@ and NamespaceManager finally syncRoot.ExitReadLock() - /// Returns the fully qualified namespace name of the given namespace alias (short name). If the alias is already a fully qualified name, + /// Returns the fully qualified namespace name of the given namespace alias (short name). If the alias is already a fully qualified name, /// returns the name unchanged. Returns null if no such name exists within the given parent namespace and source file. /// Throws an ArgumentException if the given parent namespace does not exist. member this.TryResolveNamespaceAlias alias (nsName, source) = @@ -1333,16 +1333,16 @@ and NamespaceManager | Some ns -> ns.Name.Value finally syncRoot.ExitReadLock() - /// Returns the names of all namespaces in which a callable with the given name is declared. - member this.NamespacesContainingCallable cName = + /// Returns the names of all namespaces in which a callable with the given name is declared. + member this.NamespacesContainingCallable cName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! syncRoot.EnterReadLock() try let containsCallable (ns : Namespace) = ns.ContainsCallable cName |> QsNullable<_>.Map (fun _ -> ns.Name) (Namespaces.Values |> QsNullable<_>.Choose containsCallable).ToImmutableArray() finally syncRoot.ExitReadLock() - /// Returns the names of all namespaces in which a type with the given name is declared. - member this.NamespacesContainingType tName = + /// Returns the names of all namespaces in which a type with the given name is declared. + member this.NamespacesContainingType tName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! syncRoot.EnterReadLock() try let containsType (ns : Namespace) = ns.ContainsType tName |> QsNullable<_>.Map (fun _ -> ns.Name) @@ -1383,60 +1383,60 @@ and NamespaceManager /// All entries in the source file have to be fully resolved beforehand. /// That hash does not contain any information about the imported namespaces, positional information, or about any documentation. /// Returns the generated hash as well as a separate hash providing information about the imported namespaces. - /// Throws an InvalidOperationException if the given source file contains unresolved entries. - member this.HeaderHash source = + /// Throws an InvalidOperationException if the given source file contains unresolved entries. + member this.HeaderHash source = let invalidOperationEx = InvalidOperationException "everything needs to be resolved before constructing the HeaderString" if not this.ContainsResolutions then invalidOperationEx |> raise - let inconsistentStateException () = + let inconsistentStateException () = QsCompilerError.Raise "contains unresolved entries despite supposedly being resolved" invalidOperationEx |> raise - let attributesHash (attributes : QsDeclarationAttribute seq) = + let attributesHash (attributes : QsDeclarationAttribute seq) = let getHash arg (id : UserDefinedType) = hash (id.Namespace.Value, id.Name.Value, NamespaceManager.ExpressionHash arg) attributes |> QsNullable<_>.Choose (fun att -> att.TypeId |> QsNullable<_>.Map (getHash att.Argument)) |> Seq.toList let callableHash (kind, (signature,_), specs, attributes : QsDeclarationAttribute seq) = - let signatureHash (signature : ResolvedSignature) = + let signatureHash (signature : ResolvedSignature) = let argStr = signature.ArgumentType |> NamespaceManager.TypeHash let reStr = signature.ReturnType |> NamespaceManager.TypeHash let nameOrInvalid = function | InvalidName -> InvalidName |> JsonConvert.SerializeObject | ValidName sym -> sym.Value let typeParams = signature.TypeParameters |> Seq.map nameOrInvalid |> Seq.toList hash (argStr, reStr, typeParams) let specsStr = - let genHash (gen : ResolvedGenerator) = + let genHash (gen : ResolvedGenerator) = let tArgs = gen.TypeArguments |> QsNullable<_>.Map (fun tArgs -> tArgs |> Seq.map NamespaceManager.TypeHash |> Seq.toList) hash (gen.Directive, hash tArgs) let kinds, gens = specs |> Seq.sort |> Seq.toList |> List.unzip hash (kinds, gens |> List.map genHash) hash (kind, specsStr, signatureHash signature, attributes |> attributesHash) - let typeHash (t, typeItems : QsTuple, attributes) = + let typeHash (t, typeItems : QsTuple, attributes) = let getItemHash (itemName, itemType) = hash (itemName, NamespaceManager.TypeHash itemType) let namedItems = typeItems.Items |> Seq.choose (function | Named item -> Some item | _ -> None) let itemHashes = namedItems.Select (fun d -> d.VariableName, d.Type) |> Seq.map getItemHash hash (NamespaceManager.TypeHash t, itemHashes |> Seq.toList, attributes |> attributesHash) syncRoot.EnterReadLock() - try let relevantNamespaces = - Namespaces.Values - |> Seq.filter (fun ns -> ns.Sources.Contains source) + try let relevantNamespaces = + Namespaces.Values + |> Seq.filter (fun ns -> ns.Sources.Contains source) |> Seq.sortBy (fun ns -> ns.Name) |> Seq.toList - let callables = relevantNamespaces |> Seq.collect (fun ns -> - let inSource = ns.CallablesDefinedInSource source |> Seq.sortBy (fun (cName, _) -> cName.Value) - inSource |> Seq.map (fun (cName, (kind, signature)) -> - let specs = ns.SpecializationsDefinedInAllSources cName |> Seq.map (fun (kind, (_, resolution)) -> + let callables = relevantNamespaces |> Seq.collect (fun ns -> + let inSource = ns.CallablesDefinedInSource source |> Seq.sortBy (fun (cName, _) -> cName.Value) + inSource |> Seq.map (fun (cName, (kind, signature)) -> + let specs = ns.SpecializationsDefinedInAllSources cName |> Seq.map (fun (kind, (_, resolution)) -> kind, resolution.Resolved.ValueOrApply inconsistentStateException) let resolved = signature.Resolved.ValueOrApply inconsistentStateException ns.Name.Value, cName.Value, (kind, resolved, specs, signature.ResolvedAttributes))) - let types = relevantNamespaces |> Seq.collect (fun ns -> + let types = relevantNamespaces |> Seq.collect (fun ns -> let inSources = ns.TypesDefinedInSource source |> Seq.sortBy (fun (tName,_) -> tName.Value) inSources |> Seq.map (fun (tName, qsType) -> let resolved, resItems = qsType.Resolved.ValueOrApply inconsistentStateException ns.Name.Value, tName.Value, (resolved, resItems, qsType.ResolvedAttributes))) - let imports = relevantNamespaces |> Seq.collect (fun ns -> - ns.ImportedNamespaces source |> Seq.sortBy (fun x -> x.Value) |> Seq.map (fun opened -> ns.Name.Value, opened.Value)) + let imports = relevantNamespaces |> Seq.collect (fun ns -> + ns.ImportedNamespaces source |> Seq.sortBy (fun x -> x.Value) |> Seq.map (fun opened -> ns.Name.Value, opened.Value)) let callablesHash = callables |> Seq.map (fun (ns, name, c) -> (ns, name, callableHash c)) |> Seq.toList |> hash let typesHash = types |> Seq.map (fun (ns, name, t) -> ns, name, typeHash t) |> Seq.toList |> hash let importsHash = imports |> Seq.toList |> hash hash (callablesHash, typesHash), importsHash - finally syncRoot.ExitReadLock() + finally syncRoot.ExitReadLock() diff --git a/src/QsCompiler/Core/SyntaxGenerator.fs b/src/QsCompiler/Core/SyntaxGenerator.fs index d7dfe233d9..245cbfa417 100644 --- a/src/QsCompiler/Core/SyntaxGenerator.fs +++ b/src/QsCompiler/Core/SyntaxGenerator.fs @@ -16,29 +16,29 @@ open Microsoft.Quantum.QsCompiler.Transformations.Core // transformations used to strip range information for auto-generated syntax -type private StripPositionInfoFromType (parent : StripPositionInfo) = +type private StripPositionInfoFromType (parent : StripPositionInfo) = inherit TypeTransformation(parent) override this.OnRangeInformation _ = Null -and private StripPositionInfoFromExpression (parent : StripPositionInfo) = +and private StripPositionInfoFromExpression (parent : StripPositionInfo) = inherit ExpressionTransformation(parent) override this.OnRangeInformation _ = Null -and private StripPositionInfoFromStatement(parent : StripPositionInfo) = +and private StripPositionInfoFromStatement(parent : StripPositionInfo) = inherit StatementTransformation(parent) override this.OnLocation _ = Null -and private StripPositionInfoFromNamespace(parent : StripPositionInfo) = +and private StripPositionInfoFromNamespace(parent : StripPositionInfo) = inherit NamespaceTransformation(parent) override this.OnLocation _ = Null -and public StripPositionInfo private (_internal_) = +and public StripPositionInfo private (_internal_) = inherit SyntaxTreeTransformation() static let defaultInstance = new StripPositionInfo() new () as this = - StripPositionInfo("_internal_") then - this.Types <- new StripPositionInfoFromType(this) + StripPositionInfo("_internal_") then + this.Types <- new StripPositionInfoFromType(this) this.Expressions <- new StripPositionInfoFromExpression(this) this.Statements <- new StripPositionInfoFromStatement(this) this.Namespaces <- new StripPositionInfoFromNamespace(this) @@ -50,46 +50,46 @@ and public StripPositionInfo private (_internal_) = static member public Apply a = defaultInstance.Namespaces.OnNamespace a -module SyntaxGenerator = +module SyntaxGenerator = - /// Matches only if the string consists of a fully qualified name and nothing else. - let internal FullyQualifiedName = + /// Matches only if the string consists of a fully qualified name and nothing else. + let internal FullyQualifiedName = new Regex(@"^[\p{L}_][\p{L}\p{Nd}_]*(\.[\p{L}_][\p{L}\p{Nd}_]*)+$") - // literal expresssions + // literal expressions /// Builds an immutable typed expression of the given kind and type kind, - /// setting the quantum dependency to the given value and assuming no type parameter resolutions. - /// Sets the range information for the built expression to Null. - let private AutoGeneratedExpression kind exTypeKind qDep = + /// setting the quantum dependency to the given value and assuming no type parameter resolutions. + /// Sets the range information for the built expression to Null. + let private AutoGeneratedExpression kind exTypeKind qDep = let noInferredInfo = InferredExpressionInformation.New (false, quantumDep = qDep) TypedExpression.New (kind, ImmutableDictionary.Empty, exTypeKind |> ResolvedType.New, noInferredInfo, QsRangeInfo.Null) - /// Creates a typed expression that corresponds to a Unit value. - /// Sets the range information for the built expression to Null. - let UnitValue = + /// Creates a typed expression that corresponds to a Unit value. + /// Sets the range information for the built expression to Null. + let UnitValue = AutoGeneratedExpression UnitValue QsTypeKind.UnitType false - /// Creates a typed expression that corresponds to an Int literal with the given value. - /// Sets the range information for the built expression to Null. - let IntLiteral v = + /// Creates a typed expression that corresponds to an Int literal with the given value. + /// Sets the range information for the built expression to Null. + let IntLiteral v = AutoGeneratedExpression (IntLiteral v) QsTypeKind.Int false - /// Creates a typed expression that corresponds to a BigInt literal with the given value. - /// Sets the range information for the built expression to Null. - let BigIntLiteral (v : int) = + /// Creates a typed expression that corresponds to a BigInt literal with the given value. + /// Sets the range information for the built expression to Null. + let BigIntLiteral (v : int) = AutoGeneratedExpression (BigIntLiteral (bigint v)) QsTypeKind.BigInt false - /// Creates a typed expression that corresponds to a Double literal with the given value. - /// Sets the range information for the built expression to Null. - let DoubleLiteral v = + /// Creates a typed expression that corresponds to a Double literal with the given value. + /// Sets the range information for the built expression to Null. + let DoubleLiteral v = AutoGeneratedExpression (DoubleLiteral v) QsTypeKind.Double false - /// Creates a typed expression that corresponds to a Range literal with the given left hand side and right hand side. - /// Sets the range information for the built expression to Null. - /// Does *not* verify the given left and right hand side. - let RangeLiteral (lhs, rhs) = + /// Creates a typed expression that corresponds to a Range literal with the given left hand side and right hand side. + /// Sets the range information for the built expression to Null. + /// Does *not* verify the given left and right hand side. + let RangeLiteral (lhs, rhs) = AutoGeneratedExpression (RangeLiteral (lhs, rhs)) QsTypeKind.Range false @@ -97,81 +97,85 @@ module SyntaxGenerator = /// Creates a typed expression corresponding to a call to a non-type-parametrized callable. /// Does *not* verify whether the given lhs and rhs besides - /// throwing an ArgumentException if the type of the given lhs is valid but not a Function or Operation type. - let private CallNonGeneric (lhs : TypedExpression, rhs : TypedExpression) = - let kind = CallLikeExpression (lhs, rhs) + /// throwing an ArgumentException if the type of the given lhs is valid but not a Function or Operation type. + let private CallNonGeneric (lhs : TypedExpression, rhs : TypedExpression) = + let kind = CallLikeExpression (lhs, rhs) let quantumDep = lhs.InferredInformation.HasLocalQuantumDependency || rhs.InferredInformation.HasLocalQuantumDependency - match lhs.ResolvedType.Resolution with + match lhs.ResolvedType.Resolution with | QsTypeKind.InvalidType -> AutoGeneratedExpression kind InvalidType quantumDep - | QsTypeKind.Operation ((_, ot), _) + | QsTypeKind.Operation ((_, ot), _) | QsTypeKind.Function (_,ot) -> AutoGeneratedExpression kind ot.Resolution quantumDep | _ -> ArgumentException "given lhs is not callable" |> raise /// Given a typed expression of type range, - /// creates a typed expression that when executed will generate the reverse sequence for the given range. + /// creates a typed expression that when executed will generate the reverse sequence for the given range. /// Assumes that the RangeReverse function is part of the standard library. - /// Throws an ArgumentException if the given expression is of a valid type but not of type Range. - let private ReverseRange (ex : TypedExpression) = - let buildCallToReverse ex = - let kind = Identifier.GlobalCallable {Namespace = BuiltIn.RangeReverse.Namespace; Name = BuiltIn.RangeReverse.Name} + /// Throws an ArgumentException if the given expression is of a valid type but not of type Range. + let private ReverseRange (ex : TypedExpression) = + let buildCallToReverse ex = + let kind = Identifier.GlobalCallable BuiltIn.RangeReverse.FullName let exTypeKind = QsTypeKind.Function (QsTypeKind.Range |> ResolvedType.New, QsTypeKind.Range |> ResolvedType.New) let reverse = AutoGeneratedExpression (QsExpressionKind.Identifier (kind, Null)) exTypeKind false - CallNonGeneric (reverse, ex) - match ex.ResolvedType.Resolution with + CallNonGeneric (reverse, ex) + match ex.ResolvedType.Resolution with | QsTypeKind.InvalidType | QsTypeKind.Range _ -> buildCallToReverse ex | _ -> ArgumentException "given expression is not a range" |> raise /// Builds a range expression for the given lhs and rhs of the range operator. /// Does *not* verify the type of the given lhs or rhs. - let private RangeExpression (lhs : TypedExpression, rhs : TypedExpression) = + let private RangeExpression (lhs : TypedExpression, rhs : TypedExpression) = let kind = QsExpressionKind.RangeLiteral (lhs, rhs) let quantumDep = lhs.InferredInformation.HasLocalQuantumDependency || rhs.InferredInformation.HasLocalQuantumDependency AutoGeneratedExpression kind QsTypeKind.Range quantumDep - /// Creates a typed expression that corresponds to a call to the Length function. - /// The Length function needs to be part of the QsCore library, and its type parameter name needs to match the one here. - /// Throws an ArgumentException if the given expression is not of type array or of invalid type. - let private Length (ex : TypedExpression) = - let callableName = {Namespace = BuiltIn.Length.Namespace; Name = BuiltIn.Length.Name} + /// Creates a typed expression that corresponds to a call to the Length function. + /// The Length function needs to be part of the QsCore library, and its type parameter name needs to match the one here. + /// Throws an ArgumentException if the given expression is not of type array or of invalid type. + let private Length (ex : TypedExpression) = + let callableName = BuiltIn.Length.FullName let kind = Identifier.GlobalCallable callableName - let typeParameter = QsTypeParameter.New (callableName, BuiltIn.Length.TypeParameters.[0], Null) + let typeParameterName = + match BuiltIn.Length.Kind with + | BuiltInKind.Function typeParams -> typeParams.[0] + | _ -> ArgumentException "Length is expected to be a function" |> raise + let typeParameter = QsTypeParameter.New (callableName, typeParameterName, Null) let genArrayType = QsTypeKind.ArrayType (QsTypeKind.TypeParameter typeParameter |> ResolvedType.New) |> ResolvedType.New let exTypeKind = QsTypeKind.Function (genArrayType, QsTypeKind.Int |> ResolvedType.New) let length = AutoGeneratedExpression (QsExpressionKind.Identifier (kind, Null)) exTypeKind false - let callToLength tpRes = + let callToLength tpRes = let resolutions = (seq { yield (typeParameter.Origin, typeParameter.TypeName, tpRes) }).ToImmutableArray() {CallNonGeneric (length, ex) with TypeArguments = resolutions} - match ex.ResolvedType.Resolution with + match ex.ResolvedType.Resolution with | ArrayType b -> callToLength b | InvalidType -> callToLength ex.ResolvedType | _ -> ArgumentException "the given expression is not of array type" |> raise - - /// Creates a typed expression that corresponds to subracting one from the call to the Length function. - /// Sets any range information in the built expression to Null, including inner expressions. - /// Throws an ArgumentException if the given expression is not of type array or of invalid type. - let LengthMinusOne (ex : TypedExpression) = + + /// Creates a typed expression that corresponds to subtracting one from the call to the Length function. + /// Sets any range information in the built expression to Null, including inner expressions. + /// Throws an ArgumentException if the given expression is not of type array or of invalid type. + let LengthMinusOne (ex : TypedExpression) = let callToLength = ex |> StripPositionInfo.Apply |> Length let kind = QsExpressionKind.SUB (callToLength, IntLiteral 1L) AutoGeneratedExpression kind QsTypeKind.Int callToLength.InferredInformation.HasLocalQuantumDependency /// Given a typed expression of array type, /// creates a typed expression that when evaluated returns a new array with the order of the elements reversed. - /// Throws an ArgumentException if the given expression is not of type array or of invalid type. + /// Throws an ArgumentException if the given expression is not of type array or of invalid type. let private ReverseArray (ex : TypedExpression) = let built = let reversingRange = RangeExpression (RangeExpression (LengthMinusOne ex, IntLiteral -1L), IntLiteral 0L) let kind = ArrayItem (ex, reversingRange) AutoGeneratedExpression kind ex.ResolvedType.Resolution ex.InferredInformation.HasLocalQuantumDependency - match ex.ResolvedType.Resolution with - | ArrayType _ + match ex.ResolvedType.Resolution with + | ArrayType _ | InvalidType -> built | _ -> ArgumentException "the given expression is not of array type" |> raise /// Given a typed expression of a type that supports iteration, /// creates a typed expression that when evaluated returns the reversed sequence. - /// Throws an ArgumentException if the given expression is of a valid type but not either of type Range or of array type. - let ReverseIterable (ex : TypedExpression) = + /// Throws an ArgumentException if the given expression is of a valid type but not either of type Range or of array type. + let ReverseIterable (ex : TypedExpression) = let ex = StripPositionInfo.Apply ex match ex.ResolvedType.Resolution with | QsTypeKind.Range -> ReverseRange ex @@ -179,10 +183,10 @@ module SyntaxGenerator = | QsTypeKind.InvalidType -> ex | _ -> ArgumentException "the given expression is not iterable" |> raise - /// Returns a boolean expression that evaluates to true if the given expression is negative. - /// Returns an invalid expression of type Bool if the given expression is invalid. - /// Throws an ArgumentException if the type of the given expression does not support arithmetic. - let IsNegative (ex : TypedExpression) = + /// Returns a boolean expression that evaluates to true if the given expression is negative. + /// Returns an invalid expression of type Bool if the given expression is invalid. + /// Throws an ArgumentException if the type of the given expression does not support arithmetic. + let IsNegative (ex : TypedExpression) = let kind = ex.ResolvedType.Resolution |> function | Int -> LT (ex, IntLiteral 0L) | BigInt -> LT (ex, BigIntLiteral 0) @@ -197,107 +201,107 @@ module SyntaxGenerator = let private QubitArray = Qubit |> ResolvedType.New |> ArrayType |> ResolvedType.New /// Given a QsTuple, recursively extracts and returns all of its items. - let ExtractItems (this : QsTuple<_>) = + let ExtractItems (this : QsTuple<_>) = this.Items.ToImmutableArray() - /// Strips all range information from the given signature. - let WithoutRangeInfo (signature : ResolvedSignature) = + /// Strips all range information from the given signature. + let WithoutRangeInfo (signature : ResolvedSignature) = let argType = signature.ArgumentType |> StripPositionInfo.Apply let returnType = signature.ReturnType |> StripPositionInfo.Apply ResolvedSignature.New ((argType, returnType), signature.Information, signature.TypeParameters) - /// Given the resolved argument type of an operation, returns the argument type of its controlled version. - let AddControlQubits (argT : ResolvedType) = + /// Given the resolved argument type of an operation, returns the argument type of its controlled version. + let AddControlQubits (argT : ResolvedType) = [QubitArray; argT].ToImmutableArray() |> TupleType |> ResolvedType.New - /// Given a resolved signature, returns the corresponding signature for the controlled version. - let BuildControlled (this : ResolvedSignature) = + /// Given a resolved signature, returns the corresponding signature for the controlled version. + let BuildControlled (this : ResolvedSignature) = { this with ArgumentType = this.ArgumentType |> AddControlQubits } /// Given an argument tuple of a callable, the name and the range of the control qubits symbol, as well as the position offset for that range, - /// builds and returns the argument tuple for the controlled specialization. - /// Throws an ArgumentException if the given argument tuple is not a QsTuple. - let WithControlQubits arg offset (name, symRange : QsNullable<_>) = + /// builds and returns the argument tuple for the controlled specialization. + /// Throws an ArgumentException if the given argument tuple is not a QsTuple. + let WithControlQubits arg offset (name, symRange : QsNullable<_>) = let range = symRange.ValueOr QsCompilerDiagnostic.DefaultRange let ctlQs = LocalVariableDeclaration.New false ((offset, range), name, QubitArray, false) - let unitArg = + let unitArg = let argName = NonNullable<_>.New InternalUse.UnitArgument |> ValidName; let unitT = UnitType |> ResolvedType.New LocalVariableDeclaration.New false ((offset,range), argName, unitT, false) |> QsTupleItem // the range is not accurate here, but also irrelevant - match arg with + match arg with | QsTuple ts when ts.Length = 0 -> [ctlQs |> QsTupleItem; unitArg].ToImmutableArray() | QsTuple ts when ts.Length = 1 -> [ctlQs |> QsTupleItem; ts.[0]].ToImmutableArray() | QsTuple _ -> [ctlQs |> QsTupleItem; arg].ToImmutableArray() | _ -> ArgumentException "expecting the given argument tuple to be a QsTuple" |> raise |> QsTuple - /// Given a typed expression that is used as argument to an operation and a typed expression for the control qubits, + /// Given a typed expression that is used as argument to an operation and a typed expression for the control qubits, /// combines them to a suitable argument for the controlled version of the originally called operation under the assumption that the argument was correct. - /// The range information for the built expression is set to Null. + /// The range information for the built expression is set to Null. /// Throws an ArgumentException if the given expression for the control qubits is a valid type but not of type Qubit[]. - let ArgumentWithControlQubits (arg : TypedExpression) (ctlQs : TypedExpression) = + let ArgumentWithControlQubits (arg : TypedExpression) (ctlQs : TypedExpression) = let isInvalid = function | Tuple _ | Item _ | Missing -> false | _ -> true - if ctlQs.ResolvedType.Resolution <> QubitArray.Resolution && not (ctlQs.ResolvedType |> isInvalid) then + if ctlQs.ResolvedType.Resolution <> QubitArray.Resolution && not (ctlQs.ResolvedType |> isInvalid) then new ArgumentException "expression for the control qubits is valid but not of type Qubit[]" |> raise - let buildControlledArgument orig = + let buildControlledArgument orig = let kind = QsExpressionKind.ValueTuple ([ctlQs; orig].ToImmutableArray()) let quantumDep = orig.InferredInformation.HasLocalQuantumDependency || ctlQs.InferredInformation.HasLocalQuantumDependency let exInfo = InferredExpressionInformation.New (isMutable = false, quantumDep = quantumDep) TypedExpression.New (kind, orig.TypeParameterResolutions, AddControlQubits orig.ResolvedType, exInfo, QsRangeInfo.Null) buildControlledArgument arg - /// Returns the name of the control qubits + /// Returns the name of the control qubits /// if the given argument tuple is consistent with the argument tuple of a controlled specialization. - /// Return null otherwise, or if the name of the control qubits is invalid. + /// Return null otherwise, or if the name of the control qubits is invalid. let ControlledFunctorArgument arg = - let getItemName = function - | QsTupleItem (item : LocalVariableDeclaration) -> - item.VariableName |> function + let getItemName = function + | QsTupleItem (item : LocalVariableDeclaration) -> + item.VariableName |> function | ValidName name -> name.Value | InvalidName -> null | _ -> null - match arg with + match arg with | QsTuple ts when ts.Length = 2 -> ts.[0] |> getItemName | _ -> null /// Creates a typed expression that corresponds to an immutable local variable with the given name /// that contains an expression of type Qubit[] and has no quantum dependencies. - let ImmutableQubitArrayWithName name = + let ImmutableQubitArrayWithName name = let kind = (Identifier.LocalVariable name, Null) |> QsExpressionKind.Identifier let exTypeKind = QsTypeKind.ArrayType (QsTypeKind.Qubit |> ResolvedType.New) AutoGeneratedExpression kind exTypeKind false /// Given a typed expression of operation type, creates a typed expression corresponding to a Controlled application on that operation. - /// Creates an expression of invalid type if the given expression is invalid. + /// Creates an expression of invalid type if the given expression is invalid. /// Blindly builds the controlled application if the characteristics of the given operation are invalid. /// Throws an ArgumentException if the given expression is valid but not of an operation type, or if it does not support the Controlled functor. - let ControlledOperation (ex : TypedExpression) = + let ControlledOperation (ex : TypedExpression) = let ex = StripPositionInfo.Apply ex let kind = QsExpressionKind.ControlledApplication ex let built exTypeKind = AutoGeneratedExpression kind exTypeKind ex.InferredInformation.HasLocalQuantumDependency let ctlOpType ((it, ot), opInfo) = QsTypeKind.Operation ((AddControlQubits it, ot), opInfo) - match ex.ResolvedType.Resolution with + match ex.ResolvedType.Resolution with | QsTypeKind.InvalidType -> built InvalidType - | QsTypeKind.Operation ((it,ot), opInfo) when opInfo.Characteristics.AreInvalid -> ctlOpType ((it, ot), opInfo) |> built - | QsTypeKind.Operation ((it,ot), opInfo) -> opInfo.Characteristics.SupportedFunctors |> function + | QsTypeKind.Operation ((it,ot), opInfo) when opInfo.Characteristics.AreInvalid -> ctlOpType ((it, ot), opInfo) |> built + | QsTypeKind.Operation ((it,ot), opInfo) -> opInfo.Characteristics.SupportedFunctors |> function | Value functors when functors.Contains Controlled -> ctlOpType ((it, ot), opInfo) |> built - | _ -> ArgumentException "given expression does not correspond to a suitable operation" |> raise - | _ -> ArgumentException "given expression does not correspond to a suitable operation" |> raise + | _ -> ArgumentException "given expression does not correspond to a suitable operation" |> raise + | _ -> ArgumentException "given expression does not correspond to a suitable operation" |> raise /// Given a typed expression of operation type, creates a typed expression corresponding to a Adjoint application on that operation. - /// Creates an expression of invalid type if the given expression is invalid. + /// Creates an expression of invalid type if the given expression is invalid. /// Blindly builds the adjoint application if the characteristics of the given operation are invalid. /// Throws an ArgumentException if the given expression is valid but not of an operation type, or if it does not support the Adjoint functor. - let AdjointOperation (ex : TypedExpression) = + let AdjointOperation (ex : TypedExpression) = let ex = StripPositionInfo.Apply ex let kind = QsExpressionKind.AdjointApplication (ex) let built = AutoGeneratedExpression kind ex.ResolvedType.Resolution ex.InferredInformation.HasLocalQuantumDependency - match ex.ResolvedType.Resolution with + match ex.ResolvedType.Resolution with | QsTypeKind.InvalidType -> built | QsTypeKind.Operation (_, opInfo) when opInfo.Characteristics.AreInvalid -> built - | QsTypeKind.Operation (_, opInfo) -> opInfo.Characteristics.SupportedFunctors |> function + | QsTypeKind.Operation (_, opInfo) -> opInfo.Characteristics.SupportedFunctors |> function | Value functors when functors.Contains Adjoint -> built - | _ -> ArgumentException "given expression does not correspond to a suitable operation" |> raise - | _ -> ArgumentException "given expression does not correspond to a suitable operation" |> raise + | _ -> ArgumentException "given expression does not correspond to a suitable operation" |> raise + | _ -> ArgumentException "given expression does not correspond to a suitable operation" |> raise diff --git a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index d5f6ec5a25..38291ceefc 100644 --- a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -69,7 +69,7 @@ type ClassicalControlTests () = |> writer.Statements.OnScope |> ignore - writer.SharedState.StatementOutputHandle + writer.SharedState.StatementOutputHandle |> Seq.filter (not << String.IsNullOrWhiteSpace) |> Seq.toArray @@ -121,7 +121,7 @@ type ClassicalControlTests () = let AssertSpecializationHasCalls specialization calls = Assert.True(CheckIfSpecializationHasCalls specialization calls, sprintf "Callable %O(%A) did not have expected content" specialization.Parent specialization.Kind) - let ExpandBuiltInQualifiedSymbol (i, (builtin : BuiltIn)) = (i, builtin.Namespace.Value, builtin.Name.Value) + let ExpandBuiltInQualifiedSymbol (i, (builtin : BuiltIn)) = (i, builtin.FullName.Namespace.Value, builtin.FullName.Name.Value) let IdentifyGeneratedByCalls generatedCallables calls = let mutable callables = generatedCallables |> Seq.map (fun x -> x, x |> (GetBodyFromCallable >> GetLinesFromSpecialization)) @@ -163,13 +163,13 @@ type ClassicalControlTests () = |> (fun x -> x.Value) let ApplyIfElseTest compilation = - + let original = GetCallableWithName compilation Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable let lines = original |> GetLinesFromSpecialization Assert.True(2 = Seq.length lines, sprintf "Callable %O(%A) did not have the expected number of statements" original.Parent original.Kind) - let (success, targs, args) = CheckIfLineIsCall BuiltIn.ApplyIfElseR.Namespace.Value BuiltIn.ApplyIfElseR.Name.Value lines.[1] + let (success, targs, args) = CheckIfLineIsCall BuiltIn.ApplyIfElseR.FullName.Namespace.Value BuiltIn.ApplyIfElseR.FullName.Name.Value lines.[1] Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent original.Kind) targs, args @@ -271,10 +271,10 @@ type ClassicalControlTests () = [] member this.``Apply If Zero Else One`` () = let (targs, args) = CompileClassicalControlTest 8 |> ApplyIfElseTest - + let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} let SubOp1 = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} - + IsApplyIfElseArgsMatch args "r" Bar SubOp1 |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) @@ -304,7 +304,7 @@ type ClassicalControlTests () = let elseOp = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp3"} let errorMsg = "ApplyIfElse did not have the correct arguments" - let (success, _, _, _, subArgs) = IsApplyIfElseArgsMatch args "r" ifOp { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } + let (success, _, _, _, subArgs) = IsApplyIfElseArgsMatch args "r" ifOp BuiltIn.ApplyIfElseR.FullName Assert.True(success, errorMsg) IsApplyIfElseArgsMatch subArgs "r" elseOp elifOp // elif and else are swapped because second condition is against One |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg)) @@ -318,7 +318,7 @@ type ClassicalControlTests () = let elseOp = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp2"} let errorMsg = "ApplyIfElse did not have the correct arguments" - let (success, _, subArgs, _, _) = IsApplyIfElseArgsMatch args "r" { Namespace = BuiltIn.ApplyIfElseRCA.Namespace; Name = BuiltIn.ApplyIfElseR.Name } elseOp + let (success, _, subArgs, _, _) = IsApplyIfElseArgsMatch args "r" BuiltIn.ApplyIfElseRCA.FullName elseOp Assert.True(success, errorMsg) IsApplyIfElseArgsMatch subArgs "r" elseOp ifOp // if and else are swapped because second condition is against One |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg)) @@ -332,7 +332,7 @@ type ClassicalControlTests () = let elseOp = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp2"} let errorMsg = "ApplyIfElse did not have the correct arguments" - let (success, _, _, _, subArgs) = IsApplyIfElseArgsMatch args "r" ifOp { Namespace = BuiltIn.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } + let (success, _, _, _, subArgs) = IsApplyIfElseArgsMatch args "r" ifOp BuiltIn.ApplyIfElseR.FullName Assert.True(success, errorMsg) IsApplyIfElseArgsMatch subArgs "r" elseOp ifOp // if and else are swapped because second condition is against One |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg)) @@ -385,7 +385,7 @@ type ClassicalControlTests () = // Assert that the original operation calls the generated operation with the appropriate type arguments let lines = GetBodyFromCallable original |> GetLinesFromSpecialization - let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfZero.Namespace.Value BuiltIn.ApplyIfZero.Name.Value lines.[1] + let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfZero.FullName.Namespace.Value BuiltIn.ApplyIfZero.FullName.Name.Value lines.[1] Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.FullName QsSpecializationKind.QsBody) let (success, typeArgs, _) = IsApplyIfArgMatch args "r" generated.FullName @@ -1179,7 +1179,7 @@ type ClassicalControlTests () = AssertCallSupportsFunctors [QsFunctor.Adjoint] outerOp let lines = GetLinesFromSpecialization original - let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfZeroA.Namespace.Value BuiltIn.ApplyIfZeroA.Name.Value lines.[2] + let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfZeroA.FullName.Namespace.Value BuiltIn.ApplyIfZeroA.FullName.Name.Value lines.[2] Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent QsSpecializationKind.QsBody) let (success, _, _) = IsApplyIfArgMatch args "r" outerOp.FullName @@ -1193,7 +1193,7 @@ type ClassicalControlTests () = let original = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable let lines = GetLinesFromSpecialization original - let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfZero.Namespace.Value BuiltIn.ApplyIfZero.Name.Value lines.[1] + let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfZero.FullName.Namespace.Value BuiltIn.ApplyIfZero.FullName.Name.Value lines.[1] Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent QsSpecializationKind.QsBody) let (success, typeArgs, _) = IsApplyIfArgMatch args "r" {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} @@ -1235,12 +1235,12 @@ type ClassicalControlTests () = Assert.True(3 = Seq.length lines, sprintf "Callable %O(%A) did not have the expected number of statements" original.Parent original.Kind) - let (success, targs, args) = CheckIfLineIsCall BuiltIn.ApplyConditionally.Namespace.Value BuiltIn.ApplyConditionally.Name.Value lines.[2] + let (success, targs, args) = CheckIfLineIsCall BuiltIn.ApplyConditionally.FullName.Namespace.Value BuiltIn.ApplyConditionally.FullName.Name.Value lines.[2] Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent original.Kind) let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} let SubOp1 = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} - + IsApplyIfElseArgsMatch args "[r1], [r2]" Bar SubOp1 |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyConditionally did not have the correct arguments")) @@ -1256,12 +1256,12 @@ type ClassicalControlTests () = Assert.True(3 = Seq.length lines, sprintf "Callable %O(%A) did not have the expected number of statements" original.Parent original.Kind) - let (success, targs, args) = CheckIfLineIsCall BuiltIn.ApplyConditionally.Namespace.Value BuiltIn.ApplyConditionally.Name.Value lines.[2] + let (success, targs, args) = CheckIfLineIsCall BuiltIn.ApplyConditionally.FullName.Namespace.Value BuiltIn.ApplyConditionally.FullName.Name.Value lines.[2] Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent original.Kind) let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} let NoOp = {Namespace = NonNullable<_>.New "Microsoft.Quantum.Canon"; Name = NonNullable<_>.New "NoOp"} - + IsApplyIfElseArgsMatch args "[r1], [r2]" Bar NoOp |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyConditionally did not have the correct arguments")) @@ -1271,18 +1271,18 @@ type ClassicalControlTests () = [] member this.``Inequality with ApplyConditionally`` () = let result = CompileClassicalControlTest 31 - + let original = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable let lines = original |> GetLinesFromSpecialization Assert.True(3 = Seq.length lines, sprintf "Callable %O(%A) did not have the expected number of statements" original.Parent original.Kind) - let (success, targs, args) = CheckIfLineIsCall BuiltIn.ApplyConditionally.Namespace.Value BuiltIn.ApplyConditionally.Name.Value lines.[2] + let (success, targs, args) = CheckIfLineIsCall BuiltIn.ApplyConditionally.FullName.Namespace.Value BuiltIn.ApplyConditionally.FullName.Name.Value lines.[2] Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent original.Kind) let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} let SubOp1 = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} - + IsApplyIfElseArgsMatch args "[r1], [r2]" SubOp1 Bar |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyConditionally did not have the correct arguments")) @@ -1292,23 +1292,23 @@ type ClassicalControlTests () = [] member this.``Inequality with Apply If One Else Zero`` () = let (targs, args) = CompileClassicalControlTest 32 |> ApplyIfElseTest - + let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} let SubOp1 = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} - + IsApplyIfElseArgsMatch args "r" SubOp1 Bar |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) - + Assert.True(IsTypeArgsMatch targs "Unit, Result", "ApplyIfElse did not have the correct type arguments") [] [] member this.``Inequality with Apply If Zero Else One`` () = let (targs, args) = CompileClassicalControlTest 33 |> ApplyIfElseTest - + let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} let SubOp1 = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} - + IsApplyIfElseArgsMatch args "r" Bar SubOp1 |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) @@ -1318,7 +1318,7 @@ type ClassicalControlTests () = [] member this.``Inequality with ApplyIfOne`` () = let result = CompileClassicalControlTest 34 - + let originalOp = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable [ (1, BuiltIn.ApplyIfOne) ] @@ -1329,7 +1329,7 @@ type ClassicalControlTests () = [] member this.``Inequality with ApplyIfZero`` () = let result = CompileClassicalControlTest 35 - + let originalOp = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable [ (1, BuiltIn.ApplyIfZero) ] diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index ad96e0cdc3..c19fefb2b5 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -182,10 +182,10 @@ private TypeArgsResolution GetCombinedTypeResolution(TypeArgsResolution outer, T var identifier = new TypedExpression( ExpressionKind.NewIdentifier( - Identifier.NewGlobalCallable(new QsQualifiedName(opInfo.Namespace, opInfo.Name)), + Identifier.NewGlobalCallable(opInfo.FullName), QsNullable>.NewValue(typeArgs)), typeArgs - .Zip(opInfo.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(opInfo.Namespace, opInfo.Name), param, type)) + .Zip(((BuiltInKind.Operation)opInfo.Kind).TypeParameters, (type, param) => Tuple.Create(opInfo.FullName, param, type)) .ToImmutableArray(), operationType, new InferredExpressionInformation(false, false), @@ -225,12 +225,12 @@ private TypedExpression CreateControlCall(BuiltIn opInfo, IEnumerable>.NewValue(typeArgs.ToImmutableArray()) : QsNullable>.Null), typeArgs - .Zip(opInfo.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(opInfo.Namespace, opInfo.Name), param, type)) + .Zip(((BuiltInKind.Operation)opInfo.Kind).TypeParameters, (type, param) => Tuple.Create(opInfo.FullName, param, type)) .ToImmutableArray(), operationType, new InferredExpressionInformation(false, false), @@ -338,7 +338,7 @@ TypedExpression BoxResultInArray(TypedExpression expression) => equality, inequality); var targetArgsTypes = ImmutableArray.Create(equalityArgs.ResolvedType, inequalityArgs.ResolvedType); - + return CreateControlCall(controlOpInfo, props, controlArgs, targetArgsTypes); } @@ -493,7 +493,7 @@ private QsStatement ConvertConditionalToControlCall(QsStatement statement) // The scope arguments are reversed to account for the negation of the NEQ return CreateControlStatement(statement, CreateApplyConditionallyExpression(lhsConditionExpression, rhsConditionExpression, defaultScope, conditionScope)); } - + // ToDo: Diagnostic message return statement; // The condition does not fit a supported format. } From f4d67616bdcefa4f89b74de593355074558a5aa1 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 13 Mar 2020 11:22:12 -0700 Subject: [PATCH 094/135] Made use of IsSelfAdjoint on built-in operations. --- src/QsCompiler/Transformations/ClassicallyControlled.cs | 4 ++-- src/QsCompiler/Transformations/ContentLifting.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index c19fefb2b5..f9f7b04e2a 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -165,7 +165,7 @@ private TypeArgsResolution GetCombinedTypeResolution(TypeArgsResolution outer, T var properties = new[] { OpProperty.Adjointable, OpProperty.Controllable }; var characteristics = new CallableInformation( ResolvedCharacteristics.FromProperties(properties), - InferredCallableInformation.NoInformation); // ToDo: Set the IsSelfAdjoint flag appropriately + new InferredCallableInformation(((BuiltInKind.Operation)opInfo.Kind).IsSelfAdjoint, false)); var unitType = ResolvedType.New(ResolvedTypeKind.UnitType); var operationType = ResolvedType.New(ResolvedTypeKind.NewOperation( @@ -215,7 +215,7 @@ private TypedExpression CreateControlCall(BuiltIn opInfo, IEnumerable>> GeneratedOpParams = @@ -232,7 +232,7 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature /// The generated operation is returned, along with a call to the new operation is /// also returned with all the type parameters and known variables being forwarded to /// the new operation as arguments. - /// + /// /// The given body should be validated with the SharedState.IsValidScope before using this function. /// public (QsCallable, QsStatement) LiftBody(QsScope body) From 486ddd6b3248efa0aab3d3b31630867cac42143c Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 13:43:40 -0700 Subject: [PATCH 095/135] Allow shadowing imported internal types --- src/QsCompiler/Core/SymbolTable.fs | 373 ++++++++++++++++------------- 1 file changed, 201 insertions(+), 172 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 0084f24d83..3659ebbe4c 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -40,16 +40,14 @@ module private ResolutionResult = | Inaccessible -> Inaccessible | NotFound -> NotFound - /// Converts the resolution result to a 0- or 1-element list containing the Found value if the result is a Found. - let private toList = function - | Found value -> [value] - | _ -> [] - /// Converts the resolution result to an option containing the Found value. let internal toOption = function | Found value -> Some value | _ -> None + /// Converts the resolution result to a 0- or 1-element list containing the Found value if the result is a Found. + let private toList = toOption >> Option.toList + /// Returns the first result matching Found, Inaccessible or NotFound, in decreasing order of priority. If the /// sequence contains a Found result, the rest of the sequence after it is not evaluated. let internal tryFirst results = @@ -74,13 +72,21 @@ module private ResolutionResult = /// Returns a Found result if there is only one in the sequence. If there is more than one, raises an exception. /// Otherwise, returns the same value as ResolutionResult.tryFirst. - let internal exactlyOne = tryExactlyOne (fun _ -> NonNullable<_>.New "") >> function + let internal exactlyOne<'T> : seq> -> ResolutionResult<'T> = + tryExactlyOne (fun _ -> NonNullable<_>.New "") >> function | Found value -> Found value | Ambiguous _ -> QsCompilerError.Raise "Resolution is ambiguous" Exception () |> raise | Inaccessible -> Inaccessible | NotFound -> NotFound + /// Returns true if the resolution result indicates that a resolution exists (Found or Ambiguous). + let internal exists = function + | Found _ + | Ambiguous _ -> true + | Inaccessible + | NotFound -> false + /// Represents the partial declaration of a namespace in a single file. /// @@ -89,7 +95,7 @@ type private PartialNamespace private (name : NonNullable, source : NonNullable, documentation : IEnumerable>, - openNS : IEnumerable, string>>, + openNS : IEnumerable, string>>, typeDecl : IEnumerable, Resolution, ResolvedType * QsTuple<_>>>>, callableDecl : IEnumerable, QsCallableKind * Resolution>>>, specializations : IEnumerable, List>>>) = @@ -508,28 +514,44 @@ and Namespace private // ArgumentException "no callable with the given name exist within the namespace" |> raise ImmutableArray<_>.Empty - /// If this namespace contains a declaration for the given type name, returns a Value with: - /// - /// (1) the name of the source file or the name of the file within a referenced assembly in which it is - /// declared; - /// (2) a string option indicating the redirection for the type if it has been deprecated; - /// (3) true if the declaration is declared in the assembly currently being compiled, or false if it is declared - /// in a referenced assembly; - /// (4) the modifiers for the declaration. - /// - /// Returns Null otherwise. + + /// Returns a resolution result for the type with the given name containing the name of the source file or + /// referenced assembly in which it is declared, a string indicating the redirection if it has been deprecated, and + /// its access modifier. Resolution is based on accessibility to source files in this compilation unit. /// - /// Whether the type has been deprecated is determined by checking the associated attributes for an attribute with the corresponding name. - /// Note that if the type is declared in a source files, the *unresolved* attributes will be checked. - /// In that case checkDeprecation is used to validate the namespace qualification of the attribute. - /// If checkDeprecation is not specified, it is assumed that no qualification is needed in the relevant namespace and source file. - member this.TryFindType (tName, ?checkDeprecation : (string -> bool)) = - let checkDeprecation = defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) - match TypesInReferences.TryGetValue tName with - | true, tDecl -> Value (tDecl.SourceFile, tDecl.Attributes |> SymbolResolution.TryFindRedirect, false, tDecl.Modifiers) - | false, _ -> FromSingleSource (fun partialNS -> partialNS.TryGetType tName |> function - | true, tDecl -> Some (partialNS.Source, tDecl.DefinedAttributes |> SymbolResolution.TryFindRedirectInUnresolved checkDeprecation, true, tDecl.Modifiers) - | false, _ -> None) + /// Whether the type has been deprecated is determined by checking the associated attributes for an attribute with + /// the corresponding name. Note that if the type is declared in a source files, the *unresolved* attributes will be + /// checked. In that case checkDeprecation is used to validate the namespace qualification of the attribute. If + /// checkDeprecation is not specified, it is assumed that no qualification is needed in the relevant namespace and + /// source file. + member this.TryFindType (tName, ?checkDeprecation : (string -> bool)) = + let checkDeprecation = + defaultArg checkDeprecation + (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) + + let findInReferences () = + match TypesInReferences.TryGetValue tName with + | true, qsType -> + if Namespace.IsDeclarationAccessible false qsType.Modifiers.Access + then Found (qsType.SourceFile, + SymbolResolution.TryFindRedirect qsType.Attributes, + qsType.Modifiers.Access) + else Inaccessible + | false, _ -> NotFound + + let findInPartial (partial : PartialNamespace) = + match partial.TryGetType tName with + | true, qsType -> + if Namespace.IsDeclarationAccessible true qsType.Modifiers.Access + then Found (partial.Source, + SymbolResolution.TryFindRedirectInUnresolved checkDeprecation qsType.DefinedAttributes, + qsType.Modifiers.Access) + else Inaccessible + | false, _ -> NotFound + + seq { yield findInReferences () + yield Seq.map findInPartial Parts.Values |> ResolutionResult.exactlyOne } + |> ResolutionResult.tryFirst /// Returns a resolution result for the callable with the given name containing the name of the source file or /// referenced assembly in which it is declared, and a string indicating the redirection if it has been deprecated. @@ -543,12 +565,19 @@ and Namespace private /// will be checked. In that case checkDeprecation is used to validate the namespace qualification of the attribute. /// If checkDeprecation is not specified, it is assumed that no qualification is needed in the relevant namespace /// and source file. - member this.TryFindCallable (cName, ?checkDeprecation : (string -> bool)) - : ResolutionResult * QsNullable> = + member this.TryFindCallable (cName, ?checkDeprecation : (string -> bool)) = let checkDeprecation = defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) + let findInReferences () = + match CallablesInReferences.TryGetValue cName with + | true, callable -> + if Namespace.IsDeclarationAccessible false callable.Modifiers.Access + then Found (callable.SourceFile, SymbolResolution.TryFindRedirect callable.Attributes) + else Inaccessible + | false, _ -> NotFound + let findInPartial (partial : PartialNamespace) = match partial.TryGetCallable cName with | true, (_, callable) -> @@ -558,16 +587,8 @@ and Namespace private else Inaccessible | false, _ -> NotFound - seq { - yield match CallablesInReferences.TryGetValue cName with - | true, callable -> - if Namespace.IsDeclarationAccessible false callable.Modifiers.Access - then Found (callable.SourceFile, SymbolResolution.TryFindRedirect callable.Attributes) - else Inaccessible - | false, _ -> NotFound - - yield Seq.map findInPartial Parts.Values |> ResolutionResult.exactlyOne - } + seq { yield findInReferences () + yield Seq.map findInPartial Parts.Values |> ResolutionResult.exactlyOne } |> ResolutionResult.tryFirst /// Sets the resolution for the type with the given name in the given source file to the given type, @@ -648,26 +669,30 @@ and Namespace private partial.AddOpenDirective(openedNS, alias); [||] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise - /// If no type with the given name exists in this namespace, adds the given type declaration - /// as well as the corresponding constructor declaration to the given source, and returns an empty array. - /// The given location is associated with both the type constructur and the type itself and accessible via the record properties Position and SymbolRange. + /// If no type with the given name exists in this namespace, adds the given type declaration + /// as well as the corresponding constructor declaration to the given source, and returns an empty array. + /// The given location is associated with both the type constructur and the type itself and accessible via the record properties Position and SymbolRange. /// If a type or callable with that name already exists, returns an array of suitable diagnostics. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. - member this.TryAddType (source, location) ((tName, tRange), typeTuple, attributes, modifiers, documentation) : QsCompilerDiagnostic[] = + member this.TryAddType (source, location) ((tName, tRange), typeTuple, attributes, modifiers, documentation) : QsCompilerDiagnostic[] = match Parts.TryGetValue source with | true, partial when isNameAvailable tName -> TypesDefinedInAllSourcesCache <- null CallablesDefinedInAllSourcesCache <- null partial.AddType location (tName, typeTuple, attributes, modifiers, documentation); [||] - | true, _ -> this.TryFindType tName |> function - | Value _ -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeRedefinition, [tName.Value]) |] - | Null -> [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeConstructorOverlapWithCallable, [tName.Value]) |] + | true, _ -> + match this.TryFindType tName with + | Found _ + | Ambiguous _ -> + [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeRedefinition, [tName.Value]) |] + | _ -> + [| tRange |> QsCompilerDiagnostic.Error (ErrorCode.TypeConstructorOverlapWithCallable, [tName.Value]) |] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise /// If no callable (function, operation, or type constructor) with the given name exists in this namespace, /// adds a declaration for the callable of the given kind (operation or function) with the given name and signature /// to the given source, and returns an empty array. - /// The given location is associated with the callable declaration and accessible via the record properties Position and SymbolRange. + /// The given location is associated with the callable declaration and accessible via the record properties Position and SymbolRange. /// If a callable with that name already exists, returns an array of suitable diagnostics. /// Throws an ArgumentException if the given source file is not listed as a source for (part of) the namespace. member this.TryAddCallableDeclaration (source, location) ((cName, cRange), (kind, signature), attributes, modifiers, documentation) = @@ -675,9 +700,13 @@ and Namespace private | true, partial when isNameAvailable cName -> CallablesDefinedInAllSourcesCache <- null partial.AddCallableDeclaration location (cName, (kind, signature), attributes, modifiers, documentation); [||] - | true, _ -> this.TryFindType cName |> function - | Value _ -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableOverlapWithTypeConstructor, [cName.Value]) |] - | Null -> [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableRedefinition, [cName.Value]) |] + | true, _ -> + match this.TryFindType cName with + | Found _ + | Ambiguous _ -> + [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableOverlapWithTypeConstructor, [cName.Value]) |] + | _ -> + [| cRange |> QsCompilerDiagnostic.Error (ErrorCode.CallableRedefinition, [cName.Value]) |] | false, _ -> ArgumentException "given source is not listed as a source of (parts of) the namespace" |> raise /// If a declaration for a callable of the given name exists within this namespace, @@ -851,14 +880,17 @@ and NamespaceManager | true, ns -> Some ns /// Returns the possible qualifications for the built-in type or callable used in the given namespace and source. - /// where the given source may either be the name of a source file or of a referenced assembly. - /// If the given source is not listed as source file of the namespace, assumes that the source if one of the references - /// and returns the namespace name of the given built in type or callable as only possible qualification. - /// Throws an ArgumentException if no namespace with the given name exists. - let PossibleQualifications (nsName, source) (builtIn : BuiltIn) = - match Namespaces.TryGetValue nsName with - | true, ns when ns.Sources.Contains source -> (ns.ImportedNamespaces source).TryGetValue builtIn.Namespace |> function - | true, null when ns.TryFindType builtIn.Name = Null || nsName.Value = builtIn.Namespace.Value -> [""; builtIn.Namespace.Value] + /// where the given source may either be the name of a source file or of a referenced assembly. + /// If the given source is not listed as source file of the namespace, assumes that the source if one of the references + /// and returns the namespace name of the given built in type or callable as only possible qualification. + /// Throws an ArgumentException if no namespace with the given name exists. + let PossibleQualifications (nsName, source) (builtIn : BuiltIn) = + match Namespaces.TryGetValue nsName with + | true, ns when ns.Sources.Contains source -> + match (ns.ImportedNamespaces source).TryGetValue builtIn.Namespace with + | true, null when ResolutionResult.exists (ns.TryFindType builtIn.Name) || + nsName.Value = builtIn.Namespace.Value -> + [""; builtIn.Namespace.Value] | true, null -> [builtIn.Namespace.Value] // the built-in type or callable is shadowed | true, alias -> [alias; builtIn.Namespace.Value] | false, _ -> [builtIn.Namespace.Value] @@ -866,8 +898,8 @@ and NamespaceManager | false, _ -> ArgumentException "no namespace with the given name exists" |> raise /// Given the qualified or unqualified name of a type used within the given parent namespace and source file, - /// determines if such a type is accessible, and returns its full name and the source file or referenced assembly in - /// which it is defined as Some if it is. + /// determines if such a type is accessible, and returns its namespace name and the source file or referenced + /// assembly in which it is defined as Some if it is. /// /// Returns None if no such type exists, the type is inaccessible, or if the type name is unqualified and ambiguous. /// @@ -875,47 +907,44 @@ and NamespaceManager /// /// Throws an ArgumentException if the given parent namespace does not exist, or if no source file with the given /// name is listed as source of that namespace. - let TryResolveTypeName (parentNS, source) ((nsName, symName), symRange) = - let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange - let checkQualificationForDeprecation qual = BuiltIn.Deprecated |> PossibleQualifications (parentNS, source) |> Seq.contains qual - let buildAndReturn (ns, declSource, deprecation, modifiers, errs) = - let deprecatedWarnings = - deprecation - |> SymbolResolution.GenerateDeprecationWarning ({Namespace = ns; Name = symName}, symRange |> orDefault) - (Some ({Namespace = ns; Name = symName; Range = symRange}, declSource, modifiers), - Array.append errs deprecatedWarnings) - let tryFind (parentNS, source) (tName, tRange) = - let allResolutions = - PossibleResolutions - (fun ns -> ns.TryFindType (tName, checkQualificationForDeprecation)) - (parentNS, source) - let accessibleResolutions = allResolutions |> List.filter (fun (_, (_, _, sameAssembly, modifiers)) -> - Namespace.IsDeclarationAccessible sameAssembly modifiers.Access) - match accessibleResolutions with - | [] -> - if List.isEmpty allResolutions - then Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownType, [tName.Value]) |] - else Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InaccessibleType, [tName.Value]) |] - | [(nsName, (declSource, deprecated, _, modifiers))] -> - Value (nsName, declSource, deprecated, modifiers), [||] - | _ -> - let diagArg = String.Join(", ", accessibleResolutions.Select (fun (ns,_) -> ns.Value)) - Null, [| tRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AmbiguousType, [tName.Value; diagArg]) |] - + let tryResolveTypeName (parentNS, source) ((nsName, symName), symRange : QsRangeInfo) = + let checkQualificationForDeprecation qual = + BuiltIn.Deprecated |> PossibleQualifications (parentNS, source) |> Seq.contains qual + + let success ns declSource deprecation access errs = + let warnings = + SymbolResolution.GenerateDeprecationWarning + ({Namespace = ns; Name = symName}, symRange.ValueOr QsCompilerDiagnostic.DefaultRange) + deprecation + Some ({Namespace = ns; Name = symName; Range = symRange}, declSource, access), Array.append errs warnings + + let error code args = + None, [| QsCompilerDiagnostic.Error (code, args) (symRange.ValueOr QsCompilerDiagnostic.DefaultRange) |] + + let findUnqualified () = + match resolveInOpenNamespaces (fun ns -> ns.TryFindType (symName, checkQualificationForDeprecation)) + (parentNS, source) with + | Found (nsName, (declSource, deprecation, access)) -> success nsName declSource deprecation access [||] + | Ambiguous namespaces -> + let names = String.Join(", ", Seq.map (fun (ns : NonNullable) -> ns.Value) namespaces) + error ErrorCode.AmbiguousType [symName.Value; names] + | Inaccessible -> error ErrorCode.InaccessibleType [symName.Value] + | NotFound -> error ErrorCode.UnknownType [symName.Value] + + let findQualified (ns : Namespace) qualifier = + match ns.TryFindType (symName, checkQualificationForDeprecation) with + | Found (declSource, deprecation, access) -> success ns.Name declSource deprecation access [||] + | Ambiguous _ -> QsCompilerError.Raise "Qualified name should not be ambiguous" + Exception () |> raise + | Inaccessible -> error ErrorCode.InaccessibleTypeInNamespace [symName.Value; qualifier] + | NotFound -> error ErrorCode.UnknownTypeInNamespace [symName.Value; qualifier] + match nsName with - | None -> tryFind (parentNS, source) (symName, symRange) |> function - | Value (ns, declSource, deprecation, modifiers), errs -> - buildAndReturn (ns, declSource, deprecation, modifiers, errs) - | Null, errs -> None, errs - | Some qualifier -> (parentNS, source) |> TryResolveQualifier qualifier |> function - | None -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownNamespace, [qualifier.Value]) |] - | Some ns -> - ns.TryFindType (symName, checkQualificationForDeprecation) |> function - | Value (declSource, deprecation, sameAssembly, modifiers) -> - if Namespace.IsDeclarationAccessible sameAssembly modifiers.Access - then buildAndReturn (ns.Name, declSource, deprecation, modifiers, [||]) - else None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InaccessibleTypeInNamespace, [symName.Value; qualifier.Value]) |] - | _ -> None, [| symRange |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.UnknownTypeInNamespace, [symName.Value; qualifier.Value]) |] + | None -> findUnqualified () + | Some qualifier -> + match TryResolveQualifier qualifier (parentNS, source) with + | None -> error ErrorCode.UnknownNamespace [qualifier.Value] + | Some ns -> findQualified ns qualifier.Value /// Fully (i.e. recursively) resolves the given Q# type used within the given parent in the given source file. The /// resolution consists of replacing all unqualified names for user defined types by their qualified name. @@ -937,9 +966,8 @@ and NamespaceManager /// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations. /// Throws a NotSupportedException if the QsType to resolve contains a MissingType. let resolveType (parent : QsQualifiedName, tpNames, source) qsType checkUdt = - let processUDT = TryResolveTypeName (parent.Namespace, source) >> function - | Some (udt, _, modifiers), errs -> - UserDefinedType udt, Array.append errs (checkUdt (udt, modifiers.Access)) + let processUDT = tryResolveTypeName (parent.Namespace, source) >> function + | Some (udt, _, access), errs -> UserDefinedType udt, Array.append errs (checkUdt (udt, access)) | None, errs -> InvalidType, errs let processTP (symName, symRange) = if tpNames |> Seq.contains symName @@ -978,7 +1006,7 @@ and NamespaceManager /// name is listed as source of that namespace. member private this.ResolveAttribute (parentNS, source) attribute = let getAttribute ((nsName, symName), symRange) = - match TryResolveTypeName (parentNS, source) ((nsName, symName), symRange) with + match tryResolveTypeName (parentNS, source) ((nsName, symName), symRange) with | Some (udt, declSource, _), errs -> // declSource may be the name of an assembly! let fullName = sprintf "%s.%s" udt.Namespace.Value udt.Name.Value let validQualifications = BuiltIn.Attribute |> PossibleQualifications (udt.Namespace, declSource) @@ -1497,7 +1525,7 @@ and NamespaceManager /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent /// namespace does not exist. member private this.TryGetCallableHeader (callableName : QsQualifiedName, declSource) (nsName, source) = - let buildHeader fullName (source, kind, declaration : Resolution<_, _>) = + let buildHeader fullName (source, kind, declaration) = let fallback () = (declaration.Defined, [CallableInformation.Invalid]) |> this.ResolveCallableSignature (kind, callableName, source, declaration.Modifiers.Access) @@ -1516,6 +1544,14 @@ and NamespaceManager Documentation = declaration.Documentation } + let findInReferences (ns : Namespace) = + match ns.CallablesInReferencedAssemblies.TryGetValue callableName.Name with + | true, callable -> + if Namespace.IsDeclarationAccessible false callable.Modifiers.Access + then Found callable + else Inaccessible + | false, _ -> NotFound + let findInSources (ns : Namespace) = function | Some source -> // OK to use CallableInSource because this is only evaluated if the callable is not in a @@ -1536,16 +1572,8 @@ and NamespaceManager try match (nsName, source) |> TryResolveQualifier callableName.Namespace with | None -> NotFound | Some ns -> - seq { - yield match ns.CallablesInReferencedAssemblies.TryGetValue callableName.Name with - | true, callable -> - if Namespace.IsDeclarationAccessible false callable.Modifiers.Access - then Found callable - else Inaccessible - | false, _ -> NotFound - - yield findInSources ns declSource - } + seq { yield findInReferences ns + yield findInSources ns declSource } |> ResolutionResult.tryFirst finally syncRoot.ExitReadLock() @@ -1564,17 +1592,16 @@ and NamespaceManager /// Returns an Ambiguous result with a list with namespaces containing a type with that name if the name cannot be /// uniquely resolved. member this.TryResolveAndGetCallable cName (nsName, source) = + let toHeader (declaredNs, (declaredSource, _)) = + match this.TryGetCallableHeader ({Namespace = declaredNs; Name = cName}, Some declaredSource) + (nsName, source) with + | Found value -> value + | _ -> QsCompilerError.Raise "Expected to find the header corresponding to a possible resolution" + Exception () |> raise + syncRoot.EnterReadLock() - try match resolveInOpenNamespaces (fun ns -> ns.TryFindCallable cName) (nsName, source) with - | Found (declaredNs, (declaredSource, _)) -> - let header = this.TryGetCallableHeader ({Namespace = declaredNs; Name = cName}, Some declaredSource) - (nsName, source) - QsCompilerError.Verify ((match header with | Found _ -> true | _ -> false), - "Expected to find the header corresponding to a possible resolution") - header - | Ambiguous namespaces -> Ambiguous namespaces - | Inaccessible -> Inaccessible - | NotFound -> NotFound + try resolveInOpenNamespaces (fun ns -> ns.TryFindCallable cName) (nsName, source) + |> ResolutionResult.map toHeader finally syncRoot.ExitReadLock() /// Given a qualified type name, returns the corresponding TypeDeclarationHeader in a ResolutionResult if the @@ -1586,7 +1613,7 @@ and NamespaceManager /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent /// namespace does not exist. member private this.TryGetTypeHeader (typeName : QsQualifiedName, declSource) (nsName, source) = - let BuildHeader fullName (source, declaration) = + let buildHeader fullName (source, declaration) = let fallback () = declaration.Defined |> this.ResolveTypeDeclaration (typeName, source, declaration.Modifiers) |> fst let underlyingType, items = declaration.Resolved.ValueOrApply fallback @@ -1601,28 +1628,37 @@ and NamespaceManager TypeItems = items Documentation = declaration.Documentation } + + let findInReferences (ns : Namespace) = + match ns.TypesInReferencedAssemblies.TryGetValue typeName.Name with + | true, qsType -> + if Namespace.IsDeclarationAccessible false qsType.Modifiers.Access + then Found qsType + else Inaccessible + | false, _ -> NotFound + + let findInSources (ns : Namespace) = function + | Some source -> + // OK to use TypeInSource because this is only evaluated if the type is not in a reference. + let declaration = ns.TypeInSource source typeName.Name + if Namespace.IsDeclarationAccessible true declaration.Modifiers.Access + then Found (buildHeader {typeName with Namespace = ns.Name} (source, declaration)) + else Inaccessible + | None -> + match ns.TypesDefinedInAllSources().TryGetValue typeName.Name with + | true, (source, declaration) -> + if Namespace.IsDeclarationAccessible true declaration.Modifiers.Access + then Found (buildHeader {typeName with Namespace = ns.Name} (source, declaration)) + else Inaccessible + | false, _ -> NotFound + syncRoot.EnterReadLock() - try - match (nsName, source) |> TryResolveQualifier typeName.Namespace with + try match (nsName, source) |> TryResolveQualifier typeName.Namespace with | None -> NotFound - | Some ns -> ns.TypesInReferencedAssemblies.TryGetValue typeName.Name |> function - | true, tDecl -> - if Namespace.IsDeclarationAccessible false tDecl.Modifiers.Access - then Found tDecl - else Inaccessible - | false, _ -> declSource |> function - | Some source -> - let decl = ns.TypeInSource source typeName.Name // ok only because/if we have covered that the type is not in a reference! - if Namespace.IsDeclarationAccessible true decl.Modifiers.Access - then BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Found - else Inaccessible - | None -> - ns.TypesDefinedInAllSources().TryGetValue typeName.Name |> function - | true, (source, decl) -> - if Namespace.IsDeclarationAccessible true decl.Modifiers.Access - then BuildHeader {typeName with Namespace = ns.Name} (source, decl) |> Found - else Inaccessible - | false, _ -> NotFound + | Some ns -> + seq { yield findInReferences ns + yield findInSources ns declSource } + |> ResolutionResult.tryFirst finally syncRoot.ExitReadLock() /// Given a qualified type name, returns the corresponding TypeDeclarationHeader in a ResolutionResult if the @@ -1630,7 +1666,8 @@ and NamespaceManager /// /// Throws an ArgumentException if the qualifier does not correspond to a known namespace and the given parent /// namespace does not exist. - member this.TryGetType (typeName : QsQualifiedName) (nsName, source) = this.TryGetTypeHeader (typeName, None) (nsName, source) + member this.TryGetType (typeName : QsQualifiedName) (nsName, source) = + this.TryGetTypeHeader (typeName, None) (nsName, source) /// Given an unqualified type name, returns the corresponding TypeDeclarationHeader in a ResolutionResult if the /// qualifier can be uniquely resolved within the given parent namespace and source file, and the type is @@ -1638,25 +1675,19 @@ and NamespaceManager /// /// Returns an Ambiguous result with a list with namespaces containing a type with that name if the name cannot be /// uniquely resolved. - member this.TryResolveAndGetType tName (nsName, source) = + member this.TryResolveAndGetType tName (nsName, source) = + let toHeader (declaredNs, (declaredSource, _, _)) = + match this.TryGetTypeHeader ({Namespace = declaredNs; Name = tName}, Some declaredSource) + (nsName, source) with + | Found value -> value + | _ -> QsCompilerError.Raise "Expected to find the header corresponding to a possible resolution" + Exception () |> raise + syncRoot.EnterReadLock() - try - let allResolutions = (nsName, source) |> PossibleResolutions (fun ns -> ns.TryFindType tName) - let accessibleResolutions = - allResolutions |> List.filter (fun (declaredNS, (_, _, sameAssembly, modifiers)) -> - Namespace.IsDeclarationAccessible sameAssembly modifiers.Access) - match accessibleResolutions with - | [] -> if not <| List.isEmpty allResolutions then Inaccessible else NotFound - | [(declNS, (declSource, _, _, _))] -> - this.TryGetTypeHeader ({Namespace = declNS; Name = tName}, Some declSource) (nsName, source) |> function - | Found decl -> Found decl - | _ -> - QsCompilerError.Raise "Expected to find the header corresponding to a possible resolution" - NotFound - | _ -> accessibleResolutions.Select fst |> Ambiguous + try resolveInOpenNamespaces (fun ns -> ns.TryFindType tName) (nsName, source) + |> ResolutionResult.map toHeader finally syncRoot.ExitReadLock() - /// Returns the fully qualified namespace name of the given namespace alias (short name). If the alias is already a fully qualified name, /// returns the name unchanged. Returns null if no such name exists within the given parent namespace and source file. /// Throws an ArgumentException if the given parent namespace does not exist. @@ -1673,7 +1704,8 @@ and NamespaceManager // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! syncRoot.EnterReadLock() try Namespaces.Values - |> Seq.choose (fun ns -> ns.TryFindCallable cName |> ResolutionResult.toOption |> Option.map fst) + |> Seq.choose (fun ns -> + ns.TryFindCallable cName |> ResolutionResult.toOption |> Option.map (fun _ -> ns.Name)) |> fun namespaces -> namespaces.ToImmutableArray () finally syncRoot.ExitReadLock() @@ -1682,13 +1714,10 @@ and NamespaceManager member this.NamespacesContainingType tName = // FIXME: we need to handle the case where a callable/type with the same qualified name is declared in several references! syncRoot.EnterReadLock() - try - let tryFindType (ns : Namespace) = - ns.TryFindType tName |> QsNullable<_>.Bind (fun (_, _, sameAssembly, modifiers) -> - if Namespace.IsDeclarationAccessible sameAssembly modifiers.Access - then Value ns.Name - else Null) - (Namespaces.Values |> QsNullable<_>.Choose tryFindType).ToImmutableArray() + try Namespaces.Values + |> Seq.choose (fun ns -> + ns.TryFindType tName |> ResolutionResult.toOption |> Option.map (fun _ -> ns.Name)) + |> fun namespaces -> namespaces.ToImmutableArray () finally syncRoot.ExitReadLock() /// Returns the name of all namespaces declared in source files or referenced assemblies. From 493327b0404c7c14f8b3c992604a45d1c1749ebd Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 13:49:01 -0700 Subject: [PATCH 096/135] Enable redeclaration test --- .../TestTargets/Libraries/Library1/AccessModifiers.qs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs index 8b47abeeb7..ac831ac137 100644 --- a/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs +++ b/src/QsCompiler/TestTargets/Libraries/Library1/AccessModifiers.qs @@ -1,11 +1,9 @@ /// This file contains redefinitions of types and callables declared in Tests.Compiler\TestCases\AccessModifiers.qs. It /// is used as an assembly reference to test support for re-using names of inaccessible declarations in references. namespace Microsoft.Quantum.Testing.AccessModifiers { - // TODO: Uncomment these definitions when re-using names of inaccessible declarations in references is supported. + internal newtype InternalType = Unit; - // internal newtype InternalType = Unit; - - // internal function InternalFunction () : Unit {} + internal function InternalFunction () : Unit {} } /// This namespace contains additional definitions of types and callables meant to be used by the From a8dca4b75ea35c0c3a80c2b3d49e2ef5017b65aa Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 13 Mar 2020 13:49:54 -0700 Subject: [PATCH 097/135] Took out some commented out code from built-in changes. --- src/QsCompiler/Core/Dependencies.fs | 34 +++++++++++------------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index cb7822ad76..43005b6003 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -18,16 +18,8 @@ type BuiltIn = { FullName : QsQualifiedName /// contains the specific kind of built-in this is, as well as information specific to that kind Kind : BuiltInKind - - - /// contains the name of the callable - //Name : NonNullable - /// contains the name of the namespace in which the callable is defined - //Namespace : NonNullable - /// contains the names of the type parameters without the leading tick (') - //TypeParameters : ImmutableArray> } - with + with static member CoreNamespace = NonNullable.New "Microsoft.Quantum.Core" static member CanonNamespace = NonNullable.New "Microsoft.Quantum.Canon" static member IntrinsicNamespace = NonNullable.New "Microsoft.Quantum.Intrinsic" @@ -39,23 +31,23 @@ type BuiltIn = { static member NamespacesToAutoOpen = ImmutableHashSet.Create (BuiltIn.CoreNamespace) /// Returns all valid targets for executing Q# code. - static member ValidExecutionTargets = + static member ValidExecutionTargets = // Note: If this is adapted, then the error message for InvalidExecutionTargetForTest needs to be adapted as well. - ["QuantumSimulator"; "ToffoliSimulator"; "ResourcesEstimator"] + ["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 + /// 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 | Null -> false - /// Returns true if the given attribute marks the corresponding declaration as deprecated. - static member MarksDeprecation (att : QsDeclarationAttribute) = att.TypeId |> function + /// Returns true if the given attribute marks the corresponding declaration as deprecated. + static member MarksDeprecation (att : QsDeclarationAttribute) = att.TypeId |> function | Value tId -> tId.Namespace.Value = BuiltIn.Deprecated.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Deprecated.FullName.Name.Value | Null -> false - /// Returns true if the given attribute marks the corresponding declaration as unit test. - static member MarksTestOperation (att : QsDeclarationAttribute) = att.TypeId |> function + /// Returns true if the given attribute marks the corresponding declaration as unit test. + static member MarksTestOperation (att : QsDeclarationAttribute) = att.TypeId |> function | Value tId -> tId.Namespace.Value = BuiltIn.Test.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Test.FullName.Name.Value | Null -> false @@ -77,17 +69,17 @@ type BuiltIn = { Kind = Attribute } - static member EntryPoint : BuiltIn = { + static member EntryPoint = { FullName = {Name = "EntryPoint" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} Kind = Attribute } - static member Deprecated : BuiltIn = { + static member Deprecated = { FullName = {Name = "Deprecated" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} Kind = Attribute } - static member Test : BuiltIn = { + static member Test = { FullName = {Name = "Test" |> NonNullable.New; Namespace = BuiltIn.DiagnosticsNamespace} Kind = Attribute } @@ -97,7 +89,7 @@ type BuiltIn = { static member NoOp = { FullName = {Name = "NoOp" |> NonNullable.New; Namespace = BuiltIn.CanonNamespace} Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New), IsSelfAdjoint = false) - + } // hard dependencies in Microsoft.Quantum.Simulation.QuantumProcessor.Extensions From 1bfd55f29952da19d8832e56329b1274a97700d8 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 13 Mar 2020 13:57:51 -0700 Subject: [PATCH 098/135] Fixed issue with test --- src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index 38291ceefc..b584f44aa0 100644 --- a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -318,7 +318,7 @@ type ClassicalControlTests () = let elseOp = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp2"} let errorMsg = "ApplyIfElse did not have the correct arguments" - let (success, _, subArgs, _, _) = IsApplyIfElseArgsMatch args "r" BuiltIn.ApplyIfElseRCA.FullName elseOp + let (success, _, subArgs, _, _) = IsApplyIfElseArgsMatch args "r" BuiltIn.ApplyIfElseR.FullName elseOp Assert.True(success, errorMsg) IsApplyIfElseArgsMatch subArgs "r" elseOp ifOp // if and else are swapped because second condition is against One |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg)) From c43a0782778aa459e71e2a93acfc96b6b5e92c7f Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 14:42:02 -0700 Subject: [PATCH 099/135] Remove unused functions --- src/QsCompiler/Core/SymbolTable.fs | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 3659ebbe4c..5d7885ebb1 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -334,16 +334,6 @@ and Namespace private isAvailableWith partial.TryGetCallable (fun c -> (snd c).Modifiers.Access) true && isAvailableWith partial.TryGetType (fun t -> t.Modifiers.Access) true) - /// Given a selector, determines the source files for which the given selector returns a Value. - /// Returns that Value if exactly one such source file exists, or Null if no such file exists. - /// Note that files contained in referenced assemblies are *not* considered to be source files for the namespace! - /// Throws an ArgumentException if the selector returns a Value for more than one source file. - let FromSingleSource (selector : PartialNamespace -> _ Option) = - let sources = Parts.Values |> Seq.choose selector - if sources.Count() > 1 then ArgumentException "given selector selects more than one partial namespace" |> raise - if sources.Any() then Value (sources.Single()) else Null - - /// Returns whether a declaration is accessible from the calling location, given whether the calling location is in /// the same assembly as the declaration, and the declaration's access modifier. static member internal IsDeclarationAccessible sameAssembly = function @@ -840,20 +830,6 @@ and NamespaceManager | true, ns -> ns, ns.ImportedNamespaces source |> Seq.choose isKnownAndNotAliased |> Seq.toList | false, _ -> ArgumentException("no namespace with the given name exists") |> raise - /// If the given function containsSymbol returns a Value for the namespace with the given name, - /// returns a list containing only a single tuple with the given namespace name and the returned value. - /// Otherwise returns a list with all possible (namespaceName, returnedValue) tuples that yielded non-Null values - /// for namespaces opened within the given namespace and source file. - /// Throws an ArgumentException if no namespace with the given name exists, - /// or the given source file is not listed as source of that namespace. - let PossibleResolutions containsSymbol (nsName, source) = - let containsSource (arg : Namespace) = - containsSymbol arg |> QsNullable<_>.Map (fun source -> (arg.Name, source)) - let currentNS, importedNS = OpenNamespaces (nsName, source) - currentNS |> containsSource |> function - | Value (nsName, source) -> [(nsName, source)] - | Null -> importedNS |> List.choose (containsSource >> function | Value v -> Some v | Null -> None) - /// Calls the resolver function on each namespace opened within the given namespace name and source file, and /// attempts to find an unambiguous resolution. let resolveInOpenNamespaces resolver (nsName, source) = From 77c748ea301083ca34199573e03807e3bf842d7b Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 14:52:05 -0700 Subject: [PATCH 100/135] Check source then references in TryGetAttributeDeclaredIn --- src/QsCompiler/Core/SymbolTable.fs | 54 +++++++++++++++++++----------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 5d7885ebb1..de872c44bb 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -407,30 +407,44 @@ and Namespace private | true, partial -> partial.NamespaceShortNames | false, _ -> ArgumentException "given source file is not listed as a source file for this namespace" |> raise - /// If a type with the given name is defined in the specified source file or reference, - /// checks if that type has been marked as attribute and returns its underlying type if it has. - /// A type is considered to be marked as attribute if the list of defined attributes contains an attribute - /// with name "Attribute" that is qualified by any of the given possible qualifications. + /// If a type with the given name is defined in the specified source file or reference, + /// checks if that type has been marked as attribute and returns its underlying type if it has. + /// A type is considered to be marked as attribute if the list of defined attributes contains an attribute + /// with name "Attribute" that is qualified by any of the given possible qualifications. /// If the list of possible qualifications contains an empty string, then the "Attribute" may be unqualified. /// Throws an ArgumentException if no such type exists in any of the references and the source file is not listed as source file of the namespace. - /// Throws an InvalidOperationExeception if the corresponding type has not been resolved. - member internal this.TryGetAttributeDeclaredIn source (attName, possibleQualifications : _ seq) = - let marksAttribute (t : QsDeclarationAttribute) = t.TypeId |> function - | Value id -> id.Namespace.Value = BuiltIn.Attribute.Namespace.Value && id.Name.Value = BuiltIn.Attribute.Name.Value + /// Throws an InvalidOperationExeception if the corresponding type has not been resolved. + member internal this.TryGetAttributeDeclaredIn source (attName, possibleQualifications : _ seq) = + let marksAttribute (t : QsDeclarationAttribute) = t.TypeId |> function + | Value id -> + id.Namespace.Value = BuiltIn.Attribute.Namespace.Value && + id.Name.Value = BuiltIn.Attribute.Name.Value | Null -> false - let missingResolutionException () = InvalidOperationException "cannot get unresolved attribute" |> raise - let compareAttributeName (att : AttributeAnnotation) = att.Id.Symbol |> function - | Symbol sym when sym.Value = BuiltIn.Attribute.Name.Value && possibleQualifications.Contains "" -> true - | QualifiedSymbol (ns, sym) when sym.Value = BuiltIn.Attribute.Name.Value && possibleQualifications.Contains ns.Value -> true + + let missingResolutionException () = InvalidOperationException "cannot get unresolved attribute" |> raise + + let compareAttributeName (att : AttributeAnnotation) = + match att.Id.Symbol with + | Symbol sym when sym.Value = BuiltIn.Attribute.Name.Value && possibleQualifications.Contains "" -> + true + | QualifiedSymbol (ns, sym) when sym.Value = BuiltIn.Attribute.Name.Value && + possibleQualifications.Contains ns.Value -> + true | _ -> false - match TypesInReferences.TryGetValue attName with - | true, tDecl -> if tDecl.Attributes |> Seq.exists marksAttribute then Some tDecl.Type else None - | false, _ -> Parts.TryGetValue source |> function // ok only because/if we have covered that the type is not in a reference! - | false, _ -> ArgumentException "given source file is not listed as source of the namespace" |> raise - | true, partialNS -> partialNS.TryGetType attName |> function - | true, resolution when resolution.DefinedAttributes |> Seq.exists compareAttributeName -> - resolution.Resolved.ValueOrApply missingResolutionException |> fst |> Some - | _ -> None + + match Parts.TryGetValue source with + | true, partial -> + match partial.TryGetType attName with + | true, resolution when Seq.exists compareAttributeName resolution.DefinedAttributes -> + resolution.Resolved.ValueOrApply missingResolutionException |> fst |> Some + | _ -> None + | false, _ -> + match TypesInReferences.TryGetValue attName with + | true, qsType -> + if Seq.exists marksAttribute qsType.Attributes + then Some qsType.Type + else None + | false, _ -> ArgumentException "Given source file is not part of the namespace" |> raise /// Returns the type with the given name defined in the given source file within this namespace. /// Note that files contained in referenced assemblies are *not* considered to be source files for the namespace! From b201e7cb0de35e78ecc6620e71ad8c93e8691338 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 13 Mar 2020 16:15:46 -0700 Subject: [PATCH 101/135] Moved the location of the classical control tests. Renamed 'hoist' to 'lift' in tests. --- .../Tests.Compiler/ClassicalControlTests.fs | 68 +- .../{LinkingTests => }/ClassicalControl.qs | 2056 +++++++++-------- .../Tests.Compiler/TestUtils/Signatures.fs | 26 +- .../Tests.Compiler/Tests.Compiler.fsproj | 6 +- 4 files changed, 1087 insertions(+), 1069 deletions(-) rename src/QsCompiler/Tests.Compiler/TestCases/{LinkingTests => }/ClassicalControl.qs (85%) diff --git a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index b584f44aa0..454ff162a2 100644 --- a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -23,12 +23,8 @@ type ClassicalControlTests () = let getTempFile () = new Uri(Path.GetFullPath(Path.GetRandomFileName())) let getManager uri content = CompilationUnitManager.InitializeFileManager(uri, content, compilationManager.PublishDiagnostics, compilationManager.LogException) - do let addOrUpdateSourceFile filePath = getManager (new Uri(filePath)) (File.ReadAllText filePath) |> compilationManager.AddOrUpdateSourceFileAsync |> ignore - Path.Combine ("TestCases", "LinkingTests", "Core.qs") |> Path.GetFullPath |> addOrUpdateSourceFile - Path.Combine ("TestCases", "LinkingTests", "QuantumProcessorExtensions.qs") |> Path.GetFullPath |> addOrUpdateSourceFile - let ReadAndChunkSourceFile fileName = - let sourceInput = Path.Combine ("TestCases", "LinkingTests", fileName) |> File.ReadAllText + let sourceInput = Path.Combine ("TestCases", fileName) |> File.ReadAllText sourceInput.Split ([|"==="|], StringSplitOptions.RemoveEmptyEntries) let BuildContent content = @@ -212,8 +208,8 @@ type ClassicalControlTests () = Assert.True(DoesCallSupportFunctors expectedFunctors call, sprintf "Callable %O did not support the expected functors" call.FullName) [] - [] - member this.``Basic Hoist`` () = + [] + member this.``Basic Lift`` () = let result = CompileClassicalControlTest 1 let generated = GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo" @@ -227,30 +223,30 @@ type ClassicalControlTests () = |> AssertSpecializationHasCalls generated [] - [] - member this.``Hoist Loops`` () = + [] + member this.``Lift Loops`` () = CompileClassicalControlTest 2 |> ignore [] - [] - member this.``Don't Hoist Single Call`` () = - // Single calls should not be hoisted into their own operation + [] + member this.``Don't Lift Single Call`` () = + // Single calls should not be lifted into their own operation CompileClassicalControlTest 3 |> ignore [] - [] - member this.``Hoist Single Non-Call`` () = - // Single expressions that are not calls should be hoisted into their own operation + [] + member this.``Lift Single Non-Call`` () = + // Single expressions that are not calls should be lifted into their own operation CompileClassicalControlTest 4 |> ignore [] - [] - member this.``Don't Hoist Return Statements`` () = + [] + member this.``Don't Lift Return Statements`` () = CompileClassicalControlTest 5 |> ignore [] - [] - member this.``All-Or-None Hoisting`` () = + [] + member this.``All-Or-None Lifting`` () = CompileClassicalControlTest 6 |> ignore [] @@ -338,18 +334,18 @@ type ClassicalControlTests () = |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg)) [] - [] - member this.``Don't Hoist Functions`` () = + [] + member this.``Don't Lift Functions`` () = CompileClassicalControlTest 13 |> ignore [] - [] - member this.``Hoist Self-Contained Mutable`` () = + [] + member this.``Lift Self-Contained Mutable`` () = CompileClassicalControlTest 14 |> ignore [] - [] - member this.``Don't Hoist General Mutable`` () = + [] + member this.``Don't Lift General Mutable`` () = CompileClassicalControlTest 15 |> ignore [] @@ -1202,27 +1198,27 @@ type ClassicalControlTests () = Assert.True((typeArgs = "Int, Double"), "Bar did not have the correct type arguments") [] - [] - member this.``Hoist Functor Application`` () = + [] + member this.``Lift Functor Application`` () = CompileClassicalControlTest 25 |> ignore [] - [] - member this.``Hoist Partial Application`` () = + [] + member this.``Lift Partial Application`` () = CompileClassicalControlTest 26 |> ignore [] - [] - member this.``Hoist Array Item Call`` () = + [] + member this.``Lift Array Item Call`` () = CompileClassicalControlTest 27 |> ignore [] - [] - member this.``Hoist One Not Both`` () = - // If hoisting is not needed on one of the blocks, it should not - // prevent the other blocks from being hoisted, as it would in + [] + member this.``Lift One Not Both`` () = + // If lifting is not needed on one of the blocks, it should not + // prevent the other blocks from being lifted, as it would in // the All-Or-Nothing test where a block is *invalid* for - // hoisting due to a set statement or return statement. + // lifting due to a set statement or return statement. CompileClassicalControlTest 28 |> ignore [] diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/ClassicalControl.qs similarity index 85% rename from src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs rename to src/QsCompiler/Tests.Compiler/TestCases/ClassicalControl.qs index fcfcfd5db8..f7f581b485 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/ClassicalControl.qs @@ -1,1018 +1,1040 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - - -namespace SubOps { - operation SubOp1() : Unit { } - operation SubOp2() : Unit { } - operation SubOp3() : Unit { } - - operation SubOpCA1() : Unit is Ctl + Adj { } - operation SubOpCA2() : Unit is Ctl + Adj { } - operation SubOpCA3() : Unit is Ctl + Adj { } -} - -// ================================= - -// Basic Hoist -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - SubOp3(); - let temp = 4; - using (q = Qubit()) { - let temp2 = q; - } - } - } -} - -// ================================= - -// Hoist Loops -namespace Microsoft.Quantum.Testing.ClassicalControl { - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero) { - for (index in 0 .. 3) { - let temp = index; - } - - repeat { - let success = true; - } until (success) - fixup { - let temp2 = 0; - } - } - } -} - -// ================================= - -// Don't Hoist Single Call -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - if (r == Zero) { - SubOp1(); - } - } -} - -// ================================= - -// Hoist Single Non-Call -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero) { - let temp = 2; - } - } -} - -// ================================= - -// Don't Hoist Return Statements -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - if (r == Zero) { - SubOp1(); - return (); - } - } -} - -// ================================= - -// All-Or-None Hoisting -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation IfInvalid() : Unit { - let r = Zero; - if (r == Zero) { - SubOp1(); - SubOp2(); - return (); - } else { - SubOp2(); - SubOp3(); - } - } - - operation ElseInvalid() : Unit { - let r = Zero; - if (r == Zero) { - SubOp1(); - SubOp2(); - } else { - SubOp2(); - SubOp3(); - return (); - } - } - - operation BothInvalid() : Unit { - let r = Zero; - if (r == Zero) { - SubOp1(); - SubOp2(); - return (); - } else { - SubOp2(); - SubOp3(); - return (); - } - } -} - -// ================================= - -// ApplyIfZero And ApplyIfOne -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero) { - SubOp1(); - } - - let temp = 0; - - if (r == One) { - SubOp2(); - } - } -} - -// ================================= - -// Apply If Zero Else One -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Bar(r : Result) : Unit { } - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero) { - Bar(r); - } else { - SubOp1(); - } - } -} - -// ================================= - -// Apply If One Else Zero -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Bar(r : Result) : Unit { } - - operation Foo() : Unit { - let r = One; - - if (r == One) { - Bar(r); - } else { - SubOp1(); - } - } -} - -// ================================= - -// If Elif -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero) { - SubOp1(); - } elif (r == One) { - SubOp2(); - } else { - SubOp3(); - } - } -} - -// ================================= - -// And Condition -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero and r == One) { - SubOp1(); - } else { - SubOp2(); - } - } -} - -// ================================= - -// Or Condition -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero or r == One) { - SubOp1(); - } else { - SubOp2(); - } - } -} - -// ================================= - -// Don't Hoist Functions -namespace Microsoft.Quantum.Testing.ClassicalControl { - - function Foo() : Unit { - let r = Zero; - - if (r == Zero) { - SubFunc1(); - SubFunc2(); - SubFunc3(); - } - } - - function SubFunc1() : Unit { } - function SubFunc2() : Unit { } - function SubFunc3() : Unit { } -} - -// ================================= - -// Hoist Self-Contained Mutable -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero) { - mutable temp = 3; - set temp = 4; - } - } -} - -// ================================= - -// Don't Hoist General Mutable -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - mutable temp = 3; - if (r == Zero) { - set temp = 4; - } - } -} - -// ================================= - -// Generics Support -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo<'A, 'B, 'C>() : Unit { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } -} - -// ================================= - -// Adjoint Support -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Provided() : Unit is Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - adjoint (...) { - let w = One; - - if (w == One) { - SubOpCA2(); - SubOpCA3(); - } - } - } - - operation Self() : Unit is Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - adjoint self; - } - - operation Invert() : Unit is Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - adjoint invert; - } -} - -// ================================= - -// Controlled Support -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Provided() : Unit is Ctl { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOpCA2(); - SubOpCA3(); - } - } - } - - operation Distribute() : Unit is Ctl { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled distribute; - } -} - -// ================================= - -// Controlled Adjoint Support - Provided -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation ProvidedBody() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled adjoint (ctl, ...) { - let y = One; - - if (y == One) { - SubOpCA2(); - SubOpCA3(); - } - } - } - - operation ProvidedAdjoint() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - adjoint (...) { - let w = One; - - if (w == One) { - SubOpCA3(); - SubOpCA1(); - } - } - - controlled adjoint (ctl, ...) { - let y = One; - - if (y == One) { - SubOpCA2(); - SubOpCA3(); - } - } - } - - operation ProvidedControlled() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOpCA3(); - SubOpCA1(); - } - } - - controlled adjoint (ctl, ...) { - let y = One; - - if (y == One) { - SubOpCA2(); - SubOpCA3(); - } - } - } - - operation ProvidedAll() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOpCA3(); - SubOpCA1(); - } - } - - adjoint (...) { - let y = One; - - if (y == One) { - SubOpCA2(); - SubOpCA3(); - } - } - - controlled adjoint (ctl, ...) { - let b = One; - - if (b == One) { - let temp1 = 0; - let temp2 = 0; - SubOpCA3(); - } - } - } -} - -// ================================= - -// Controlled Adjoint Support - Distribute -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation DistributeBody() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled adjoint distribute; - } - - operation DistributeAdjoint() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - adjoint (...) { - let w = One; - - if (w == One) { - SubOpCA3(); - SubOpCA1(); - } - } - - controlled adjoint distribute; - } - - operation DistributeControlled() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOpCA3(); - SubOpCA1(); - } - } - - controlled adjoint distribute; - } - - operation DistributeAll() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOpCA3(); - SubOpCA1(); - } - } - - adjoint (...) { - let y = One; - - if (y == One) { - SubOpCA2(); - SubOpCA3(); - } - } - - controlled adjoint distribute; - } -} - -// ================================= - -// Controlled Adjoint Support - Invert -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation InvertBody() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled adjoint invert; - } - - operation InvertAdjoint() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - adjoint (...) { - let w = One; - - if (w == One) { - SubOpCA3(); - SubOpCA1(); - } - } - - controlled adjoint invert; - } - - operation InvertControlled() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOpCA3(); - SubOpCA1(); - } - } - - controlled adjoint invert; - } - - operation InvertAll() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOpCA3(); - SubOpCA1(); - } - } - - adjoint (...) { - let y = One; - - if (y == One) { - SubOpCA2(); - SubOpCA3(); - } - } - - controlled adjoint invert; - } -} - -// ================================= - -// Controlled Adjoint Support - Self -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation SelfBody() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled adjoint self; - } - - operation SelfControlled() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOpCA3(); - SubOpCA1(); - } - } - - controlled adjoint self; - } -} - -// ================================= - -// Within Block Support -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = One; - within { - if (r == Zero) { - SubOpCA1(); - SubOpCA2(); - } - } apply { - if (r == One) { - SubOpCA2(); - SubOpCA3(); - } - } - } -} - -// ================================= - -// Arguments Partially Resolve Type Parameters -namespace Microsoft.Quantum.Testing.ClassicalControl { - - operation Bar<'Q, 'W> (q : 'Q, w : 'W) : Unit { } - - operation Foo() : Unit { - let r = Zero; - if (r == Zero) { - Bar(1, 1.0); - } - } -} - -// ================================= - -// Hoist Functor Application -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - if (r == Zero) { - Adjoint SubOpCA1(); - } - } -} - -// ================================= - -// Hoist Partial Application -namespace Microsoft.Quantum.Testing.ClassicalControl { - - operation Bar (q : Int, w : Double) : Unit { } - - operation Foo() : Unit { - let r = Zero; - if (r == Zero) { - (Bar(1, _))(1.0); - } - } -} - -// ================================= - -// Hoist Array Item Call -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let f = [SubOp1]; - let r = Zero; - if (r == Zero) { - f[0](); - } - } -} - -// ================================= - -// Hoist One Not Both -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - if (r == Zero) { - SubOp1(); - SubOp2(); - } - else { - SubOp3(); - } - } -} - -// ================================= - -// Apply Conditionally -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Bar(r : Result) : Unit { } - - operation Foo() : Unit { - let r1 = Zero; - let r2 = Zero; - - if (r1 == r2) { - Bar(r1); - } - else { - SubOp1(); - } - } -} - -// ================================= - -// Apply Conditionally With NoOp -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Bar(r : Result) : Unit { } - - operation Foo() : Unit { - let r1 = Zero; - let r2 = Zero; - - if (r1 == r2) { - Bar(r1); - } - } -} - -// ================================= - -// Inequality with ApplyConditionally -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Bar(r : Result) : Unit { } - - operation Foo() : Unit { - let r1 = Zero; - let r2 = Zero; - - if (r1 != r2) { - Bar(r1); - } - else { - SubOp1(); - } - } -} - -// ================================= - -// Inequality with Apply If One Else Zero -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Bar(r : Result) : Unit { } - - operation Foo() : Unit { - let r = Zero; - - if (r != Zero) { - Bar(r); - } - else { - SubOp1(); - } - } -} - -// ================================= - -// Inequality with Apply If Zero Else One -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Bar(r : Result) : Unit { } - - operation Foo() : Unit { - let r = Zero; - - if (r != One) { - Bar(r); - } - else { - SubOp1(); - } - } -} - -// ================================= - -// Inequality with ApplyIfOne -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r != Zero) { - SubOp1(); - } - } -} - -// ================================= - -// Inequality with ApplyIfZero -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r != One) { - SubOp1(); - } - } -} - -// ================================= - -// Literal on the Left -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (Zero == r) { - SubOp1(); - } - } +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + + +namespace Microsoft.Quantum.Simulation.QuantumProcessor.Extensions { + operation ApplyIfZero<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit), zeroArg : 'T)) : Unit { } + operation ApplyIfZeroA<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj), zeroArg : 'T)) : Unit is Adj { } + operation ApplyIfZeroC<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl), zeroArg : 'T)) : Unit is Ctl { } + operation ApplyIfZeroCA<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl + Adj), zeroArg : 'T)) : Unit is Ctl + Adj { } + + operation ApplyIfOne<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit), oneArg : 'T)) : Unit { } + operation ApplyIfOneA<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Adj), oneArg : 'T)) : Unit is Adj { } + operation ApplyIfOneC<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Ctl), oneArg : 'T)) : Unit is Ctl { } + operation ApplyIfOneCA<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Ctl + Adj), oneArg : 'T)) : Unit is Ctl + Adj { } + + operation ApplyIfElseR<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit), zeroArg : 'T) , (onResultOneOp : ('U => Unit), oneArg : 'U)) : Unit { } + operation ApplyIfElseRA<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Adj), oneArg : 'U)) : Unit is Adj { } + operation ApplyIfElseRC<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Ctl), oneArg : 'U)) : Unit is Ctl { } + operation ApplyIfElseRCA<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj + Ctl), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Adj + Ctl), oneArg : 'U)) : Unit is Ctl + Adj { } + + operation ApplyConditionally<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit), equalArg : 'T) , (onNonEqualOp : ('U => Unit), nonEqualArg : 'U)) : Unit { } + operation ApplyConditionallyA<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit is Adj), equalArg : 'T) , (onNonEqualOp : ('U => Unit is Adj), nonEqualArg : 'U)) : Unit is Adj { } + operation ApplyConditionallyC<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit is Ctl), equalArg : 'T) , (onNonEqualOp : ('U => Unit is Ctl), nonEqualArg : 'U)) : Unit is Ctl { } + operation ApplyConditionallyCA<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit is Ctl + Adj), equalArg : 'T) , (onNonEqualOp : ('U => Unit is Ctl + Adj), nonEqualArg : 'U)) : Unit is Ctl + Adj { } +} + +namespace SubOps { + operation SubOp1() : Unit { } + operation SubOp2() : Unit { } + operation SubOp3() : Unit { } + + operation SubOpCA1() : Unit is Ctl + Adj { } + operation SubOpCA2() : Unit is Ctl + Adj { } + operation SubOpCA3() : Unit is Ctl + Adj { } +} + +// ================================= + +// Basic Lift +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (r == Zero) { + SubOp1(); + SubOp2(); + SubOp3(); + let temp = 4; + using (q = Qubit()) { + let temp2 = q; + } + } + } +} + +// ================================= + +// Lift Loops +namespace Microsoft.Quantum.Testing.ClassicalControl { + + operation Foo() : Unit { + let r = Zero; + + if (r == Zero) { + for (index in 0 .. 3) { + let temp = index; + } + + repeat { + let success = true; + } until (success) + fixup { + let temp2 = 0; + } + } + } +} + +// ================================= + +// Don't Lift Single Call +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + if (r == Zero) { + SubOp1(); + } + } +} + +// ================================= + +// Lift Single Non-Call +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (r == Zero) { + let temp = 2; + } + } +} + +// ================================= + +// Don't Lift Return Statements +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + if (r == Zero) { + SubOp1(); + return (); + } + } +} + +// ================================= + +// All-Or-None Lifting +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation IfInvalid() : Unit { + let r = Zero; + if (r == Zero) { + SubOp1(); + SubOp2(); + return (); + } else { + SubOp2(); + SubOp3(); + } + } + + operation ElseInvalid() : Unit { + let r = Zero; + if (r == Zero) { + SubOp1(); + SubOp2(); + } else { + SubOp2(); + SubOp3(); + return (); + } + } + + operation BothInvalid() : Unit { + let r = Zero; + if (r == Zero) { + SubOp1(); + SubOp2(); + return (); + } else { + SubOp2(); + SubOp3(); + return (); + } + } +} + +// ================================= + +// ApplyIfZero And ApplyIfOne +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (r == Zero) { + SubOp1(); + } + + let temp = 0; + + if (r == One) { + SubOp2(); + } + } +} + +// ================================= + +// Apply If Zero Else One +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Bar(r : Result) : Unit { } + + operation Foo() : Unit { + let r = Zero; + + if (r == Zero) { + Bar(r); + } else { + SubOp1(); + } + } +} + +// ================================= + +// Apply If One Else Zero +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Bar(r : Result) : Unit { } + + operation Foo() : Unit { + let r = One; + + if (r == One) { + Bar(r); + } else { + SubOp1(); + } + } +} + +// ================================= + +// If Elif +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (r == Zero) { + SubOp1(); + } elif (r == One) { + SubOp2(); + } else { + SubOp3(); + } + } +} + +// ================================= + +// And Condition +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (r == Zero and r == One) { + SubOp1(); + } else { + SubOp2(); + } + } +} + +// ================================= + +// Or Condition +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (r == Zero or r == One) { + SubOp1(); + } else { + SubOp2(); + } + } +} + +// ================================= + +// Don't Lift Functions +namespace Microsoft.Quantum.Testing.ClassicalControl { + + function Foo() : Unit { + let r = Zero; + + if (r == Zero) { + SubFunc1(); + SubFunc2(); + SubFunc3(); + } + } + + function SubFunc1() : Unit { } + function SubFunc2() : Unit { } + function SubFunc3() : Unit { } +} + +// ================================= + +// Lift Self-Contained Mutable +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (r == Zero) { + mutable temp = 3; + set temp = 4; + } + } +} + +// ================================= + +// Don't Lift General Mutable +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + mutable temp = 3; + if (r == Zero) { + set temp = 4; + } + } +} + +// ================================= + +// Generics Support +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo<'A, 'B, 'C>() : Unit { + let r = Zero; + + if (r == Zero) { + SubOp1(); + SubOp2(); + } + } +} + +// ================================= + +// Adjoint Support +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Provided() : Unit is Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + adjoint (...) { + let w = One; + + if (w == One) { + SubOpCA2(); + SubOpCA3(); + } + } + } + + operation Self() : Unit is Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + adjoint self; + } + + operation Invert() : Unit is Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + adjoint invert; + } +} + +// ================================= + +// Controlled Support +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Provided() : Unit is Ctl { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled (ctl, ...) { + let w = One; + + if (w == One) { + SubOpCA2(); + SubOpCA3(); + } + } + } + + operation Distribute() : Unit is Ctl { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled distribute; + } +} + +// ================================= + +// Controlled Adjoint Support - Provided +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation ProvidedBody() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled adjoint (ctl, ...) { + let y = One; + + if (y == One) { + SubOpCA2(); + SubOpCA3(); + } + } + } + + operation ProvidedAdjoint() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + adjoint (...) { + let w = One; + + if (w == One) { + SubOpCA3(); + SubOpCA1(); + } + } + + controlled adjoint (ctl, ...) { + let y = One; + + if (y == One) { + SubOpCA2(); + SubOpCA3(); + } + } + } + + operation ProvidedControlled() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled (ctl, ...) { + let w = One; + + if (w == One) { + SubOpCA3(); + SubOpCA1(); + } + } + + controlled adjoint (ctl, ...) { + let y = One; + + if (y == One) { + SubOpCA2(); + SubOpCA3(); + } + } + } + + operation ProvidedAll() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled (ctl, ...) { + let w = One; + + if (w == One) { + SubOpCA3(); + SubOpCA1(); + } + } + + adjoint (...) { + let y = One; + + if (y == One) { + SubOpCA2(); + SubOpCA3(); + } + } + + controlled adjoint (ctl, ...) { + let b = One; + + if (b == One) { + let temp1 = 0; + let temp2 = 0; + SubOpCA3(); + } + } + } +} + +// ================================= + +// Controlled Adjoint Support - Distribute +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation DistributeBody() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled adjoint distribute; + } + + operation DistributeAdjoint() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + adjoint (...) { + let w = One; + + if (w == One) { + SubOpCA3(); + SubOpCA1(); + } + } + + controlled adjoint distribute; + } + + operation DistributeControlled() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled (ctl, ...) { + let w = One; + + if (w == One) { + SubOpCA3(); + SubOpCA1(); + } + } + + controlled adjoint distribute; + } + + operation DistributeAll() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled (ctl, ...) { + let w = One; + + if (w == One) { + SubOpCA3(); + SubOpCA1(); + } + } + + adjoint (...) { + let y = One; + + if (y == One) { + SubOpCA2(); + SubOpCA3(); + } + } + + controlled adjoint distribute; + } +} + +// ================================= + +// Controlled Adjoint Support - Invert +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation InvertBody() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled adjoint invert; + } + + operation InvertAdjoint() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + adjoint (...) { + let w = One; + + if (w == One) { + SubOpCA3(); + SubOpCA1(); + } + } + + controlled adjoint invert; + } + + operation InvertControlled() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled (ctl, ...) { + let w = One; + + if (w == One) { + SubOpCA3(); + SubOpCA1(); + } + } + + controlled adjoint invert; + } + + operation InvertAll() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled (ctl, ...) { + let w = One; + + if (w == One) { + SubOpCA3(); + SubOpCA1(); + } + } + + adjoint (...) { + let y = One; + + if (y == One) { + SubOpCA2(); + SubOpCA3(); + } + } + + controlled adjoint invert; + } +} + +// ================================= + +// Controlled Adjoint Support - Self +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation SelfBody() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled adjoint self; + } + + operation SelfControlled() : Unit is Ctl + Adj { + body (...) { + let r = Zero; + + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } + + controlled (ctl, ...) { + let w = One; + + if (w == One) { + SubOpCA3(); + SubOpCA1(); + } + } + + controlled adjoint self; + } +} + +// ================================= + +// Within Block Support +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = One; + within { + if (r == Zero) { + SubOpCA1(); + SubOpCA2(); + } + } apply { + if (r == One) { + SubOpCA2(); + SubOpCA3(); + } + } + } +} + +// ================================= + +// Arguments Partially Resolve Type Parameters +namespace Microsoft.Quantum.Testing.ClassicalControl { + + operation Bar<'Q, 'W> (q : 'Q, w : 'W) : Unit { } + + operation Foo() : Unit { + let r = Zero; + if (r == Zero) { + Bar(1, 1.0); + } + } +} + +// ================================= + +// Lift Functor Application +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + if (r == Zero) { + Adjoint SubOpCA1(); + } + } +} + +// ================================= + +// Lift Partial Application +namespace Microsoft.Quantum.Testing.ClassicalControl { + + operation Bar (q : Int, w : Double) : Unit { } + + operation Foo() : Unit { + let r = Zero; + if (r == Zero) { + (Bar(1, _))(1.0); + } + } +} + +// ================================= + +// Lift Array Item Call +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let f = [SubOp1]; + let r = Zero; + if (r == Zero) { + f[0](); + } + } +} + +// ================================= + +// Lift One Not Both +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + if (r == Zero) { + SubOp1(); + SubOp2(); + } + else { + SubOp3(); + } + } +} + +// ================================= + +// Apply Conditionally +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Bar(r : Result) : Unit { } + + operation Foo() : Unit { + let r1 = Zero; + let r2 = Zero; + + if (r1 == r2) { + Bar(r1); + } + else { + SubOp1(); + } + } +} + +// ================================= + +// Apply Conditionally With NoOp +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Bar(r : Result) : Unit { } + + operation Foo() : Unit { + let r1 = Zero; + let r2 = Zero; + + if (r1 == r2) { + Bar(r1); + } + } +} + +// ================================= + +// Inequality with ApplyConditionally +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Bar(r : Result) : Unit { } + + operation Foo() : Unit { + let r1 = Zero; + let r2 = Zero; + + if (r1 != r2) { + Bar(r1); + } + else { + SubOp1(); + } + } +} + +// ================================= + +// Inequality with Apply If One Else Zero +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Bar(r : Result) : Unit { } + + operation Foo() : Unit { + let r = Zero; + + if (r != Zero) { + Bar(r); + } + else { + SubOp1(); + } + } +} + +// ================================= + +// Inequality with Apply If Zero Else One +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Bar(r : Result) : Unit { } + + operation Foo() : Unit { + let r = Zero; + + if (r != One) { + Bar(r); + } + else { + SubOp1(); + } + } +} + +// ================================= + +// Inequality with ApplyIfOne +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (r != Zero) { + SubOp1(); + } + } +} + +// ================================= + +// Inequality with ApplyIfZero +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (r != One) { + SubOp1(); + } + } +} + +// ================================= + +// Literal on the Left +namespace Microsoft.Quantum.Testing.ClassicalControl { + open SubOps; + + operation Foo() : Unit { + let r = Zero; + + if (Zero == r) { + SubOp1(); + } + } } \ No newline at end of file diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs index c4d7f6efed..7abc75e0ac 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -216,25 +216,25 @@ let private _DefaultWithOperation = _MakeTypeMap [| /// Expected callable signatures to be found when running Classical Control tests let public ClassicalControlSignatures = [| - (_DefaultTypes, [| // Basic Hoist + (_DefaultTypes, [| // Basic Lift ClassicalControlNs, "Foo", [||], "Unit"; // The original operation ClassicalControlNs, "_Foo", [|"Result"|], "Unit"; // The generated operation |]) - (_DefaultTypes, [| // Hoist Loops + (_DefaultTypes, [| // Lift Loops ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| // Don't Hoist Single Call + (_DefaultTypes, [| // Don't Lift Single Call ClassicalControlNs, "Foo", [||], "Unit" |]) - (_DefaultTypes, [| // Hoist Single Non-Call + (_DefaultTypes, [| // Lift Single Non-Call ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| // Don't Hoist Return Statements + (_DefaultTypes, [| // Don't Lift Return Statements ClassicalControlNs, "Foo", [||], "Unit" |]) - (_DefaultTypes, [| // All-Or-None Hoisting + (_DefaultTypes, [| // All-Or-None Lifting ClassicalControlNs, "IfInvalid", [||], "Unit" ClassicalControlNs, "ElseInvalid", [||], "Unit" ClassicalControlNs, "BothInvalid", [||], "Unit" @@ -259,17 +259,17 @@ let public ClassicalControlSignatures = (_DefaultTypes, [| // Or Condition ClassicalControlNs, "Foo", [||], "Unit" |]) - (_DefaultTypes, [| // Don't Hoist Functions + (_DefaultTypes, [| // Don't Lift Functions ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "SubFunc1", [||], "Unit" ClassicalControlNs, "SubFunc2", [||], "Unit" ClassicalControlNs, "SubFunc3", [||], "Unit" |]) - (_DefaultTypes, [| // Hoist Self-Contained Mutable + (_DefaultTypes, [| // Lift Self-Contained Mutable ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| // Don't Hoist General Mutable + (_DefaultTypes, [| // Don't Lift General Mutable ClassicalControlNs, "Foo", [||], "Unit" |]) (_DefaultTypes, [| // Generics Support @@ -368,20 +368,20 @@ let public ClassicalControlSignatures = ClassicalControlNs, "Bar", [|"Bar.Q";"Bar.W"|], "Unit" ClassicalControlNs, "Foo", [||], "Unit" |]) - (_DefaultTypes, [| // Hoist Functor Application + (_DefaultTypes, [| // Lift Functor Application ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultTypes, [| // Hoist Partial Application + (_DefaultTypes, [| // Lift Partial Application ClassicalControlNs, "Bar", [|"Int";"Double"|], "Unit" ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) - (_DefaultWithOperation, [| // Hoist Array Item Call + (_DefaultWithOperation, [| // Lift Array Item Call ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"SubOp1Type[]";"Result"|], "Unit" |]) - (_DefaultTypes, [| // Hoist One Not Both + (_DefaultTypes, [| // Lift One Not Both ClassicalControlNs, "Foo", [||], "Unit" ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index b7f2118e59..3578b1cd5f 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -44,9 +44,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -126,6 +123,9 @@ PreserveNewest + + PreserveNewest + From 3b42eee9447ec508b35cceb5df1aab17e839f4ee Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 13 Mar 2020 16:37:44 -0700 Subject: [PATCH 102/135] Removed unused file. --- .../QuantumProcessorExtensions.qs | 25 ------------------- .../Tests.Compiler/Tests.Compiler.fsproj | 3 --- 2 files changed, 28 deletions(-) delete mode 100644 src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/QuantumProcessorExtensions.qs diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/QuantumProcessorExtensions.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/QuantumProcessorExtensions.qs deleted file mode 100644 index d125169a19..0000000000 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/QuantumProcessorExtensions.qs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - - -namespace Microsoft.Quantum.Simulation.QuantumProcessor.Extensions { - operation ApplyIfZero<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit), zeroArg : 'T)) : Unit { } - operation ApplyIfZeroA<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj), zeroArg : 'T)) : Unit is Adj { } - operation ApplyIfZeroC<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl), zeroArg : 'T)) : Unit is Ctl { } - operation ApplyIfZeroCA<'T>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl + Adj), zeroArg : 'T)) : Unit is Ctl + Adj { } - - operation ApplyIfOne<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit), oneArg : 'T)) : Unit { } - operation ApplyIfOneA<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Adj), oneArg : 'T)) : Unit is Adj { } - operation ApplyIfOneC<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Ctl), oneArg : 'T)) : Unit is Ctl { } - operation ApplyIfOneCA<'T>(measurementResult : Result, (onResultOneOp : ('T => Unit is Ctl + Adj), oneArg : 'T)) : Unit is Ctl + Adj { } - - operation ApplyIfElseR<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit), zeroArg : 'T) , (onResultOneOp : ('U => Unit), oneArg : 'U)) : Unit { } - operation ApplyIfElseRA<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Adj), oneArg : 'U)) : Unit is Adj { } - operation ApplyIfElseRC<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Ctl), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Ctl), oneArg : 'U)) : Unit is Ctl { } - operation ApplyIfElseRCA<'T,'U>(measurementResult : Result, (onResultZeroOp : ('T => Unit is Adj + Ctl), zeroArg : 'T) , (onResultOneOp : ('U => Unit is Adj + Ctl), oneArg : 'U)) : Unit is Ctl + Adj { } - - operation ApplyConditionally<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit), equalArg : 'T) , (onNonEqualOp : ('U => Unit), nonEqualArg : 'U)) : Unit { } - operation ApplyConditionallyA<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit is Adj), equalArg : 'T) , (onNonEqualOp : ('U => Unit is Adj), nonEqualArg : 'U)) : Unit is Adj { } - operation ApplyConditionallyC<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit is Ctl), equalArg : 'T) , (onNonEqualOp : ('U => Unit is Ctl), nonEqualArg : 'U)) : Unit is Ctl { } - operation ApplyConditionallyCA<'T,'U>(measurementResults : Result[], resultsValues : Result[], (onEqualOp : ('T => Unit is Ctl + Adj), equalArg : 'T) , (onNonEqualOp : ('U => Unit is Ctl + Adj), nonEqualArg : 'U)) : Unit is Ctl + Adj { } -} \ No newline at end of file diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 2ccc57550c..1138d77d0f 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -23,9 +23,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest From db58ff2f3f517206563b14b509aa5651d1c521d9 Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Fri, 13 Mar 2020 17:09:31 -0700 Subject: [PATCH 103/135] Added ToDo for lifting functions. --- src/QsCompiler/Transformations/ContentLifting.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/QsCompiler/Transformations/ContentLifting.cs b/src/QsCompiler/Transformations/ContentLifting.cs index c5b22396f2..3368999cd6 100644 --- a/src/QsCompiler/Transformations/ContentLifting.cs +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -434,6 +434,8 @@ public override ResolvedTypeKind OnTypeParameter(QsTypeParameter tp) /// the operation will have all the valid type parameters of the calling context as type parameters. /// A call to the new operation is also returned with all the valid type parameters and known variables /// being forwarded to the new operation as arguments. + /// + /// ToDo: This transformation currently does not support lifting inside of functions. /// public class LiftContent : SyntaxTreeTransformation where T : LiftContent.TransformationState @@ -489,6 +491,7 @@ public override QsSpecialization OnControlledAdjointSpecialization(QsSpecializat return rtrn; } + // ToDo: We will want to support lifting of functions in the future public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being lifted public override QsNamespace OnNamespace(QsNamespace ns) From 4a384764d3c30a1b9fa20dad1022724388f14dc5 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 17:17:30 -0700 Subject: [PATCH 104/135] Fix bug in PossibleQualifications --- src/QsCompiler/Core/SymbolTable.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index de872c44bb..94e2c3942f 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -878,7 +878,7 @@ and NamespaceManager match Namespaces.TryGetValue nsName with | true, ns when ns.Sources.Contains source -> match (ns.ImportedNamespaces source).TryGetValue builtIn.Namespace with - | true, null when ResolutionResult.exists (ns.TryFindType builtIn.Name) || + | true, null when not (ns.TryFindType builtIn.Name |> ResolutionResult.exists) || nsName.Value = builtIn.Namespace.Value -> [""; builtIn.Namespace.Value] | true, null -> [builtIn.Namespace.Value] // the built-in type or callable is shadowed From 9b614ae48670c2d279db26a5f9e0ba130aafc53d Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 19:05:06 -0700 Subject: [PATCH 105/135] Fix bug in resolveInOpenNamespaces --- src/QsCompiler/Core/SymbolTable.fs | 68 ++++++++++++++++++------------ 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 94e2c3942f..07471484ed 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -20,14 +20,15 @@ open Newtonsoft.Json /// Represents the outcome of resolving a symbol in the symbol table. type ResolutionResult<'T> = - /// The symbol resolved successfully. + /// A result indicating that the symbol resolved successfully. | Found of 'T - /// An unqualified symbol is ambiguous, and it is possible to resolve it to more than one namespace. Includes the - /// list of possible namespaces. + /// A result indicating that an unqualified symbol is ambiguous, and it is possible to resolve it to more than one + /// namespace. Includes the list of possible namespaces. | Ambiguous of NonNullable seq - /// The symbol resolved to a declaration which is not accessible from the location referencing it. + /// A result indicating that the symbol resolved to a declaration which is not accessible from the location + /// referencing it. | Inaccessible - /// No declaration with that name was found. + /// A result indicating that no declaration with that name was found. | NotFound @@ -48,18 +49,34 @@ module private ResolutionResult = /// Converts the resolution result to a 0- or 1-element list containing the Found value if the result is a Found. let private toList = toOption >> Option.toList - /// Returns the first result matching Found, Inaccessible or NotFound, in decreasing order of priority. If the - /// sequence contains a Found result, the rest of the sequence after it is not evaluated. - let internal tryFirst results = - results - |> Seq.tryPick (function | Found callable -> Some (Found callable) | _ -> None) - |> Option.orElseWith (fun () -> - results |> Seq.tryPick (function | Inaccessible -> Some (Inaccessible) | _ -> None)) - |> Option.defaultValue NotFound + /// Sorts the sequence of results in order of Found, Ambiguous, Inaccessible, and NotFound. + /// + /// If the sequence contains a Found result, the rest of the sequence after it is not automatically evaluated. + /// Otherwise, the whole sequence may be evaluated by calling sort. + let private sort results = + let choosers = [ + function | Found callable -> Some (Found callable) | _ -> None + function | Ambiguous namespaces -> Some (Ambiguous namespaces) | _ -> None + function | Inaccessible -> Some Inaccessible | _ -> None + function | NotFound -> Some NotFound | _ -> None + ] + choosers + |> Seq.map (fun chooser -> Seq.choose chooser results) + |> Seq.concat + + /// Returns the first item of the sequence of results if there is one, or NotFound if the sequence is empty. + let private tryHead<'T> : seq> -> ResolutionResult<'T> = + Seq.tryHead >> Option.defaultValue NotFound + + /// Returns the first Found result in the sequence if it exists. If not, returns the first Ambiguous result if it + /// exists. Repeats for Inaccessible and NotFound in this order. If the sequence is empty, returns NotFound. + let internal tryFirstBest<'T> : seq> -> ResolutionResult<'T> = + sort >> tryHead /// Returns a Found result if there is only one in the sequence. If there is more than one, returns an Ambiguous - /// result. Otherwise, returns the same value as ResolutionResult.tryFirst. - let internal tryExactlyOne nsGetter results = + /// result. Otherwise, returns the first value from ResolutionResult.sort. + let internal tryExactlyOne<'T> (nsGetter : 'T -> NonNullable) + (results : seq>) : ResolutionResult<'T> = let found = results |> Seq.filter (function | Found _ -> true | _ -> false) if Seq.length found > 1 then found @@ -68,17 +85,15 @@ module private ResolutionResult = |> Ambiguous else found |> Seq.tryExactlyOne - |> Option.defaultWith (fun () -> tryFirst results) + |> Option.defaultWith (fun () -> tryFirstBest results) /// Returns a Found result if there is only one in the sequence. If there is more than one, raises an exception. /// Otherwise, returns the same value as ResolutionResult.tryFirst. let internal exactlyOne<'T> : seq> -> ResolutionResult<'T> = tryExactlyOne (fun _ -> NonNullable<_>.New "") >> function - | Found value -> Found value | Ambiguous _ -> QsCompilerError.Raise "Resolution is ambiguous" Exception () |> raise - | Inaccessible -> Inaccessible - | NotFound -> NotFound + | result -> result /// Returns true if the resolution result indicates that a resolution exists (Found or Ambiguous). let internal exists = function @@ -555,7 +570,7 @@ and Namespace private seq { yield findInReferences () yield Seq.map findInPartial Parts.Values |> ResolutionResult.exactlyOne } - |> ResolutionResult.tryFirst + |> ResolutionResult.tryFirstBest /// Returns a resolution result for the callable with the given name containing the name of the source file or /// referenced assembly in which it is declared, and a string indicating the redirection if it has been deprecated. @@ -593,7 +608,7 @@ and Namespace private seq { yield findInReferences () yield Seq.map findInPartial Parts.Values |> ResolutionResult.exactlyOne } - |> ResolutionResult.tryFirst + |> ResolutionResult.tryFirstBest /// Sets the resolution for the type with the given name in the given source file to the given type, /// and replaces the resolved attributes with the given values. @@ -850,10 +865,9 @@ and NamespaceManager let resolveWithNsName (ns : Namespace) = resolver ns |> ResolutionResult.map (fun value -> (ns.Name, value)) let currentNs, importedNs = OpenNamespaces (nsName, source) - List.Cons (currentNs, importedNs) - |> List.distinctBy (fun ns -> ns.Name) - |> Seq.map resolveWithNsName - |> ResolutionResult.tryExactlyOne fst + seq { yield resolveWithNsName currentNs + yield Seq.map resolveWithNsName importedNs |> ResolutionResult.tryExactlyOne fst } + |> ResolutionResult.tryFirstBest /// Given a qualifier for a symbol name, returns the corresponding namespace as Some /// if such a namespace or such a namespace short name within the given parent namespace and source file exists. @@ -1564,7 +1578,7 @@ and NamespaceManager | Some ns -> seq { yield findInReferences ns yield findInSources ns declSource } - |> ResolutionResult.tryFirst + |> ResolutionResult.tryFirstBest finally syncRoot.ExitReadLock() /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader in a ResolutionResult if @@ -1648,7 +1662,7 @@ and NamespaceManager | Some ns -> seq { yield findInReferences ns yield findInSources ns declSource } - |> ResolutionResult.tryFirst + |> ResolutionResult.tryFirstBest finally syncRoot.ExitReadLock() /// Given a qualified type name, returns the corresponding TypeDeclarationHeader in a ResolutionResult if the From 7c0e5e4d1a274dddc3028f1834a4bb8d567e9f30 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 19:35:20 -0700 Subject: [PATCH 106/135] Re-enable exception in SpecializationsDefinedInAllSources --- src/QsCompiler/Core/SymbolTable.fs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 07471484ed..0ab96e84e5 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -95,8 +95,15 @@ module private ResolutionResult = Exception () |> raise | result -> result - /// Returns true if the resolution result indicates that a resolution exists (Found or Ambiguous). + /// Returns true if the resolution result indicates that a resolution exists (Found, Ambiguous or Inaccessible). let internal exists = function + | Found _ + | Ambiguous _ + | Inaccessible -> true + | NotFound -> false + + /// Returns true if the resolution result indicates that a resolution is accessible (Found or Ambiguous). + let internal isAccessible = function | Found _ | Ambiguous _ -> true | Inaccessible @@ -523,15 +530,13 @@ and Namespace private /// This excludes specializations that are defined in files contained in referenced assemblies. /// Throws an ArgumentException if no callable with the given name is defined in this namespace. member internal this.SpecializationsDefinedInAllSources cName = - match this.TryFindCallable cName with - | Found _ -> - (Parts.Values.SelectMany (fun partial -> - partial.GetSpecializations cName - |> Seq.map (fun (kind, decl) -> kind, (partial.Source, decl)))).ToImmutableArray() - | _ -> - // TODO: - // ArgumentException "no callable with the given name exist within the namespace" |> raise - ImmutableArray<_>.Empty + let getSpecializationInPartial (partial : PartialNamespace) = + partial.GetSpecializations cName + |> Seq.map (fun (kind, decl) -> kind, (partial.Source, decl)) + + if this.TryFindCallable cName |> ResolutionResult.exists + then (Parts.Values.SelectMany getSpecializationInPartial).ToImmutableArray() + else ArgumentException "no callable with the given name exist within the namespace" |> raise /// Returns a resolution result for the type with the given name containing the name of the source file or @@ -892,7 +897,7 @@ and NamespaceManager match Namespaces.TryGetValue nsName with | true, ns when ns.Sources.Contains source -> match (ns.ImportedNamespaces source).TryGetValue builtIn.Namespace with - | true, null when not (ns.TryFindType builtIn.Name |> ResolutionResult.exists) || + | true, null when not (ns.TryFindType builtIn.Name |> ResolutionResult.isAccessible) || nsName.Value = builtIn.Namespace.Value -> [""; builtIn.Namespace.Value] | true, null -> [builtIn.Namespace.Value] // the built-in type or callable is shadowed From 05b2b2a23a3240131e3b999f8bb0da8a96bb1728 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 19:59:30 -0700 Subject: [PATCH 107/135] Re-enable assertion about external specializations --- .../CompilationManager/CompilationUnit.cs | 12 ++++--- src/QsCompiler/Core/SymbolTable.fs | 33 ++++++++++--------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index c73c7bdedc..b8fefef753 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -476,9 +476,13 @@ private QsCallable GetImportedCallable(CallableDeclarationHeader header) // TODO: this needs to be adapted if we want to support external specializations if (header == null) throw new ArgumentNullException(nameof(header)); var importedSpecs = this.GlobalSymbols.ImportedSpecializations(header.QualifiedName); - var definedSpecs = this.GlobalSymbols.DefinedSpecializations(header.QualifiedName); - // TODO: This assertion is not valid if a defined callable is shadowing an imported internal callable. - // QsCompilerError.Verify(definedSpecs.Length == 0, "external specializations are currently not supported"); + if (Namespace.IsDeclarationAccessible(false, header.Modifiers.Access)) + { + var definedSpecs = this.GlobalSymbols.DefinedSpecializations(header.QualifiedName); + QsCompilerError.Verify(definedSpecs.Length == 0, + "external specializations are currently not supported"); + } + var specializations = importedSpecs.Select(imported => { var (specHeader, implementation) = imported; @@ -496,7 +500,7 @@ private QsCallable GetImportedCallable(CallableDeclarationHeader header) header.Attributes, header.Modifiers, header.SourceFile, - header.Location, + header.Location, header.Signature, header.ArgumentTuple, specializations, diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 0ab96e84e5..0a2028d5eb 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -347,7 +347,7 @@ and Namespace private let isNameAvailable name = let isAvailableWith declarationGetter accessibilityGetter sameAssembly = match declarationGetter name with - | true, value -> not <| Namespace.IsDeclarationAccessible sameAssembly (accessibilityGetter value) + | true, value -> not <| Namespace.IsDeclarationAccessible (sameAssembly, accessibilityGetter value) | false, _ -> true isAvailableWith CallablesInReferences.TryGetValue (fun c -> c.Modifiers.Access) false && @@ -358,7 +358,8 @@ and Namespace private /// Returns whether a declaration is accessible from the calling location, given whether the calling location is in /// the same assembly as the declaration, and the declaration's access modifier. - static member internal IsDeclarationAccessible sameAssembly = function + static member IsDeclarationAccessible (sameAssembly, access) = + match access with | DefaultAccess -> true | Internal -> sameAssembly @@ -556,7 +557,7 @@ and Namespace private let findInReferences () = match TypesInReferences.TryGetValue tName with | true, qsType -> - if Namespace.IsDeclarationAccessible false qsType.Modifiers.Access + if Namespace.IsDeclarationAccessible (false, qsType.Modifiers.Access) then Found (qsType.SourceFile, SymbolResolution.TryFindRedirect qsType.Attributes, qsType.Modifiers.Access) @@ -566,7 +567,7 @@ and Namespace private let findInPartial (partial : PartialNamespace) = match partial.TryGetType tName with | true, qsType -> - if Namespace.IsDeclarationAccessible true qsType.Modifiers.Access + if Namespace.IsDeclarationAccessible (true, qsType.Modifiers.Access) then Found (partial.Source, SymbolResolution.TryFindRedirectInUnresolved checkDeprecation qsType.DefinedAttributes, qsType.Modifiers.Access) @@ -597,7 +598,7 @@ and Namespace private let findInReferences () = match CallablesInReferences.TryGetValue cName with | true, callable -> - if Namespace.IsDeclarationAccessible false callable.Modifiers.Access + if Namespace.IsDeclarationAccessible (false, callable.Modifiers.Access) then Found (callable.SourceFile, SymbolResolution.TryFindRedirect callable.Attributes) else Inaccessible | false, _ -> NotFound @@ -605,7 +606,7 @@ and Namespace private let findInPartial (partial : PartialNamespace) = match partial.TryGetCallable cName with | true, (_, callable) -> - if Namespace.IsDeclarationAccessible true callable.Modifiers.Access + if Namespace.IsDeclarationAccessible (true, callable.Modifiers.Access) then Found (partial.Source, SymbolResolution.TryFindRedirectInUnresolved checkDeprecation callable.DefinedAttributes) else Inaccessible @@ -1402,8 +1403,8 @@ and NamespaceManager Seq.append (Seq.map (fun callable -> callable, true) (this.DefinedCallables())) (Seq.map (fun callable -> callable, false) (this.ImportedCallables())) - |> Seq.filter - (fun (callable, sameAssembly) -> Namespace.IsDeclarationAccessible sameAssembly callable.Modifiers.Access) + |> Seq.filter (fun (callable, sameAssembly) -> + Namespace.IsDeclarationAccessible (sameAssembly, callable.Modifiers.Access)) |> Seq.map fst /// Returns the source file and TypeDeclarationHeader of all types imported from referenced assemblies, regardless @@ -1448,8 +1449,8 @@ and NamespaceManager Seq.append (Seq.map (fun qsType -> qsType, true) (this.DefinedTypes())) (Seq.map (fun qsType -> qsType, false) (this.ImportedTypes())) - |> Seq.filter - (fun (qsType, sameAssembly) -> Namespace.IsDeclarationAccessible sameAssembly qsType.Modifiers.Access) + |> Seq.filter (fun (qsType, sameAssembly) -> + Namespace.IsDeclarationAccessible (sameAssembly, qsType.Modifiers.Access)) |> Seq.map fst /// removes the given source file and all its content from all namespaces @@ -1556,7 +1557,7 @@ and NamespaceManager let findInReferences (ns : Namespace) = match ns.CallablesInReferencedAssemblies.TryGetValue callableName.Name with | true, callable -> - if Namespace.IsDeclarationAccessible false callable.Modifiers.Access + if Namespace.IsDeclarationAccessible (false, callable.Modifiers.Access) then Found callable else Inaccessible | false, _ -> NotFound @@ -1566,13 +1567,13 @@ and NamespaceManager // OK to use CallableInSource because this is only evaluated if the callable is not in a // reference. let kind, declaration = ns.CallableInSource source callableName.Name - if Namespace.IsDeclarationAccessible true declaration.Modifiers.Access + if Namespace.IsDeclarationAccessible (true, declaration.Modifiers.Access) then Found (buildHeader {callableName with Namespace = ns.Name} (source, kind, declaration)) else Inaccessible | None -> match ns.CallablesDefinedInAllSources().TryGetValue callableName.Name with | true, (source, (kind, declaration)) -> - if Namespace.IsDeclarationAccessible true declaration.Modifiers.Access + if Namespace.IsDeclarationAccessible (true, declaration.Modifiers.Access) then Found (buildHeader {callableName with Namespace = ns.Name} (source, kind, declaration)) else Inaccessible | false, _ -> NotFound @@ -1641,7 +1642,7 @@ and NamespaceManager let findInReferences (ns : Namespace) = match ns.TypesInReferencedAssemblies.TryGetValue typeName.Name with | true, qsType -> - if Namespace.IsDeclarationAccessible false qsType.Modifiers.Access + if Namespace.IsDeclarationAccessible (false, qsType.Modifiers.Access) then Found qsType else Inaccessible | false, _ -> NotFound @@ -1650,13 +1651,13 @@ and NamespaceManager | Some source -> // OK to use TypeInSource because this is only evaluated if the type is not in a reference. let declaration = ns.TypeInSource source typeName.Name - if Namespace.IsDeclarationAccessible true declaration.Modifiers.Access + if Namespace.IsDeclarationAccessible (true, declaration.Modifiers.Access) then Found (buildHeader {typeName with Namespace = ns.Name} (source, declaration)) else Inaccessible | None -> match ns.TypesDefinedInAllSources().TryGetValue typeName.Name with | true, (source, declaration) -> - if Namespace.IsDeclarationAccessible true declaration.Modifiers.Access + if Namespace.IsDeclarationAccessible (true, declaration.Modifiers.Access) then Found (buildHeader {typeName with Namespace = ns.Name} (source, declaration)) else Inaccessible | false, _ -> NotFound From f45eb97958c1410903a4526ca14c55ecefde5504 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 23:16:06 -0700 Subject: [PATCH 108/135] Add name mangling tests --- .../CompilationManager/CompilationUnit.cs | 29 +++- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 143 ++++++++++++++++-- .../LinkingTests/InternalRenaming.qs | 114 ++++++++++++++ .../Tests.Compiler/TestUtils/Signatures.fs | 1 + .../Tests.Compiler/Tests.Compiler.fsproj | 3 + 5 files changed, 272 insertions(+), 18 deletions(-) create mode 100644 src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InternalRenaming.qs diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index b8fefef753..4f86ff5053 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -696,6 +696,25 @@ internal LocalDeclarations TryGetLocalDeclarations(FileContentManager file, Posi return this.PositionedDeclarations(parentCallable, callablePos, specPos, declarations); } + /// + /// Returns a short tag name for the given source file. The tag is not unique. If multiple source files are + /// being tagged, make sure there are no name conflicts. + /// + /// The source file to get the tag for. + /// A short tag name for the given source file. + internal static string GetTagForSource(string source) => + Regex.Replace(Path.GetFileNameWithoutExtension(source), "[^A-Za-z0-9_]", ""); + + /// + /// Embeds the tag name into the qualified name. + /// + /// The tag name to embed. + /// The qualified name in which to embed the tag name. + /// A new qualified name with the tag embedded into it. + internal static QsQualifiedName EmbedTag(string tag, QsQualifiedName name) => + // TODO: We might need to change the format to make it easier to reverse later. + new QsQualifiedName(name.Namespace, NonNullable.New($"__{name.Name.Value}_{tag}__")); + /// /// Tags the names of imported internal callables and types with a unique identifier based on the path to their /// assembly, so that they do not conflict with callables and types defined locally. Renames all references to @@ -707,28 +726,24 @@ internal LocalDeclarations TryGetLocalDeclarations(FileContentManager file, Posi private static (IEnumerable, IEnumerable) TagImportedInternalNames(IEnumerable callables, IEnumerable types) { - // Create a mapping from source file names to a short, unique identifying tag. + // Create a mapping from source file names to a short, unique identifying tag name. var tags = callables.Select(callable => callable.SourceFile.Value) .Concat(types.Select(type => type.SourceFile.Value)) .Distinct() - .GroupBy(source => Regex.Replace(Path.GetFileNameWithoutExtension(source), "[^A-Za-z0-9_]", "")) + .GroupBy(GetTagForSource) .SelectMany(group => group.Count() == 1 ? new[] { (key: group.Single(), value: group.Key) } : group.Select((source, index) => (key: source, value: group.Key + index))) .ToImmutableDictionary(item => item.key, item => item.value); - QsQualifiedName GetNewName(QsQualifiedName name, string source) => - // TODO: We might need to change the name format to make it easier to reverse later. - new QsQualifiedName(name.Namespace, NonNullable.New($"__{name.Name.Value}_{tags[source]}__")); - ImmutableDictionary GetMappingForSourceGroup( IGrouping group) => // TODO: Is there another way besides file extension to check if the source file is a reference? group .Where(item => item.access.IsInternal && !item.source.EndsWith(".qs")) - .ToImmutableDictionary(item => item.name, item => GetNewName(item.name, item.source)); + .ToImmutableDictionary(item => item.name, item => EmbedTag(tags[item.source], item.name)); var transformations = callables.Select(callable => diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 760730135e..a4e3d7e684 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -5,7 +5,9 @@ namespace Microsoft.Quantum.QsCompiler.Testing open System open System.Collections.Generic +open System.Collections.Immutable open System.IO +open System.Threading.Tasks open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.CompilationBuilder open Microsoft.Quantum.QsCompiler.DataTypes @@ -15,6 +17,8 @@ open Microsoft.Quantum.QsCompiler.SyntaxTree open Microsoft.Quantum.QsCompiler.Transformations.IntrinsicResolution open Microsoft.Quantum.QsCompiler.Transformations.Monomorphization open Microsoft.Quantum.QsCompiler.Transformations.Monomorphization.Validation +open Microsoft.Quantum.QsCompiler.Transformations.SearchAndReplace +open Microsoft.VisualStudio.LanguageServer.Protocol open Xunit open Xunit.Abstractions @@ -24,10 +28,37 @@ type LinkingTests (output:ITestOutputHelper) = let compilationManager = new CompilationUnitManager(new Action (fun ex -> failwith ex.Message)) - let getTempFile () = new Uri(Path.GetFullPath(Path.GetRandomFileName())) + let getTempFile () = + // The file name needs to end in ".qs" so that it isn't ignored by the References.Headers class during the + // internal renaming tests. + Path.GetRandomFileName() + ".qs" |> Path.GetFullPath |> Uri + let getManager uri content = CompilationUnitManager.InitializeFileManager(uri, content, compilationManager.PublishDiagnostics, compilationManager.LogException) - do let addOrUpdateSourceFile filePath = getManager (new Uri(filePath)) (File.ReadAllText filePath) |> compilationManager.AddOrUpdateSourceFileAsync |> ignore + let defaultOffset = + { + Offset = DiagnosticTools.AsTuple (Position (0, 0)) + Range = QsCompilerDiagnostic.DefaultRange + } + + let qualifiedName ns name = + { + Namespace = NonNullable<_>.New ns + Name = NonNullable<_>.New name + } + + /// Counts the number of references to the qualified name in all of the namespaces, including the declaration. + let countReferences namespaces (name : QsQualifiedName) = + let references = IdentifierReferences (name, defaultOffset) + Seq.iter (references.Namespaces.OnNamespace >> ignore) namespaces + let declaration = if obj.ReferenceEquals (references.SharedState.DeclarationLocation, null) then 0 else 1 + references.SharedState.Locations.Count + declaration + + do + let addOrUpdateSourceFile filePath = + getManager (new Uri(filePath)) (File.ReadAllText filePath) + |> compilationManager.AddOrUpdateSourceFileAsync + |> ignore Path.Combine ("TestCases", "LinkingTests", "Core.qs") |> Path.GetFullPath |> addOrUpdateSourceFile static member private ReadAndChunkSourceFile fileName = @@ -53,19 +84,24 @@ type LinkingTests (output:ITestOutputHelper) = for callable in built.Callables.Values |> Seq.filter inFile do tests.Verify (callable.FullName, diag) - member private this.BuildContent content = + member private this.BuildContent (source, ?references) = + let fileId = getTempFile () + let file = getManager fileId source + let wait : Task -> Unit = Async.AwaitTask >> Async.RunSynchronously - let fileId = getTempFile() - let file = getManager fileId content + match references with + | Some references -> compilationManager.UpdateReferencesAsync references |> wait + | None -> () + compilationManager.AddOrUpdateSourceFileAsync file |> wait - compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore - let compilationDataStructures = compilationManager.Build() - compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore + let compilation = compilationManager.Build () + compilationManager.TryRemoveSourceFileAsync (fileId, false) |> wait + compilationManager.UpdateReferencesAsync (References ImmutableDictionary<_, _>.Empty) |> wait - compilationDataStructures.Diagnostics() |> Seq.exists (fun d -> d.IsError()) |> Assert.False - Assert.NotNull compilationDataStructures.BuiltCompilation + compilation.Diagnostics () |> Seq.exists (fun d -> d.IsError ()) |> Assert.False + Assert.NotNull compilation.BuiltCompilation - compilationDataStructures + compilation member private this.CompileMonomorphization input = @@ -111,6 +147,38 @@ type LinkingTests (output:ITestOutputHelper) = |> Assert.True) |> ignore + /// Runs the nth internal renaming test, asserting that declarations with the given name and references to them have + /// been renamed across the compilation unit. + member private this.RunInternalRenamingTest num renamed notRenamed = + let chunks = LinkingTests.ReadAndChunkSourceFile "InternalRenaming.qs" + let sourceCompilation = this.BuildContent chunks.[num - 1] + + let dllSource = "InternalRenaming.dll" + let namespaces = + sourceCompilation.BuiltCompilation.Namespaces + |> Seq.filter (fun ns -> ns.Name.Value.StartsWith Signatures.InternalRenamingNs) + let headers = References.Headers (NonNullable.New dllSource, namespaces) + let references = + [KeyValuePair.Create(NonNullable<_>.New dllSource, headers)] + |> ImmutableDictionary.CreateRange + |> References + let referenceCompilation = this.BuildContent ("", references) + + let countAll namespaces names = + names |> Seq.map (countReferences namespaces) |> Seq.sum + + let beforeCount = countAll sourceCompilation.BuiltCompilation.Namespaces (Seq.concat [renamed; notRenamed]) + let afterCountOriginal = countAll referenceCompilation.BuiltCompilation.Namespaces renamed + + let newNames = + renamed |> Seq.map (CompilationUnit.GetTagForSource dllSource + |> FuncConvert.FuncFromTupled CompilationUnit.EmbedTag) + let afterCount = countAll referenceCompilation.BuiltCompilation.Namespaces (Seq.concat [newNames; notRenamed]) + + Assert.NotEqual (0, beforeCount) + Assert.Equal (0, afterCountOriginal) + Assert.Equal (beforeCount, afterCount) + [] member this.``Monomorphization`` () = @@ -245,3 +313,56 @@ type LinkingTests (output:ITestOutputHelper) = this.Expect "InvalidEntryPoint35" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] this.Expect "InvalidEntryPoint36" [Error ErrorCode.CallableTypeInEntryPointSignature; Error ErrorCode.UserDefinedTypeInEntryPointSignature] + [] + member this.``Rename internal operation call references`` () = + this.RunInternalRenamingTest 1 + [qualifiedName Signatures.InternalRenamingNs "Foo"] + [qualifiedName Signatures.InternalRenamingNs "Bar"] + + [] + member this.``Rename internal function call references`` () = + this.RunInternalRenamingTest 2 + [qualifiedName Signatures.InternalRenamingNs "Foo"] + [qualifiedName Signatures.InternalRenamingNs "Bar"] + + [] + member this.``Rename internal type references`` () = + this.RunInternalRenamingTest 3 + [ + qualifiedName Signatures.InternalRenamingNs "Foo" + qualifiedName Signatures.InternalRenamingNs "Bar" + qualifiedName Signatures.InternalRenamingNs "Baz" + ] + [] + + [] + member this.``Rename internal references across namespaces`` () = + this.RunInternalRenamingTest 4 + [ + qualifiedName Signatures.InternalRenamingNs "Foo" + qualifiedName Signatures.InternalRenamingNs "Bar" + qualifiedName (Signatures.InternalRenamingNs + ".Extra") "Qux" + ] + [qualifiedName (Signatures.InternalRenamingNs + ".Extra") "Baz"] + + [] + member this.``Rename internal qualified references`` () = + this.RunInternalRenamingTest 5 + [ + qualifiedName Signatures.InternalRenamingNs "Foo" + qualifiedName Signatures.InternalRenamingNs "Bar" + qualifiedName (Signatures.InternalRenamingNs + ".Extra") "Qux" + ] + [qualifiedName (Signatures.InternalRenamingNs + ".Extra") "Baz"] + + [] + member this.``Rename internal attribute references`` () = + this.RunInternalRenamingTest 6 + [qualifiedName Signatures.InternalRenamingNs "Foo"] + [qualifiedName Signatures.InternalRenamingNs "Bar"] + + [] + member this.``Rename specializations for internal operations`` () = + this.RunInternalRenamingTest 7 + [qualifiedName Signatures.InternalRenamingNs "Foo"] + [qualifiedName Signatures.InternalRenamingNs "Bar"] diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InternalRenaming.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InternalRenaming.qs new file mode 100644 index 0000000000..3f67ca815a --- /dev/null +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InternalRenaming.qs @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Test 1: Rename internal operation call references + +namespace Microsoft.Quantum.Testing.InternalRenaming { + internal operation Foo () : Unit { + } + + operation Bar () : Unit { + Foo(); + } +} + +// ================================= +// Test 2: Rename internal function call references + +namespace Microsoft.Quantum.Testing.InternalRenaming { + internal function Foo () : Int { + return 42; + } + + function Bar () : Unit { + let x = Foo(); + let y = 1 + (Foo() - 5); + let z = (Foo(), 9); + } +} + +// ================================= +// Test 3: Rename internal type references + +namespace Microsoft.Quantum.Testing.InternalRenaming { + internal newtype Foo = Unit; + + internal newtype Bar = (Int, Foo); + + internal function Baz (x : Foo) : Foo { + return Foo(); + } +} + +// ================================= +// Test 4: Rename internal references across namespaces + +namespace Microsoft.Quantum.Testing.InternalRenaming { + internal newtype Foo = Unit; + + internal function Bar () : Unit { + } +} + +namespace Microsoft.Quantum.Testing.InternalRenaming.Extra { + open Microsoft.Quantum.Testing.InternalRenaming; + + function Baz () : Unit { + return Bar(); + } + + internal function Qux (x : Foo) : Foo { + return x; + } +} + +// ================================= +// Test 5: Rename internal qualified references + +namespace Microsoft.Quantum.Testing.InternalRenaming { + internal newtype Foo = Unit; + + internal function Bar () : Unit { + } +} + +namespace Microsoft.Quantum.Testing.InternalRenaming.Extra { + function Baz () : Unit { + return Microsoft.Quantum.Testing.InternalRenaming.Bar(); + } + + internal function Qux (x : Microsoft.Quantum.Testing.InternalRenaming.Foo) + : Microsoft.Quantum.Testing.InternalRenaming.Foo { + return x; + } +} + +// ================================= +// Test 6: Rename internal attribute references + +namespace Microsoft.Quantum.Testing.InternalRenaming { + @Attribute() + internal newtype Foo = Unit; + + @Foo() + function Bar () : Unit { + } +} + +// ================================= +// Test 7: Rename specializations for internal operations + +namespace Microsoft.Quantum.Testing.InternalRenaming { + internal operation Foo () : Unit is Adj { + body { + } + + adjoint { + } + } + + operation Bar () : Unit { + Foo(); + Adjoint Foo(); + } +} diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs index e7fd7462ac..5a8be35942 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -113,6 +113,7 @@ let public MonomorphizationNs = "Microsoft.Quantum.Testing.Monomorphization" let public GenericsNs = "Microsoft.Quantum.Testing.Generics" let public IntrinsicResolutionNs = "Microsoft.Quantum.Testing.IntrinsicResolution" let public ClassicalControlNs = "Microsoft.Quantum.Testing.ClassicalControl" +let public InternalRenamingNs = "Microsoft.Quantum.Testing.InternalRenaming" /// Expected callable signatures to be found when running Monomorphization tests let public MonomorphizationSignatures = diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 53a48775d0..293938f255 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -47,6 +47,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest From 7bbd6aa4472a886cdcd84e286b75118d5af783ca Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 23:54:51 -0700 Subject: [PATCH 109/135] Add test for using same internal name in multiple references --- src/QsCompiler/Tests.Compiler/AccessModifierTests.fs | 9 +++++---- src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj | 6 +++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index 5480b88ab3..06713edf20 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -14,10 +14,11 @@ open System.Linq type AccessModifierTests (output) = - inherit CompilerTests (CompilerTests.Compile "TestCases" - ["AccessModifiers.qs"] - [File.ReadAllLines("ReferenceTargets.txt").ElementAt(1)], - output) + // We load two references, but they are both identical copies. Both define only internal declarations, so this + // implicitly tests that multiple references can re-use the same internal names. + inherit CompilerTests + (CompilerTests.Compile "TestCases" ["AccessModifiers.qs"] (File.ReadAllLines("ReferenceTargets.txt").[1..2]), + output) member private this.Expect name (diagnostics : IEnumerable) = let ns = "Microsoft.Quantum.Testing.AccessModifiers" |> NonNullable<_>.New diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 293938f255..3ec1a5528b 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -178,8 +178,12 @@ $(MSBuildThisFileDirectory)..\TestTargets\Simulation\Example\bin\$(Configuration)\netcoreapp3.0\Example.dll $(MSBuildThisFileDirectory)..\TestTargets\Libraries\Library1\bin\$(Configuration)\netcoreapp3.0\Library1.dll + $(MSBuildThisFileDirectory)..\TestTargets\Libraries\Library1\bin\$(Configuration)\netcoreapp3.0\Library1Copy.dll - + + From 11ecd7529cd6aad676dced32486b1a87f9503e0a Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 00:00:35 -0700 Subject: [PATCH 110/135] No diagnostics for "conflicting" internal names --- .../CompilationManager/CompilationUnit.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 4f86ff5053..69cf557864 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -123,11 +123,17 @@ public References(ImmutableDictionary, Headers> refs, Action this.Declarations = refs ?? throw new ArgumentNullException(nameof(refs)); if (onError == null) return; - var conflictingCallables = refs.Values.SelectMany(r => r.Callables) - .GroupBy(c => c.QualifiedName).Where(g => g.Count() != 1) + var conflictingCallables = refs.Values + .SelectMany(r => r.Callables) + .Where(c => Namespace.IsDeclarationAccessible(false, c.Modifiers.Access)) + .GroupBy(c => c.QualifiedName) + .Where(g => g.Count() != 1) .Select(g => (g.Key, String.Join(", ", g.Select(c => c.SourceFile.Value)))); - var conflictingTypes = refs.Values.SelectMany(r => r.Types) - .GroupBy(t => t.QualifiedName).Where(g => g.Count() != 1) + var conflictingTypes = refs.Values + .SelectMany(r => r.Types) + .Where(t => Namespace.IsDeclarationAccessible(false, t.Modifiers.Access)) + .GroupBy(t => t.QualifiedName) + .Where(g => g.Count() != 1) .Select(g => (g.Key, String.Join(", ", g.Select(c => c.SourceFile.Value)))); foreach (var (name, conflicts) in conflictingCallables.Concat(conflictingTypes).Distinct()) From 9c5284cb6d20422fb2f636716f916f70b71328cb Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 12:22:32 -0700 Subject: [PATCH 111/135] Move ResolutionResult to SymbolResolution.fs --- src/QsCompiler/Core/SymbolResolution.fs | 91 ++++++++++++++++++++++++ src/QsCompiler/Core/SymbolTable.fs | 92 ------------------------- 2 files changed, 91 insertions(+), 92 deletions(-) diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index a2710b8f54..cf1bf0f672 100644 --- a/src/QsCompiler/Core/SymbolResolution.fs +++ b/src/QsCompiler/Core/SymbolResolution.fs @@ -76,6 +76,97 @@ type SpecializationBundleProperties = internal { (fun group -> group.ToImmutableDictionary(getKind))) +/// Represents the outcome of resolving a symbol. +type ResolutionResult<'T> = + /// A result indicating that the symbol resolved successfully. + | Found of 'T + /// A result indicating that an unqualified symbol is ambiguous, and it is possible to resolve it to more than one + /// namespace. Includes the list of possible namespaces. + | Ambiguous of NonNullable seq + /// A result indicating that the symbol resolved to a declaration which is not accessible from the location + /// referencing it. + | Inaccessible + /// A result indicating that no declaration with that name was found. + | NotFound + +/// Tools for processing resolution results. +module internal ResolutionResult = + /// Applies the mapping function to the value of a Found result. If the result is not a Found, returns it unchanged. + let internal map mapping = function + | Found value -> Found (mapping value) + | Ambiguous namespaces -> Ambiguous namespaces + | Inaccessible -> Inaccessible + | NotFound -> NotFound + + /// Converts the resolution result to an option containing the Found value. + let internal toOption = function + | Found value -> Some value + | _ -> None + + /// Converts the resolution result to a 0- or 1-element list containing the Found value if the result is a Found. + let private toList = toOption >> Option.toList + + /// Sorts the sequence of results in order of Found, Ambiguous, Inaccessible, and NotFound. + /// + /// If the sequence contains a Found result, the rest of the sequence after it is not automatically evaluated. + /// Otherwise, the whole sequence may be evaluated by calling sort. + let private sort results = + let choosers = [ + function | Found value -> Some (Found value) | _ -> None + function | Ambiguous namespaces -> Some (Ambiguous namespaces) | _ -> None + function | Inaccessible -> Some Inaccessible | _ -> None + function | NotFound -> Some NotFound | _ -> None + ] + choosers + |> Seq.map (fun chooser -> Seq.choose chooser results) + |> Seq.concat + + /// Returns the first item of the sequence of results if there is one, or NotFound if the sequence is empty. + let private tryHead<'T> : seq> -> ResolutionResult<'T> = + Seq.tryHead >> Option.defaultValue NotFound + + /// Returns the first Found result in the sequence if it exists. If not, returns the first Ambiguous result if it + /// exists. Repeats for Inaccessible and NotFound in this order. If the sequence is empty, returns NotFound. + let internal tryFirstBest<'T> : seq> -> ResolutionResult<'T> = + sort >> tryHead + + /// Returns a Found result if there is only one in the sequence. If there is more than one, returns an Ambiguous + /// result. Otherwise, returns the first value from ResolutionResult.sort. + let internal tryExactlyOne<'T> (nsGetter : 'T -> NonNullable) + (results : seq>) : ResolutionResult<'T> = + let found = results |> Seq.filter (function | Found _ -> true | _ -> false) + if Seq.length found > 1 + then found + |> Seq.map (map nsGetter >> toList) + |> Seq.concat + |> Ambiguous + else found + |> Seq.tryExactlyOne + |> Option.defaultWith (fun () -> tryFirstBest results) + + /// Returns a Found result if there is only one in the sequence. If there is more than one, raises an exception. + /// Otherwise, returns the same value as ResolutionResult.tryFirst. + let internal exactlyOne<'T> : seq> -> ResolutionResult<'T> = + tryExactlyOne (fun _ -> NonNullable<_>.New "") >> function + | Ambiguous _ -> QsCompilerError.Raise "Resolution is ambiguous" + Exception () |> raise + | result -> result + + /// Returns true if the resolution result indicates that a resolution exists (Found, Ambiguous or Inaccessible). + let internal exists = function + | Found _ + | Ambiguous _ + | Inaccessible -> true + | NotFound -> false + + /// Returns true if the resolution result indicates that a resolution is accessible (Found or Ambiguous). + let internal isAccessible = function + | Found _ + | Ambiguous _ -> true + | Inaccessible + | NotFound -> false + + module SymbolResolution = // routines for giving deprecation warnings diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 0a2028d5eb..bd465c4915 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -18,98 +18,6 @@ open Microsoft.Quantum.QsCompiler.SyntaxTree open Newtonsoft.Json -/// Represents the outcome of resolving a symbol in the symbol table. -type ResolutionResult<'T> = - /// A result indicating that the symbol resolved successfully. - | Found of 'T - /// A result indicating that an unqualified symbol is ambiguous, and it is possible to resolve it to more than one - /// namespace. Includes the list of possible namespaces. - | Ambiguous of NonNullable seq - /// A result indicating that the symbol resolved to a declaration which is not accessible from the location - /// referencing it. - | Inaccessible - /// A result indicating that no declaration with that name was found. - | NotFound - - -/// Tools for processing resolution results. -module private ResolutionResult = - /// Applies the mapping function to the value of a Found result. If the result is not a Found, returns it unchanged. - let internal map mapping = function - | Found value -> Found (mapping value) - | Ambiguous namespaces -> Ambiguous namespaces - | Inaccessible -> Inaccessible - | NotFound -> NotFound - - /// Converts the resolution result to an option containing the Found value. - let internal toOption = function - | Found value -> Some value - | _ -> None - - /// Converts the resolution result to a 0- or 1-element list containing the Found value if the result is a Found. - let private toList = toOption >> Option.toList - - /// Sorts the sequence of results in order of Found, Ambiguous, Inaccessible, and NotFound. - /// - /// If the sequence contains a Found result, the rest of the sequence after it is not automatically evaluated. - /// Otherwise, the whole sequence may be evaluated by calling sort. - let private sort results = - let choosers = [ - function | Found callable -> Some (Found callable) | _ -> None - function | Ambiguous namespaces -> Some (Ambiguous namespaces) | _ -> None - function | Inaccessible -> Some Inaccessible | _ -> None - function | NotFound -> Some NotFound | _ -> None - ] - choosers - |> Seq.map (fun chooser -> Seq.choose chooser results) - |> Seq.concat - - /// Returns the first item of the sequence of results if there is one, or NotFound if the sequence is empty. - let private tryHead<'T> : seq> -> ResolutionResult<'T> = - Seq.tryHead >> Option.defaultValue NotFound - - /// Returns the first Found result in the sequence if it exists. If not, returns the first Ambiguous result if it - /// exists. Repeats for Inaccessible and NotFound in this order. If the sequence is empty, returns NotFound. - let internal tryFirstBest<'T> : seq> -> ResolutionResult<'T> = - sort >> tryHead - - /// Returns a Found result if there is only one in the sequence. If there is more than one, returns an Ambiguous - /// result. Otherwise, returns the first value from ResolutionResult.sort. - let internal tryExactlyOne<'T> (nsGetter : 'T -> NonNullable) - (results : seq>) : ResolutionResult<'T> = - let found = results |> Seq.filter (function | Found _ -> true | _ -> false) - if Seq.length found > 1 - then found - |> Seq.map (map nsGetter >> toList) - |> Seq.concat - |> Ambiguous - else found - |> Seq.tryExactlyOne - |> Option.defaultWith (fun () -> tryFirstBest results) - - /// Returns a Found result if there is only one in the sequence. If there is more than one, raises an exception. - /// Otherwise, returns the same value as ResolutionResult.tryFirst. - let internal exactlyOne<'T> : seq> -> ResolutionResult<'T> = - tryExactlyOne (fun _ -> NonNullable<_>.New "") >> function - | Ambiguous _ -> QsCompilerError.Raise "Resolution is ambiguous" - Exception () |> raise - | result -> result - - /// Returns true if the resolution result indicates that a resolution exists (Found, Ambiguous or Inaccessible). - let internal exists = function - | Found _ - | Ambiguous _ - | Inaccessible -> true - | NotFound -> false - - /// Returns true if the resolution result indicates that a resolution is accessible (Found or Ambiguous). - let internal isAccessible = function - | Found _ - | Ambiguous _ -> true - | Inaccessible - | NotFound -> false - - /// Represents the partial declaration of a namespace in a single file. /// /// Note that this class is *not* thread-safe, and access modifiers are always ignored when looking up declarations. From d05003feae45ba172943ab9b4ad1cbbf29001640 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 13:05:07 -0700 Subject: [PATCH 112/135] Add comment about name tagging --- src/QsCompiler/CompilationManager/CompilationUnit.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 69cf557864..f88fc8c112 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -600,6 +600,8 @@ public QsCompilation Build() // build the syntax tree var callables = this.CompiledCallables.Values.Concat(this.GlobalSymbols.ImportedCallables().Select(this.GetImportedCallable)); var types = this.CompiledTypes.Values.Concat(this.GlobalSymbols.ImportedTypes().Select(this.GetImportedType)); + // Rename imported internal declarations by tagging them with their source file to avoid potentially + // having duplicate names in the syntax tree. var (taggedCallables, taggedTypes) = TagImportedInternalNames(callables, types); var tree = NewSyntaxTree(taggedCallables, taggedTypes, this.GlobalSymbols.Documentation()); var entryPoints = tree.Callables().Where(c => c.Attributes.Any(BuiltIn.MarksEntryPoint)).Select(c => c.FullName).ToImmutableArray(); From d0162cdddc3d90e966d30bf799536b8cb15dc56d Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 13:16:35 -0700 Subject: [PATCH 113/135] Use Library2 instead of Library1Copy --- src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 3ec1a5528b..6e6efcd1fc 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -178,7 +178,7 @@ $(MSBuildThisFileDirectory)..\TestTargets\Simulation\Example\bin\$(Configuration)\netcoreapp3.0\Example.dll $(MSBuildThisFileDirectory)..\TestTargets\Libraries\Library1\bin\$(Configuration)\netcoreapp3.0\Library1.dll - $(MSBuildThisFileDirectory)..\TestTargets\Libraries\Library1\bin\$(Configuration)\netcoreapp3.0\Library1Copy.dll + $(MSBuildThisFileDirectory)..\TestTargets\Libraries\Library1\bin\$(Configuration)\netcoreapp3.0\Library2.dll Date: Sat, 14 Mar 2020 13:23:07 -0700 Subject: [PATCH 114/135] Ignore async tasks instead of waiting --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index a4e3d7e684..54b0db19c6 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -31,7 +31,7 @@ type LinkingTests (output:ITestOutputHelper) = let getTempFile () = // The file name needs to end in ".qs" so that it isn't ignored by the References.Headers class during the // internal renaming tests. - Path.GetRandomFileName() + ".qs" |> Path.GetFullPath |> Uri + Path.GetRandomFileName () + ".qs" |> Path.GetFullPath |> Uri let getManager uri content = CompilationUnitManager.InitializeFileManager(uri, content, compilationManager.PublishDiagnostics, compilationManager.LogException) @@ -87,16 +87,15 @@ type LinkingTests (output:ITestOutputHelper) = member private this.BuildContent (source, ?references) = let fileId = getTempFile () let file = getManager fileId source - let wait : Task -> Unit = Async.AwaitTask >> Async.RunSynchronously match references with - | Some references -> compilationManager.UpdateReferencesAsync references |> wait + | Some references -> compilationManager.UpdateReferencesAsync references |> ignore | None -> () - compilationManager.AddOrUpdateSourceFileAsync file |> wait + compilationManager.AddOrUpdateSourceFileAsync file |> ignore let compilation = compilationManager.Build () - compilationManager.TryRemoveSourceFileAsync (fileId, false) |> wait - compilationManager.UpdateReferencesAsync (References ImmutableDictionary<_, _>.Empty) |> wait + compilationManager.TryRemoveSourceFileAsync (fileId, false) |> ignore + compilationManager.UpdateReferencesAsync (References ImmutableDictionary<_, _>.Empty) |> ignore compilation.Diagnostics () |> Seq.exists (fun d -> d.IsError ()) |> Assert.False Assert.NotNull compilation.BuiltCompilation From 29b80d5e2fe68e926f50e90842473c3b0e33a114 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 13:30:38 -0700 Subject: [PATCH 115/135] Disable multiple reference test --- .../Tests.Compiler/AccessModifierTests.fs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index 06713edf20..be164d3fd5 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -4,21 +4,26 @@ namespace Microsoft.Quantum.QsCompiler.Testing open System.Collections.Generic +open System.IO open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTree open Xunit -open System.IO -open System.Linq type AccessModifierTests (output) = + // TODO: Replace this inherit statement with the one below to enable the test for supporting multiple references + // with the same internal name. + inherit CompilerTests + (CompilerTests.Compile "TestCases" ["AccessModifiers.qs"] [File.ReadAllLines("ReferenceTargets.txt").[1]], + output) + // We load two references, but they are both identical copies. Both define only internal declarations, so this // implicitly tests that multiple references can re-use the same internal names. - inherit CompilerTests - (CompilerTests.Compile "TestCases" ["AccessModifiers.qs"] (File.ReadAllLines("ReferenceTargets.txt").[1..2]), - output) + // inherit CompilerTests + // (CompilerTests.Compile "TestCases" ["AccessModifiers.qs"] (File.ReadAllLines("ReferenceTargets.txt").[1..2]), + // output) member private this.Expect name (diagnostics : IEnumerable) = let ns = "Microsoft.Quantum.Testing.AccessModifiers" |> NonNullable<_>.New From f197dc2c6ec0edabbce08a9ffc4ced500e02762a Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 18:57:51 -0700 Subject: [PATCH 116/135] Use PascalCase in ResolutionResult module --- src/QsCompiler/Core/SymbolResolution.fs | 31 +++++++++++++------------ src/QsCompiler/Core/SymbolTable.fs | 30 ++++++++++++------------ 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index cf1bf0f672..80db1f071e 100644 --- a/src/QsCompiler/Core/SymbolResolution.fs +++ b/src/QsCompiler/Core/SymbolResolution.fs @@ -92,25 +92,25 @@ type ResolutionResult<'T> = /// Tools for processing resolution results. module internal ResolutionResult = /// Applies the mapping function to the value of a Found result. If the result is not a Found, returns it unchanged. - let internal map mapping = function + let internal Map mapping = function | Found value -> Found (mapping value) | Ambiguous namespaces -> Ambiguous namespaces | Inaccessible -> Inaccessible | NotFound -> NotFound /// Converts the resolution result to an option containing the Found value. - let internal toOption = function + let internal ToOption = function | Found value -> Some value | _ -> None /// Converts the resolution result to a 0- or 1-element list containing the Found value if the result is a Found. - let private toList = toOption >> Option.toList + let private ToList = ToOption >> Option.toList /// Sorts the sequence of results in order of Found, Ambiguous, Inaccessible, and NotFound. /// /// If the sequence contains a Found result, the rest of the sequence after it is not automatically evaluated. /// Otherwise, the whole sequence may be evaluated by calling sort. - let private sort results = + let private Sort results = let choosers = [ function | Found value -> Some (Found value) | _ -> None function | Ambiguous namespaces -> Some (Ambiguous namespaces) | _ -> None @@ -122,45 +122,46 @@ module internal ResolutionResult = |> Seq.concat /// Returns the first item of the sequence of results if there is one, or NotFound if the sequence is empty. - let private tryHead<'T> : seq> -> ResolutionResult<'T> = + let private TryHead<'T> : seq> -> ResolutionResult<'T> = Seq.tryHead >> Option.defaultValue NotFound /// Returns the first Found result in the sequence if it exists. If not, returns the first Ambiguous result if it /// exists. Repeats for Inaccessible and NotFound in this order. If the sequence is empty, returns NotFound. - let internal tryFirstBest<'T> : seq> -> ResolutionResult<'T> = - sort >> tryHead + let internal TryFirstBest<'T> : seq> -> ResolutionResult<'T> = + Sort >> TryHead /// Returns a Found result if there is only one in the sequence. If there is more than one, returns an Ambiguous - /// result. Otherwise, returns the first value from ResolutionResult.sort. - let internal tryExactlyOne<'T> (nsGetter : 'T -> NonNullable) + /// result containing the namespaces of all Found results given by applying nsGetter to each value. Otherwise, + /// returns the same value as TryFirstBest. + let internal TryExactlyOne<'T> (nsGetter : 'T -> NonNullable) (results : seq>) : ResolutionResult<'T> = let found = results |> Seq.filter (function | Found _ -> true | _ -> false) if Seq.length found > 1 then found - |> Seq.map (map nsGetter >> toList) + |> Seq.map (Map nsGetter >> ToList) |> Seq.concat |> Ambiguous else found |> Seq.tryExactlyOne - |> Option.defaultWith (fun () -> tryFirstBest results) + |> Option.defaultWith (fun () -> TryFirstBest results) /// Returns a Found result if there is only one in the sequence. If there is more than one, raises an exception. /// Otherwise, returns the same value as ResolutionResult.tryFirst. - let internal exactlyOne<'T> : seq> -> ResolutionResult<'T> = - tryExactlyOne (fun _ -> NonNullable<_>.New "") >> function + let internal ExactlyOne<'T> : seq> -> ResolutionResult<'T> = + TryExactlyOne (fun _ -> NonNullable<_>.New "") >> function | Ambiguous _ -> QsCompilerError.Raise "Resolution is ambiguous" Exception () |> raise | result -> result /// Returns true if the resolution result indicates that a resolution exists (Found, Ambiguous or Inaccessible). - let internal exists = function + let internal Exists = function | Found _ | Ambiguous _ | Inaccessible -> true | NotFound -> false /// Returns true if the resolution result indicates that a resolution is accessible (Found or Ambiguous). - let internal isAccessible = function + let internal IsAccessible = function | Found _ | Ambiguous _ -> true | Inaccessible diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index bd465c4915..830bcd4877 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -443,7 +443,7 @@ and Namespace private partial.GetSpecializations cName |> Seq.map (fun (kind, decl) -> kind, (partial.Source, decl)) - if this.TryFindCallable cName |> ResolutionResult.exists + if this.TryFindCallable cName |> ResolutionResult.Exists then (Parts.Values.SelectMany getSpecializationInPartial).ToImmutableArray() else ArgumentException "no callable with the given name exist within the namespace" |> raise @@ -483,8 +483,8 @@ and Namespace private | false, _ -> NotFound seq { yield findInReferences () - yield Seq.map findInPartial Parts.Values |> ResolutionResult.exactlyOne } - |> ResolutionResult.tryFirstBest + yield Seq.map findInPartial Parts.Values |> ResolutionResult.ExactlyOne } + |> ResolutionResult.TryFirstBest /// Returns a resolution result for the callable with the given name containing the name of the source file or /// referenced assembly in which it is declared, and a string indicating the redirection if it has been deprecated. @@ -521,8 +521,8 @@ and Namespace private | false, _ -> NotFound seq { yield findInReferences () - yield Seq.map findInPartial Parts.Values |> ResolutionResult.exactlyOne } - |> ResolutionResult.tryFirstBest + yield Seq.map findInPartial Parts.Values |> ResolutionResult.ExactlyOne } + |> ResolutionResult.TryFirstBest /// Sets the resolution for the type with the given name in the given source file to the given type, /// and replaces the resolved attributes with the given values. @@ -777,11 +777,11 @@ and NamespaceManager /// attempts to find an unambiguous resolution. let resolveInOpenNamespaces resolver (nsName, source) = let resolveWithNsName (ns : Namespace) = - resolver ns |> ResolutionResult.map (fun value -> (ns.Name, value)) + resolver ns |> ResolutionResult.Map (fun value -> (ns.Name, value)) let currentNs, importedNs = OpenNamespaces (nsName, source) seq { yield resolveWithNsName currentNs - yield Seq.map resolveWithNsName importedNs |> ResolutionResult.tryExactlyOne fst } - |> ResolutionResult.tryFirstBest + yield Seq.map resolveWithNsName importedNs |> ResolutionResult.TryExactlyOne fst } + |> ResolutionResult.TryFirstBest /// Given a qualifier for a symbol name, returns the corresponding namespace as Some /// if such a namespace or such a namespace short name within the given parent namespace and source file exists. @@ -806,7 +806,7 @@ and NamespaceManager match Namespaces.TryGetValue nsName with | true, ns when ns.Sources.Contains source -> match (ns.ImportedNamespaces source).TryGetValue builtIn.Namespace with - | true, null when not (ns.TryFindType builtIn.Name |> ResolutionResult.isAccessible) || + | true, null when not (ns.TryFindType builtIn.Name |> ResolutionResult.IsAccessible) || nsName.Value = builtIn.Namespace.Value -> [""; builtIn.Namespace.Value] | true, null -> [builtIn.Namespace.Value] // the built-in type or callable is shadowed @@ -1492,7 +1492,7 @@ and NamespaceManager | Some ns -> seq { yield findInReferences ns yield findInSources ns declSource } - |> ResolutionResult.tryFirstBest + |> ResolutionResult.TryFirstBest finally syncRoot.ExitReadLock() /// Given a qualified callable name, returns the corresponding CallableDeclarationHeader in a ResolutionResult if @@ -1519,7 +1519,7 @@ and NamespaceManager syncRoot.EnterReadLock() try resolveInOpenNamespaces (fun ns -> ns.TryFindCallable cName) (nsName, source) - |> ResolutionResult.map toHeader + |> ResolutionResult.Map toHeader finally syncRoot.ExitReadLock() /// Given a qualified type name, returns the corresponding TypeDeclarationHeader in a ResolutionResult if the @@ -1576,7 +1576,7 @@ and NamespaceManager | Some ns -> seq { yield findInReferences ns yield findInSources ns declSource } - |> ResolutionResult.tryFirstBest + |> ResolutionResult.TryFirstBest finally syncRoot.ExitReadLock() /// Given a qualified type name, returns the corresponding TypeDeclarationHeader in a ResolutionResult if the @@ -1603,7 +1603,7 @@ and NamespaceManager syncRoot.EnterReadLock() try resolveInOpenNamespaces (fun ns -> ns.TryFindType tName) (nsName, source) - |> ResolutionResult.map toHeader + |> ResolutionResult.Map toHeader finally syncRoot.ExitReadLock() /// Returns the fully qualified namespace name of the given namespace alias (short name). If the alias is already a fully qualified name, @@ -1623,7 +1623,7 @@ and NamespaceManager syncRoot.EnterReadLock() try Namespaces.Values |> Seq.choose (fun ns -> - ns.TryFindCallable cName |> ResolutionResult.toOption |> Option.map (fun _ -> ns.Name)) + ns.TryFindCallable cName |> ResolutionResult.ToOption |> Option.map (fun _ -> ns.Name)) |> fun namespaces -> namespaces.ToImmutableArray () finally syncRoot.ExitReadLock() @@ -1634,7 +1634,7 @@ and NamespaceManager syncRoot.EnterReadLock() try Namespaces.Values |> Seq.choose (fun ns -> - ns.TryFindType tName |> ResolutionResult.toOption |> Option.map (fun _ -> ns.Name)) + ns.TryFindType tName |> ResolutionResult.ToOption |> Option.map (fun _ -> ns.Name)) |> fun namespaces -> namespaces.ToImmutableArray () finally syncRoot.ExitReadLock() From eb27b21266884fac3bc97bf2deeb2e5759d6beb0 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 19:19:14 -0700 Subject: [PATCH 117/135] Check if source is in references dictionary --- src/QsCompiler/CompilationManager/CompilationUnit.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index f88fc8c112..03e12f2a20 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -731,7 +731,7 @@ internal static QsQualifiedName EmbedTag(string tag, QsQualifiedName name) => /// The callables to rename and update references in. /// The types to rename and update references in. /// The tagged callables and types with references renamed everywhere. - private static (IEnumerable, IEnumerable) + private (IEnumerable, IEnumerable) TagImportedInternalNames(IEnumerable callables, IEnumerable types) { // Create a mapping from source file names to a short, unique identifying tag name. @@ -748,9 +748,10 @@ private static (IEnumerable, IEnumerable) ImmutableDictionary GetMappingForSourceGroup( IGrouping group) => - // TODO: Is there another way besides file extension to check if the source file is a reference? group - .Where(item => item.access.IsInternal && !item.source.EndsWith(".qs")) + .Where(item => + !Namespace.IsDeclarationAccessible(false, item.access) && + Externals.Declarations.ContainsKey(NonNullable.New(item.source))) .ToImmutableDictionary(item => item.name, item => EmbedTag(tags[item.source], item.name)); var transformations = From ade77a813e2c79fb8c7cb5a6e66130a6ca84767b Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Sat, 14 Mar 2020 19:57:58 -0700 Subject: [PATCH 118/135] basic outline for sdk - now we need to distinguish the partner package --- src/ProjectTemplates/Quantum.App1/Driver.cs | 11 ++----- .../DocumentationParser/DocComment.cs | 14 ++++---- .../DefaultItems/DefaultItems.targets | 33 ++++++++++++++----- src/QuantumSdk/Sdk/Sdk.props | 6 ++-- src/QuantumSdk/Sdk/Sdk.targets | 2 +- 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/ProjectTemplates/Quantum.App1/Driver.cs b/src/ProjectTemplates/Quantum.App1/Driver.cs index 5ab9441939..20a7ecae50 100644 --- a/src/ProjectTemplates/Quantum.App1/Driver.cs +++ b/src/ProjectTemplates/Quantum.App1/Driver.cs @@ -1,7 +1,4 @@ -using System; -using System.Threading.Tasks; - -using Microsoft.Quantum.Simulation.Core; +using System.Threading.Tasks; using Microsoft.Quantum.Simulation.Simulators; namespace Quantum.App1 @@ -10,10 +7,8 @@ class Driver { static async Task Main(string[] args) { - using (var qsim = new QuantumSimulator()) - { - await HelloQ.Run(qsim); - } + using var qsim = new QuantumSimulator(); + await HelloQ.Run(qsim); } } } \ No newline at end of file diff --git a/src/QsCompiler/DocumentationParser/DocComment.cs b/src/QsCompiler/DocumentationParser/DocComment.cs index 0c501cb27b..527ee9a4df 100644 --- a/src/QsCompiler/DocumentationParser/DocComment.cs +++ b/src/QsCompiler/DocumentationParser/DocComment.cs @@ -101,7 +101,7 @@ public class DocComment /// The name of the replacement element for deprecated elements, if given public DocComment(IEnumerable docComments, string name, bool deprecated, string replacement) { - string GetHeadingText(HeadingBlock heading) + static string GetHeadingText(HeadingBlock heading) { var sb = new StringBuilder(); foreach (var item in heading.Inline) @@ -111,7 +111,7 @@ string GetHeadingText(HeadingBlock heading) return sb.ToString(); } - string GetParagraphText(LeafBlock leaf) + static string GetParagraphText(LeafBlock leaf) { var sb = new StringBuilder(); foreach (var item in leaf.Inline) @@ -121,7 +121,7 @@ string GetParagraphText(LeafBlock leaf) return sb.ToString(); } - string ToMarkdown(IEnumerable blocks) + static string ToMarkdown(IEnumerable blocks) { var writer = new StringWriter(); var renderer = new NormalizeRenderer(writer); @@ -136,7 +136,7 @@ string ToMarkdown(IEnumerable blocks) return writer.ToString().TrimEnd().Replace('\n', '\r'); } - List>> BreakIntoSections(IEnumerable blocks, int level) + static List>> BreakIntoSections(IEnumerable blocks, int level) { var key = ""; var accum = new List(); @@ -174,7 +174,7 @@ List>> BreakIntoSections(IEnumerable block return result; } - void ParseListSection(IEnumerable blocks, List accum, bool lowerCase) + static void ParseListSection(IEnumerable blocks, List accum, bool lowerCase) { foreach (var block in blocks) { @@ -202,7 +202,7 @@ void ParseListSection(IEnumerable blocks, List accum, bool lowerC } } - void ParseMapSection(IEnumerable blocks, Dictionary accum) + static void ParseMapSection(IEnumerable blocks, Dictionary accum) { var subsections = BreakIntoSections(blocks, 2); foreach ((var key, var subs) in subsections) @@ -214,7 +214,7 @@ void ParseMapSection(IEnumerable blocks, Dictionary accum } // First element is not matching, second is matching - (List, List) PartitionNestedSection(IEnumerable blocks, int level, string name) + static (List, List) PartitionNestedSection(IEnumerable blocks, int level, string name) { var inMatch = false; var result = (new List(), new List()); diff --git a/src/QuantumSdk/DefaultItems/DefaultItems.targets b/src/QuantumSdk/DefaultItems/DefaultItems.targets index 764107d873..5950afd17d 100644 --- a/src/QuantumSdk/DefaultItems/DefaultItems.targets +++ b/src/QuantumSdk/DefaultItems/DefaultItems.targets @@ -17,7 +17,7 @@ - + QsharpLibrary QsharpExe @@ -25,15 +25,32 @@ Possible values are 'Exe', or 'Library'. - + - QuantumProcessorBackend - SimulatorBackend + HoneywellProcessor + IonQProcessor + QCIProcessor Unspecified - - Possible values are 'QuantumSimulator', 'ToffoliSimulator', 'ResourcesEstimator', or 'QuantumProcessor'. - The execution target for a Q# library needs to be 'Any'. - true + + Possible values are 'Honeywell', 'IonQ', 'QCI', or 'Any'. + The execution target for a Q# library needs to be 'Any'. + + + + + OpenQASM + ExtendedQASM + OpenQASM + + + + + + + QPGen1 + QPGen0 + QPGen1 + diff --git a/src/QuantumSdk/Sdk/Sdk.props b/src/QuantumSdk/Sdk/Sdk.props index ba31d6536e..f7853654a8 100644 --- a/src/QuantumSdk/Sdk/Sdk.props +++ b/src/QuantumSdk/Sdk/Sdk.props @@ -31,11 +31,13 @@ - + + - + + --input "@(QsharpCompile,'" "')" <_QscCommandReferencesFlag Condition="@(ResolvedQsharpReferences->Count()) > 0">--references "@(ResolvedQsharpReferences,'" "')" <_QscCommandLoadFlag Condition="@(_PrioritizedResolvedQscReferences->Count()) > 0">--load "@(_PrioritizedResolvedQscReferences,'" "')" - <_QscCommandTrimFlag Condition="'$(ResolvedQsharpExecutionTarget)' == 'QuantumProcessorBackend'">--trim 2 + <_QscCommandTrimFlag Condition="'$(ResolvedQuantumProcessor)' == 'QPGen1'">--trim 2 <_QscPackageLoadFallbackFoldersFlag Condition="@(ResolvedPackageLoadFallbackFolders->Count()) > 0">--package-load-fallback-folders "@(ResolvedPackageLoadFallbackFolders,'" "')" <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandTrimFlag) $(_QscPackageLoadFallbackFoldersFlag) $(AdditionalQscArguments) <_QscCommandArgsFile>$(QscBuildConfigOutputPath)qsc.rsp From 6341cb3aff9001052a6a27f6511f5b91e093c05c Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Sat, 14 Mar 2020 20:11:37 -0700 Subject: [PATCH 119/135] splitting out options merging --- .../CommandLineTool/Commands/Build.cs | 59 +++++++++++-------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index 9450a86433..fd671a64b8 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -57,6 +57,40 @@ public static IEnumerable UsageExamples [Option('p', "perf", Required = false, SetName = CODE_MODE, HelpText = "Destination folder where the output of the performance assessment will be generated.")] public string PerfFolder { get; set; } + + + /// + /// Reads the content of all specified response files and processes it using FromResponseFiles. + /// Updates the options accordingly, prioritizing already specified non-default values over the values from response-files. + /// Returns false if the content of the specified response-files could not be processed. + /// + internal bool IncorporateResponseFiles() + { + while (this.ResponseFiles != null && this.ResponseFiles.Any()) + { + var fromResponseFiles = FromResponseFiles(this.ResponseFiles); + if (fromResponseFiles == null) return false; + + this.CodeSnippet ??= fromResponseFiles.CodeSnippet; + this.DocFolder ??= fromResponseFiles.DocFolder; + this.EmitDll = this.EmitDll || fromResponseFiles.EmitDll; + this.Input = (this.Input ?? new string[0]).Concat(fromResponseFiles.Input ?? new string[0]); + this.NoWarn = (this.NoWarn ?? new int[0]).Concat(fromResponseFiles.NoWarn ?? new int[0]); + this.OutputFolder ??= fromResponseFiles.OutputFolder; + this.OutputFormat = this.OutputFormat != DefaultOptions.OutputFormat ? this.OutputFormat : fromResponseFiles.OutputFormat; + this.PackageLoadFallbackFolders = (this.PackageLoadFallbackFolders ?? new string[0]).Concat(fromResponseFiles.PackageLoadFallbackFolders ?? new string[0]); + this.PerfFolder ??= fromResponseFiles.PerfFolder; + this.Plugins = (this.Plugins ?? new string[0]).Concat(fromResponseFiles.Plugins ?? new string[0]); + this.ProjectName ??= fromResponseFiles.ProjectName; + this.References = (this.References ?? new string[0]).Concat(fromResponseFiles.References ?? new string[0]); + this.ResponseFiles = fromResponseFiles.ResponseFiles; + this.TargetPackage ??= fromResponseFiles.TargetPackage; + this.TrimLevel = this.TrimLevel != DefaultOptions.TrimLevel ? this.TrimLevel : fromResponseFiles.TrimLevel; + this.Verbosity = this.Verbosity != DefaultOptions.Verbosity ? this.Verbosity : fromResponseFiles.Verbosity; + this.WithinFunction = this.WithinFunction || fromResponseFiles.WithinFunction; + } + return true; + } } /// @@ -112,30 +146,7 @@ public static int Run(BuildOptions options, ConsoleLogger logger) { if (options == null) throw new ArgumentNullException(nameof(options)); if (logger == null) throw new ArgumentNullException(nameof(logger)); - - while (options.ResponseFiles != null && options.ResponseFiles.Any()) - { - var fromResponseFiles = FromResponseFiles(options.ResponseFiles); - if (fromResponseFiles == null) return ReturnCode.INVALID_ARGUMENTS; - - options.CodeSnippet ??= fromResponseFiles.CodeSnippet; - options.DocFolder ??= fromResponseFiles.DocFolder; - options.EmitDll = options.EmitDll || fromResponseFiles.EmitDll; - options.Input = (options.Input ?? new string[0]).Concat(fromResponseFiles.Input ?? new string[0]); - options.NoWarn = (options.NoWarn ?? new int[0]).Concat(fromResponseFiles.NoWarn ?? new int[0]); - options.OutputFolder ??= fromResponseFiles.OutputFolder; - options.OutputFormat = options.OutputFormat != DefaultOptions.OutputFormat ? options.OutputFormat : fromResponseFiles.OutputFormat; - options.PackageLoadFallbackFolders = (options.PackageLoadFallbackFolders ?? new string[0]).Concat(fromResponseFiles.PackageLoadFallbackFolders ?? new string[0]); - options.PerfFolder ??= fromResponseFiles.PerfFolder; - options.Plugins = (options.Plugins ?? new string[0]).Concat(fromResponseFiles.Plugins ?? new string[0]); - options.ProjectName ??= fromResponseFiles.ProjectName; - options.References = (options.References ?? new string[0]).Concat(fromResponseFiles.References ?? new string[0]); - options.ResponseFiles = fromResponseFiles.ResponseFiles; - options.TargetPackage ??= fromResponseFiles.TargetPackage; - options.TrimLevel = options.TrimLevel != DefaultOptions.TrimLevel ? options.TrimLevel : fromResponseFiles.TrimLevel; - options.Verbosity = options.Verbosity != DefaultOptions.Verbosity ? options.Verbosity : fromResponseFiles.Verbosity; - options.WithinFunction = options.WithinFunction || fromResponseFiles.WithinFunction; - } + if (!options.IncorporateResponseFiles()) return ReturnCode.INVALID_ARGUMENTS; var usesPlugins = options.Plugins != null && options.Plugins.Any(); var loadOptions = new CompilationLoader.Configuration From 0292bd7157dabf63bb2350d94839bce988d589ec Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Sat, 14 Mar 2020 20:11:52 -0700 Subject: [PATCH 120/135] whitespace --- src/QsCompiler/CommandLineTool/Commands/Build.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index fd671a64b8..cb1718542a 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -93,6 +93,7 @@ internal bool IncorporateResponseFiles() } } + /// /// Given a string representing the command line arguments, splits them into a suitable string array. /// From ad5efe1879ef4f919bd8c9a73d54516a3ad87e2b Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Sat, 14 Mar 2020 20:42:35 -0700 Subject: [PATCH 121/135] take exclusive sets into account when merging options --- .../CommandLineTool/Commands/Build.cs | 19 +-------------- src/QsCompiler/CommandLineTool/Options.cs | 24 +++++++++++++++---- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index cb1718542a..a269d13506 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -70,24 +70,7 @@ internal bool IncorporateResponseFiles() { var fromResponseFiles = FromResponseFiles(this.ResponseFiles); if (fromResponseFiles == null) return false; - - this.CodeSnippet ??= fromResponseFiles.CodeSnippet; - this.DocFolder ??= fromResponseFiles.DocFolder; - this.EmitDll = this.EmitDll || fromResponseFiles.EmitDll; - this.Input = (this.Input ?? new string[0]).Concat(fromResponseFiles.Input ?? new string[0]); - this.NoWarn = (this.NoWarn ?? new int[0]).Concat(fromResponseFiles.NoWarn ?? new int[0]); - this.OutputFolder ??= fromResponseFiles.OutputFolder; - this.OutputFormat = this.OutputFormat != DefaultOptions.OutputFormat ? this.OutputFormat : fromResponseFiles.OutputFormat; - this.PackageLoadFallbackFolders = (this.PackageLoadFallbackFolders ?? new string[0]).Concat(fromResponseFiles.PackageLoadFallbackFolders ?? new string[0]); - this.PerfFolder ??= fromResponseFiles.PerfFolder; - this.Plugins = (this.Plugins ?? new string[0]).Concat(fromResponseFiles.Plugins ?? new string[0]); - this.ProjectName ??= fromResponseFiles.ProjectName; - this.References = (this.References ?? new string[0]).Concat(fromResponseFiles.References ?? new string[0]); - this.ResponseFiles = fromResponseFiles.ResponseFiles; - this.TargetPackage ??= fromResponseFiles.TargetPackage; - this.TrimLevel = this.TrimLevel != DefaultOptions.TrimLevel ? this.TrimLevel : fromResponseFiles.TrimLevel; - this.Verbosity = this.Verbosity != DefaultOptions.Verbosity ? this.Verbosity : fromResponseFiles.Verbosity; - this.WithinFunction = this.WithinFunction || fromResponseFiles.WithinFunction; + this.UpdateSetIndependentSettings(fromResponseFiles); } return true; } diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs index 5b3b5ea8ed..5efa1dbc99 100644 --- a/src/QsCompiler/CommandLineTool/Options.cs +++ b/src/QsCompiler/CommandLineTool/Options.cs @@ -30,7 +30,7 @@ internal static class DefaultOptions public class CompilationOptions : Options { - [Option("trim", Required = false, Default = DefaultOptions.TrimLevel, + [Option("trim", Required = false, Default = DefaultOptions.TrimLevel, SetName = CODE_MODE, HelpText = "[Experimental feature] Integer indicating how much to simplify the syntax tree by eliminating selective abstractions.")] public int TrimLevel { get; set; } @@ -88,6 +88,10 @@ public enum LogFormat HelpText = "Specifies the verbosity of the logged output. Valid values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic].")] public string Verbosity { get; set; } + [Option("format", Required = false, Default = DefaultOptions.OutputFormat, + HelpText = "Specifies the output format of the command line compiler.")] + public LogFormat OutputFormat { get; set; } + [Option('i', "input", Required = true, SetName = CODE_MODE, HelpText = "Q# code or name of the Q# file to compile.")] public IEnumerable Input { get; set; } @@ -108,15 +112,25 @@ public enum LogFormat HelpText = "Warnings with the given code(s) will be ignored.")] public IEnumerable NoWarn { get; set; } - [Option("format", Required = false, Default = DefaultOptions.OutputFormat, - HelpText = "Specifies the output format of the command line compiler.")] - public LogFormat OutputFormat { get; set; } - [Option("package-load-fallback-folders", Required = false, SetName = CODE_MODE, HelpText = "Specifies the directories the compiler will search when a compiler dependency could not be found.")] public IEnumerable PackageLoadFallbackFolders { get; set; } + /// + /// Updates the settings that can be used independent on the other arguments according to the setting in the given options. + /// Already specified non-default values are prioritized over the values in the given options, + /// unless overwriteNonDefaultValues is set to true. Sequences are merged. + /// + internal void UpdateSetIndependentSettings(Options updates, bool overwriteNonDefaultValues = false) + { + this.Verbosity = overwriteNonDefaultValues || this.Verbosity == DefaultOptions.Verbosity ? updates.Verbosity : this.Verbosity; + this.OutputFormat = overwriteNonDefaultValues || this.OutputFormat == DefaultOptions.OutputFormat ? updates.OutputFormat : this.OutputFormat; + this.NoWarn = (this.NoWarn ?? new int[0]).Concat(updates.NoWarn ?? new int[0]); + this.References = (this.References ?? new string[0]).Concat(updates.References ?? new string[0]); + } + + // routines related to logging /// From 9a742588873a83cf06141f5dd253ba83db1ff1a5 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 21:37:31 -0700 Subject: [PATCH 122/135] Use reversible name mangling --- .../CompilationManager/CompilationUnit.cs | 37 +++------ src/QsCompiler/Tests.Compiler/LinkingTests.fs | 4 +- .../Transformations/SearchAndReplace.cs | 75 ++++++++++++++++--- 3 files changed, 73 insertions(+), 43 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 03e12f2a20..5c1007a7d3 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -153,6 +153,8 @@ public References(ImmutableDictionary, Headers> refs, Action /// public class CompilationUnit : IReaderWriterLock, IDisposable { + internal static readonly NameDecorator ReferenceDecorator = new NameDecorator("QsReference"); + internal References Externals { get; private set; } internal NamespaceManager GlobalSymbols { get; private set; } private readonly Dictionary CompiledCallables; @@ -704,25 +706,6 @@ internal LocalDeclarations TryGetLocalDeclarations(FileContentManager file, Posi return this.PositionedDeclarations(parentCallable, callablePos, specPos, declarations); } - /// - /// Returns a short tag name for the given source file. The tag is not unique. If multiple source files are - /// being tagged, make sure there are no name conflicts. - /// - /// The source file to get the tag for. - /// A short tag name for the given source file. - internal static string GetTagForSource(string source) => - Regex.Replace(Path.GetFileNameWithoutExtension(source), "[^A-Za-z0-9_]", ""); - - /// - /// Embeds the tag name into the qualified name. - /// - /// The tag name to embed. - /// The qualified name in which to embed the tag name. - /// A new qualified name with the tag embedded into it. - internal static QsQualifiedName EmbedTag(string tag, QsQualifiedName name) => - // TODO: We might need to change the format to make it easier to reverse later. - new QsQualifiedName(name.Namespace, NonNullable.New($"__{name.Name.Value}_{tag}__")); - /// /// Tags the names of imported internal callables and types with a unique identifier based on the path to their /// assembly, so that they do not conflict with callables and types defined locally. Renames all references to @@ -734,17 +717,14 @@ internal static QsQualifiedName EmbedTag(string tag, QsQualifiedName name) => private (IEnumerable, IEnumerable) TagImportedInternalNames(IEnumerable callables, IEnumerable types) { - // Create a mapping from source file names to a short, unique identifying tag name. - var tags = + // Assign a unique ID to each reference. + var ids = callables.Select(callable => callable.SourceFile.Value) .Concat(types.Select(type => type.SourceFile.Value)) .Distinct() - .GroupBy(GetTagForSource) - .SelectMany(group => - group.Count() == 1 - ? new[] { (key: group.Single(), value: group.Key) } - : group.Select((source, index) => (key: source, value: group.Key + index))) - .ToImmutableDictionary(item => item.key, item => item.value); + .Where(source => Externals.Declarations.ContainsKey(NonNullable.New(source))) + .Select((source, index) => (source, index)) + .ToImmutableDictionary(item => item.source, item => item.index); ImmutableDictionary GetMappingForSourceGroup( IGrouping group) => @@ -752,7 +732,8 @@ ImmutableDictionary GetMappingForSourceGroup( .Where(item => !Namespace.IsDeclarationAccessible(false, item.access) && Externals.Declarations.ContainsKey(NonNullable.New(item.source))) - .ToImmutableDictionary(item => item.name, item => EmbedTag(tags[item.source], item.name)); + .ToImmutableDictionary(item => item.name, + item => ReferenceDecorator.Decorate(item.name, ids[item.source])); var transformations = callables.Select(callable => diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 54b0db19c6..f91a90430e 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -169,9 +169,7 @@ type LinkingTests (output:ITestOutputHelper) = let beforeCount = countAll sourceCompilation.BuiltCompilation.Namespaces (Seq.concat [renamed; notRenamed]) let afterCountOriginal = countAll referenceCompilation.BuiltCompilation.Namespaces renamed - let newNames = - renamed |> Seq.map (CompilationUnit.GetTagForSource dllSource - |> FuncConvert.FuncFromTupled CompilationUnit.EmbedTag) + let newNames = renamed |> Seq.map (fun name -> CompilationUnit.ReferenceDecorator.Decorate (name, Nullable 0)) let afterCount = countAll referenceCompilation.BuiltCompilation.Namespaces (Seq.concat [newNames; notRenamed]) Assert.NotEqual (0, beforeCount) diff --git a/src/QsCompiler/Transformations/SearchAndReplace.cs b/src/QsCompiler/Transformations/SearchAndReplace.cs index e823e8b8ad..b991b924ba 100644 --- a/src/QsCompiler/Transformations/SearchAndReplace.cs +++ b/src/QsCompiler/Transformations/SearchAndReplace.cs @@ -363,6 +363,60 @@ public override QsStatementKind OnValueUpdate(QsValueUpdate stm) // routines for replacing symbols/identifiers + /// + /// Provides simple name decoration, or name mangling, by prefixing names with a label and number. + /// + public class NameDecorator + { + private const string original = "original"; + + private readonly string label; + + private readonly Regex pattern; + + /// + /// Creates a new name decorator using the label. + /// + /// The label to use as the prefix for decorated names. + public NameDecorator(string label) + { + this.label = label; + pattern = new Regex($"^__{Regex.Escape(label)}[0-9]*__(?<{original}>.*)__$"); + } + + /// + /// Decorates the name with the label of this name decorator and the given number. + /// + /// The name to decorate. + /// The number to use along with the label to decorate the name. + /// + public string Decorate(string name, int? number = null) => + number is null ? $"__{label}__{name}" : $"__{label}{number}__{name}"; + + /// + /// Decorates the name of the qualified name with the label of this name decorator and the given number. + /// + /// + /// + /// + public QsQualifiedName Decorate(QsQualifiedName name, int? number = null) => + new QsQualifiedName(name.Namespace, NonNullable.New(Decorate(name.Name.Value, number))); + + /// + /// Reverses decoration previously done to the name using the same label as this name decorator. + /// + /// The decorated name to undecorate. + /// + /// The original name before decoration, if the decorated name uses the same label as this name decorator; + /// otherwise, null. + /// + public string Undecorate(string name) + { + var match = pattern.Match(name).Groups[original]; + return match.Success ? match.Value : null; + } + } + /// /// Upon transformation, assigns each defined variable a unique name, independent on the scope, and replaces all references to it accordingly. /// The original variable name can be recovered by using the static method StripUniqueName. @@ -371,6 +425,8 @@ public override QsStatementKind OnValueUpdate(QsValueUpdate stm) public class UniqueVariableNames : SyntaxTreeTransformation { + private static readonly NameDecorator decorator = new NameDecorator("qsVar"); + public class TransformationState { private int VariableNr = 0; @@ -387,18 +443,14 @@ internal QsExpressionKind AdaptIdentifier(Identifier sym, QsNullable internal NonNullable GenerateUniqueName(NonNullable varName) { - var unique = NonNullable.New($"__{Prefix}{this.VariableNr++}__{varName.Value}__"); + var unique = NonNullable.New(decorator.Decorate(varName.Value, VariableNr++)); this.UniqueNames[varName] = unique; return unique; } } - private const string Prefix = "qsVar"; - private const string OrigVarName = "origVarName"; - private static readonly Regex WrappedVarName = new Regex($"^__{Prefix}[0-9]*__(?<{OrigVarName}>.*)__$"); - - public UniqueVariableNames() + public UniqueVariableNames() : base(new TransformationState()) { this.StatementKinds = new StatementKindTransformation(this); @@ -410,13 +462,12 @@ public UniqueVariableNames() // static methods for convenience internal static QsQualifiedName PrependGuid(QsQualifiedName original) => - new QsQualifiedName(original.Namespace, NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + original.Name.Value)); + new QsQualifiedName( + original.Namespace, + NonNullable.New("_" + Guid.NewGuid().ToString("N") + "_" + original.Name.Value)); - public static NonNullable StripUniqueName(NonNullable uniqueName) - { - var matched = WrappedVarName.Match(uniqueName.Value).Groups[OrigVarName]; - return matched.Success ? NonNullable.New(matched.Value) : uniqueName; - } + public static NonNullable StripUniqueName(NonNullable uniqueName) => + NonNullable.New(decorator.Undecorate(uniqueName.Value) ?? uniqueName.Value); // helper classes From 293b16a26b30e44d82c5869b3debb49447640c33 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 21:44:14 -0700 Subject: [PATCH 123/135] Add missing doc comments --- src/QsCompiler/Transformations/SearchAndReplace.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/Transformations/SearchAndReplace.cs b/src/QsCompiler/Transformations/SearchAndReplace.cs index b991b924ba..6fba8be87b 100644 --- a/src/QsCompiler/Transformations/SearchAndReplace.cs +++ b/src/QsCompiler/Transformations/SearchAndReplace.cs @@ -389,16 +389,16 @@ public NameDecorator(string label) /// /// The name to decorate. /// The number to use along with the label to decorate the name. - /// + /// The decorated name. public string Decorate(string name, int? number = null) => number is null ? $"__{label}__{name}" : $"__{label}{number}__{name}"; /// /// Decorates the name of the qualified name with the label of this name decorator and the given number. /// - /// - /// - /// + /// The qualified name to decorate. + /// The number to use along with the label to decorate the qualified name. + /// The decorated qualified name. public QsQualifiedName Decorate(QsQualifiedName name, int? number = null) => new QsQualifiedName(name.Namespace, NonNullable.New(Decorate(name.Name.Value, number))); From 2c1c99432df07b21be4a66b38f9f8b4c29a17c0e Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 22:17:25 -0700 Subject: [PATCH 124/135] Add missing underscores --- src/QsCompiler/Transformations/SearchAndReplace.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/Transformations/SearchAndReplace.cs b/src/QsCompiler/Transformations/SearchAndReplace.cs index 6fba8be87b..ead9612ab2 100644 --- a/src/QsCompiler/Transformations/SearchAndReplace.cs +++ b/src/QsCompiler/Transformations/SearchAndReplace.cs @@ -391,7 +391,7 @@ public NameDecorator(string label) /// The number to use along with the label to decorate the name. /// The decorated name. public string Decorate(string name, int? number = null) => - number is null ? $"__{label}__{name}" : $"__{label}{number}__{name}"; + number is null ? $"__{label}__{name}__" : $"__{label}{number}__{name}__"; /// /// Decorates the name of the qualified name with the label of this name decorator and the given number. From 5e01a0a17f5d53cfb105273b081d7f85c283f504 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 22:28:11 -0700 Subject: [PATCH 125/135] Remove unnecessary thunking --- src/QsCompiler/Core/SymbolTable.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 830bcd4877..fa5c8a2803 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -462,7 +462,7 @@ and Namespace private defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) - let findInReferences () = + let fromReferences = match TypesInReferences.TryGetValue tName with | true, qsType -> if Namespace.IsDeclarationAccessible (false, qsType.Modifiers.Access) @@ -482,7 +482,7 @@ and Namespace private else Inaccessible | false, _ -> NotFound - seq { yield findInReferences () + seq { yield fromReferences yield Seq.map findInPartial Parts.Values |> ResolutionResult.ExactlyOne } |> ResolutionResult.TryFirstBest @@ -503,7 +503,7 @@ and Namespace private defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) - let findInReferences () = + let fromReferences = match CallablesInReferences.TryGetValue cName with | true, callable -> if Namespace.IsDeclarationAccessible (false, callable.Modifiers.Access) @@ -520,7 +520,7 @@ and Namespace private else Inaccessible | false, _ -> NotFound - seq { yield findInReferences () + seq { yield fromReferences yield Seq.map findInPartial Parts.Values |> ResolutionResult.ExactlyOne } |> ResolutionResult.TryFirstBest From 7d5ebfa9ba226ec88133fc6a3f93caff08e2c34d Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sun, 15 Mar 2020 01:22:15 -0700 Subject: [PATCH 126/135] Remove unnecessary nullability --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 2 +- src/QsCompiler/Transformations/SearchAndReplace.cs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index f91a90430e..162945e9ed 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -169,7 +169,7 @@ type LinkingTests (output:ITestOutputHelper) = let beforeCount = countAll sourceCompilation.BuiltCompilation.Namespaces (Seq.concat [renamed; notRenamed]) let afterCountOriginal = countAll referenceCompilation.BuiltCompilation.Namespaces renamed - let newNames = renamed |> Seq.map (fun name -> CompilationUnit.ReferenceDecorator.Decorate (name, Nullable 0)) + let newNames = renamed |> Seq.map (fun name -> CompilationUnit.ReferenceDecorator.Decorate (name, 0)) let afterCount = countAll referenceCompilation.BuiltCompilation.Namespaces (Seq.concat [newNames; notRenamed]) Assert.NotEqual (0, beforeCount) diff --git a/src/QsCompiler/Transformations/SearchAndReplace.cs b/src/QsCompiler/Transformations/SearchAndReplace.cs index ead9612ab2..9ae03cfbf0 100644 --- a/src/QsCompiler/Transformations/SearchAndReplace.cs +++ b/src/QsCompiler/Transformations/SearchAndReplace.cs @@ -364,7 +364,7 @@ public override QsStatementKind OnValueUpdate(QsValueUpdate stm) // routines for replacing symbols/identifiers /// - /// Provides simple name decoration, or name mangling, by prefixing names with a label and number. + /// Provides simple name decoration (or name mangling) by prefixing names with a label and number. /// public class NameDecorator { @@ -390,8 +390,7 @@ public NameDecorator(string label) /// The name to decorate. /// The number to use along with the label to decorate the name. /// The decorated name. - public string Decorate(string name, int? number = null) => - number is null ? $"__{label}__{name}__" : $"__{label}{number}__{name}__"; + public string Decorate(string name, int number) => $"__{label}{number}__{name}__"; /// /// Decorates the name of the qualified name with the label of this name decorator and the given number. @@ -399,7 +398,7 @@ public string Decorate(string name, int? number = null) => /// The qualified name to decorate. /// The number to use along with the label to decorate the qualified name. /// The decorated qualified name. - public QsQualifiedName Decorate(QsQualifiedName name, int? number = null) => + public QsQualifiedName Decorate(QsQualifiedName name, int number) => new QsQualifiedName(name.Namespace, NonNullable.New(Decorate(name.Name.Value, number))); /// From 3ccda80e921d44a13ef5f6d20e95a29de89ee0e6 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Sun, 15 Mar 2020 11:27:58 -0700 Subject: [PATCH 127/135] propagating target package info --- .../CommandLineTool/Commands/Build.cs | 16 ++++++---- .../DefaultItems/DefaultItems.targets | 30 +++++++++++++++---- src/QuantumSdk/Sdk/Sdk.props | 4 +-- src/QuantumSdk/Sdk/Sdk.targets | 7 +++-- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index a269d13506..7ec60f8ca0 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -61,17 +61,21 @@ public static IEnumerable UsageExamples /// /// Reads the content of all specified response files and processes it using FromResponseFiles. - /// Updates the options accordingly, prioritizing already specified non-default values over the values from response-files. + /// Updates the settings accordingly, prioritizing already specified non-default values over the values from response-files. + /// Returns true and a new BuildOptions object as out parameter with all the settings from response files incorporated. /// Returns false if the content of the specified response-files could not be processed. /// - internal bool IncorporateResponseFiles() + internal static bool IncorporateResponseFiles(BuildOptions options, out BuildOptions incorporated) { - while (this.ResponseFiles != null && this.ResponseFiles.Any()) + incorporated = null; + while (options.ResponseFiles != null && options.ResponseFiles.Any()) { - var fromResponseFiles = FromResponseFiles(this.ResponseFiles); + var fromResponseFiles = FromResponseFiles(options.ResponseFiles); if (fromResponseFiles == null) return false; - this.UpdateSetIndependentSettings(fromResponseFiles); + fromResponseFiles.UpdateSetIndependentSettings(options); + options = fromResponseFiles; } + incorporated = options; return true; } } @@ -130,7 +134,7 @@ public static int Run(BuildOptions options, ConsoleLogger logger) { if (options == null) throw new ArgumentNullException(nameof(options)); if (logger == null) throw new ArgumentNullException(nameof(logger)); - if (!options.IncorporateResponseFiles()) return ReturnCode.INVALID_ARGUMENTS; + if (!BuildOptions.IncorporateResponseFiles(options, out options)) return ReturnCode.INVALID_ARGUMENTS; var usesPlugins = options.Plugins != null && options.Plugins.Any(); var loadOptions = new CompilationLoader.Configuration diff --git a/src/QuantumSdk/DefaultItems/DefaultItems.targets b/src/QuantumSdk/DefaultItems/DefaultItems.targets index 5950afd17d..caf136ac33 100644 --- a/src/QuantumSdk/DefaultItems/DefaultItems.targets +++ b/src/QuantumSdk/DefaultItems/DefaultItems.targets @@ -9,8 +9,8 @@ - - + + @@ -44,7 +44,6 @@ - QPGen1 @@ -53,7 +52,7 @@ - + $([System.String]::Copy('$(AssemblyName)').Replace(' ','')) @@ -72,7 +71,28 @@ - + + + + <_TargetPackageReference Include="@(PackageReference)" Condition="@(PackageReference->Count()) > 0 And %(PackageReference.IsTargetPackage)" /> + <_TargetPackageReferencePathProperty Include="@(_TargetPackageReference->'Pkg$([System.String]::Copy('%(_TargetPackageReference.Identity)').Replace('.','_'))')" /> + <_ResolvedTargetPackageReferences Include="$(%(_TargetPackageReferencePathProperty.Identity))" /> + + + + + %(_ResolvedTargetPackageReferences.Identity) + + + + + diff --git a/src/QuantumSdk/Sdk/Sdk.props b/src/QuantumSdk/Sdk/Sdk.props index f7853654a8..f44ea733eb 100644 --- a/src/QuantumSdk/Sdk/Sdk.props +++ b/src/QuantumSdk/Sdk/Sdk.props @@ -31,8 +31,8 @@ - - + + diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index 8edc625354..b7bdbb32eb 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -43,7 +43,7 @@ @@ -64,7 +64,7 @@ - + @@ -74,8 +74,9 @@ <_QscCommandReferencesFlag Condition="@(ResolvedQsharpReferences->Count()) > 0">--references "@(ResolvedQsharpReferences,'" "')" <_QscCommandLoadFlag Condition="@(_PrioritizedResolvedQscReferences->Count()) > 0">--load "@(_PrioritizedResolvedQscReferences,'" "')" <_QscCommandTrimFlag Condition="'$(ResolvedQuantumProcessor)' == 'QPGen1'">--trim 2 + <_QscCommandTargetPackageFlag Condition="'$(ResolvedTargetPackage)' != ''">--target-package "$(ResolvedTargetPackage)" <_QscPackageLoadFallbackFoldersFlag Condition="@(ResolvedPackageLoadFallbackFolders->Count()) > 0">--package-load-fallback-folders "@(ResolvedPackageLoadFallbackFolders,'" "')" - <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandTrimFlag) $(_QscPackageLoadFallbackFoldersFlag) $(AdditionalQscArguments) + <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandTrimFlag) $(_QscCommandTargetPackageFlag) $(_QscPackageLoadFallbackFoldersFlag) $(AdditionalQscArguments) <_QscCommandArgsFile>$(QscBuildConfigOutputPath)qsc.rsp $(QscExe) build --format MsBuild $(_VerbosityFlag) --response-files $(_QscCommandArgsFile) From d87d398b9c31ac99c5056729b8081a72cdb82fc0 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Sun, 15 Mar 2020 11:43:58 -0700 Subject: [PATCH 128/135] forgot to adapt a test --- src/QsCompiler/Tests.Compiler/CommandLineTests.fs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs index 9e013ca437..38ffd7ec19 100644 --- a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs +++ b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs @@ -163,15 +163,11 @@ let ``options from response files`` () = "-i" ("TestCases","LinkingTests","Core.qs") |> Path.Combine ("TestCases","LinkingTests","Diagnostics.qs") |> Path.Combine - "--trim" - "0" |] File.WriteAllText(configFile, String.Join (" ", configArgs)) let commandLineArgs = [| "build" - "--trim" - "2" "--response-files" configFile |] From bcd1b60edee9f4b6afe7933369a4aa11ba2049d9 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Sun, 15 Mar 2020 11:51:50 -0700 Subject: [PATCH 129/135] minor thing --- src/QsCompiler/Tests.Compiler/CommandLineTests.fs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs index 38ffd7ec19..4a13ca8592 100644 --- a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs +++ b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs @@ -168,6 +168,10 @@ let ``options from response files`` () = let commandLineArgs = [| "build" + "-v" + "Detailed" + "--format" + "MsBuild" "--response-files" configFile |] From 4b9ee1afe05fb50c01f94b51118f9d19085e31d1 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sun, 15 Mar 2020 13:48:16 -0700 Subject: [PATCH 130/135] Add test for grouping internal specializations --- .../Tests.Compiler/AccessModifierTests.fs | 8 ------ src/QsCompiler/Tests.Compiler/LinkingTests.fs | 25 ++++++++++++++++++- .../LinkingTests/InternalRenaming.qs | 19 ++++++++++++++ .../Tests.Compiler/Tests.Compiler.fsproj | 6 ++--- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index be164d3fd5..72e8ddd8c9 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -13,18 +13,10 @@ open Xunit type AccessModifierTests (output) = - // TODO: Replace this inherit statement with the one below to enable the test for supporting multiple references - // with the same internal name. inherit CompilerTests (CompilerTests.Compile "TestCases" ["AccessModifiers.qs"] [File.ReadAllLines("ReferenceTargets.txt").[1]], output) - // We load two references, but they are both identical copies. Both define only internal declarations, so this - // implicitly tests that multiple references can re-use the same internal names. - // inherit CompilerTests - // (CompilerTests.Compile "TestCases" ["AccessModifiers.qs"] (File.ReadAllLines("ReferenceTargets.txt").[1..2]), - // output) - member private this.Expect name (diagnostics : IEnumerable) = let ns = "Microsoft.Quantum.Testing.AccessModifiers" |> NonNullable<_>.New let name = name |> NonNullable<_>.New diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 162945e9ed..30e88f38c2 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.Threading.Tasks open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.CompilationBuilder open Microsoft.Quantum.QsCompiler.DataTypes @@ -363,3 +362,27 @@ type LinkingTests (output:ITestOutputHelper) = this.RunInternalRenamingTest 7 [qualifiedName Signatures.InternalRenamingNs "Foo"] [qualifiedName Signatures.InternalRenamingNs "Bar"] + + [] + member this.``Group internal specializations by source file`` () = + let chunks = LinkingTests.ReadAndChunkSourceFile "InternalRenaming.qs" + let sourceCompilation = this.BuildContent chunks.[7] + let namespaces = + sourceCompilation.BuiltCompilation.Namespaces + |> Seq.filter (fun ns -> ns.Name.Value.StartsWith Signatures.InternalRenamingNs) + + let references = + ["InternalRenaming1.dll"; "InternalRenaming2.dll"] + |> Seq.map (fun source -> KeyValuePair.Create(NonNullable<_>.New source, + References.Headers (NonNullable<_>.New source, namespaces))) + |> ImmutableDictionary.CreateRange + |> References + let referenceCompilation = this.BuildContent ("", references) + let callables = GlobalCallableResolutions referenceCompilation.BuiltCompilation.Namespaces + + for i in 0 .. references.Declarations.Count - 1 do + let name = + CompilationUnit.ReferenceDecorator.Decorate (qualifiedName Signatures.InternalRenamingNs "Foo", i) + let specializations = callables.[name].Specializations + Assert.Equal (4, specializations.Length) + Assert.True (specializations |> Seq.forall (fun s -> s.SourceFile = callables.[name].SourceFile)) diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InternalRenaming.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InternalRenaming.qs index 3f67ca815a..a28e639c74 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InternalRenaming.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/InternalRenaming.qs @@ -112,3 +112,22 @@ namespace Microsoft.Quantum.Testing.InternalRenaming { Adjoint Foo(); } } + +// ================================= +// Test 8: Group internal specializations by source file + +namespace Microsoft.Quantum.Testing.InternalRenaming { + internal operation Foo () : Unit is Adj + Ctl { + body { + } + + adjoint { + } + + controlled (cs, ...) { + } + + controlled adjoint (cs, ...) { + } + } +} diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 6e6efcd1fc..e191e09213 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -171,18 +171,16 @@ - + $(MSBuildThisFileDirectory)..\TestTargets\Simulation\Example\bin\$(Configuration)\netcoreapp3.0\Example.dll $(MSBuildThisFileDirectory)..\TestTargets\Libraries\Library1\bin\$(Configuration)\netcoreapp3.0\Library1.dll - $(MSBuildThisFileDirectory)..\TestTargets\Libraries\Library1\bin\$(Configuration)\netcoreapp3.0\Library2.dll - From e7990a84e72f29d9d39649ce42fa17d98e9376de Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sun, 15 Mar 2020 14:14:30 -0700 Subject: [PATCH 131/135] Refactor creating references --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 30e88f38c2..2e8f10a4e5 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -46,6 +46,12 @@ type LinkingTests (output:ITestOutputHelper) = Name = NonNullable<_>.New name } + let createReferences : seq> -> References = + Seq.map (fun (source, namespaces) -> + KeyValuePair.Create(NonNullable<_>.New source, References.Headers (NonNullable<_>.New source, namespaces))) + >> ImmutableDictionary.CreateRange + >> References + /// Counts the number of references to the qualified name in all of the namespaces, including the declaration. let countReferences namespaces (name : QsQualifiedName) = let references = IdentifierReferences (name, defaultOffset) @@ -151,15 +157,10 @@ type LinkingTests (output:ITestOutputHelper) = let chunks = LinkingTests.ReadAndChunkSourceFile "InternalRenaming.qs" let sourceCompilation = this.BuildContent chunks.[num - 1] - let dllSource = "InternalRenaming.dll" let namespaces = sourceCompilation.BuiltCompilation.Namespaces |> Seq.filter (fun ns -> ns.Name.Value.StartsWith Signatures.InternalRenamingNs) - let headers = References.Headers (NonNullable.New dllSource, namespaces) - let references = - [KeyValuePair.Create(NonNullable<_>.New dllSource, headers)] - |> ImmutableDictionary.CreateRange - |> References + let references = createReferences ["InternalRenaming.dll", namespaces] let referenceCompilation = this.BuildContent ("", references) let countAll namespaces names = @@ -371,12 +372,8 @@ type LinkingTests (output:ITestOutputHelper) = sourceCompilation.BuiltCompilation.Namespaces |> Seq.filter (fun ns -> ns.Name.Value.StartsWith Signatures.InternalRenamingNs) - let references = - ["InternalRenaming1.dll"; "InternalRenaming2.dll"] - |> Seq.map (fun source -> KeyValuePair.Create(NonNullable<_>.New source, - References.Headers (NonNullable<_>.New source, namespaces))) - |> ImmutableDictionary.CreateRange - |> References + let references = createReferences ["InternalRenaming1.dll", namespaces + "InternalRenaming2.dll", namespaces] let referenceCompilation = this.BuildContent ("", references) let callables = GlobalCallableResolutions referenceCompilation.BuiltCompilation.Namespaces From 5a6cd3620ced14f0d2f77425ef67c05aa72bc967 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sun, 15 Mar 2020 14:16:57 -0700 Subject: [PATCH 132/135] Skip test for now --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 2e8f10a4e5..56235e36ab 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -364,7 +364,7 @@ type LinkingTests (output:ITestOutputHelper) = [qualifiedName Signatures.InternalRenamingNs "Foo"] [qualifiedName Signatures.InternalRenamingNs "Bar"] - [] + [] member this.``Group internal specializations by source file`` () = let chunks = LinkingTests.ReadAndChunkSourceFile "InternalRenaming.qs" let sourceCompilation = this.BuildContent chunks.[7] From 07c1e4af447cf627e2c75f24a663b8b609047cf2 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Sun, 15 Mar 2020 17:03:17 -0700 Subject: [PATCH 133/135] test project solution --- src/QsCompiler/TestProjects/TestProjects.sln | 28 +++++++++++++++++-- .../ProjectLoaderTests.cs | 4 +-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/TestProjects/TestProjects.sln b/src/QsCompiler/TestProjects/TestProjects.sln index 2394ad35c5..a2e0503b96 100644 --- a/src/QsCompiler/TestProjects/TestProjects.sln +++ b/src/QsCompiler/TestProjects/TestProjects.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28016.0 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29905.134 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test1", "test1\test1.csproj", "{836644B8-DEB3-4E4F-A1B9-54388E23D8AF}" EndProject @@ -21,6 +21,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test8", "test8\test8.csproj EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test9", "test9\test9.csproj", "{4C489C80-CEC0-401C-8EB0-951433EB4CB4}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test10", "test10\test10.csproj", "{B96BC26B-B357-445E-93AD-96A3CA60C39C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test11", "test11\test11.csproj", "{BA4E3734-1A61-4790-9C33-D24F13ECB9C6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test12", "test12\test12.csproj", "{96333D50-173E-420A-8084-EF2E10B5324C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test13", "test13\test13.csproj", "{3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -63,6 +71,22 @@ Global {4C489C80-CEC0-401C-8EB0-951433EB4CB4}.Debug|Any CPU.Build.0 = Debug|Any CPU {4C489C80-CEC0-401C-8EB0-951433EB4CB4}.Release|Any CPU.ActiveCfg = Release|Any CPU {4C489C80-CEC0-401C-8EB0-951433EB4CB4}.Release|Any CPU.Build.0 = Release|Any CPU + {B96BC26B-B357-445E-93AD-96A3CA60C39C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B96BC26B-B357-445E-93AD-96A3CA60C39C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B96BC26B-B357-445E-93AD-96A3CA60C39C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B96BC26B-B357-445E-93AD-96A3CA60C39C}.Release|Any CPU.Build.0 = Release|Any CPU + {BA4E3734-1A61-4790-9C33-D24F13ECB9C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA4E3734-1A61-4790-9C33-D24F13ECB9C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA4E3734-1A61-4790-9C33-D24F13ECB9C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA4E3734-1A61-4790-9C33-D24F13ECB9C6}.Release|Any CPU.Build.0 = Release|Any CPU + {96333D50-173E-420A-8084-EF2E10B5324C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96333D50-173E-420A-8084-EF2E10B5324C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96333D50-173E-420A-8084-EF2E10B5324C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96333D50-173E-420A-8084-EF2E10B5324C}.Release|Any CPU.Build.0 = Release|Any CPU + {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs index 47de9f2635..c4e98fd1e0 100644 --- a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs +++ b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs @@ -241,7 +241,7 @@ public void LoadQsharpConsoleApps() } [TestMethod] - public void LoadQsharpUnittest() + public void LoadQsharpUnitTest() { var (projectFile, context) = Context("test5"); var projDir = Path.GetDirectoryName(projectFile); @@ -293,7 +293,7 @@ internal static class CompilationContext { internal static ProjectInformation Load(Uri projectFile) { - void LogOutput(string msg, MessageType level) => + static void LogOutput(string msg, MessageType level) => Console.WriteLine($"[{level}]: {msg}"); return new EditorState(new ProjectLoader(LogOutput), null, null, null, null) .QsProjectLoader(projectFile, out var loaded) ? loaded : null; From f350f660f248075d13906b2ee83dda0e45cffcbc Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Sun, 15 Mar 2020 17:04:27 -0700 Subject: [PATCH 134/135] threading through the options to specify that references are exposed via a different name for testing purposes --- .../CommandLineTool/Commands/Build.cs | 3 ++- .../CommandLineTool/Commands/Diagnose.cs | 3 ++- src/QsCompiler/CommandLineTool/Options.cs | 5 +++++ .../CompilationManager/CompilationUnit.cs | 10 +++++---- .../CompilationManager/ProjectManager.cs | 16 +++++++++----- src/QsCompiler/Compiler/CompilationLoader.cs | 17 +++++++++++--- src/QsCompiler/Core/Dependencies.fs | 22 ++++++++++++++----- src/QsCompiler/Core/SymbolTable.fs | 16 +++++++++++--- src/QsCompiler/DataStructures/Diagnostics.fs | 2 ++ src/QsCompiler/LanguageServer/EditorState.cs | 3 ++- .../DefaultItems.props.v.template | 1 + src/QuantumSdk/Sdk/Sdk.targets | 3 ++- 12 files changed, 76 insertions(+), 25 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index 7ec60f8ca0..ae100d9543 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -149,7 +149,8 @@ public static int Run(BuildOptions options, ConsoleLogger logger) 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 RewriteSteps = options.Plugins?.Select(step => (step, (string)null)) ?? ImmutableArray<(string, string)>.Empty, - EnableAdditionalChecks = false // todo: enable debug mode? + EnableAdditionalChecks = false, // todo: enable debug mode? + ExposeReferencesViaTestNames = options.ExposeReferencesViaTestNames }; if (options.PerfFolder != null) diff --git a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs index 20d58eede8..361846dc22 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs @@ -217,7 +217,8 @@ public static int Run(DiagnoseOptions options, ConsoleLogger logger) ConvertClassicalControl = options.TrimLevel >= 2, AttemptFullPreEvaluation = options.TrimLevel > 2, RewriteSteps = options.Plugins?.Select(step => (step, (string)null)) ?? ImmutableArray<(string, string)>.Empty, - EnableAdditionalChecks = true + EnableAdditionalChecks = true, + ExposeReferencesViaTestNames = options.ExposeReferencesViaTestNames }; var loaded = new CompilationLoader(options.LoadSourcesOrSnippet(logger), options.References, loadOptions, logger); if (loaded.VerifiedCompilation == null) return ReturnCode.Status(loaded); diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs index 5efa1dbc99..0c3143e56c 100644 --- a/src/QsCompiler/CommandLineTool/Options.cs +++ b/src/QsCompiler/CommandLineTool/Options.cs @@ -42,6 +42,11 @@ public class CompilationOptions : Options HelpText = "Path to the NuGet package containing target specific information and implementations.")] public string TargetPackage { get; set; } + [Option("load-test-names", Required = false, Default = false, SetName = CODE_MODE, + HelpText = "Specifies whether public types and callables declared in referenced assemblies are exposed via their test name defined by the corresponding attribute.")] + public bool ExposeReferencesViaTestNames { get; set; } + + /// /// Returns null if TargetPackage is not null or empty, and /// returns the path to the assembly containing target specific implementations otherwise. diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 5c1007a7d3..fda7104c7e 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -85,7 +85,7 @@ private static IEnumerable TypeHeaders(IEnumerable<(strin public readonly ImmutableDictionary, Headers> Declarations; public static References Empty = - new References(ImmutableDictionary, Headers>.Empty, null); + new References(ImmutableDictionary, Headers>.Empty); /// /// Combines the current references with the given references, and verifies that there are no conflicts. @@ -98,7 +98,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); + return new References (this.Declarations.AddRange(other.Declarations), onError: onError); } /// @@ -109,16 +109,18 @@ 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); + new References(this.Declarations.Remove(source), onError: onError); /// /// Given a dictionary that maps the ids of dll files to the corresponding headers, /// initializes a new set of references based on the given headers and verifies that there are no conflicts. /// Calls the given Action onError with suitable diagnostics if two or more references conflict, /// i.e. if two or more references contain a declaration with the same fully qualified name. + /// If loadTestNames is set to true, then public types and callables declared in referenced assemblies + /// are exposed via their test name defined by the corresponding attribute. /// Throws an ArgumentNullException if the given dictionary of references is null. /// - public References(ImmutableDictionary, Headers> refs, Action onError = null) + public References(ImmutableDictionary, Headers> refs, bool loadTestNames = false, Action onError = null) { this.Declarations = refs ?? throw new ArgumentNullException(nameof(refs)); if (onError == null) return; diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index f73fed31d1..3ad375e89b 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -22,18 +22,20 @@ public class ProjectInformation public readonly string Version; public readonly string OutputPath; + public readonly bool ExposeReferencesViaTestNames; public readonly ImmutableArray SourceFiles; public readonly ImmutableArray ProjectReferences; public readonly ImmutableArray References; internal static ProjectInformation Empty(string version, string outputPath) => - new ProjectInformation(version, outputPath, Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()); + new ProjectInformation(version, outputPath, false, Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()); - public ProjectInformation(string version, string outputPath, + public ProjectInformation(string version, string outputPath, bool loadTestNames, IEnumerable sourceFiles, IEnumerable projectReferences, IEnumerable references) { this.Version = version ?? ""; this.OutputPath = outputPath ?? throw new ArgumentNullException(nameof(outputPath)); + this.ExposeReferencesViaTestNames = 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)); @@ -46,6 +48,7 @@ private class Project : IDisposable { public readonly Uri ProjectFile; public Uri OutputPath { get; private set; } + private bool ExposeReferencesViaTestNames; private bool IsLoaded; /// @@ -141,6 +144,7 @@ internal Project(Uri projectFile, ProjectInformation projectInfo, private void SetProjectInformation(ProjectInformation projectInfo) { if (projectInfo == null) throw new ArgumentNullException(nameof(projectInfo)); + this.ExposeReferencesViaTestNames = projectInfo.ExposeReferencesViaTestNames; this.IsLoaded = false; var outputPath = projectInfo.OutputPath; @@ -257,7 +261,7 @@ private Task LoadProjectReferencesAsync( projectReferences, GetProjectOutputPath(projectOutputPaths, diagnostics), diagnostics.Add, this.Manager.LogException); - this.LoadedProjectReferences = new References(loadedHeaders, null); + this.LoadedProjectReferences = new References(loadedHeaders, this.ExposeReferencesViaTestNames); var importedDeclarations = this.LoadedReferences.CombineWith(this.LoadedProjectReferences, (code,args) => diagnostics.Add(Errors.LoadError(code, args, MessageSource(this.ProjectFile)))); this.ProjectReferenceDiagnostics = diagnostics.ToImmutableArray(); @@ -284,7 +288,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, null); + var loaded = new References(loadedHeaders, this.ExposeReferencesViaTestNames); QsCompilerError.Verify(!loaded.Declarations.Any() || (loaded.Declarations.Count == 1 && loaded.Declarations.First().Key.Value == projRefId.Value), @@ -316,7 +320,7 @@ private Task LoadReferencedAssembliesAsync(IEnumerable references, bool var loadedHeaders = ProjectManager.LoadReferencedAssemblies(references, diagnostics.Add, this.Manager.LogException); - this.LoadedReferences = new References(loadedHeaders, null); + this.LoadedReferences = new References(loadedHeaders, this.ExposeReferencesViaTestNames); var importedDeclarations = this.LoadedReferences.CombineWith(this.LoadedProjectReferences, (code, args) => diagnostics.Add(Errors.LoadError(code, args, MessageSource(this.ProjectFile)))); this.ReferenceDiagnostics = diagnostics.ToImmutableArray(); @@ -338,7 +342,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, null); + var loaded = new References(loadedHeaders, this.ExposeReferencesViaTestNames); QsCompilerError.Verify(!loaded.Declarations.Any() || (loaded.Declarations.Count == 1 && loaded.Declarations.First().Key.Value == refId.Value), diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 425844ffff..1fa31c74b1 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -135,6 +135,11 @@ public struct Configuration /// public bool LoadReferencesBasedOnGeneratedCsharp; /// + /// If set to true, then public types and callables declared in referenced assemblies + /// are exposed via their test name defined by the corresponding attribute. + /// + public bool ExposeReferencesViaTestNames; + /// /// Contains a sequence of tuples with the path to a dotnet dll containing one or more rewrite steps /// (i.e. classes implementing IRewriteStep) and the corresponding output folder. /// The contained rewrite steps will be executed in the defined order and priority at the end of the compilation. @@ -404,7 +409,11 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference ?? throw new ArgumentNullException("unable to load source files"); RaiseCompilationTaskEnd("OverallCompilation", "SourcesLoading"); RaiseCompilationTaskStart("OverallCompilation", "ReferenceLoading"); - var references = loadReferences?.Invoke(refs => this.LoadAssemblies(refs, this.Config.LoadReferencesBasedOnGeneratedCsharp)) + var references = loadReferences?.Invoke( + refs => this.LoadAssemblies( + refs, + loadTestNames: this.Config.ExposeReferencesViaTestNames, + ignoreDllResources: this.Config.LoadReferencesBasedOnGeneratedCsharp)) ?? throw new ArgumentNullException("unable to load referenced binary files"); RaiseCompilationTaskEnd("OverallCompilation", "ReferenceLoading"); @@ -783,11 +792,13 @@ private ImmutableDictionary LoadSourceFiles(IEnumerable sou /// /// Used to load the content of the specified assembly references from disk. + /// If loadTestNames is set to true, then public types and callables declared in referenced assemblies + /// are exposed via their test name defined by the corresponding attribute. /// Returns the loaded content of the references. /// Logs suitable diagnostics in the process and modifies the compilation status accordingly. /// Prints all loaded files using PrintResolvedAssemblies. /// - private References LoadAssemblies(IEnumerable refs, bool ignoreDllResources) + private References LoadAssemblies(IEnumerable refs, bool loadTestNames, bool ignoreDllResources) { this.CompilationStatus.ReferenceLoading = 0; if (refs == null) this.Logger?.Log(WarningCode.ReferencesSetToNull, Enumerable.Empty()); @@ -795,7 +806,7 @@ private References LoadAssemblies(IEnumerable refs, bool ignoreDllResour void onDiagnostic(Diagnostic d) => this.LogAndUpdateLoadDiagnostics(ref this.CompilationStatus.ReferenceLoading, d); var headers = ProjectManager.LoadReferencedAssemblies(refs ?? Enumerable.Empty(), onDiagnostic, onException, ignoreDllResources); var projId = this.Config.ProjectName == null ? null : Path.ChangeExtension(Path.GetFullPath(this.Config.ProjectNameWithExtension), "qsproj"); - var references = new References(headers, (code, args) => onDiagnostic(Errors.LoadError(code, args, projId))); + var references = new References(headers, loadTestNames, (code, args) => onDiagnostic(Errors.LoadError(code, args, projId))); this.PrintResolvedAssemblies(references.Declarations.Keys); return references; } diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index 43005b6003..7763a2510f 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -13,6 +13,7 @@ type BuiltInKind = | Function of TypeParameters : ImmutableArray> | Operation of TypeParameters : ImmutableArray> * IsSelfAdjoint : bool + type BuiltIn = { /// contains the fully qualified name of the built-in FullName : QsQualifiedName @@ -20,6 +21,7 @@ type BuiltIn = { Kind : BuiltInKind } with + static member CoreNamespace = NonNullable.New "Microsoft.Quantum.Core" static member CanonNamespace = NonNullable.New "Microsoft.Quantum.Canon" static member IntrinsicNamespace = NonNullable.New "Microsoft.Quantum.Intrinsic" @@ -51,8 +53,12 @@ type BuiltIn = { | Value tId -> tId.Namespace.Value = BuiltIn.Test.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Test.FullName.Name.Value | Null -> false + static member MarksAlternateNameForTesting (att : QsDeclarationAttribute) = att.TypeId |> function + | Value tId -> tId.Namespace.Value = BuiltIn.EnableTestingViaName.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Test.FullName.Name.Value + | Null -> false - // hard dependencies in Microsoft.Quantum.Core + + // dependencies in Microsoft.Quantum.Core static member Length = { FullName = {Name = "Length" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} @@ -79,20 +85,26 @@ type BuiltIn = { Kind = Attribute } + // dependencies in Microsoft.Quantum.Diagnostics + static member Test = { FullName = {Name = "Test" |> NonNullable.New; Namespace = BuiltIn.DiagnosticsNamespace} Kind = Attribute } - // hard dependencies in Microsoft.Quantum.Canon + static member EnableTestingViaName = { + FullName = {Name = "EnableTestingViaName" |> NonNullable.New; Namespace = BuiltIn.DiagnosticsNamespace} + Kind = Attribute + } + + // dependencies in Microsoft.Quantum.Canon static member NoOp = { FullName = {Name = "NoOp" |> NonNullable.New; Namespace = BuiltIn.CanonNamespace} Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New), IsSelfAdjoint = false) - } - // hard dependencies in Microsoft.Quantum.Simulation.QuantumProcessor.Extensions + // dependencies in Microsoft.Quantum.Simulation.QuantumProcessor.Extensions // This is expected to have type <'T, 'U>((Result[], Result[], (('T => Unit), 'T) , (('U => Unit), 'U)) => Unit) static member ApplyConditionally = { @@ -190,7 +202,7 @@ type BuiltIn = { Kind = Operation (TypeParameters = ImmutableArray.Create("T" |> NonNullable.New, "U" |> NonNullable.New), IsSelfAdjoint = false) } - // "weak dependencies" in other namespaces (e.g. things used for code actions) + // dependencies in other namespaces (e.g. things used for code actions) static member IndexRange = { FullName = {Name = "IndexRange" |> NonNullable.New; Namespace = BuiltIn.StandardArrayNamespace} diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index a7a1e0c057..97624b4791 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -942,6 +942,8 @@ and NamespaceManager /// Each entry in the returned array of attributes is the resolution for the corresponding entry in the array of defined attributes. /// May throw an ArgumentException if no parent callable with the given name exists. member private this.ResolveAttributes (parent : QsQualifiedName, source) (decl : Resolution<'T,_>) = + 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 validateAttributes (alreadyDefined : int list, resAttr) (att : QsDeclarationAttribute) = @@ -954,7 +956,8 @@ and NamespaceManager | Value tId -> let orDefault (range : QsNullable<_>) = range.ValueOr QsCompilerDiagnostic.DefaultRange let attributeHash = - if tId.Namespace.Value = BuiltIn.Deprecated.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Deprecated.FullName.Name.Value then hash (tId.Namespace.Value, tId.Name.Value) + 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) else hash (tId.Namespace.Value, tId.Name.Value, NamespaceManager.ExpressionHash att.Argument) // the attribute is a duplication of another attribute on this declaration @@ -964,7 +967,7 @@ and NamespaceManager |> Seq.singleton |> returnInvalid // the attribute marks an entry point - elif tId.Namespace.Value = BuiltIn.EntryPoint.FullName.Namespace.Value && tId.Name.Value = BuiltIn.EntryPoint.FullName.Name.Value then + elif tId |> isBuiltIn BuiltIn.EntryPoint then match box decl.Defined with | :? CallableSignature as signature when not (signature.TypeParameters.Any()) -> let validateArgAndReturnTypes (qsType : QsType) = @@ -989,7 +992,7 @@ and NamespaceManager | _ -> (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidEntryPointPlacement, [])) |> Seq.singleton |> returnInvalid // the attribute marks a unit test - elif tId.Namespace.Value = BuiltIn.Test.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Test.FullName.Name.Value then + elif tId |> isBuiltIn BuiltIn.Test then let isUnitToUnit (signature : CallableSignature) = let isUnitType = function | Tuple _ | Missing -> false @@ -1008,6 +1011,13 @@ and NamespaceManager else (att.Offset, att.Argument.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidExecutionTargetForTest, [])) |> Seq.singleton |> returnInvalid | _ -> (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidTestAttributePlacement, [])) |> Seq.singleton |> returnInvalid + // the attribute defines an alternative name for testing purposes + elif tId |> isBuiltIn BuiltIn.Attribute || tId |> isBuiltIn BuiltIn.Deprecated || tId |> isBuiltIn BuiltIn.EnableTestingViaName then + match box decl.Defined with + | :? QsSpecializationGenerator -> + (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AttributeInvalidOnSpecialization, [tId.Name.Value])) |> Seq.singleton |> returnInvalid + | _ -> attributeHash :: alreadyDefined, att :: resAttr + // the attribute is another kind of attribute that requires no further verification at this point else attributeHash :: alreadyDefined, att :: resAttr diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 8a904773a7..e75ff4ce3e 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -238,6 +238,7 @@ type ErrorCode = | InvalidEntryPointSpecialization = 6235 | InvalidTestAttributePlacement = 6236 | InvalidExecutionTargetForTest = 6237 + | AttributeInvalidOnSpecialization = 6238 | TypeMismatchInReturn = 6301 | TypeMismatchInValueUpdate = 6302 @@ -601,6 +602,7 @@ type DiagnosticItem = | 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." | ErrorCode.InvalidExecutionTargetForTest -> "Invalid execution target. Currently, valid execution targets for tests are the QuantumSimulator, the ToffoliSimulator, or the ResourcesEstimator." + | ErrorCode.AttributeInvalidOnSpecialization -> "Invalid attribute placement. The attribute {0} cannot be attached to a specialization declaration." | ErrorCode.TypeMismatchInReturn -> "The type {0} of the given expression is not compatible with the expected return type {1}." | ErrorCode.TypeMismatchInValueUpdate -> "The type {0} of the given expression is not compatible with the type {1} of the identifier." diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index e107caa095..3a7e9decf6 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -108,10 +108,11 @@ internal bool QsProjectLoader(Uri projectFile, out ProjectInformation info) var projectReferences = GetItemsByType(projectInstance, "ProjectReference"); var references = GetItemsByType(projectInstance, "Reference"); var version = projectInstance.GetPropertyValue("QsharpLangVersion"); + 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, sourceFiles, projectReferences, references); + info = new ProjectInformation(version, outputPath, loadTestNames, sourceFiles, projectReferences, references); return true; } diff --git a/src/QuantumSdk/DefaultItems/DefaultItems.props.v.template b/src/QuantumSdk/DefaultItems/DefaultItems.props.v.template index 98c796f32d..ea3594c422 100644 --- a/src/QuantumSdk/DefaultItems/DefaultItems.props.v.template +++ b/src/QuantumSdk/DefaultItems/DefaultItems.props.v.template @@ -33,6 +33,7 @@ true true false + false dotnet "$(MSBuildThisFileDirectory)../tools/utils/Microsoft.Quantum.Sdk.BuildConfiguration.dll" $(DefaultQscBuildConfigExe) dotnet "$(MSBuildThisFileDirectory)../tools/qsc/qsc.dll" diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index b7bdbb32eb..55b1893d95 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -75,8 +75,9 @@ <_QscCommandLoadFlag Condition="@(_PrioritizedResolvedQscReferences->Count()) > 0">--load "@(_PrioritizedResolvedQscReferences,'" "')" <_QscCommandTrimFlag Condition="'$(ResolvedQuantumProcessor)' == 'QPGen1'">--trim 2 <_QscCommandTargetPackageFlag Condition="'$(ResolvedTargetPackage)' != ''">--target-package "$(ResolvedTargetPackage)" + <_QscCommandTestNamesFlag Condition="$(ExposeReferencesViaTestNames)">--load-test-names <_QscPackageLoadFallbackFoldersFlag Condition="@(ResolvedPackageLoadFallbackFolders->Count()) > 0">--package-load-fallback-folders "@(ResolvedPackageLoadFallbackFolders,'" "')" - <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandTrimFlag) $(_QscCommandTargetPackageFlag) $(_QscPackageLoadFallbackFoldersFlag) $(AdditionalQscArguments) + <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandTrimFlag) $(_QscCommandTargetPackageFlag) $(_QscPackageLoadFallbackFoldersFlag) $(_QscCommandTestNamesFlag) $(AdditionalQscArguments) <_QscCommandArgsFile>$(QscBuildConfigOutputPath)qsc.rsp $(QscExe) build --format MsBuild $(_VerbosityFlag) --response-files $(_QscCommandArgsFile) From e0fc5cf3b9f20d199878ae63071403ac930e0c32 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Sun, 15 Mar 2020 18:45:34 -0700 Subject: [PATCH 135/135] that should now actually be fully functional --- .../CompilationManager/CompilationUnit.cs | 36 ++++- .../CompilationManager/ProjectManager.cs | 12 +- src/QsCompiler/Core/Dependencies.fs | 3 +- src/QsCompiler/Core/SymbolResolution.fs | 13 ++ src/QsCompiler/Core/SymbolTable.fs | 20 ++- src/QsCompiler/DataStructures/Diagnostics.fs | 6 +- .../Transformations/SearchAndReplace.cs | 128 ++++++++++++++---- 7 files changed, 179 insertions(+), 39 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index fda7104c7e..77140e999b 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -32,7 +32,7 @@ public class Headers public readonly ImmutableArray<(SpecializationDeclarationHeader, SpecializationImplementation)> Specializations; public readonly ImmutableArray Types; - private Headers(string source, + internal Headers(string source, IEnumerable callables = null, IEnumerable<(SpecializationDeclarationHeader, SpecializationImplementation)> specs = null, IEnumerable types = null) @@ -78,6 +78,30 @@ private static IEnumerable TypeHeaders(IEnumerable<(strin attributes.Select(IsDeclaration("TypeDeclarationAttribute")).Where(v => v != null) .Select(TypeDeclarationHeader.FromJson).Select(built => built.Item2); + /// + /// Renames all declarations in the headers for which an alternative name is specified + /// that may be used when loading a type or callable for testing purposes. + /// Leaves declarations for which no such name is defined unchanged. + /// Does not check whether there are any conflicts when using alternative names. + /// + private static Headers LoadTestNames(string source, Headers headers) + { + var renaming = headers.Callables.Where(callable => !callable.Kind.IsTypeConstructor) + .Select(callable => (callable.QualifiedName, callable.Attributes)) + .Concat(headers.Types.Select(type => (type.QualifiedName, type.Attributes))) + .ToImmutableDictionary( + decl => decl.QualifiedName, + decl => SymbolResolution.TryGetTestName(decl.Attributes).ValueOr(decl.QualifiedName)); + + var rename = new RenameReferences(renaming); + var callables = headers.Callables.Select(rename.OnCallableDeclarationHeader); + var specializations = headers.Specializations.Select( + specialization => (rename.OnSpecializationDeclarationHeader(specialization.Item1), + rename.Namespaces.OnSpecializationImplementation(specialization.Item2))); + var types = headers.Types.Select(rename.OnTypeDeclarationHeader); + return new Headers(source, callables, specializations, types); + } + /// /// Dictionary that maps the id of a referenced assembly (given by its location on disk) to the headers defined in that assembly. @@ -123,8 +147,16 @@ internal References Remove(NonNullable source, Action, Headers> refs, bool loadTestNames = false, Action onError = null) { this.Declarations = refs ?? throw new ArgumentNullException(nameof(refs)); + if (loadTestNames) + { + this.Declarations = this.Declarations + .ToImmutableDictionary( + reference => reference.Key, + reference => LoadTestNames(reference.Key.Value, reference.Value) + ); + } + if (onError == null) return; - var conflictingCallables = refs.Values .SelectMany(r => r.Callables) .Where(c => Namespace.IsDeclarationAccessible(false, c.Modifiers.Access)) diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index 3ad375e89b..d42cb675f0 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -261,7 +261,7 @@ private Task LoadProjectReferencesAsync( projectReferences, GetProjectOutputPath(projectOutputPaths, diagnostics), diagnostics.Add, this.Manager.LogException); - this.LoadedProjectReferences = new References(loadedHeaders, this.ExposeReferencesViaTestNames); + this.LoadedProjectReferences = new References(loadedHeaders); var importedDeclarations = this.LoadedReferences.CombineWith(this.LoadedProjectReferences, (code,args) => diagnostics.Add(Errors.LoadError(code, args, MessageSource(this.ProjectFile)))); this.ProjectReferenceDiagnostics = diagnostics.ToImmutableArray(); @@ -288,12 +288,12 @@ 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, this.ExposeReferencesViaTestNames); + var loaded = new References(loadedHeaders); QsCompilerError.Verify(!loaded.Declarations.Any() || (loaded.Declarations.Count == 1 && loaded.Declarations.First().Key.Value == projRefId.Value), $"loaded references upon loading {projectReference.LocalPath}: {String.Join(", ", loaded.Declarations.Select(r => r.Value))}"); - this.LoadedProjectReferences = this.LoadedProjectReferences.Remove(projRefId, null).CombineWith(loaded, null); + this.LoadedProjectReferences = this.LoadedProjectReferences.Remove(projRefId).CombineWith(loaded); var importedDeclarations = this.LoadedReferences.CombineWith(this.LoadedProjectReferences, (code, args) => diagnostics.Add(Errors.LoadError(code, args, MessageSource(this.ProjectFile)))); @@ -320,7 +320,7 @@ private Task LoadReferencedAssembliesAsync(IEnumerable references, bool var loadedHeaders = ProjectManager.LoadReferencedAssemblies(references, diagnostics.Add, this.Manager.LogException); - this.LoadedReferences = new References(loadedHeaders, this.ExposeReferencesViaTestNames); + this.LoadedReferences = new References(loadedHeaders); var importedDeclarations = this.LoadedReferences.CombineWith(this.LoadedProjectReferences, (code, args) => diagnostics.Add(Errors.LoadError(code, args, MessageSource(this.ProjectFile)))); this.ReferenceDiagnostics = diagnostics.ToImmutableArray(); @@ -342,12 +342,12 @@ 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, this.ExposeReferencesViaTestNames); + var loaded = new References(loadedHeaders); QsCompilerError.Verify(!loaded.Declarations.Any() || (loaded.Declarations.Count == 1 && loaded.Declarations.First().Key.Value == refId.Value), $"loaded references upon loading {reference.LocalPath}: {String.Join(", ", loaded.Declarations.Select(r => r.Value))}"); - this.LoadedReferences = this.LoadedReferences.Remove(refId, null).CombineWith(loaded, null); + this.LoadedReferences = this.LoadedReferences.Remove(refId).CombineWith(loaded); var importedDeclarations = this.LoadedReferences.CombineWith(this.LoadedProjectReferences, (code, args) => diagnostics.Add(Errors.LoadError(code, args, MessageSource(this.ProjectFile)))); diff --git a/src/QsCompiler/Core/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index 7763a2510f..1a01f29fd2 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -53,7 +53,8 @@ type BuiltIn = { | Value tId -> tId.Namespace.Value = BuiltIn.Test.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Test.FullName.Name.Value | Null -> false - static member MarksAlternateNameForTesting (att : QsDeclarationAttribute) = att.TypeId |> function + /// Returns true if the given attribute defines an alternative name that may be used when loading a type or callable for testing purposes. + static member internal DefinesNameForTesting (att : QsDeclarationAttribute) = att.TypeId |> function | Value tId -> tId.Namespace.Value = BuiltIn.EnableTestingViaName.FullName.Namespace.Value && tId.Name.Value = BuiltIn.Test.FullName.Name.Value | Null -> false diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index d04354caa6..da6665f4fc 100644 --- a/src/QsCompiler/Core/SymbolResolution.fs +++ b/src/QsCompiler/Core/SymbolResolution.fs @@ -213,6 +213,19 @@ module SymbolResolution = let getRedirect (att : QsDeclarationAttribute) = if att |> BuiltIn.MarksDeprecation then Some att.Argument else None StringArgument (getRedirect, fun ex -> ex.Expression) attributes |> Seq.tryHead |> QsNullable<_>.FromOption + /// Checks whether the given attributes define an alternative name that may be used when loading a type or callable for testing purposes. + /// Returns the qualified name to use as Value if such a name is defined, or Null otherwise. + /// If several attributes define such a name the returned Value is based on the first such attribute. + let TryGetTestName attributes = + let getTestName (att : QsDeclarationAttribute) = if att |> BuiltIn.DefinesNameForTesting then Some att.Argument else None + let qualifiedName testName = + let matchQualifiedName = SyntaxGenerator.FullyQualifiedName.Match testName + let asQualifiedName (str : string) = + let pieces = str.Split '.' + {Namespace = String.Join('.', pieces.Take(pieces.Length-1)) |> NonNullable.New; Name = pieces.Last() |> NonNullable.New} + if matchQualifiedName.Success then Some (matchQualifiedName.Value |> asQualifiedName) else None + StringArgument (getTestName, fun ex -> ex.Expression) attributes |> Seq.tryHead |> Option.bind qualifiedName |> QsNullable<_>.FromOption + /// Checks whether the given attributes indicate that the corresponding declaration contains a unit test. /// Returns a sequence of strings defining all execution targets on which the test should be run. Invalid execution targets are set to null. /// The returned sequence is empty if the declaration does not contain a unit test. diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 97624b4791..53347d6a5e 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -1012,7 +1012,25 @@ and NamespaceManager | _ -> (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.InvalidTestAttributePlacement, [])) |> Seq.singleton |> returnInvalid // the attribute defines an alternative name for testing purposes - elif tId |> isBuiltIn BuiltIn.Attribute || tId |> isBuiltIn BuiltIn.Deprecated || tId |> isBuiltIn BuiltIn.EnableTestingViaName then + elif tId |> isBuiltIn BuiltIn.EnableTestingViaName then + let arg = att.Argument |> AttributeAnnotation.NonInterpolatedStringArgument (fun ex -> ex.Expression) + match box decl.Defined with + | :? QsSpecializationGenerator -> + (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AttributeInvalidOnSpecialization, [tId.Name.Value])) |> Seq.singleton |> returnInvalid + | _ when SyntaxGenerator.FullyQualifiedName.IsMatch arg -> attributeHash :: alreadyDefined, att :: resAttr + | _ -> (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.ExpectingFullNameAsAttributeArgument, [tId.Name.Value])) |> Seq.singleton |> returnInvalid + + // the attribute marks an attribute + elif tId |> isBuiltIn BuiltIn.Attribute then + match box decl.Defined with + | :? CallableSignature -> + (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AttributeInvalidOnCallable, [tId.Name.Value])) |> Seq.singleton |> returnInvalid + | :? QsSpecializationGenerator -> + (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AttributeInvalidOnSpecialization, [tId.Name.Value])) |> Seq.singleton |> returnInvalid + | _ -> attributeHash :: alreadyDefined, att :: resAttr + + // the attribute marks a deprecation + elif tId |> isBuiltIn BuiltIn.Deprecated then match box decl.Defined with | :? QsSpecializationGenerator -> (att.Offset, tId.Range |> orDefault |> QsCompilerDiagnostic.Error (ErrorCode.AttributeInvalidOnSpecialization, [tId.Name.Value])) |> Seq.singleton |> returnInvalid diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index e75ff4ce3e..195a631224 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -238,7 +238,9 @@ type ErrorCode = | InvalidEntryPointSpecialization = 6235 | InvalidTestAttributePlacement = 6236 | InvalidExecutionTargetForTest = 6237 - | AttributeInvalidOnSpecialization = 6238 + | ExpectingFullNameAsAttributeArgument = 6238 + | AttributeInvalidOnSpecialization = 6239 + | AttributeInvalidOnCallable = 6240 | TypeMismatchInReturn = 6301 | TypeMismatchInValueUpdate = 6302 @@ -602,7 +604,9 @@ type DiagnosticItem = | 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." | 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." | ErrorCode.AttributeInvalidOnSpecialization -> "Invalid attribute placement. The attribute {0} cannot be attached to a specialization declaration." + | ErrorCode.AttributeInvalidOnCallable -> "Invalid attribute placement. The attribute {0} cannot be attached to a callable declaration." | ErrorCode.TypeMismatchInReturn -> "The type {0} of the given expression is not compatible with the expected return type {1}." | ErrorCode.TypeMismatchInValueUpdate -> "The type {0} of the given expression is not compatible with the type {1} of the identifier." diff --git a/src/QsCompiler/Transformations/SearchAndReplace.cs b/src/QsCompiler/Transformations/SearchAndReplace.cs index 9ae03cfbf0..7541d512a6 100644 --- a/src/QsCompiler/Transformations/SearchAndReplace.cs +++ b/src/QsCompiler/Transformations/SearchAndReplace.cs @@ -501,18 +501,6 @@ public override QsExpressionKind OnIdentifier(Identifier sym, QsNullable public class RenameReferences : SyntaxTreeTransformation { - /// - /// Creates a new rename references transformation. - /// - /// The mapping from existing names to new names. - public RenameReferences(IImmutableDictionary names) - { - var state = new TransformationState(names); - Types = new TypeTransformation(this, state); - ExpressionKinds = new ExpressionKindTransformation(this, state); - Namespaces = new NamespaceTransformation(this, state); - } - private class TransformationState { private readonly IImmutableDictionary names; @@ -542,32 +530,116 @@ internal UserDefinedType RenameUdt(UserDefinedType udt) } } + + private readonly TransformationState State; + + /// + /// Creates a new rename references transformation. + /// + /// The mapping from existing names to new names. + public RenameReferences(IImmutableDictionary names) + { + State = new TransformationState(names); + Types = new TypeTransformation(this); + ExpressionKinds = new ExpressionKindTransformation(this); + Namespaces = new NamespaceTransformation(this); + } + + + // methods for transformations on headers + + /// + /// Renames references in the callable declaration header, including the name of the callable itself. + /// + /// The callable declaration header in which to rename references. + /// The callable declaration header with renamed references. + public CallableDeclarationHeader OnCallableDeclarationHeader(CallableDeclarationHeader callable) => + new CallableDeclarationHeader( + kind: callable.Kind, + qualifiedName: State.GetNewName(callable.QualifiedName), + attributes: callable.Attributes.Select(Namespaces.OnAttribute).ToImmutableArray(), + modifiers: callable.Modifiers, + sourceFile: callable.SourceFile, + position: callable.Position, + symbolRange: callable.SymbolRange, + argumentTuple: Namespaces.OnArgumentTuple(callable.ArgumentTuple), + signature: Namespaces.OnSignature(callable.Signature), + documentation: Namespaces.OnDocumentation(callable.Documentation)); + + /// + /// Renames references in the specialization declaration header, including the name of the specialization + /// itself. + /// + /// The specialization declaration header in which to rename references. + /// The specialization declaration header with renamed references. + public SpecializationDeclarationHeader OnSpecializationDeclarationHeader( + SpecializationDeclarationHeader specialization) + { + var typeArguments = + specialization.TypeArguments.IsValue + ? QsNullable>.NewValue( + specialization.TypeArguments.Item.Select(Types.OnType).ToImmutableArray()) + : QsNullable>.Null; + return new SpecializationDeclarationHeader( + kind: specialization.Kind, + typeArguments: typeArguments, + information: specialization.Information, + parent: State.GetNewName(specialization.Parent), + attributes: specialization.Attributes.Select(Namespaces.OnAttribute).ToImmutableArray(), + sourceFile: specialization.SourceFile, + position: specialization.Position, + headerRange: specialization.HeaderRange, + documentation: Namespaces.OnDocumentation(specialization.Documentation)); + } + + /// + /// Renames references in the type declaration header, including the name of the type itself. + /// + /// The type declaration header in which to rename references. + /// The type declaration header with renamed references. + public TypeDeclarationHeader OnTypeDeclarationHeader(TypeDeclarationHeader type) + { + return new TypeDeclarationHeader( + qualifiedName: State.GetNewName(type.QualifiedName), + attributes: type.Attributes.Select(Namespaces.OnAttribute).ToImmutableArray(), + modifiers: type.Modifiers, + sourceFile: type.SourceFile, + position: type.Position, + symbolRange: type.SymbolRange, + type: Types.OnType(type.Type), + typeItems: Namespaces.OnTypeItems(type.TypeItems), + documentation: Namespaces.OnDocumentation(type.Documentation)); + } + + + // private helper classes + private class TypeTransformation : Core.TypeTransformation { - private readonly TransformationState state; + private readonly TransformationState State; - public TypeTransformation(RenameReferences parent, TransformationState state) : base(parent) => - this.state = state; + public TypeTransformation(RenameReferences parent) : base(parent) => + this.State = parent.State; public override QsTypeKind OnUserDefinedType(UserDefinedType udt) => - base.OnUserDefinedType(state.RenameUdt(udt)); + base.OnUserDefinedType(State.RenameUdt(udt)); public override QsTypeKind OnTypeParameter(QsTypeParameter tp) => - base.OnTypeParameter(new QsTypeParameter(state.GetNewName(tp.Origin), tp.TypeName, tp.Range)); + base.OnTypeParameter(new QsTypeParameter(State.GetNewName(tp.Origin), tp.TypeName, tp.Range)); } private class ExpressionKindTransformation : Core.ExpressionKindTransformation { - private readonly TransformationState state; + private readonly TransformationState State; - public ExpressionKindTransformation(RenameReferences parent, TransformationState state) : base(parent) => - this.state = state; + public ExpressionKindTransformation(RenameReferences parent) : base(parent) => + this.State = parent.State; public override QsExpressionKind OnIdentifier(Identifier id, QsNullable> typeArgs) { if (id is Identifier.GlobalCallable global) { - id = Identifier.NewGlobalCallable(state.GetNewName(global.Item)); + id = Identifier.NewGlobalCallable(State.GetNewName(global.Item)); } return base.OnIdentifier(id, typeArgs); } @@ -575,29 +647,29 @@ public override QsExpressionKind OnIdentifier(Identifier id, QsNullable - this.state = state; + public NamespaceTransformation(RenameReferences parent) : base(parent) => + this.State = parent.State; public override QsDeclarationAttribute OnAttribute(QsDeclarationAttribute attribute) { var argument = Transformation.Expressions.OnTypedExpression(attribute.Argument); var typeId = attribute.TypeId.IsValue - ? QsNullable.NewValue(state.RenameUdt(attribute.TypeId.Item)) + ? QsNullable.NewValue(State.RenameUdt(attribute.TypeId.Item)) : attribute.TypeId; return base.OnAttribute( new QsDeclarationAttribute(typeId, argument, attribute.Offset, attribute.Comments)); } public override QsCallable OnCallableDeclaration(QsCallable callable) => - base.OnCallableDeclaration(callable.WithFullName(state.GetNewName)); + base.OnCallableDeclaration(callable.WithFullName(State.GetNewName)); public override QsCustomType OnTypeDeclaration(QsCustomType type) => - base.OnTypeDeclaration(type.WithFullName(state.GetNewName)); + base.OnTypeDeclaration(type.WithFullName(State.GetNewName)); public override QsSpecialization OnSpecializationDeclaration(QsSpecialization spec) => - base.OnSpecializationDeclaration(spec.WithParent(state.GetNewName)); + base.OnSpecializationDeclaration(spec.WithParent(State.GetNewName)); } }