From f2b65d7b937443e0f55da4f443330dc964207798 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 21 Jan 2020 17:54:28 -0800 Subject: [PATCH 001/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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/106] 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 190a8f27cd6900e928de03791dd983ad50bd89d0 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 9 Mar 2020 16:40:29 -0700 Subject: [PATCH 061/106] 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 d44ecd39d1c21a78b52a0744986f416bb89c92a2 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 11 Mar 2020 09:33:04 -0700 Subject: [PATCH 062/106] 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 063/106] 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 1f2928245395357b1caf3d6f4496cc37de6fec7d Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 12 Mar 2020 23:51:12 -0700 Subject: [PATCH 064/106] 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 065/106] 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 066/106] 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 067/106] 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 068/106] 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 069/106] 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 486ddd6b3248efa0aab3d3b31630867cac42143c Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 13:43:40 -0700 Subject: [PATCH 070/106] 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 071/106] 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 c43a0782778aa459e71e2a93acfc96b6b5e92c7f Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 14:42:02 -0700 Subject: [PATCH 072/106] 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 073/106] 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 4a384764d3c30a1b9fa20dad1022724388f14dc5 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 13 Mar 2020 17:17:30 -0700 Subject: [PATCH 074/106] 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 075/106] 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 076/106] 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 077/106] 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 078/106] 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 079/106] 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 080/106] 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 081/106] 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 082/106] 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 083/106] 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 084/106] 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 085/106] 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 086/106] 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 087/106] 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 9a742588873a83cf06141f5dd253ba83db1ff1a5 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sat, 14 Mar 2020 21:37:31 -0700 Subject: [PATCH 088/106] 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 089/106] 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 090/106] 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 091/106] 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 092/106] 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 4b9ee1afe05fb50c01f94b51118f9d19085e31d1 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Sun, 15 Mar 2020 13:48:16 -0700 Subject: [PATCH 093/106] 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 094/106] 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 095/106] 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 85fc5e398d4a52eb770fcc732341ee68f5432736 Mon Sep 17 00:00:00 2001 From: bettinaheim <34236215+bettinaheim@users.noreply.github.com> Date: Mon, 16 Mar 2020 11:03:52 -0700 Subject: [PATCH 096/106] Pulling in changes from PR 358 (#375) --- src/QsCompiler/CommandLineTool/LoadContext.cs | 4 +- .../CompilationManager/CompilationUnit.cs | 8 +- .../EditorSupport/SymbolInformation.cs | 11 ++- src/QsCompiler/Core/SymbolTable.fs | 30 +++---- src/QsCompiler/Core/TypeTransformation.fs | 3 +- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 28 +++---- .../Transformations/SearchAndReplace.cs | 78 ++++++++++--------- 7 files changed, 74 insertions(+), 88 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/LoadContext.cs b/src/QsCompiler/CommandLineTool/LoadContext.cs index 1f9284ca54..db5ec2a15e 100644 --- a/src/QsCompiler/CommandLineTool/LoadContext.cs +++ b/src/QsCompiler/CommandLineTool/LoadContext.cs @@ -23,7 +23,7 @@ namespace Microsoft.Quantum.QsCompiler public class LoadContext : AssemblyLoadContext { public readonly string PathToParentAssembly; - private AssemblyDependencyResolver _Resolver; + private readonly AssemblyDependencyResolver _Resolver; private readonly HashSet _FallbackPaths; /// @@ -111,7 +111,7 @@ private Assembly OnResolving(AssemblyLoadContext context, AssemblyName name) return path == null ? null : LoadFromAssemblyPath(path); } - private static ConcurrentBag Loaded = + private static readonly ConcurrentBag Loaded = new ConcurrentBag(); /// diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 5c1007a7d3..ae2ce5937d 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -426,7 +426,7 @@ internal void UpdateCallables(IEnumerable updates) header.Location, header.Signature, header.ArgumentTuple, - ImmutableArray.Create(defaultSpec), + ImmutableArray.Create(defaultSpec), header.Documentation, QsComments.Empty ); @@ -551,8 +551,8 @@ public static ImmutableArray NewSyntaxTree( if (types == null) throw new ArgumentNullException(nameof(types)); var emptyLookup = new NonNullable[0].ToLookup(ns => ns, _ => ImmutableArray.Empty); - string QualifiedName(QsQualifiedName fullName) => $"{fullName.Namespace.Value}.{fullName.Name.Value}"; - string ElementName(QsNamespaceElement e) => + static string QualifiedName(QsQualifiedName fullName) => $"{fullName.Namespace.Value}.{fullName.Name.Value}"; + static string ElementName(QsNamespaceElement e) => e is QsNamespaceElement.QsCustomType t ? QualifiedName(t.Item.FullName) : e is QsNamespaceElement.QsCallable c ? QualifiedName(c.Item.FullName) : null; var namespaceElements = callables.Select(c => (c.FullName.Namespace, QsNamespaceElement.NewQsCallable(c))) @@ -679,7 +679,7 @@ LocalDeclarations TryGetLocalDeclarations() } if (parentCallable == null) return LocalDeclarations.Empty; - declarations = declarations ?? TryGetLocalDeclarations(); + declarations ??= TryGetLocalDeclarations(); Tuple AbsolutePosition(QsNullable> relOffset) => relOffset.IsNull diff --git a/src/QsCompiler/CompilationManager/EditorSupport/SymbolInformation.cs b/src/QsCompiler/CompilationManager/EditorSupport/SymbolInformation.cs index f404855810..19a916ddc4 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/SymbolInformation.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/SymbolInformation.cs @@ -7,7 +7,6 @@ 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; @@ -143,10 +142,10 @@ internal static bool TryGetReferences(this CompilationUnit compilation, QsQualif referenceLocations = namespaces.SelectMany(ns => { var locs = IdentifierReferences.Find(fullName, ns, defaultOffset, out var dLoc, limitToSourceFiles); - declLoc = declLoc ?? dLoc; + declLoc ??= dLoc; return locs; }) - .Distinct().Select(AsLocation).ToArray(); // ToArray is needed here to force the execution before checking declLoc + .Select(AsLocation).ToArray(); // ToArray is needed here to force the execution before checking declLoc declarationLocation = declLoc == null ? null : AsLocation(declLoc.Item1, declLoc.Item2.Offset, declLoc.Item2.Range); return true; } @@ -218,8 +217,8 @@ internal static bool TryGetReferences( .SelectMany(spec => spec.Implementation is SpecializationImplementation.Provided impl && spec.Location.IsValue ? IdentifierReferences.Find(definition.Item.Item1, impl.Item2, file.FileName, spec.Location.Item.Offset) - : ImmutableArray.Empty) - .Distinct().Select(AsLocation); + : ImmutableHashSet.Empty) + .Select(AsLocation); } else // the given position corresponds to a variable declared as part of a specialization declaration or implementation { @@ -227,7 +226,7 @@ spec.Implementation is SpecializationImplementation.Provided impl && spec.Locati var statements = implementation.StatementsAfterDeclaration(defStart.Subtract(specPos)); var scope = new QsScope(statements.ToImmutableArray(), locals); var rootOffset = DiagnosticTools.AsTuple(specPos); - referenceLocations = IdentifierReferences.Find(definition.Item.Item1, scope, file.FileName, rootOffset).Distinct().Select(AsLocation); + referenceLocations = IdentifierReferences.Find(definition.Item.Item1, scope, file.FileName, rootOffset).Select(AsLocation); } declarationLocation = AsLocation(file.FileName, definition.Item.Item2, defRange); return true; diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index fa5c8a2803..95b5e727c1 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -899,12 +899,9 @@ and NamespaceManager /// 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, parentAccess) - (udt : UserDefinedType, udtAccess) = + let checkUdtAccessibility code (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) |] + then [| QsCompilerDiagnostic.Error (code, [udt.Name.Value; parent.Value]) (udt.Range.ValueOr QsCompilerDiagnostic.DefaultRange) |] else [||] @@ -1032,9 +1029,7 @@ 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. - member this.ResolveType (parent : QsQualifiedName, tpNames : ImmutableArray<_>, source : NonNullable) - (qsType : QsType) - : ResolvedType * QsCompilerDiagnostic[] = + member this.ResolveType (parent : QsQualifiedName, tpNames : ImmutableArray<_>, source : NonNullable) (qsType : QsType) = 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 @@ -1048,13 +1043,10 @@ and NamespaceManager /// /// 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 = + member private this.ResolveTypeDeclaration (fullName : QsQualifiedName, 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.Access)) + let checkAccessibility = checkUdtAccessibility ErrorCode.TypeLessAccessibleThanParentType (fullName.Name, modifiers.Access) + let resolveType qsType = resolveType (fullName, ImmutableArray<_>.Empty, source) qsType checkAccessibility 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 @@ -1074,14 +1066,10 @@ 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, access) - (signature, specBundleCharacteristics) = + member private this.ResolveCallableSignature (parentKind, parentName : QsQualifiedName, source, access) (signature, specBundleCharacteristics) = + let checkAccessibility = checkUdtAccessibility ErrorCode.TypeLessAccessibleThanParentCallable (parentName.Name, access) let resolveType tpNames qsType = - let res, errs = - resolveType - (parentName, tpNames, source) - qsType - (checkUdtAccessibility ErrorCode.TypeLessAccessibleThanParentCallable (parentName.Name, access)) + let res, errs = resolveType (parentName, tpNames, source) qsType checkAccessibility if parentKind <> TypeConstructor then res, errs else res.WithoutRangeInfo, errs // strip positional info for auto-generated type constructors SymbolResolution.ResolveCallableSignature (resolveType, specBundleCharacteristics) signature diff --git a/src/QsCompiler/Core/TypeTransformation.fs b/src/QsCompiler/Core/TypeTransformation.fs index 8737747695..5b619f7087 100644 --- a/src/QsCompiler/Core/TypeTransformation.fs +++ b/src/QsCompiler/Core/TypeTransformation.fs @@ -132,4 +132,5 @@ type TypeTransformationBase(options : TransformationOptions) = | ExpressionType.Result -> this.OnResult () | ExpressionType.Pauli -> this.OnPauli () | ExpressionType.Range -> this.OnRange () - ResolvedType.New |> Node.BuildOr t transformed + let ResolvedType t = ResolvedType.New (true, t) + ResolvedType |> Node.BuildOr t transformed diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 56235e36ab..e812561507 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -7,6 +7,7 @@ open System open System.Collections.Generic open System.Collections.Immutable open System.IO +open System.Linq open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.CompilationBuilder open Microsoft.Quantum.QsCompiler.DataTypes @@ -27,21 +28,16 @@ type LinkingTests (output:ITestOutputHelper) = let compilationManager = new CompilationUnitManager(new Action (fun ex -> failwith ex.Message)) - 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 - + // The file name needs to end in ".qs" so that it isn't ignored by the References.Headers class during the internal renaming tests. + let getTempFile () = Path.GetRandomFileName () + ".qs" |> Path.GetFullPath |> Uri let getManager uri content = CompilationUnitManager.InitializeFileManager(uri, content, compilationManager.PublishDiagnostics, compilationManager.LogException) - let defaultOffset = - { - Offset = DiagnosticTools.AsTuple (Position (0, 0)) - Range = QsCompilerDiagnostic.DefaultRange - } - - let qualifiedName ns name = - { + let defaultOffset = { + Offset = DiagnosticTools.AsTuple (Position (0, 0)) + Range = QsCompilerDiagnostic.DefaultRange + } + + let qualifiedName ns name = { Namespace = NonNullable<_>.New ns Name = NonNullable<_>.New name } @@ -59,11 +55,7 @@ type LinkingTests (output:ITestOutputHelper) = 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 + 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 = diff --git a/src/QsCompiler/Transformations/SearchAndReplace.cs b/src/QsCompiler/Transformations/SearchAndReplace.cs index 9ae03cfbf0..d66803179a 100644 --- a/src/QsCompiler/Transformations/SearchAndReplace.cs +++ b/src/QsCompiler/Transformations/SearchAndReplace.cs @@ -55,8 +55,9 @@ public Location(NonNullable source, Tuple declOffset, QsLocati public bool Equals(Location other) => this.SourceFile.Value == other?.SourceFile.Value - && this.DeclarationOffset == other?.DeclarationOffset - && this.RelativeStatementLocation == other?.RelativeStatementLocation + && this.DeclarationOffset.Equals(other?.DeclarationOffset) + && this.RelativeStatementLocation.Offset.Equals(other?.RelativeStatementLocation.Offset) + && this.RelativeStatementLocation.Range.Equals(other?.RelativeStatementLocation.Range) && this.SymbolRange.Item1.Equals(other?.SymbolRange?.Item1) && this.SymbolRange.Item2.Equals(other?.SymbolRange?.Item2); @@ -84,7 +85,7 @@ public override int GetHashCode() public class TransformationState { public Tuple, QsLocation> DeclarationLocation { get; internal set; } - public ImmutableList Locations { get; private set; } + public ImmutableHashSet Locations { get; private set; } /// /// Whenever DeclarationOffset is set, the current statement offset is set to this default value. @@ -101,7 +102,7 @@ internal TransformationState(Func trackId, { this.TrackIdentifier = trackId ?? throw new ArgumentNullException(nameof(trackId)); this.RelevantSourseFiles = limitToSourceFiles; - this.Locations = ImmutableList.Empty; + this.Locations = ImmutableHashSet.Empty; this.DefaultOffset = defaultOffset; } @@ -168,7 +169,7 @@ public IdentifierReferences(QsQualifiedName idName, QsLocation defaultOffset, II // static methods for convenience - public static IEnumerable Find(NonNullable idName, QsScope scope, + public static ImmutableHashSet Find(NonNullable idName, QsScope scope, NonNullable sourceFile, Tuple rootLoc) { var finder = new IdentifierReferences(idName, null, ImmutableHashSet.Create(sourceFile)); @@ -178,7 +179,7 @@ public static IEnumerable Find(NonNullable idName, QsScope sco return finder.SharedState.Locations; } - public static IEnumerable Find(QsQualifiedName idName, QsNamespace ns, QsLocation defaultOffset, + public static ImmutableHashSet Find(QsQualifiedName idName, QsNamespace ns, QsLocation defaultOffset, out Tuple, QsLocation> declarationLocation, IImmutableSet> limitToSourceFiles = null) { var finder = new IdentifierReferences(idName, defaultOffset, limitToSourceFiles); @@ -501,18 +502,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 +531,50 @@ 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); + } + + + // 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)); + QsTypeKind.NewUserDefinedType(State.RenameUdt(udt)); public override QsTypeKind OnTypeParameter(QsTypeParameter tp) => - base.OnTypeParameter(new QsTypeParameter(state.GetNewName(tp.Origin), tp.TypeName, tp.Range)); + QsTypeKind.NewTypeParameter(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 +582,28 @@ 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)); + return 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)); } } From a529fc5edccfac05b29124bf2b582f433f16bdda Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 16 Mar 2020 11:11:14 -0700 Subject: [PATCH 097/106] Bringing over change from PR #360; avoid shadowing --- src/QsCompiler/Core/TypeTransformation.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/QsCompiler/Core/TypeTransformation.fs b/src/QsCompiler/Core/TypeTransformation.fs index 5b619f7087..cc58d49419 100644 --- a/src/QsCompiler/Core/TypeTransformation.fs +++ b/src/QsCompiler/Core/TypeTransformation.fs @@ -132,5 +132,4 @@ type TypeTransformationBase(options : TransformationOptions) = | ExpressionType.Result -> this.OnResult () | ExpressionType.Pauli -> this.OnPauli () | ExpressionType.Range -> this.OnRange () - let ResolvedType t = ResolvedType.New (true, t) - ResolvedType |> Node.BuildOr t transformed + (fun t -> ResolvedType.New (true, t)) |> Node.BuildOr t transformed From 9ee21d8c6ada1bbf68dfb1219209f3102c190785 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 16 Mar 2020 11:35:52 -0700 Subject: [PATCH 098/106] Re-enable multiple internal references test --- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index e812561507..773088983c 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -36,7 +36,7 @@ type LinkingTests (output:ITestOutputHelper) = Offset = DiagnosticTools.AsTuple (Position (0, 0)) Range = QsCompilerDiagnostic.DefaultRange } - + let qualifiedName ns name = { Namespace = NonNullable<_>.New ns Name = NonNullable<_>.New name @@ -356,7 +356,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 d11d89593350838d73d2b0a2bad97c0162c721a6 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 16 Mar 2020 13:55:39 -0700 Subject: [PATCH 099/106] Update symbol table --- src/QsCompiler/Core/SymbolResolution.fs | 8 +- src/QsCompiler/Core/SymbolTable.fs | 143 +++++++++++++----------- 2 files changed, 83 insertions(+), 68 deletions(-) diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index 80db1f071e..d2e65eff98 100644 --- a/src/QsCompiler/Core/SymbolResolution.fs +++ b/src/QsCompiler/Core/SymbolResolution.fs @@ -133,7 +133,7 @@ module internal ResolutionResult = /// Returns a Found result if there is only one in the sequence. If there is more than one, returns an Ambiguous /// 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) + let internal TryAtMostOne<'T> (nsGetter : 'T -> NonNullable) (results : seq>) : ResolutionResult<'T> = let found = results |> Seq.filter (function | Found _ -> true | _ -> false) if Seq.length found > 1 @@ -146,9 +146,9 @@ module internal ResolutionResult = |> 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 + /// Otherwise, returns the same value as ResolutionResult.TryFirstBest. + let internal AtMostOne<'T> : seq> -> ResolutionResult<'T> = + TryAtMostOne (fun _ -> NonNullable<_>.New "") >> function | Ambiguous _ -> QsCompilerError.Raise "Resolution is ambiguous" Exception () |> raise | result -> result diff --git a/src/QsCompiler/Core/SymbolTable.fs b/src/QsCompiler/Core/SymbolTable.fs index 95b5e727c1..5b25b3f2bf 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -240,10 +240,12 @@ type private PartialNamespace private /// 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>, - SpecializationsInReferences : ILookup, SpecializationDeclarationHeader * SpecializationImplementation>, - TypesInReferences : ImmutableDictionary, TypeDeclarationHeader>) = + (name, + parts : IEnumerable,PartialNamespace>>, + CallablesInReferences : ILookup, CallableDeclarationHeader>, + SpecializationsInReferences : ILookup, + SpecializationDeclarationHeader * SpecializationImplementation>, + TypesInReferences : ILookup, TypeDeclarationHeader>) = /// dictionary containing a PartialNamespaces for each source file which implements a part of this namespace - /// the key is the source file where each part of the namespace is defined @@ -253,16 +255,20 @@ and Namespace private /// 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 + let isAvailableWith declarationsGetter accessibilityGetter sameAssembly = + declarationsGetter name + |> Seq.exists (fun name -> Namespace.IsDeclarationAccessible (sameAssembly, accessibilityGetter name)) + |> not - isAvailableWith CallablesInReferences.TryGetValue (fun c -> c.Modifiers.Access) false && - isAvailableWith TypesInReferences.TryGetValue (fun t -> t.Modifiers.Access) false && + let tryToList = function + | true, value -> [value] + | false, _ -> [] + + isAvailableWith (fun name -> CallablesInReferences.[name]) (fun c -> c.Modifiers.Access) false && + isAvailableWith (fun name -> TypesInReferences.[name]) (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) + isAvailableWith (partial.TryGetCallable >> tryToList) (fun c -> (snd c).Modifiers.Access) true && + isAvailableWith (partial.TryGetType >> tryToList) (fun t -> t.Modifiers.Access) true) /// 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. @@ -286,24 +292,33 @@ 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<_,_>) = - if g.Count() > 1 then None - else g.Single() |> Some - let typesInRefs = typesInRefs.GroupBy(fun t -> t.QualifiedName.Name) |> Seq.choose FilterUnique - let callablesInRefs = callablesInRefs.GroupBy(fun c -> c.QualifiedName.Name) |> Seq.choose FilterUnique - - let types = typesInRefs.ToImmutableDictionary(fun t -> t.QualifiedName.Name) - let callables = callablesInRefs.ToImmutableDictionary(fun c -> c.QualifiedName.Name) - let specializations = specializationsInRefs.Where(fun (s, _) -> callables.ContainsKey s.Parent.Name).ToLookup(fun (s, _) -> s.Parent.Name) - new Namespace(name, initialSources, callables, specializations, types) - + let discardConflicts getAccess (_, nameGroup) = + // Only one externally accessible declaration with the same name is allowed. + let isAccessible header = Namespace.IsDeclarationAccessible (false, getAccess header) + if nameGroup |> Seq.filter isAccessible |> Seq.length > 1 + then nameGroup |> Seq.filter (not << isAccessible) + else nameGroup + + let createLookup getName getAccess headers = + headers + |> Seq.groupBy getName + |> Seq.map (discardConflicts getAccess) + |> Seq.concat + |> fun headers -> headers.ToLookup (Func<_, _> getName) + + let types = typesInRefs |> createLookup (fun t -> t.QualifiedName.Name) (fun t -> t.Modifiers.Access) + let callables = callablesInRefs |> createLookup (fun c -> c.QualifiedName.Name) (fun c -> c.Modifiers.Access) + let specializations = + specializationsInRefs + .Where(fun (s, _) -> callables.[s.Parent.Name].Any()) + .ToLookup(fun (s, _) -> s.Parent.Name) + Namespace (name, initialSources, callables, specializations, types) /// returns true if the namespace currently contains no source files or referenced content member this.IsEmpty = @@ -370,12 +385,16 @@ and Namespace private resolution.Resolved.ValueOrApply missingResolutionException |> fst |> Some | _ -> None | false, _ -> - match TypesInReferences.TryGetValue attName with - | true, qsType -> + let referenceType = + TypesInReferences.[attName] + |> Seq.filter (fun qsType -> qsType.SourceFile = source) + |> Seq.tryExactlyOne + match referenceType with + | Some 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 + | None -> 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! @@ -462,15 +481,12 @@ and Namespace private defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) - let fromReferences = - 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 resolveReferenceType (typeHeader : TypeDeclarationHeader) = + if Namespace.IsDeclarationAccessible (false, typeHeader.Modifiers.Access) + then Found (typeHeader.SourceFile, + SymbolResolution.TryFindRedirect typeHeader.Attributes, + typeHeader.Modifiers.Access) + else Inaccessible let findInPartial (partial : PartialNamespace) = match partial.TryGetType tName with @@ -482,8 +498,8 @@ and Namespace private else Inaccessible | false, _ -> NotFound - seq { yield fromReferences - yield Seq.map findInPartial Parts.Values |> ResolutionResult.ExactlyOne } + seq { yield Seq.map resolveReferenceType TypesInReferences.[tName] |> ResolutionResult.AtMostOne + yield Seq.map findInPartial Parts.Values |> ResolutionResult.AtMostOne } |> ResolutionResult.TryFirstBest /// Returns a resolution result for the callable with the given name containing the name of the source file or @@ -503,13 +519,10 @@ and Namespace private defaultArg checkDeprecation (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) - let fromReferences = - 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 resolveReferenceCallable (callable : CallableDeclarationHeader) = + if Namespace.IsDeclarationAccessible (false, callable.Modifiers.Access) + then Found (callable.SourceFile, SymbolResolution.TryFindRedirect callable.Attributes) + else Inaccessible let findInPartial (partial : PartialNamespace) = match partial.TryGetCallable cName with @@ -520,8 +533,8 @@ and Namespace private else Inaccessible | false, _ -> NotFound - seq { yield fromReferences - yield Seq.map findInPartial Parts.Values |> ResolutionResult.ExactlyOne } + seq { yield Seq.map resolveReferenceCallable CallablesInReferences.[cName] |> ResolutionResult.AtMostOne + yield Seq.map findInPartial Parts.Values |> ResolutionResult.AtMostOne } |> ResolutionResult.TryFirstBest /// Sets the resolution for the type with the given name in the given source file to the given type, @@ -668,7 +681,7 @@ and Namespace private let unitReturn = cDecl.Defined.ReturnType |> unitOrInvalid (fun (t : QsType) -> t.Type) unitReturn, cDecl.Defined.TypeParameters.Length | false, _ -> - let cDecl = CallablesInReferences.[cName] + let cDecl = CallablesInReferences.[cName] |> Seq.filter (fun c -> c.SourceFile = source) |> Seq.exactlyOne let unitReturn = cDecl.Signature.ReturnType |> unitOrInvalid (fun (t : ResolvedType) -> t.Resolution) unitReturn, cDecl.Signature.TypeParameters.Length @@ -780,7 +793,7 @@ and NamespaceManager 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 } + yield Seq.map resolveWithNsName importedNs |> ResolutionResult.TryAtMostOne fst } |> ResolutionResult.TryFirstBest /// Given a qualifier for a symbol name, returns the corresponding namespace as Some @@ -1260,8 +1273,9 @@ and NamespaceManager 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) - imported.ToImmutableArray() + try Namespaces.Values + |> Seq.collect (fun ns -> ns.CallablesInReferencedAssemblies.SelectMany (fun g -> g.AsEnumerable ())) + |> fun callables -> callables.ToImmutableArray () finally syncRoot.ExitReadLock() /// Returns the declaration headers for all callables defined in source files, regardless of accessibility. @@ -1307,8 +1321,9 @@ and NamespaceManager /// of accessibility. member this.ImportedTypes() = syncRoot.EnterReadLock() - try let imported = Namespaces.Values |> Seq.collect (fun ns -> ns.TypesInReferencedAssemblies.Values) - imported.ToImmutableArray() + try Namespaces.Values + |> Seq.collect (fun ns -> ns.TypesInReferencedAssemblies.SelectMany (fun g -> g.AsEnumerable ())) + |> fun types -> types.ToImmutableArray () finally syncRoot.ExitReadLock() /// Returns the declaration headers for all types defined in source files, regardless of accessibility. @@ -1451,12 +1466,12 @@ and NamespaceManager } let findInReferences (ns : Namespace) = - match ns.CallablesInReferencedAssemblies.TryGetValue callableName.Name with - | true, callable -> + ns.CallablesInReferencedAssemblies.[callableName.Name] + |> Seq.map (fun callable -> if Namespace.IsDeclarationAccessible (false, callable.Modifiers.Access) then Found callable - else Inaccessible - | false, _ -> NotFound + else Inaccessible) + |> ResolutionResult.AtMostOne let findInSources (ns : Namespace) = function | Some source -> @@ -1536,12 +1551,12 @@ and NamespaceManager } 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 + ns.TypesInReferencedAssemblies.[typeName.Name] + |> Seq.map (fun typeHeader -> + if Namespace.IsDeclarationAccessible (false, typeHeader.Modifiers.Access) + then Found typeHeader + else Inaccessible) + |> ResolutionResult.AtMostOne let findInSources (ns : Namespace) = function | Some source -> From 7e3a6564a324a0c7af4df541844c5d6395bee98c Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 16 Mar 2020 14:08:25 -0700 Subject: [PATCH 100/106] Group specializations by source file --- .../CompilationManager/CompilationUnit.cs | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index ae2ce5937d..8e9f77a820 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -483,25 +483,29 @@ 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); if (Namespace.IsDeclarationAccessible(false, header.Modifiers.Access)) { - var definedSpecs = this.GlobalSymbols.DefinedSpecializations(header.QualifiedName); + var definedSpecs = GlobalSymbols.DefinedSpecializations(header.QualifiedName); QsCompilerError.Verify(definedSpecs.Length == 0, "external specializations are currently not supported"); } - var specializations = importedSpecs.Select(imported => - { - var (specHeader, implementation) = imported; - var specSignature = specHeader.Kind.IsQsControlled || specHeader.Kind.IsQsControlledAdjoint - ? SyntaxGenerator.BuildControlled(header.Signature) - : header.Signature; - return new QsSpecialization(specHeader.Kind, header.QualifiedName, specHeader.Attributes, - specHeader.SourceFile, specHeader.Location, specHeader.TypeArguments, specSignature, - implementation, specHeader.Documentation, QsComments.Empty); - }) - .ToImmutableArray(); + var specializations = + GlobalSymbols + .ImportedSpecializations(header.QualifiedName) + .Where(specialization => specialization.Item1.SourceFile.Equals(header.SourceFile)) + .Select(specialization => + { + var (specHeader, implementation) = specialization; + var specSignature = specHeader.Kind.IsQsControlled || specHeader.Kind.IsQsControlledAdjoint + ? SyntaxGenerator.BuildControlled(header.Signature) + : header.Signature; + return new QsSpecialization( + specHeader.Kind, header.QualifiedName, specHeader.Attributes, specHeader.SourceFile, + specHeader.Location, specHeader.TypeArguments, specSignature, implementation, + specHeader.Documentation, QsComments.Empty); + }) + .ToImmutableArray(); return new QsCallable( header.Kind, header.QualifiedName, From 4a15dcd73b9e80152c7344b3f576148e20725028 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 16 Mar 2020 15:05:18 -0700 Subject: [PATCH 101/106] Fix alignment --- src/QsCompiler/Core/SymbolResolution.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index d2e65eff98..0d3a0620cd 100644 --- a/src/QsCompiler/Core/SymbolResolution.fs +++ b/src/QsCompiler/Core/SymbolResolution.fs @@ -133,8 +133,8 @@ module internal ResolutionResult = /// Returns a Found result if there is only one in the sequence. If there is more than one, returns an Ambiguous /// result containing the namespaces of all Found results given by applying nsGetter to each value. Otherwise, /// returns the same value as TryFirstBest. - let internal TryAtMostOne<'T> (nsGetter : 'T -> NonNullable) - (results : seq>) : ResolutionResult<'T> = + let internal TryAtMostOne<'T> (nsGetter : 'T -> NonNullable) (results : seq>) + : ResolutionResult<'T> = let found = results |> Seq.filter (function | Found _ -> true | _ -> false) if Seq.length found > 1 then found From ec84fa13a666deb5ceef72eac913a5f78e27b066 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 16 Mar 2020 17:19:17 -0700 Subject: [PATCH 102/106] Only group specializations by source if callable is internal --- src/QsCompiler/CompilationManager/CompilationUnit.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 8e9f77a820..3cb0980f27 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -493,7 +493,11 @@ private QsCallable GetImportedCallable(CallableDeclarationHeader header) var specializations = GlobalSymbols .ImportedSpecializations(header.QualifiedName) - .Where(specialization => specialization.Item1.SourceFile.Equals(header.SourceFile)) + .Where(specialization => + // Either the callable is externally accessible, or all of its specializations must be defined in + // the same reference as the callable. + Namespace.IsDeclarationAccessible(false, header.Modifiers.Access) || + specialization.Item1.SourceFile.Equals(header.SourceFile)) .Select(specialization => { var (specHeader, implementation) = specialization; From 75e464811b164aac6896866ce56fc2c864fdfe1e Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 16 Mar 2020 17:47:59 -0700 Subject: [PATCH 103/106] Re-add explicit this --- src/QsCompiler/CompilationManager/CompilationUnit.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 3cb0980f27..9c824bb4c6 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -485,13 +485,13 @@ private QsCallable GetImportedCallable(CallableDeclarationHeader header) if (header == null) throw new ArgumentNullException(nameof(header)); if (Namespace.IsDeclarationAccessible(false, header.Modifiers.Access)) { - var definedSpecs = GlobalSymbols.DefinedSpecializations(header.QualifiedName); + var definedSpecs = this.GlobalSymbols.DefinedSpecializations(header.QualifiedName); QsCompilerError.Verify(definedSpecs.Length == 0, "external specializations are currently not supported"); } var specializations = - GlobalSymbols + this.GlobalSymbols .ImportedSpecializations(header.QualifiedName) .Where(specialization => // Either the callable is externally accessible, or all of its specializations must be defined in From 10cc84cf165a9ff19c601a0c3357bdc97ebdd308 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 16 Mar 2020 21:00:07 -0700 Subject: [PATCH 104/106] getting updates from master --- .gitignore | 5 + examples/CompilerExtensions/Demo/Demo.csproj | 2 +- .../Quantum.App1.csproj.v.template | 2 +- .../Quantum.Test1.csproj.v.template | 2 +- .../CommandLineTool/CommandLineTool.csproj | 2 +- .../EditorSupport/CodeActions.cs | 95 +- src/QsCompiler/Compiler/CompilationLoader.cs | 3 +- .../RewriteSteps/ClassicallyControlled.cs | 28 +- src/QsCompiler/Core/Dependencies.fs | 168 +- src/QsCompiler/Core/SymbolResolution.fs | 442 ++-- src/QsCompiler/Core/SymbolTable.fs | 516 ++--- src/QsCompiler/Core/SyntaxGenerator.fs | 208 +- .../LanguageServer/LanguageServer.csproj | 2 +- src/QsCompiler/TestProjects/test13/Driver.cs | 13 + .../TestProjects/test13/Operation13.qs | 10 + .../TestProjects/test13/test13.csproj | 14 + .../Libraries/Library1/Library1.csproj | 6 +- .../Simulation/Example/Example.csproj | 6 +- .../Simulation/Target/Simulation.csproj | 2 +- .../Tests.Compiler/ClassicalControlTests.fs | 457 ++-- .../{LinkingTests => }/ClassicalControl.qs | 1923 +++++++++-------- .../QuantumProcessorExtensions.qs | 20 - .../Tests.Compiler/TestUtils/Signatures.fs | 74 +- .../Tests.Compiler/Tests.Compiler.fsproj | 15 +- .../Tests.DocGenerator.csproj | 2 +- .../ProjectLoaderTests.cs | 2 + .../Tests.LanguageServer.csproj | 2 +- .../Transformations/ClassicallyControlled.cs | 1206 +++++------ .../ClassicallyControlledUtils.cs | 92 - .../Transformations/ContentLifting.cs | 583 +++++ src/QuantumSdk/QuantumSdk.nuspec | 4 +- .../BuildConfiguration.csproj | 2 +- src/VSCodeExtension/BUILDING.md | 4 +- .../AppProjectTemplate.xml.v.template | 2 +- .../TestProjectTemplate.xml.v.template | 2 +- .../QsharpVSIX/QsharpVSIX.csproj | 2 +- 36 files changed, 3285 insertions(+), 2633 deletions(-) create mode 100644 src/QsCompiler/TestProjects/test13/Driver.cs create mode 100644 src/QsCompiler/TestProjects/test13/Operation13.qs create mode 100644 src/QsCompiler/TestProjects/test13/test13.csproj rename src/QsCompiler/Tests.Compiler/TestCases/{LinkingTests => }/ClassicalControl.qs (61%) delete mode 100644 src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/QuantumProcessorExtensions.qs delete mode 100644 src/QsCompiler/Transformations/ClassicallyControlledUtils.cs create mode 100644 src/QsCompiler/Transformations/ContentLifting.cs diff --git a/.gitignore b/.gitignore index 35e2298b34..0c66261d06 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,11 @@ drops/ # Visual Studio 2017 auto generated files Generated\ Files/ +# Visual Studio Code options directory +.vscode/ +# Visual Studio Code Ionide-FSharp extension cache directory +.ionide/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* diff --git a/examples/CompilerExtensions/Demo/Demo.csproj b/examples/CompilerExtensions/Demo/Demo.csproj index f734c5b986..e2c57cfdd9 100644 --- a/examples/CompilerExtensions/Demo/Demo.csproj +++ b/examples/CompilerExtensions/Demo/Demo.csproj @@ -3,7 +3,7 @@ Detailed Exe - netcoreapp3.0 + netcoreapp3.1 true diff --git a/src/ProjectTemplates/Quantum.App1/Quantum.App1.csproj.v.template b/src/ProjectTemplates/Quantum.App1/Quantum.App1.csproj.v.template index f3188b9c3a..2061e46bde 100644 --- a/src/ProjectTemplates/Quantum.App1/Quantum.App1.csproj.v.template +++ b/src/ProjectTemplates/Quantum.App1/Quantum.App1.csproj.v.template @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 diff --git a/src/ProjectTemplates/Quantum.Test1/Quantum.Test1.csproj.v.template b/src/ProjectTemplates/Quantum.Test1/Quantum.Test1.csproj.v.template index b85d725b5f..40bccfea26 100644 --- a/src/ProjectTemplates/Quantum.Test1/Quantum.Test1.csproj.v.template +++ b/src/ProjectTemplates/Quantum.Test1/Quantum.Test1.csproj.v.template @@ -1,7 +1,7 @@ - netcoreapp3.0 + netcoreapp3.1 false diff --git a/src/QsCompiler/CommandLineTool/CommandLineTool.csproj b/src/QsCompiler/CommandLineTool/CommandLineTool.csproj index 84d1a2af65..dc959da91a 100644 --- a/src/QsCompiler/CommandLineTool/CommandLineTool.csproj +++ b/src/QsCompiler/CommandLineTool/CommandLineTool.csproj @@ -4,7 +4,7 @@ qsc Microsoft Q# compiler command line tool. - netcoreapp3.0 + netcoreapp3.1 Exe diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index e1dd3a4c61..5ac7811a7d 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -23,9 +23,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)); @@ -81,8 +81,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) { @@ -103,9 +103,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) { @@ -142,9 +142,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) @@ -170,10 +170,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) @@ -192,9 +192,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) @@ -208,7 +208,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); @@ -229,7 +229,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}"; @@ -237,10 +237,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.Item3.Argument) : @@ -265,9 +266,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) @@ -297,9 +298,9 @@ 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) @@ -316,7 +317,7 @@ IEnumerable GetCharacteristics(QsTuple> return Enumerable.Empty<(string, WorkspaceEdit)>(); } var indexRange = compilation.GlobalSymbols.TryGetCallable( - new QsQualifiedName(BuiltIn.IndexRange.Namespace, BuiltIn.IndexRange.Name), + new QsQualifiedName(BuiltIn.IndexRange.FullName.Namespace, BuiltIn.IndexRange.FullName.Name), NonNullable.New(nsName), file.FileName); if (!indexRange.IsFound) @@ -324,17 +325,17 @@ IEnumerable GetCharacteristics(QsTuple> return Enumerable.Empty<(string, WorkspaceEdit)>(); } - /// 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); @@ -347,8 +348,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)) @@ -356,7 +357,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() { @@ -368,16 +369,16 @@ IEnumerable IndexRangeEdits(CodeFragment fragment) var fragments = file.FragmentsOverlappingWithRange(range); var edits = fragments.SelectMany(IndexRangeEdits); - var suggestedOpenDir = file.OpenDirectiveSuggestions(range.Start.Line, BuiltIn.IndexRange.Namespace); + var suggestedOpenDir = file.OpenDirectiveSuggestions(range.Start.Line, BuiltIn.IndexRange.FullName.Namespace); 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) @@ -407,7 +408,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}"; @@ -425,11 +426,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) { @@ -438,14 +439,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()); @@ -475,7 +476,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/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 200969bb40..5bd3c0e3fa 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -21,6 +21,7 @@ using Microsoft.VisualStudio.LanguageServer.Protocol; using Newtonsoft.Json.Bson; using MetadataReference = Microsoft.CodeAnalysis.MetadataReference; +using OptimizationLevel = Microsoft.CodeAnalysis.OptimizationLevel; namespace Microsoft.Quantum.QsCompiler @@ -842,7 +843,7 @@ bool CanBeIncluded(NonNullable dll) this.Config.ProjectNameWithoutExtension ?? Path.GetFileNameWithoutExtension(outputPath), syntaxTrees: new[] { csharpTree }, references: references.Select(r => r.Item2).Append(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)), // if System.Object can't be found a warning is generated - options: new CodeAnalysis.CSharp.CSharpCompilationOptions(outputKind: CodeAnalysis.OutputKind.DynamicallyLinkedLibrary) + options: new CodeAnalysis.CSharp.CSharpCompilationOptions(outputKind: CodeAnalysis.OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release) ); using var outputStream = File.OpenWrite(outputPath); 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/Dependencies.fs b/src/QsCompiler/Core/Dependencies.fs index 000e21a2ca..8b16ccab37 100644 --- a/src/QsCompiler/Core/Dependencies.fs +++ b/src/QsCompiler/Core/Dependencies.fs @@ -6,19 +6,22 @@ namespace Microsoft.Quantum.QsCompiler open System.Collections.Immutable open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.SyntaxTree -open Microsoft.Quantum.QsCompiler.SyntaxTokens +type BuiltInKind = + | Attribute + | Function of TypeParameters : ImmutableArray> + | Operation of TypeParameters : ImmutableArray> * IsSelfAdjoint : bool + type BuiltIn = { - /// 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> + /// 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 } - 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" static member StandardArrayNamespace = NonNullable.New "Microsoft.Quantum.Arrays" static member DiagnosticsNamespace = NonNullable.New "Microsoft.Quantum.Diagnostics" @@ -28,155 +31,168 @@ 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 - | Value tId -> tId.Namespace.Value = BuiltIn.EntryPoint.Namespace.Value && tId.Name.Value = BuiltIn.EntryPoint.Name.Value + /// 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 - | Value tId -> tId.Namespace.Value = BuiltIn.Deprecated.Namespace.Value && tId.Name.Value = BuiltIn.Deprecated.Name.Value + /// 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 - | Value tId -> tId.Namespace.Value = BuiltIn.Test.Namespace.Value && tId.Name.Value = BuiltIn.Test.Name.Value + /// 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 // 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 + FullName = {Name = "EntryPoint" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} + Kind = Attribute } static member Deprecated = { - Name = "Deprecated" |> NonNullable.New - Namespace = BuiltIn.CoreNamespace - TypeParameters = ImmutableArray.Empty + FullName = {Name = "Deprecated" |> NonNullable.New; Namespace = BuiltIn.CoreNamespace} + Kind = Attribute } static member Test = { - Name = "Test" |> NonNullable.New - Namespace = BuiltIn.DiagnosticsNamespace - TypeParameters = ImmutableArray.Empty + FullName = {Name = "Test" |> NonNullable.New; Namespace = BuiltIn.DiagnosticsNamespace} + Kind = Attribute + } + + // hard 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 = true) + } // 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 = { + 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 = { + 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 = { + 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 = { + 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) } diff --git a/src/QsCompiler/Core/SymbolResolution.fs b/src/QsCompiler/Core/SymbolResolution.fs index 0d3a0620cd..02479b65ff 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 @@ -48,31 +48,31 @@ 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))) @@ -172,60 +172,60 @@ 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 @@ -235,7 +235,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 @@ -246,35 +246,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 @@ -289,26 +289,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 @@ -324,7 +324,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 @@ -332,28 +332,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. @@ -373,14 +373,14 @@ module SymbolResolution = 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) @@ -390,25 +390,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 @@ -416,16 +416,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, [||] @@ -436,109 +436,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, [||] @@ -547,15 +547,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 @@ -563,16 +563,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 @@ -580,21 +580,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) @@ -602,53 +602,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) @@ -659,32 +659,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)) @@ -698,25 +698,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 5b25b3f2bf..a0f1c1571c 100644 --- a/src/QsCompiler/Core/SymbolTable.fs +++ b/src/QsCompiler/Core/SymbolTable.fs @@ -28,7 +28,7 @@ type private PartialNamespace private 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 @@ -52,24 +52,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 @@ -92,16 +92,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. @@ -110,80 +110,80 @@ 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, modifiers, 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, 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 - 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, 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) + 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, 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. - /// -> 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) = @@ -196,34 +196,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 @@ -280,7 +280,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 @@ -320,36 +320,36 @@ and Namespace private .ToLookup(fun (s, _) -> s.Parent.Name) 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 @@ -363,17 +363,17 @@ and Namespace private 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 + 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) = match att.Id.Symbol with - | Symbol sym when sym.Value = BuiltIn.Attribute.Name.Value && possibleQualifications.Contains "" -> + | Symbol sym when sym.Value = BuiltIn.Attribute.FullName.Name.Value && possibleQualifications.Contains "" -> true - | QualifiedSymbol (ns, sym) when sym.Value = BuiltIn.Attribute.Name.Value && + | QualifiedSymbol (ns, sym) when sym.Value = BuiltIn.Attribute.FullName.Name.Value && possibleQualifications.Contains ns.Value -> true | _ -> false @@ -398,10 +398,10 @@ and Namespace private /// 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 @@ -410,46 +410,46 @@ 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 @@ -479,7 +479,7 @@ and Namespace private member this.TryFindType (tName, ?checkDeprecation : (string -> bool)) = let checkDeprecation = defaultArg checkDeprecation - (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) + (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.FullName.Namespace.Value) let resolveReferenceType (typeHeader : TypeDeclarationHeader) = if Namespace.IsDeclarationAccessible (false, typeHeader.Modifiers.Access) @@ -517,7 +517,7 @@ and Namespace private member this.TryFindCallable (cName, ?checkDeprecation : (string -> bool)) = let checkDeprecation = defaultArg checkDeprecation - (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.Namespace.Value) + (fun qual -> String.IsNullOrWhiteSpace qual || qual = BuiltIn.Deprecated.FullName.Namespace.Value) let resolveReferenceCallable (callable : CallableDeclarationHeader) = if Namespace.IsDeclarationAccessible (false, callable.Modifiers.Access) @@ -540,35 +540,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)) @@ -578,30 +578,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, []) |] @@ -609,7 +609,7 @@ 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); [||] @@ -617,7 +617,7 @@ and Namespace private /// 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. + /// 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, modifiers, documentation) : QsCompilerDiagnostic[] = @@ -707,22 +707,22 @@ and Namespace private | _ -> [| 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 @@ -753,7 +753,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) @@ -764,26 +764,26 @@ 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 /// Calls the resolver function on each namespace opened within the given namespace name and source file, and @@ -797,9 +797,9 @@ and NamespaceManager |> 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. + /// 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 @@ -818,14 +818,14 @@ and NamespaceManager 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 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 - | true, alias -> [alias; builtIn.Namespace.Value] - | false, _ -> [builtIn.Namespace.Value] - | true, _ -> [builtIn.Namespace.Value]; + match (ns.ImportedNamespaces source).TryGetValue builtIn.FullName.Namespace with + | true, null when not (ns.TryFindType builtIn.FullName.Name |> ResolutionResult.IsAccessible) || + 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 unqualified name of a type used within the given parent namespace and source file, @@ -938,88 +938,88 @@ and NamespaceManager | 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 @@ -1090,10 +1090,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 @@ -1103,7 +1103,7 @@ 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 -> @@ -1113,31 +1113,31 @@ and NamespaceManager 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. + // ... 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, [||]) @@ -1151,16 +1151,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) @@ -1179,28 +1179,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() @@ -1208,44 +1208,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 @@ -1285,7 +1285,7 @@ and NamespaceManager 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 @@ -1333,7 +1333,7 @@ and NamespaceManager 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 @@ -1372,34 +1372,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 @@ -1408,7 +1408,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() @@ -1417,23 +1417,23 @@ 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 in a ResolutionResult if @@ -1675,56 +1675,56 @@ 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 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/LanguageServer/LanguageServer.csproj b/src/QsCompiler/LanguageServer/LanguageServer.csproj index e323703f45..ade2b39519 100644 --- a/src/QsCompiler/LanguageServer/LanguageServer.csproj +++ b/src/QsCompiler/LanguageServer/LanguageServer.csproj @@ -2,7 +2,7 @@ Microsoft.Quantum.QsLanguageServer - netcoreapp3.0 + netcoreapp3.1 Exe diff --git a/src/QsCompiler/TestProjects/test13/Driver.cs b/src/QsCompiler/TestProjects/test13/Driver.cs new file mode 100644 index 0000000000..a74c06d12b --- /dev/null +++ b/src/QsCompiler/TestProjects/test13/Driver.cs @@ -0,0 +1,13 @@ +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; + +namespace test3 +{ + class Driver + { + static void Main(string[] args) + { + + } + } +} \ No newline at end of file diff --git a/src/QsCompiler/TestProjects/test13/Operation13.qs b/src/QsCompiler/TestProjects/test13/Operation13.qs new file mode 100644 index 0000000000..a9fde39beb --- /dev/null +++ b/src/QsCompiler/TestProjects/test13/Operation13.qs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace test3 { + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Intrinsic; + + operation Operation () : Unit { + } +} diff --git a/src/QsCompiler/TestProjects/test13/test13.csproj b/src/QsCompiler/TestProjects/test13/test13.csproj new file mode 100644 index 0000000000..c2c94b133c --- /dev/null +++ b/src/QsCompiler/TestProjects/test13/test13.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp3.1 + x64 + + + + + + + + diff --git a/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj b/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj index 4b95b74dce..865a61ebb9 100644 --- a/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj +++ b/src/QsCompiler/TestTargets/Libraries/Library1/Library1.csproj @@ -1,13 +1,13 @@  Library - netcoreapp3.0 + netcoreapp3.1 x64 0.10 - + @@ -17,7 +17,7 @@ - dotnet "../../../CommandLineTool/bin/$(Configuration)/netcoreapp3.0/qsc.dll" + dotnet "../../../CommandLineTool/bin/$(Configuration)/netcoreapp3.1/qsc.dll" diff --git a/src/QsCompiler/TestTargets/Simulation/Example/Example.csproj b/src/QsCompiler/TestTargets/Simulation/Example/Example.csproj index 79ff1984b2..80ea2e6a7f 100644 --- a/src/QsCompiler/TestTargets/Simulation/Example/Example.csproj +++ b/src/QsCompiler/TestTargets/Simulation/Example/Example.csproj @@ -1,9 +1,9 @@ - + Detailed Exe - netcoreapp3.0 + netcoreapp3.1 false false 0219 @@ -14,7 +14,7 @@ ExecutionTests/ ../Target/bin/$(Configuration)/netstandard2.1/Simulation.dll --load $(SimulationTarget) - dotnet "../../../CommandLineTool/bin/$(Configuration)/netcoreapp3.0/qsc.dll" + dotnet "../../../CommandLineTool/bin/$(Configuration)/netcoreapp3.1/qsc.dll" diff --git a/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj b/src/QsCompiler/TestTargets/Simulation/Target/Simulation.csproj index 65781cd08e..3a41cae4ab 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/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index 89be97a4bc..454ff162a2 100644 --- a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -5,15 +5,15 @@ namespace Microsoft.Quantum.QsCompiler.Testing open System open System.IO +open System.Text.RegularExpressions open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.CompilationBuilder open Microsoft.Quantum.QsCompiler.DataTypes +open Microsoft.Quantum.QsCompiler.SyntaxTokens open Microsoft.Quantum.QsCompiler.SyntaxTree open Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlled -open Xunit open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput -open System.Text.RegularExpressions -open Microsoft.Quantum.QsCompiler.SyntaxTokens +open Xunit type ClassicalControlTests () = @@ -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 = @@ -69,7 +65,7 @@ type ClassicalControlTests () = |> writer.Statements.OnScope |> ignore - writer.SharedState.StatementOutputHandle + writer.SharedState.StatementOutputHandle |> Seq.filter (not << String.IsNullOrWhiteSpace) |> Seq.toArray @@ -88,7 +84,7 @@ type ClassicalControlTests () = let MakeApplicationRegex (opName : QsQualifiedName) = let call = sprintf @"(%s\.)?%s" <| Regex.Escape opName.Namespace.Value <| Regex.Escape opName.Name.Value let typeArgs = @"(<\s*([^<]*[^<\s])\s*>)?" // Does not support nested type args - let args = @"\(\s*(.*[^\s])?\s*\)" + let args = @"\(?\s*(.*[^\s])?\s*\)?" sprintf @"\(%s\s*%s,\s*%s\)" <| call <| typeArgs <| args @@ -112,6 +108,8 @@ type ClassicalControlTests () = else (false, "", "", "", "") + let IsTypeArgsMatch input targs = Regex.Match(input, sprintf @"^%s$" <| Regex.Escape targs).Success + let CheckIfSpecializationHasCalls specialization (calls : seq) = let lines = GetLinesFromSpecialization specialization Seq.forall (fun (i, ns, name) -> CheckIfLineIsCall ns name lines.[i] |> (fun (x, _, _) -> x)) calls @@ -119,7 +117,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)) @@ -161,33 +159,16 @@ type ClassicalControlTests () = |> (fun x -> x.Value) let ApplyIfElseTest compilation = - let generated = GetCallablesWithSuffix compilation Signatures.ClassicalControlNs "_Foo" - - Assert.True(2 = Seq.length generated) // Should already be asserted by the signature check - - let ifContent = - [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); - ] - let elseContent = - [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); - ] - - let orderedGens = IdentifyGeneratedByCalls generated [ifContent; elseContent] - let ifOp, elseOp = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens) let original = GetCallableWithName compilation Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable let lines = original |> GetLinesFromSpecialization Assert.True(2 = Seq.length lines, sprintf "Callable %O(%A) did not have the expected number of statements" original.Parent original.Kind) - let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfElseR.Namespace.Value BuiltIn.ApplyIfElseR.Name.Value lines.[1] + 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) - args, ifOp.FullName, elseOp.FullName + targs, args let DoesCallSupportFunctors expectedFunctors call = let hasAdjoint = expectedFunctors |> Seq.contains QsFunctor.Adjoint @@ -227,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" @@ -242,34 +223,34 @@ 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 [] - [] + [] member this.``ApplyIfZero And ApplyIfOne`` () = let result = CompileClassicalControlTest 7 @@ -283,69 +264,57 @@ type ClassicalControlTests () = |> AssertSpecializationHasCalls originalOp [] - [] + [] member this.``Apply If Zero Else One`` () = - let (args, ifOp, elseOp) = CompileClassicalControlTest 8 |> ApplyIfElseTest - IsApplyIfElseArgsMatch args "r" ifOp elseOp + 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")) + Assert.True(IsTypeArgsMatch targs "Result, Unit", "ApplyIfElse did not have the correct type arguments") + [] - [] + [] member this.``Apply If One Else Zero`` () = - let (args, ifOp, elseOp) = CompileClassicalControlTest 9 |> ApplyIfElseTest + let (targs, args) = CompileClassicalControlTest 9 |> ApplyIfElseTest + + let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} + let SubOp1 = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} + // The operation arguments should be swapped from the previous test - IsApplyIfElseArgsMatch args "r" elseOp ifOp + 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.``If Elif`` () = - let result = CompileClassicalControlTest 10 - - let generated = GetCallablesWithSuffix result Signatures.ClassicalControlNs "_Foo" + let (_, args) = CompileClassicalControlTest 10 |> ApplyIfElseTest - Assert.True(3 = Seq.length generated) // Should already be asserted by the signature check - - let ifContent = - [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); - ] - let elifContent = - [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); - ] - let elseContent = - [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); - ] - - let orderedGens = IdentifyGeneratedByCalls generated [ifContent; elifContent; elseContent] - let ifOp, elifOp, elseOp = (Seq.item 0 orderedGens), (Seq.item 1 orderedGens), (Seq.item 2 orderedGens) - - let original = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable - let lines = original |> GetLinesFromSpecialization - - Assert.True(2 = Seq.length lines, sprintf "Callable %O(%A) did not have the expected number of statements" original.Parent original.Kind) - - let (success, _, args) = CheckIfLineIsCall BuiltIn.ApplyIfElseR.Namespace.Value BuiltIn.ApplyIfElseR.Name.Value lines.[1] - Assert.True(success, sprintf "Callable %O(%A) did not have expected content" original.Parent original.Kind) + let ifOp = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} + let elifOp = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp2"} + 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.FullName { 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.FullName elifOp.FullName // elif and else are swapped because second condition is against One + IsApplyIfElseArgsMatch subArgs "r" elseOp elifOp // elif and else are swapped because second condition is against One |> (fun (x, _, _, _, _) -> Assert.True(x, errorMsg)) [] [] member this.``And Condition`` () = - let (args, ifOp, elseOp) = CompileClassicalControlTest 11 |> ApplyIfElseTest + let (_, args) = CompileClassicalControlTest 11 |> ApplyIfElseTest + + let ifOp = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} + 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.ApplyIfElseR.Namespace; Name = BuiltIn.ApplyIfElseR.Name } 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)) @@ -353,27 +322,30 @@ type ClassicalControlTests () = [] [] member this.``Or Condition`` () = - let (args, ifOp, elseOp) = CompileClassicalControlTest 12 |> ApplyIfElseTest + let (_, args) = CompileClassicalControlTest 12 |> ApplyIfElseTest + + let ifOp = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} + 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)) [] - [] - 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 [] @@ -409,7 +381,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 @@ -462,13 +434,13 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let adjointContent = [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); + (0, "SubOps", "SubOpCA2"); + (1, "SubOps", "SubOpCA3"); ] let orderedGens = IdentifyGeneratedByCalls _providedOps [bodyContent; adjointContent] @@ -514,13 +486,13 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let controlledContent = [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); + (0, "SubOps", "SubOpCA2"); + (1, "SubOps", "SubOpCA3"); ] let orderedGens = IdentifyGeneratedByCalls _providedOps [bodyContent; controlledContent] @@ -562,13 +534,13 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let ctlAdjContent = [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); + (0, "SubOps", "SubOpCA2"); + (1, "SubOps", "SubOpCA3"); ] let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlAdjContent] @@ -607,18 +579,18 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let ctlContent = [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); + (0, "SubOps", "SubOpCA3"); + (1, "SubOps", "SubOpCA1"); ] let ctlAdjContent = [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); + (0, "SubOps", "SubOpCA2"); + (1, "SubOps", "SubOpCA3"); ] let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent; ctlAdjContent] @@ -658,18 +630,18 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let adjContent = [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); + (0, "SubOps", "SubOpCA3"); + (1, "SubOps", "SubOpCA1"); ] let ctlAdjContent = [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); + (0, "SubOps", "SubOpCA2"); + (1, "SubOps", "SubOpCA3"); ] let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; adjContent; ctlAdjContent] @@ -713,22 +685,22 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let ctlContent = [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); + (0, "SubOps", "SubOpCA3"); + (1, "SubOps", "SubOpCA1"); ] let adjContent = [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); + (0, "SubOps", "SubOpCA2"); + (1, "SubOps", "SubOpCA3"); ] let ctlAdjContent = [ - (2, "SubOps", "SubOp3"); + (2, "SubOps", "SubOpCA3"); ] let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent; adjContent; ctlAdjContent] @@ -770,8 +742,8 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let bodyGen = (Seq.item 0 generated) @@ -805,13 +777,13 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let ctlContent = [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); + (0, "SubOps", "SubOpCA3"); + (1, "SubOps", "SubOpCA1"); ] let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent] @@ -846,13 +818,13 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let adjContent = [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); + (0, "SubOps", "SubOpCA3"); + (1, "SubOps", "SubOpCA1"); ] let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; adjContent] @@ -891,18 +863,18 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let ctlContent = [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); + (0, "SubOps", "SubOpCA3"); + (1, "SubOps", "SubOpCA1"); ] let adjContent = [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); + (0, "SubOps", "SubOpCA2"); + (1, "SubOps", "SubOpCA3"); ] let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent; adjContent] @@ -943,8 +915,8 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let bodyGen = (Seq.item 0 generated) @@ -978,13 +950,13 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let ctlContent = [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); + (0, "SubOps", "SubOpCA3"); + (1, "SubOps", "SubOpCA1"); ] let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent] @@ -1019,13 +991,13 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let adjContent = [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); + (0, "SubOps", "SubOpCA3"); + (1, "SubOps", "SubOpCA1"); ] let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; adjContent] @@ -1064,18 +1036,18 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let ctlContent = [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); + (0, "SubOps", "SubOpCA3"); + (1, "SubOps", "SubOpCA1"); ] let adjContent = [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); + (0, "SubOps", "SubOpCA2"); + (1, "SubOps", "SubOpCA3"); ] let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent; adjContent] @@ -1116,8 +1088,8 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let bodyGen = (Seq.item 0 generated) @@ -1151,13 +1123,13 @@ type ClassicalControlTests () = let bodyContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let ctlContent = [ - (0, "SubOps", "SubOp3"); - (1, "SubOps", "SubOp1"); + (0, "SubOps", "SubOpCA3"); + (1, "SubOps", "SubOpCA1"); ] let orderedGens = IdentifyGeneratedByCalls generated [bodyContent; ctlContent] @@ -1186,13 +1158,13 @@ type ClassicalControlTests () = ] |> Seq.map ExpandBuiltInQualifiedSymbol let outerContent = [ - (0, "SubOps", "SubOp1"); - (1, "SubOps", "SubOp2"); + (0, "SubOps", "SubOpCA1"); + (1, "SubOps", "SubOpCA2"); ] let innerContent = [ - (0, "SubOps", "SubOp2"); - (1, "SubOps", "SubOp3"); + (0, "SubOps", "SubOpCA2"); + (1, "SubOps", "SubOpCA3"); ] AssertSpecializationHasCalls original originalContent @@ -1203,7 +1175,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 @@ -1217,7 +1189,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"} @@ -1226,16 +1198,147 @@ 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`` () = - CompileClassicalControlTest 27 |> ignore \ No newline at end of file + [] + member this.``Lift Array Item Call`` () = + CompileClassicalControlTest 27 |> ignore + + [] + [] + 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 + // lifting due to a set statement or return statement. + CompileClassicalControlTest 28 |> ignore + + [] + [] + member this.``Apply Conditionally`` () = + let result = CompileClassicalControlTest 29 + + 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.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")) + + Assert.True(IsTypeArgsMatch targs "Result, Unit", "ApplyConditionally did not have the correct type arguments") + + [] + [] + member this.``Apply Conditionally With NoOp`` () = + let result = CompileClassicalControlTest 30 + + 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.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")) + + Assert.True(IsTypeArgsMatch targs "Result, Unit", "ApplyConditionally did not have the correct type arguments") + + [] + [] + member this.``Inequality with ApplyConditionally`` () = + let result = CompileClassicalControlTest 31 + + let original = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable + let lines = original |> GetLinesFromSpecialization + + Assert.True(3 = Seq.length lines, sprintf "Callable %O(%A) did not have the expected number of statements" original.Parent original.Kind) + + let (success, targs, args) = CheckIfLineIsCall BuiltIn.ApplyConditionally.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")) + + Assert.True(IsTypeArgsMatch targs "Unit, Result", "ApplyConditionally did not have the correct type arguments") + + [] + [] + member this.``Inequality with Apply If One Else Zero`` () = + let (targs, args) = CompileClassicalControlTest 32 |> ApplyIfElseTest + + let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} + let SubOp1 = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} + + IsApplyIfElseArgsMatch args "r" SubOp1 Bar + |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) + + Assert.True(IsTypeArgsMatch targs "Unit, Result", "ApplyIfElse did not have the correct type arguments") + + [] + [] + member this.``Inequality with Apply If Zero Else One`` () = + let (targs, args) = CompileClassicalControlTest 33 |> ApplyIfElseTest + + let Bar = {Namespace = NonNullable<_>.New Signatures.ClassicalControlNs; Name = NonNullable<_>.New "Bar"} + let SubOp1 = {Namespace = NonNullable<_>.New "SubOps"; Name = NonNullable<_>.New "SubOp1"} + + IsApplyIfElseArgsMatch args "r" Bar SubOp1 + |> (fun (x, _, _, _, _) -> Assert.True(x, "ApplyIfElse did not have the correct arguments")) + + Assert.True(IsTypeArgsMatch targs "Result, Unit", "ApplyIfElse did not have the correct type arguments") + + [] + [] + member this.``Inequality with ApplyIfOne`` () = + let result = CompileClassicalControlTest 34 + + let originalOp = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable + + [ (1, BuiltIn.ApplyIfOne) ] + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls originalOp + + [] + [] + member this.``Inequality with ApplyIfZero`` () = + let result = CompileClassicalControlTest 35 + + let originalOp = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable + + [ (1, BuiltIn.ApplyIfZero) ] + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls originalOp + + [] + [] + member this.``Literal on the Left`` () = + let result = CompileClassicalControlTest 36 + + let originalOp = GetCallableWithName result Signatures.ClassicalControlNs "Foo" |> GetBodyFromCallable + + [(1, BuiltIn.ApplyIfZero)] + |> Seq.map ExpandBuiltInQualifiedSymbol + |> AssertSpecializationHasCalls originalOp \ No newline at end of file diff --git a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs b/src/QsCompiler/Tests.Compiler/TestCases/ClassicalControl.qs similarity index 61% rename from src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs rename to src/QsCompiler/Tests.Compiler/TestCases/ClassicalControl.qs index cbe56be9c4..f7f581b485 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/ClassicalControl.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/ClassicalControl.qs @@ -1,885 +1,1040 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - - -namespace SubOps { - operation SubOp1() : Unit is Adj + Ctl { } - operation SubOp2() : Unit is Adj + Ctl { } - operation SubOp3() : Unit is Adj + Ctl { } -} - -// ================================= - -// 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(); - SubOp2(); - SubOp3(); - } - - let temp = 0; - - if (r == One) { - SubOp2(); - SubOp3(); - SubOp1(); - } - } - -} - -// ================================= - -// Apply If Zero Else One -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } else { - SubOp2(); - SubOp3(); - } - } - -} - -// ================================= - -// Apply If One Else Zero -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = One; - - if (r == One) { - SubOp1(); - SubOp2(); - } else { - SubOp2(); - SubOp3(); - } - } - -} - -// ================================= - -// If Elif -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } elif (r == One) { - SubOp3(); - SubOp1(); - } else { - SubOp2(); - SubOp3(); - } - } - -} - -// ================================= - -// And Condition -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero and r == One) { - SubOp1(); - SubOp2(); - } else { - SubOp2(); - SubOp3(); - } - } - -} - -// ================================= - -// Or Condition -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - - if (r == Zero or r == One) { - SubOp1(); - SubOp2(); - } else { - SubOp2(); - SubOp3(); - } - } - -} - -// ================================= - -// 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) { - SubOp1(); - SubOp2(); - } - } - - adjoint (...) { - let w = One; - - if (w == One) { - SubOp2(); - SubOp3(); - } - } - } - - operation Self() : Unit is Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - adjoint self; - } - - operation Invert() : Unit is Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - adjoint invert; - } - -} - -// ================================= - -// Controlled Support -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Provided() : Unit is Ctl { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOp2(); - SubOp3(); - } - } - } - - operation Distribute() : Unit is Ctl { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled distribute; - } - -} - -// ================================= - -// Controlled Adjoint Support - Provided -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation ProvidedBody() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled adjoint (ctl, ...) { - let y = One; - - if (y == One) { - SubOp2(); - SubOp3(); - } - } - } - - operation ProvidedAdjoint() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - adjoint (...) { - let w = One; - - if (w == One) { - SubOp3(); - SubOp1(); - } - } - - controlled adjoint (ctl, ...) { - let y = One; - - if (y == One) { - SubOp2(); - SubOp3(); - } - } - } - - operation ProvidedControlled() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOp3(); - SubOp1(); - } - } - - controlled adjoint (ctl, ...) { - let y = One; - - if (y == One) { - SubOp2(); - SubOp3(); - } - } - } - - operation ProvidedAll() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOp3(); - SubOp1(); - } - } - - adjoint (...) { - let y = One; - - if (y == One) { - SubOp2(); - SubOp3(); - } - } - - controlled adjoint (ctl, ...) { - let b = One; - - if (b == One) { - let temp1 = 0; - let temp2 = 0; - SubOp3(); - } - } - } - -} - -// ================================= - -// Controlled Adjoint Support - Distribute -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation DistributeBody() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled adjoint distribute; - } - - operation DistributeAdjoint() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - adjoint (...) { - let w = One; - - if (w == One) { - SubOp3(); - SubOp1(); - } - } - - controlled adjoint distribute; - } - - operation DistributeControlled() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOp3(); - SubOp1(); - } - } - - controlled adjoint distribute; - } - - operation DistributeAll() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOp3(); - SubOp1(); - } - } - - adjoint (...) { - let y = One; - - if (y == One) { - SubOp2(); - SubOp3(); - } - } - - controlled adjoint distribute; - } - -} - -// ================================= - -// Controlled Adjoint Support - Invert -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation InvertBody() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled adjoint invert; - } - - operation InvertAdjoint() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - adjoint (...) { - let w = One; - - if (w == One) { - SubOp3(); - SubOp1(); - } - } - - controlled adjoint invert; - } - - operation InvertControlled() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOp3(); - SubOp1(); - } - } - - controlled adjoint invert; - } - - operation InvertAll() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOp3(); - SubOp1(); - } - } - - adjoint (...) { - let y = One; - - if (y == One) { - SubOp2(); - SubOp3(); - } - } - - controlled adjoint invert; - } - -} - -// ================================= - -// Controlled Adjoint Support - Self -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation SelfBody() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled adjoint self; - } - - operation SelfControlled() : Unit is Ctl + Adj { - body (...) { - let r = Zero; - - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } - - controlled (ctl, ...) { - let w = One; - - if (w == One) { - SubOp3(); - SubOp1(); - } - } - - controlled adjoint self; - } - -} - -// ================================= - -// Within Block Support -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = One; - within { - if (r == Zero) { - SubOp1(); - SubOp2(); - } - } apply { - if (r == One) { - SubOp2(); - SubOp3(); - } - } - } - -} - -// ================================= - -// Arguments Partially Resolve Type Parameters -namespace Microsoft.Quantum.Testing.ClassicalControl { - - operation Bar<'Q, 'W> (q : 'Q, w : 'W) : Unit { } - - operation Foo() : Unit { - let r = Zero; - if (r == Zero) { - Bar(1, 1.0); - } - } -} - -// ================================= - -// Hoist Functor Application -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let r = Zero; - if (r == Zero) { - Adjoint SubOp1(); - } - } -} - -// ================================= - -// Hoist Partial Application -namespace Microsoft.Quantum.Testing.ClassicalControl { - - operation Bar (q : Int, w : Double) : Unit { } - - operation Foo() : Unit { - let r = Zero; - if (r == Zero) { - (Bar(1, _))(1.0); - } - } -} - -// ================================= - -// Hoist Array Item Call -namespace Microsoft.Quantum.Testing.ClassicalControl { - open SubOps; - - operation Foo() : Unit { - let f = [SubOp1]; - let r = Zero; - if (r == Zero) { - f[0](); - } - } +// 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/TestCases/LinkingTests/QuantumProcessorExtensions.qs b/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/QuantumProcessorExtensions.qs deleted file mode 100644 index f719f2b5f5..0000000000 --- a/src/QsCompiler/Tests.Compiler/TestCases/LinkingTests/QuantumProcessorExtensions.qs +++ /dev/null @@ -1,20 +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 { } -} \ 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 5a8be35942..fafd1a95c8 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/Signatures.fs @@ -209,7 +209,7 @@ let private _TypeParameterTypes = _MakeTypeMap [| let private _DefaultWithOperation = _MakeTypeMap [| "SubOp1Type[]", ((ResolvedType.New UnitType, ResolvedType.New UnitType), { - Characteristics = (ResolvedCharacteristics.FromProperties [OpProperty.Adjointable; OpProperty.Controllable]) + Characteristics = ResolvedCharacteristics.Empty InferredInformation = InferredCallableInformation.NoInformation }) |> QsTypeKind.Operation |> ResolvedType.New |> ArrayType |] @@ -217,71 +217,60 @@ 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" |]) (_DefaultTypes, [| // ApplyIfZero And ApplyIfOne ClassicalControlNs, "Foo", [||], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" - ClassicalControlNs, "_Foo", [|"Result"; "Int"|], "Unit" |]) (_DefaultTypes, [| // Apply If Zero Else One + ClassicalControlNs, "Bar", [|"Result"|], "Unit" ClassicalControlNs, "Foo", [||], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) (_DefaultTypes, [| // Apply If One Else Zero + ClassicalControlNs, "Bar", [|"Result"|], "Unit" ClassicalControlNs, "Foo", [||], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) (_DefaultTypes, [| // If Elif ClassicalControlNs, "Foo", [||], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) (_DefaultTypes, [| // And Condition ClassicalControlNs, "Foo", [||], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" |]) (_DefaultTypes, [| // Or Condition ClassicalControlNs, "Foo", [||], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "Unit" - ClassicalControlNs, "_Foo", [|"Result"|], "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 @@ -380,18 +369,51 @@ 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, [| // Lift One Not Both + ClassicalControlNs, "Foo", [||], "Unit" + ClassicalControlNs, "_Foo", [|"Result"|], "Unit" + |]) + (_DefaultTypes, [| // Apply Conditionally + ClassicalControlNs, "Bar", [|"Result"|], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + |]) + (_DefaultTypes, [| // Apply Conditionally With NoOp + ClassicalControlNs, "Bar", [|"Result"|], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + |]) + (_DefaultTypes, [| // Inequality with ApplyConditionally + ClassicalControlNs, "Bar", [|"Result"|], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + |]) + (_DefaultTypes, [| // Inequality with Apply If One Else Zero + ClassicalControlNs, "Bar", [|"Result"|], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + |]) + (_DefaultTypes, [| // Inequality with Apply If Zero Else One + ClassicalControlNs, "Bar", [|"Result"|], "Unit" + ClassicalControlNs, "Foo", [||], "Unit" + |]) + (_DefaultTypes, [| // Inequality with ApplyIfOne + ClassicalControlNs, "Foo", [||], "Unit" + |]) + (_DefaultTypes, [| // Inequality with ApplyIfZero + ClassicalControlNs, "Foo", [||], "Unit" + |]) + (_DefaultTypes, [| // Literal on the Left + ClassicalControlNs, "Foo", [||], "Unit" + |]) |] |> _MakeSignatures \ No newline at end of file diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index e191e09213..4f9d319cbb 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -2,7 +2,7 @@ - netcoreapp3.0 + netcoreapp3.1 false Tests.Microsoft.Quantum.QsCompiler Library @@ -23,9 +23,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -44,9 +41,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -132,6 +126,9 @@ PreserveNewest + + PreserveNewest + @@ -176,8 +173,8 @@ - $(MSBuildThisFileDirectory)..\TestTargets\Simulation\Example\bin\$(Configuration)\netcoreapp3.0\Example.dll - $(MSBuildThisFileDirectory)..\TestTargets\Libraries\Library1\bin\$(Configuration)\netcoreapp3.0\Library1.dll + $(MSBuildThisFileDirectory)..\TestTargets\Simulation\Example\bin\$(Configuration)\netcoreapp3.1\Example.dll + $(MSBuildThisFileDirectory)..\TestTargets\Libraries\Library1\bin\$(Configuration)\netcoreapp3.1\Library1.dll - netcoreapp3.0 + netcoreapp3.1 false Microsoft.Quantum.QsCompiler.Documentation.Testing Tests.Microsoft.Quantum.QsDocumentationParser diff --git a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs index c14b060010..47de9f2635 100644 --- a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs +++ b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs @@ -52,6 +52,7 @@ public void SupportedTargetFrameworks() Assert.IsTrue(loader.IsSupportedQsFramework("netcoreapp2.1")); Assert.IsTrue(loader.IsSupportedQsFramework("netcoreapp2.2")); Assert.IsTrue(loader.IsSupportedQsFramework("netcoreapp3.0")); + Assert.IsTrue(loader.IsSupportedQsFramework("netcoreapp3.1")); } [TestMethod] @@ -80,6 +81,7 @@ void CompareFramework(string project, string expected) ("test10", "netcoreapp2.1"), ("test11", "netcoreapp3.0"), ("test12", "netstandard2.1"), + ("test13", "netcoreapp3.1") }; foreach (var (project, framework) in testProjects) diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj index f2c9ab5a58..2956e09c98 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj +++ b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj @@ -2,7 +2,7 @@ - netcoreapp3.0 + netcoreapp3.1 false Tests.Microsoft.Quantum.QsLanguageServer Library diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index 16313d6bce..c39d2838b7 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.SearchAndReplace; namespace Microsoft.Quantum.QsCompiler.Transformations.ClassicallyControlled @@ -20,17 +19,17 @@ 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 ApplyIf calls, where possible. + /// the tree, convert conditional statements into interface calls, where possible. /// This relies on anything having type parameters must be a global callable. /// public static class ReplaceClassicalControl { public static QsCompilation Apply(QsCompilation compilation) { - compilation = HoistTransformation.Apply(compilation); + compilation = LiftConditionBlocks.Apply(compilation); return ConvertConditions.Apply(compilation); } @@ -73,6 +72,35 @@ 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. + /// If valid, returns true with the identifier of the call like expression and the arguments of the + /// call like expression, otherwise returns false with nulls. + /// private (bool, TypedExpression, TypedExpression) IsValidScope(QsScope scope) { // if the scope has exactly one statement in it and that statement is a call like expression statement @@ -89,7 +117,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; @@ -127,25 +155,217 @@ public StatementTransformation(SyntaxTreeTransformation par return (false, null, null); } - #region Apply If + /// + /// 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), + new InferredCallableInformation(((BuiltInKind.Operation)opInfo.Kind).IsSelfAdjoint, false)); + + 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(opInfo.FullName), + QsNullable>.NewValue(typeArgs)), + typeArgs + .Zip(((BuiltInKind.Operation)opInfo.Kind).TypeParameters, (type, param) => Tuple.Create(opInfo.FullName, 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 + + /// + /// Creates an operation call from the conditional control API, given information + /// about which operation to call and with what arguments. + /// + private TypedExpression CreateControlCall(BuiltIn opInfo, IEnumerable properties, TypedExpression args, IEnumerable typeArgs) + { + var characteristics = new CallableInformation( + ResolvedCharacteristics.FromProperties(properties), + new InferredCallableInformation(((BuiltInKind.Operation)opInfo.Kind).IsSelfAdjoint, false)); + + 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 = new TypedExpression( + ExpressionKind.NewIdentifier( + Identifier.NewGlobalCallable(opInfo.FullName), + typeArgs.Any() + ? QsNullable>.NewValue(typeArgs.ToImmutableArray()) + : QsNullable>.Null), + typeArgs + .Zip(((BuiltInKind.Operation)opInfo.Kind).TypeParameters, (type, param) => Tuple.Create(opInfo.FullName, param, type)) + .ToImmutableArray(), + operationType, + new InferredExpressionInformation(false, false), + QsNullable>.Null); + + // Creates type resolutions for the call expression + var opTypeArgResolutions = typeArgs + .SelectMany(x => + x.Resolution is ResolvedTypeKind.TupleType tup + ? tup.Item + : ImmutableArray.Create(x)) + .Where(x => x.Resolution.IsTypeParameter) + .Select(x => (x.Resolution as ResolvedTypeKind.TypeParameter).Item) + .GroupBy(x => (x.Origin, x.TypeName)) + .Select(group => + { + var typeParam = group.First(); + return Tuple.Create(typeParam.Origin, typeParam.TypeName, ResolvedType.New(ResolvedTypeKind.NewTypeParameter(typeParam))); + }) + .ToImmutableArray(); + + return new TypedExpression( + ExpressionKind.NewCallLikeExpression(identifier, args), + opTypeArgResolutions, + unitType, + new InferredExpressionInformation(false, true), + QsNullable>.Null); + } + + /// + /// Creates an operation call from the conditional control API for non-literal Result comparisons. + /// The equalityScope and inequalityScope cannot both be null. + /// + private TypedExpression CreateApplyConditionallyExpression(TypedExpression conditionExpr1, TypedExpression conditionExpr2, QsScope equalityScope, QsScope inequalityScope) + { + QsCompilerError.Verify(equalityScope != null || inequalityScope != null, $"Cannot have null for both equality and inequality scopes when creating ApplyConditionally expressions."); + + var (isEqualityValid, equalityId, equalityArgs) = IsValidScope(equalityScope); + var (isInequaltiyValid, inequalityId, inequalityArgs) = IsValidScope(inequalityScope); + + if (!isEqualityValid && equalityScope != null) + { + return null; // ToDo: Diagnostic message - equality block exists, but is not valid + } + + if (!isInequaltiyValid && inequalityScope != null) + { + return null; // ToDo: Diagnostic message - inequality block exists, but is not valid + } + + if (equalityScope == null) + { + (equalityId, equalityArgs) = GetNoOp(); + } + else if (inequalityScope == null) + { + (inequalityId, inequalityArgs) = GetNoOp(); + } + + // Get characteristic properties from global id + var props = ImmutableHashSet.Empty; + if (equalityId.ResolvedType.Resolution is ResolvedTypeKind.Operation op) + { + props = op.Item2.Characteristics.GetProperties(); + if (inequalityId != null && inequalityId.ResolvedType.Resolution is ResolvedTypeKind.Operation defaultOp) + { + props = props.Intersect(defaultOp.Item2.Characteristics.GetProperties()); + } + } + + BuiltIn controlOpInfo; + (bool adj, bool ctl) = (props.Contains(OpProperty.Adjointable), props.Contains(OpProperty.Controllable)); + if (adj && ctl) + { + controlOpInfo = BuiltIn.ApplyConditionallyCA; + } + else if (adj) + { + controlOpInfo = BuiltIn.ApplyConditionallyA; + } + else if (ctl) + { + controlOpInfo = BuiltIn.ApplyConditionallyC; + } + else + { + controlOpInfo = BuiltIn.ApplyConditionally; + } + + // 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); + return CreateControlCall(controlOpInfo, props, controlArgs, targetArgsTypes); + } + + /// + /// Creates an operation call from the conditional control API for Result literal comparisons. + /// private TypedExpression CreateApplyIfExpression(QsResult result, TypedExpression conditionExpression, QsScope conditionScope, QsScope defaultScope) { - var (isCondValid, condId, condArgs) = IsValidScope(conditionScope); + var (isConditionValid, conditionId, conditionArgs) = IsValidScope(conditionScope); var (isDefaultValid, defaultId, defaultArgs) = IsValidScope(defaultScope); BuiltIn controlOpInfo; TypedExpression controlArgs; - ImmutableArray targetArgs; + ImmutableArray targetArgsTypes; var props = ImmutableHashSet.Empty; - if (isCondValid) + if (isConditionValid) { // Get characteristic properties from global id - if (condId.ResolvedType.Resolution is ResolvedTypeKind.Operation op) + if (conditionId.ResolvedType.Resolution is ResolvedTypeKind.Operation op) { props = op.Item2.Characteristics.GetProperties(); + if (defaultId != null && defaultId.ResolvedType.Resolution is ResolvedTypeKind.Operation defaultOp) + { + props = props.Intersect(defaultOp.Item2.Characteristics.GetProperties()); + } } (bool adj, bool ctl) = (props.Contains(OpProperty.Adjointable), props.Contains(OpProperty.Controllable)); @@ -169,13 +389,19 @@ private TypedExpression CreateApplyIfExpression(QsResult result, TypedExpression controlOpInfo = BuiltIn.ApplyIfElseR; } - var (zeroOpArg, oneOpArg) = (result == QsResult.Zero) - ? (Utils.CreateValueTupleExpression(condId, condArgs), Utils.CreateValueTupleExpression(defaultId, defaultArgs)) - : (Utils.CreateValueTupleExpression(defaultId, defaultArgs), Utils.CreateValueTupleExpression(condId, condArgs)); + (TypedExpression, ImmutableArray) GetArgs(TypedExpression zeroId, TypedExpression zeroArgs, TypedExpression oneId, TypedExpression oneArgs) => + ( + CreateValueTupleExpression( + conditionExpression, + CreateValueTupleExpression(zeroId, zeroArgs), + CreateValueTupleExpression(oneId, oneArgs)), - controlArgs = Utils.CreateValueTupleExpression(conditionExpression, zeroOpArg, oneOpArg); + ImmutableArray.Create(zeroArgs.ResolvedType, oneArgs.ResolvedType) + ); - targetArgs = ImmutableArray.Create(condArgs.ResolvedType, defaultArgs.ResolvedType); + (controlArgs, targetArgsTypes) = (result == QsResult.Zero) + ? GetArgs(conditionId, conditionArgs, defaultId, defaultArgs) + : GetArgs(defaultId, defaultArgs, conditionId, conditionArgs); } else if (defaultScope == null) { @@ -204,58 +430,36 @@ private TypedExpression CreateApplyIfExpression(QsResult result, TypedExpression : BuiltIn.ApplyIfOne; } - controlArgs = Utils.CreateValueTupleExpression( + controlArgs = CreateValueTupleExpression( conditionExpression, - Utils.CreateValueTupleExpression(condId, condArgs)); + CreateValueTupleExpression(conditionId, conditionArgs)); - targetArgs = ImmutableArray.Create(condArgs.ResolvedType); + targetArgsTypes = ImmutableArray.Create(conditionArgs.ResolvedType); } else { - return null; // ToDo: Diagnostic message - default body exists, but is not valid + return null; // ToDo: Diagnostic message - default block exists, but is not valid } } else { - return null; // ToDo: Diagnostic message - cond body not valid + return null; // ToDo: Diagnostic message - condition block not valid } - // Build the surrounding apply-if call - var controlOpId = Utils.CreateIdentifierExpression( - Identifier.NewGlobalCallable(new QsQualifiedName(controlOpInfo.Namespace, controlOpInfo.Name)), - targetArgs - .Zip(controlOpInfo.TypeParameters, (type, param) => Tuple.Create(new QsQualifiedName(controlOpInfo.Namespace, controlOpInfo.Name), param, type)) - .ToImmutableArray(), - Utils.GetOperationType(props, controlArgs.ResolvedType)); - - // Creates identity resolutions for the call expression - var opTypeArgResolutions = targetArgs - .SelectMany(x => - x.Resolution is ResolvedTypeKind.TupleType tup - ? tup.Item - : ImmutableArray.Create(x)) - .Where(x => x.Resolution.IsTypeParameter) - .Select(x => (x.Resolution as ResolvedTypeKind.TypeParameter).Item) - .GroupBy(x => (x.Origin, x.TypeName)) - .Select(group => - { - var typeParam = group.First(); - return Tuple.Create(typeParam.Origin, typeParam.TypeName, ResolvedType.New(ResolvedTypeKind.NewTypeParameter(typeParam))); - }) - .ToImmutableArray(); - - return Utils.CreateCallLikeExpression(controlOpId, controlArgs, opTypeArgResolutions); + return CreateControlCall(controlOpInfo, props, controlArgs, targetArgsTypes); } - private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult result, TypedExpression conditionExpression, QsScope conditionScope, QsScope defaultScope) + /// + /// Takes an expression that is the call to a conditional control API operation and the original statement, + /// and creates a statement from the given expression. + /// + private QsStatement CreateControlStatement(QsStatement statement, TypedExpression callExpression) { - var controlCall = CreateApplyIfExpression(result, conditionExpression, conditionScope, defaultScope); - - if (controlCall != null) + if (callExpression != null) { return new QsStatement( - QsStatementKind.NewQsExpressionStatement(controlCall), + QsStatementKind.NewQsExpressionStatement(callExpression), statement.SymbolDeclarations, QsNullable.Null, statement.Comments); @@ -267,98 +471,246 @@ private QsStatement CreateApplyIfStatement(QsStatement statement, QsResult resul } } + /// + /// Converts a conditional statement to an operation call from the conditional control API. + /// + private QsStatement ConvertConditionalToControlCall(QsStatement statement) + { + var (isCondition, condition, conditionScope, defaultScope) = IsConditionWithSingleBlock(statement); + + if (isCondition) + { + if (IsConditionedOnResultLiteralExpression(condition, out var literal, out var conditionExpression)) + { + return CreateControlStatement(statement, CreateApplyIfExpression(literal, conditionExpression, conditionScope, defaultScope)); + } + else if (IsConditionedOnResultEqualityExpression(condition, out var lhsConditionExpression, out var rhsConditionExpression)) + { + return CreateControlStatement(statement, CreateApplyConditionallyExpression(lhsConditionExpression, rhsConditionExpression, conditionScope, defaultScope)); + } + else if (IsConditionedOnResultInequalityExpression(condition, out lhsConditionExpression, out rhsConditionExpression)) + { + // The scope arguments are reversed to account for the negation of the NEQ + return CreateControlStatement(statement, CreateApplyConditionallyExpression(lhsConditionExpression, rhsConditionExpression, defaultScope, conditionScope)); + } + + // ToDo: Diagnostic message + return statement; // The condition does not fit a supported format. + } + else + { + // ToDo: Diagnostic message + return statement; // The reshaping of the conditional did not succeed. + } + } + #endregion - #region Condition Reshaping Logic + #region Condition Checking Logic - private (bool, QsConditionalStatement) ProcessElif(QsConditionalStatement cond) + /// + /// Checks if the statement is a condition statement that only has one conditional block in it (default blocks are optional). + /// If it is, returns true along with the condition, the body of the conditional block, and, optionally, the body of the + /// default block, otherwise returns false with nulls. If there is no default block, the last value of the return tuple will be null. + /// + private (bool, TypedExpression, QsScope, QsScope) IsConditionWithSingleBlock(QsStatement statement) { - if (cond.ConditionalBlocks.Length < 2) return (false, cond); + if (statement.Statement is QsStatementKind.QsConditionalStatement condition && condition.Item.ConditionalBlocks.Length == 1) + { + return (true, condition.Item.ConditionalBlocks[0].Item1, condition.Item.ConditionalBlocks[0].Item2.Body, condition.Item.Default.ValueOr(null)?.Body); + } - var subCond = new QsConditionalStatement(cond.ConditionalBlocks.RemoveAt(0), cond.Default); - var secondCondBlock = cond.ConditionalBlocks[1].Item2; + return (false, null, null, null); + } - var subIfStatment = new QsStatement - ( - QsStatementKind.NewQsConditionalStatement(subCond), - LocalDeclarations.Empty, - secondCondBlock.Location, - secondCondBlock.Comments - ); + /// + /// Checks if the expression is an equality or inequality comparison where one side is a + /// Result literal. If it is, returns true along with the Result literal and the other + /// expression in the (in)equality, otherwise returns false with nulls. If it is an + /// inequality, the returned result value will be the opposite of the result literal found. + /// + private bool IsConditionedOnResultLiteralExpression(TypedExpression expression, out QsResult literal, out TypedExpression conditionExpression) + { + literal = null; + conditionExpression = null; + + if (expression.Expression is ExpressionKind.EQ eq) + { + if (eq.Item1.Expression is ExpressionKind.ResultLiteral literal1) + { + literal = literal1.Item; + conditionExpression = eq.Item2; + return true; + } + else if (eq.Item2.Expression is ExpressionKind.ResultLiteral literal2) + { + literal = literal2.Item; + conditionExpression = eq.Item1; + return true; + } + } + else if (expression.Expression is ExpressionKind.NEQ neq) + { + QsResult FlipResult(QsResult result) => result.IsZero ? QsResult.One : QsResult.Zero; + + if (neq.Item1.Expression is ExpressionKind.ResultLiteral literal1) + { + literal = FlipResult(literal1.Item); + conditionExpression = neq.Item2; + return true; + } + else if (neq.Item2.Expression is ExpressionKind.ResultLiteral literal2) + { + literal = FlipResult(literal2.Item); + conditionExpression = neq.Item1; + return true; + } + } + + return false; + } + + /// + /// Checks if the expression is an equality comparison between two Result-typed expressions. + /// If it is, returns true along with the two expressions, otherwise returns false with nulls. + /// + private bool IsConditionedOnResultEqualityExpression(TypedExpression expression, out TypedExpression lhs, out TypedExpression rhs) + { + lhs = null; + rhs = null; + + if (expression.Expression is ExpressionKind.EQ eq + && eq.Item1.ResolvedType.Resolution == ResolvedTypeKind.Result + && eq.Item2.ResolvedType.Resolution == ResolvedTypeKind.Result) + { + lhs = eq.Item1; + rhs = eq.Item2; + return true; + } + + return false; + } + + /// + /// Checks if the expression is an inequality comparison between two Result-typed expressions. + /// If it is, returns true along with the two expressions, otherwise returns false with nulls. + /// + private bool IsConditionedOnResultInequalityExpression(TypedExpression expression, out TypedExpression lhs, out TypedExpression rhs) + { + lhs = null; + rhs = null; + + if (expression.Expression is ExpressionKind.NEQ neq + && neq.Item1.ResolvedType.Resolution == ResolvedTypeKind.Result + && neq.Item2.ResolvedType.Resolution == ResolvedTypeKind.Result) + { + lhs = neq.Item1; + rhs = neq.Item2; + return true; + } + return false; + } + + #endregion + + #region Condition Reshaping Logic + + /// + /// Converts if-elif-else structures to nested if-else structures. + /// + private (bool, QsConditionalStatement) ProcessElif(QsConditionalStatement conditionStatment) + { + if (conditionStatment.ConditionalBlocks.Length < 2) return (false, conditionStatment); + + var subCondition = new QsConditionalStatement(conditionStatment.ConditionalBlocks.RemoveAt(0), conditionStatment.Default); + var secondConditionBlock = conditionStatment.ConditionalBlocks[1].Item2; + var subIfStatment = new QsStatement( + QsStatementKind.NewQsConditionalStatement(subCondition), + LocalDeclarations.Empty, + secondConditionBlock.Location, + secondConditionBlock.Comments); var newDefault = QsNullable.NewValue(new QsPositionedBlock( - new QsScope(ImmutableArray.Create(subIfStatment), secondCondBlock.Body.KnownSymbols), - secondCondBlock.Location, + new QsScope(ImmutableArray.Create(subIfStatment), secondConditionBlock.Body.KnownSymbols), + secondConditionBlock.Location, QsComments.Empty)); - return (true, new QsConditionalStatement(ImmutableArray.Create(cond.ConditionalBlocks[0]), newDefault)); + return (true, new QsConditionalStatement(ImmutableArray.Create(conditionStatment.ConditionalBlocks[0]), newDefault)); } - private (bool, QsConditionalStatement) ProcessOR(QsConditionalStatement cond) + /// + /// Converts conditional statements whose top-most condition is an OR. + /// Creates a nested structure without the top-most OR. + /// + private (bool, QsConditionalStatement) ProcessOR(QsConditionalStatement conditionStatment) { // This method expects elif blocks to have been abstracted out - if (cond.ConditionalBlocks.Length != 1) return (false, cond); + if (conditionStatment.ConditionalBlocks.Length != 1) return (false, conditionStatment); - var (condition, block) = cond.ConditionalBlocks[0]; + var (condition, block) = conditionStatment.ConditionalBlocks[0]; - if (condition.Expression is ExpressionKind.OR orCond) + if (condition.Expression is ExpressionKind.OR orCondition) { - var subCond = new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(orCond.Item2, block)), cond.Default); - var subIfStatment = new QsStatement - ( - QsStatementKind.NewQsConditionalStatement(subCond), + var subCondition = new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(orCondition.Item2, block)), conditionStatment.Default); + 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, QsComments.Empty)); - return (true, new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(orCond.Item1, block)), newDefault)); + return (true, new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(orCondition.Item1, block)), newDefault)); } else { - return (false, cond); + return (false, conditionStatment); } } - private (bool, QsConditionalStatement) ProcessAND(QsConditionalStatement cond) + /// + /// Converts conditional statements whose top-most condition is an AND. + /// Creates a nested structure without the top-most AND. + /// + private (bool, QsConditionalStatement) ProcessAND(QsConditionalStatement conditionStatment) { // This method expects elif blocks to have been abstracted out - if (cond.ConditionalBlocks.Length != 1) return (false, cond); + if (conditionStatment.ConditionalBlocks.Length != 1) return (false, conditionStatment); - var (condition, block) = cond.ConditionalBlocks[0]; + var (condition, block) = conditionStatment.ConditionalBlocks[0]; - if (condition.Expression is ExpressionKind.AND andCond) + if (condition.Expression is ExpressionKind.AND andCondition) { - var subCond = new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(andCond.Item2, block)), cond.Default); - var subIfStatment = new QsStatement - ( - QsStatementKind.NewQsConditionalStatement(subCond), + var subCondition = new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(andCondition.Item2, block)), conditionStatment.Default); + 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, QsComments.Empty); - return (true, new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(andCond.Item1, newBlock)), cond.Default)); + return (true, new QsConditionalStatement(ImmutableArray.Create(Tuple.Create(andCondition.Item1, newBlock)), conditionStatment.Default)); } else { - return (false, cond); + return (false, conditionStatment); } } + /// + /// Converts conditional statements to nested structures so they do not + /// have elif blocks or top-most OR or AND conditions. + /// private QsStatement ReshapeConditional(QsStatement statement) { - if (statement.Statement is QsStatementKind.QsConditionalStatement cond) + if (statement.Statement is QsStatementKind.QsConditionalStatement condition) { - var stm = cond.Item; + var stm = condition.Item; (_, stm) = ProcessElif(stm); bool wasOrProcessed, wasAndProcessed; do @@ -367,50 +719,17 @@ 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; } #endregion - #region Condition Checking Logic - - private (bool, TypedExpression, QsScope, QsScope) IsConditionWithSingleBlock(QsStatement statement) - { - if (statement.Statement is QsStatementKind.QsConditionalStatement cond && cond.Item.ConditionalBlocks.Length == 1) - { - return (true, cond.Item.ConditionalBlocks[0].Item1, cond.Item.ConditionalBlocks[0].Item2.Body, cond.Item.Default.ValueOr(null)?.Body); - } - - return (false, null, null, null); - } - - private (bool, QsResult, TypedExpression) IsConditionedOnResultLiteralExpression(TypedExpression expression) - { - if (expression.Expression is ExpressionKind.EQ eq) - { - if (eq.Item1.Expression is ExpressionKind.ResultLiteral exp1) - { - return (true, exp1.Item, eq.Item2); - } - else if (eq.Item2.Expression is ExpressionKind.ResultLiteral exp2) - { - return (true, exp2.Item, eq.Item1); - } - } - - return (false, null, null); - } - - #endregion - public override QsScope OnScope(QsScope scope) { var parentSymbols = this.OnLocalDeclarations(scope.KnownSymbols); @@ -422,23 +741,9 @@ public override QsScope OnScope(QsScope scope) { var stm = ReshapeConditional(statement); stm = this.OnStatement(stm); + stm = ConvertConditionalToControlCall(stm); - var (isCondition, cond, conditionScope, defaultScope) = IsConditionWithSingleBlock(stm); - - if (isCondition) - { - /*ToDo: this could be a separate function.*/ - var (isCompareLiteral, literal, nonLiteral) = IsConditionedOnResultLiteralExpression(cond); - if (isCompareLiteral) - { - statements.Add(CreateApplyIfStatement(stm, literal, nonLiteral, conditionScope, defaultScope)); - } - else - { - statements.Add(stm); - } - /**/ - } + statements.Add(stm); } else { @@ -452,613 +757,146 @@ 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 LiftConditionBlocks { - internal static QsCompilation Apply(QsCompilation compilation) => HoistContents.Apply(compilation); - - private class HoistContents : SyntaxTreeTransformation + public static QsCompilation Apply(QsCompilation compilation) { - 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; + var filter = new LiftContent(); - 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; - } - } + return new QsCompilation(compilation.Namespaces.Select(ns => filter.Namespaces.OnNamespace(ns)).ToImmutableArray(), compilation.EntryPoints); + } - public class TransformationState + private class LiftContent : ContentLifting.LiftContent + { + internal class TransformationState : ContentLifting.LiftContent.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, - new Modifiers(AccessModifier.Internal), - 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); - } + internal bool IsConditionLiftable = false; } - private HoistContents() : base(new TransformationState()) + 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); } - 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 + private new class StatementKindTransformation : ContentLifting.LiftContent.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)); + && call.Item1.ResolvedType.Resolution.IsOperation + && call.Item1.Expression is ExpressionKind.Identifier + && !TypedExpression.IsPartialApplication(expr.Item.Expression); } public override QsStatementKind OnConditionalStatement(QsConditionalStatement stm) { - var contextValidScope = SharedState.IsValidScope; - var contextHoistParams = SharedState.CurrentHoistParams; - - var isHoistValid = true; + var contextIsConditionLiftable = SharedState.IsConditionLiftable; + SharedState.IsConditionLiftable = true; var newConditionBlocks = new List>(); var generatedOperations = new List(); - foreach (var condBlock in stm.ConditionalBlocks) + foreach (var conditionBlock in stm.ConditionalBlocks) { + var contextValidScope = SharedState.IsValidScope; + var contextParams = SharedState.GeneratedOpParams; + SharedState.IsValidScope = true; - SharedState.CurrentHoistParams = condBlock.Item2.Body.KnownSymbols.IsEmpty - ? ImmutableArray>>.Empty - : condBlock.Item2.Body.KnownSymbols.Variables; + SharedState.GeneratedOpParams = conditionBlock.Item2.Body.KnownSymbols.Variables; - var (expr, block) = this.OnPositionedBlock(QsNullable.NewValue(condBlock.Item1), condBlock.Item2); + 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 (isExprCond, _, _) = IsConditionedOnResultLiteralExpression(expr.Item); + //var (isExprCondition, _, _) = IsConditionedOnResultLiteralExpression(expr.Item); - if (block.Body.Statements.Length > 0 /*&& isExprCond*/ && SharedState.IsValidScope && !IsScopeSingleCall(block.Body)) // if sub-scope is valid, hoist content + if (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 + // ToDo: We may want to prevent empty blocks from getting lifted + else //if(block.Body.Statements.Length > 0) { - isHoistValid = false; - break; + // Lift the scope to its own operation + if (SharedState.LiftBody(block.Body, out var callable, out var call)) + { + 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 + { + SharedState.IsConditionLiftable = false; + } } + + SharedState.GeneratedOpParams = contextParams; + SharedState.IsValidScope = contextValidScope; + + if (!SharedState.IsConditionLiftable) break; } var newDefault = QsNullable.Null; - if (isHoistValid && stm.Default.IsValue) + if (SharedState.IsConditionLiftable && stm.Default.IsValue) { + var contextValidScope = SharedState.IsValidScope; + var contextParams = SharedState.GeneratedOpParams; + SharedState.IsValidScope = true; - SharedState.CurrentHoistParams = 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 (block.Body.Statements.Length > 0 && SharedState.IsValidScope && !IsScopeSingleCall(block.Body)) // if sub-scope is valid, hoist content + + if (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); newDefault = QsNullable.NewValue(block); - generatedOperations.Add(callable); } - else + // ToDo: We may want to prevent empty blocks from getting lifted + else //if(block.Body.Statements.Length > 0) { - isHoistValid = false; + // Lift the scope to its own operation + if (SharedState.LiftBody(block.Body, out var callable, out var call)) + { + block = new QsPositionedBlock( + new QsScope(ImmutableArray.Create(call), block.Body.KnownSymbols), + block.Location, + block.Comments); + newDefault = QsNullable.NewValue(block); + generatedOperations.Add(callable); + } + else + { + SharedState.IsConditionLiftable = false; + } } + + SharedState.GeneratedOpParams = contextParams; + SharedState.IsValidScope = contextValidScope; } - if (isHoistValid) + if (SharedState.IsConditionLiftable) { - SharedState.ControlOperations.AddRange(generatedOperations); + SharedState.GeneratedOperations.AddRange(generatedOperations); } - SharedState.CurrentHoistParams = contextHoistParams; - SharedState.IsValidScope = contextValidScope; - - return isHoistValid + var rtrn = SharedState.IsConditionLiftable ? 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 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); + SharedState.IsConditionLiftable = contextIsConditionLiftable; - // 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); - } - } } } } diff --git a/src/QsCompiler/Transformations/ClassicallyControlledUtils.cs b/src/QsCompiler/Transformations/ClassicallyControlledUtils.cs deleted file mode 100644 index bc030db7a7..0000000000 --- a/src/QsCompiler/Transformations/ClassicallyControlledUtils.cs +++ /dev/null @@ -1,92 +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 purpuse 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, false), - 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 - ); - - 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(); - } - } -} diff --git a/src/QsCompiler/Transformations/ContentLifting.cs b/src/QsCompiler/Transformations/ContentLifting.cs new file mode 100644 index 0000000000..c44f3cde4a --- /dev/null +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -0,0 +1,583 @@ +// 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; +using Microsoft.Quantum.QsCompiler.Transformations.Core; +using Microsoft.Quantum.QsCompiler.Transformations.SearchAndReplace; + + +namespace Microsoft.Quantum.QsCompiler.Transformations.ContentLifting +{ + using ExpressionKind = QsExpressionKind; + using ResolvedTypeKind = QsTypeKind; + using TypeArgsResolution = ImmutableArray, ResolvedType>>; + + /// + /// Static class to accumulate all type parameter independent subclasses used by LiftContent. + /// + public static class LiftContent + { + internal class CallableDetails + { + internal readonly QsCallable Callable; + internal readonly QsSpecialization Adjoint; + internal readonly QsSpecialization Controlled; + internal readonly QsSpecialization ControlledAdjoint; + internal readonly QsNullable> TypeParameters; + + internal 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) + .Select(param => + ResolvedType.New(ResolvedTypeKind.NewTypeParameter(new QsTypeParameter( + callable.FullName, + ((QsLocalSymbol.ValidName)param).Item, + QsNullable>.Null)))) + .ToImmutableArray()) + : QsNullable>.Null; + } + } + + 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; + internal bool ContainsParamRef = false; + internal ImmutableArray>> GeneratedOpParams = + ImmutableArray>>.Empty; + + 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) + { + 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; + bool isSelfAdjoint = false; + + if (InWithinBlock) + { + addAdjoint = true; + addControlled = false; + } + else if (InBody) + { + if (adj != null && adj.Implementation is SpecializationImplementation.Generated adjGen) + { + addAdjoint = adjGen.Item.IsInvert; + isSelfAdjoint = adjGen.Item.IsSelfInverse; + } + 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; + 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, + 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))); + } + + 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.Variables; + + 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()); + + 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 generatedCallable = new QsCallable( + QsCallableKind.Operation, + newName, + ImmutableArray.Empty, + new Modifiers(AccessModifier.Internal), + CurrentCallable.Callable.SourceFile, + QsNullable.Null, + signature, + parameters, + specializations.ToImmutableArray(), + ImmutableArray.Empty, + QsComments.Empty); + + // 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 (generatedCallable, signature.ArgumentType); + } + + /// + /// 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. + /// + 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( + originalArgumentType, + ResolvedType.New(ResolvedTypeKind.UnitType)), + generatedOp.Signature.Information)); + + // 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( + 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 + { + arguments = new TypedExpression( + ExpressionKind.UnitValue, + TypeArgsResolution.Empty, + ResolvedType.New(ResolvedTypeKind.UnitType), + new InferredExpressionInformation(false, false), + QsNullable>.Null); + } + + 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); + + // set output parameters + callable = generatedOp; + callStatement = new QsStatement( + QsStatementKind.NewQsExpressionStatement(call), + LocalDeclarations.Empty, + QsNullable.Null, + QsComments.Empty); + + return true; + } + } + + /// + /// 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. + /// + /// ToDo: This transformation currently does not support lifting inside of functions. + /// + 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); + } + + protected class NamespaceTransformation : NamespaceTransformation + { + public NamespaceTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override QsCallable OnCallableDeclaration(QsCallable c) + { + SharedState.CurrentCallable = new LiftContent.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 QsSpecialization OnControlledAdjointSpecialization(QsSpecialization spec) + { + SharedState.InControlledAdjoint = true; + var rtrn = base.OnControlledAdjointSpecialization(spec); + SharedState.InControlledAdjoint = false; + 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) + { + // 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) { } + + 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.ContainsParamRef) + { + SharedState.IsValidScope = false; + } + + var rhs = this.Expressions.OnTypedExpression(stm.Rhs); + return QsStatementKind.NewQsValueUpdate(new QsValueUpdate(lhs, rhs)); + } + + public override QsStatementKind OnStatementKind(QsStatementKind kind) + { + SharedState.ContainsParamRef = false; // Every statement kind starts off false + return base.OnStatementKind(kind); + } + } + + protected class ExpressionTransformation : ExpressionTransformation + { + public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override TypedExpression OnTypedExpression(TypedExpression ex) + { + 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 + SharedState.ContainsParamRef |= contextContainsParamRef; + + return rtrn; + } + } + + protected class ExpressionKindTransformation : ExpressionKindTransformation + { + public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) + { + if (sym is Identifier.LocalVariable local && + SharedState.GeneratedOpParams.Any(param => param.VariableName.Equals(local.Item))) + { + SharedState.ContainsParamRef = true; + } + return base.OnIdentifier(sym, tArgs); + } + } + } +} diff --git a/src/QuantumSdk/QuantumSdk.nuspec b/src/QuantumSdk/QuantumSdk.nuspec index f24e7cc016..b3ffa5ec23 100644 --- a/src/QuantumSdk/QuantumSdk.nuspec +++ b/src/QuantumSdk/QuantumSdk.nuspec @@ -22,7 +22,7 @@ - - + + diff --git a/src/QuantumSdk/Tools/BuildConfiguration/BuildConfiguration.csproj b/src/QuantumSdk/Tools/BuildConfiguration/BuildConfiguration.csproj index 0ac8055472..640ed2a1ed 100644 --- a/src/QuantumSdk/Tools/BuildConfiguration/BuildConfiguration.csproj +++ b/src/QuantumSdk/Tools/BuildConfiguration/BuildConfiguration.csproj @@ -3,7 +3,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 Microsoft.Quantum.Sdk.BuildConfiguration Build configuration tools included in the Microsoft Quantum Sdk. diff --git a/src/VSCodeExtension/BUILDING.md b/src/VSCodeExtension/BUILDING.md index 3a674c307d..209e41cdbb 100644 --- a/src/VSCodeExtension/BUILDING.md +++ b/src/VSCodeExtension/BUILDING.md @@ -4,7 +4,7 @@ - npm and vsce - PowerShell Core (6.0 or later) -- .NET Core SDK 3.0 or later +- .NET Core SDK 3.1 or later ### Obtaining npm and vsce ### @@ -67,7 +67,7 @@ PS> dotnet publish --self-contained --runtime win10-x64 To get the value that you need for the `quantumDevKit.languageServerPath` preference: ``` -PS> Resolve-Path bin/Debug/netcoreapp3.0/win10-x64/publish/Microsoft.Quantum.QsLanguageServer.exe +PS> Resolve-Path bin/Debug/netcoreapp3.1/win10-x64/publish/Microsoft.Quantum.QsLanguageServer.exe ``` ## Debugging ## diff --git a/src/VisualStudioExtension/QsharpAppTemplate/AppProjectTemplate.xml.v.template b/src/VisualStudioExtension/QsharpAppTemplate/AppProjectTemplate.xml.v.template index f3188b9c3a..2061e46bde 100644 --- a/src/VisualStudioExtension/QsharpAppTemplate/AppProjectTemplate.xml.v.template +++ b/src/VisualStudioExtension/QsharpAppTemplate/AppProjectTemplate.xml.v.template @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 diff --git a/src/VisualStudioExtension/QsharpTestTemplate/TestProjectTemplate.xml.v.template b/src/VisualStudioExtension/QsharpTestTemplate/TestProjectTemplate.xml.v.template index b85d725b5f..40bccfea26 100644 --- a/src/VisualStudioExtension/QsharpTestTemplate/TestProjectTemplate.xml.v.template +++ b/src/VisualStudioExtension/QsharpTestTemplate/TestProjectTemplate.xml.v.template @@ -1,7 +1,7 @@ - netcoreapp3.0 + netcoreapp3.1 false diff --git a/src/VisualStudioExtension/QsharpVSIX/QsharpVSIX.csproj b/src/VisualStudioExtension/QsharpVSIX/QsharpVSIX.csproj index aa8fd17773..56ec60b3f3 100644 --- a/src/VisualStudioExtension/QsharpVSIX/QsharpVSIX.csproj +++ b/src/VisualStudioExtension/QsharpVSIX/QsharpVSIX.csproj @@ -16,7 +16,7 @@ Microsoft.Quantum.VisualStudio Microsoft.Quantum.VisualStudio.Extension Microsoft.Quantum.Development.Kit-$(AssemblyVersion).vsix - ..\..\QsCompiler\LanguageServer\bin\$(Configuration)\netcoreapp3.0\publish\ + ..\..\QsCompiler\LanguageServer\bin\$(Configuration)\netcoreapp3.1\publish\ v4.7.2 false true From a47b3027f40901170867c3cb47a7f84c5cc22757 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 17 Mar 2020 12:39:49 -0700 Subject: [PATCH 105/106] Undo checks for current NS in code actions/completion --- .../EditorSupport/CodeActions.cs | 14 +++++------ .../EditorSupport/CodeCompletion.cs | 23 ++++++++----------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index 5ac7811a7d..6276a8676f 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -41,10 +41,10 @@ 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, 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 @@ -52,8 +52,7 @@ private static IEnumerable> NamespaceSuggestionsForIdAtPosit { var variables = file?.TryGetQsSymbolInfo(pos, true, out CodeFragment _)?.UsedVariables; idName = variables != null && variables.Any() ? variables.Single().Symbol.AsDeclarationName(null) : null; - var nsName = file.TryGetNamespaceAt(pos); - return idName != null && compilation != null && nsName != null + return idName != null && compilation != null ? compilation.GlobalSymbols.NamespacesContainingCallable(NonNullable.New(idName)) : ImmutableArray>.Empty; } @@ -61,10 +60,10 @@ 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, 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 @@ -74,8 +73,7 @@ private static IEnumerable> NamespaceSuggestionsForTypeAtPos typeName = types != null && types.Any() && types.Single().Type is QsTypeKind.UserDefinedType udt ? udt.Item.Symbol.AsDeclarationName(null) : null; - var nsName = file.TryGetNamespaceAt(pos); - return typeName != null && compilation != null && nsName != null + return typeName != null && compilation != null ? 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 5729fc8548..4ec82b6449 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, currentNamespace); + return GetNamedItemCompletions(compilation); case CompletionKind.Tags.Namespace: return GetGlobalNamespaceCompletions(compilation, namespacePrefix) @@ -312,7 +312,7 @@ private static IEnumerable GetLocalCompletions( /// /// 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. + /// namespaces, or an empty enumerable if symbols haven't been resolved yet. /// /// /// Thrown when any argument except is null. @@ -330,7 +330,7 @@ private static IEnumerable GetCallableCompletions( if (openNamespaces == null) throw new ArgumentNullException(nameof(openNamespaces)); - if (currentNamespace == null || !compilation.GlobalSymbols.ContainsResolutions) + if (!compilation.GlobalSymbols.ContainsResolutions) return Array.Empty(); return compilation.GlobalSymbols.AccessibleCallables() @@ -351,7 +351,7 @@ private static IEnumerable GetCallableCompletions( /// /// 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. + /// an empty enumerable if symbols haven't been resolved yet. /// /// /// Thrown when any argument except is null. @@ -369,7 +369,7 @@ private static IEnumerable GetTypeCompletions( if (openNamespaces == null) throw new ArgumentNullException(nameof(openNamespaces)); - if (currentNamespace == null || !compilation.GlobalSymbols.ContainsResolutions) + if (!compilation.GlobalSymbols.ContainsResolutions) return Array.Empty(); return compilation.GlobalSymbols.AccessibleTypes() @@ -387,19 +387,16 @@ private static IEnumerable GetTypeCompletions( } /// - /// 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. + /// Returns completions for all named items in any accessible type, or an empty enumerable if symbols haven't + /// been resolved yet. /// - /// - /// Thrown when any argument except is null. - /// - private static IEnumerable GetNamedItemCompletions( - CompilationUnit compilation, string currentNamespace) + /// Thrown when the argument is null. + private static IEnumerable GetNamedItemCompletions(CompilationUnit compilation) { if (compilation == null) throw new ArgumentNullException(nameof(compilation)); - if (currentNamespace == null || !compilation.GlobalSymbols.ContainsResolutions) + if (!compilation.GlobalSymbols.ContainsResolutions) return Array.Empty(); return compilation.GlobalSymbols.AccessibleTypes() .SelectMany(type => ExtractItems(type.TypeItems)) From 6ab8bf91792a583817cf1bf55ac1edc392a39874 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 17 Mar 2020 12:56:00 -0700 Subject: [PATCH 106/106] Check if methodDecl is null --- .../CompilationManager/EditorSupport/EditorCommands.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs b/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs index 7a5fe606ba..5adda7ec1e 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs @@ -249,7 +249,7 @@ List FunctorApplications(ref QsExpression ex) // extracting and adapting the relevant information for the called callable - ResolutionResult.Found methodDecl; + ResolutionResult.Found methodDecl = null; if (id.Item1.Symbol is QsSymbolKind.Symbol sym) { methodDecl = @@ -266,7 +266,8 @@ List FunctorApplications(ref QsExpression ex) file.FileName) as ResolutionResult.Found; } - else + + if (methodDecl == null) { return null; }