From e647958de4cec8331086f471d5edddaa0c918485 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Wed, 3 Jun 2020 18:22:27 -0700 Subject: [PATCH 01/20] Prevent result equality with QPRGen0 --- .../CompilationManager/TypeChecking.cs | 359 +++++++++--------- .../SyntaxProcessor/ExpressionVerification.fs | 37 +- .../SyntaxProcessor/StatementVerification.fs | 118 +++--- .../SyntaxProcessor/VerificationTools.fs | 10 +- 4 files changed, 284 insertions(+), 240 deletions(-) diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index 99934ce13e..2b19931272 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -9,14 +9,17 @@ using Microsoft.Quantum.QsCompiler.CompilationBuilder.DataStructures; using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.Diagnostics; +using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.Quantum.QsCompiler.SymbolManagement; using Microsoft.Quantum.QsCompiler.SymbolTracker; +using Microsoft.Quantum.QsCompiler.SyntaxProcessing; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; -using Microsoft.Quantum.QsCompiler.SyntaxProcessing; using Microsoft.Quantum.QsCompiler.TextProcessing; using Microsoft.VisualStudio.LanguageServer.Protocol; - +using static Microsoft.Quantum.QsCompiler.SyntaxProcessing.Expressions; +using static Microsoft.Quantum.QsCompiler.CompilationBuilder.DiagnosticTools; +using Statements = Microsoft.Quantum.QsCompiler.SyntaxProcessing.Statements; namespace Microsoft.Quantum.QsCompiler.CompilationBuilder { @@ -43,7 +46,7 @@ internal static class TypeChecking precedingFragment = preceding.GetFragment(); if (precedingFragment.IncludeInCompilation && precedingFragment.Kind is QsFragmentKind.DeclarationAttribute att) { - var offset = DiagnosticTools.AsTuple(precedingFragment.GetRange().Start); + var offset = AsTuple(precedingFragment.GetRange().Start); attributes.Add(new AttributeAnnotation(att.Item1, att.Item2, offset, precedingFragment.Comments)); } else break; @@ -87,7 +90,7 @@ internal static ImmutableArray DocumentingComments(this FileContentManag /// For namespaces with an invalid namespace name the symbol name in the header item will be set to an UnknownNamespace. /// private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry)> GetNamespaceHeaderItems(this FileContentManager file) => - file.GetHeaderItems(file?.NamespaceDeclarationTokens(), frag => frag.Kind.DeclaredNamespace(), ReservedKeywords.InternalUse.UnknownNamespace); + file.GetHeaderItems(file?.NamespaceDeclarationTokens(), frag => frag.Kind.DeclaredNamespace(), InternalUse.UnknownNamespace); /// /// Returns the HeaderItems corresponding to all open directives with a valid name in the given file, or null if the given file is null. @@ -211,7 +214,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position try { compilation.GlobalSymbols.RemoveSource(file.FileName); - QsLocation Location(Position pos, Tuple range) => new QsLocation(DiagnosticTools.AsTuple(pos), range); + QsLocation Location(Position pos, Tuple range) => new QsLocation(AsTuple(pos), range); // add all namespace declarations var namespaceHeaders = file.GetNamespaceHeaderItems().ToImmutableArray(); @@ -287,7 +290,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position { var position = headerItem.GetPosition(); var (specKind, generator, introRange) = headerItem.Declaration; - var location = new QsLocation(DiagnosticTools.AsTuple(position), introRange); + var location = new QsLocation(AsTuple(position), introRange); var messages = ns.TryAddCallableSpecialization(specKind, file.FileName, location, parentName, generator, headerItem.Attributes, headerItem.Documentation); if (!messages.Any(msg => msg.Diagnostic.IsError)) contentToCompile.Add(tIndex); foreach (var msg in messages) @@ -310,7 +313,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position // if the declaration directly contains statements and no specialization, auto-generate a default body for the callable if (!specializations.Any()) { - var location = new QsLocation(DiagnosticTools.AsTuple(parent.Item2.GetPosition()), parentName.Item2); + var location = new QsLocation(AsTuple(parent.Item2.GetPosition()), parentName.Item2); var genRange = QsRangeInfo.NewValue(location.Range); // set to the range of the callable name var omittedSymbol = new QsSymbol(QsSymbolKind.OmittedSymbols, QsRangeInfo.Null); var generatorKind = QsSpecializationGeneratorKind.NewUserDefinedImplementation(omittedSymbol); @@ -340,7 +343,7 @@ internal static void ResolveGlobalSymbols(NamespaceManager symbols, List source, IEnumerable, QsCompilerDiagnostic>> msgs) => - diagnostics.AddRange(msgs.Select(msg => Diagnostics.Generate(source.Value, msg.Item2, DiagnosticTools.AsPosition(msg.Item1)))); + diagnostics.AddRange(msgs.Select(msg => Diagnostics.Generate(source.Value, msg.Item2, AsPosition(msg.Item1)))); if (fileName != null) { @@ -473,44 +476,46 @@ internal static void ImportGlobalSymbols(this FileContentManager file, Compilati /// If the set of required functors is unspecified or null, then the functors to support are determined by the parent scope. /// Throws an ArgumentNullException if the given list of tree nodes, symbol tracker or diagnostics are null. /// - private static QsScope BuildScope(IReadOnlyList nodeContent, - SymbolTracker symbolTracker, List diagnostics, ImmutableHashSet requiredFunctorSupport = null) + private static QsScope BuildScope(IReadOnlyList nodeContent, + ResolutionContext context, List diagnostics, ImmutableHashSet requiredFunctorSupport = null) { if (nodeContent == null) throw new ArgumentNullException(nameof(nodeContent)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); + if (context == null) throw new ArgumentNullException(nameof(context)); if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - var inheritedSymbols = symbolTracker.CurrentDeclarations; - symbolTracker.BeginScope(requiredFunctorSupport); - var statements = BuildStatements(nodeContent.GetEnumerator(), symbolTracker, diagnostics); - symbolTracker.EndScope(); + var inheritedSymbols = context.Symbols.CurrentDeclarations; + context.Symbols.BeginScope(requiredFunctorSupport); + var statements = BuildStatements(nodeContent.GetEnumerator(), context, diagnostics); + context.Symbols.EndScope(); return new QsScope(statements, inheritedSymbols); } /// - /// Applies the given build function to the position relative to the tree root and the given symbol tracker + /// Applies the given build function to the position relative to the tree root and the given resolution context /// to get the desired object as well as a list of diagnostics. /// Adds the generated diagnostics to the given list of diagnostics, and returns the build object. - /// Throws an ArgumentNullException if the given build function, symbolTracker, or diagnostics are null. /// + /// + /// Thrown if the given build function, symbolTracker, or diagnostics are null. + /// private static T BuildStatement(FragmentTree.TreeNode node, - Func,Tuple> build, - SymbolTracker symbolTracker, List diagnostics) + Func, Tuple> build, + ResolutionContext context, List diagnostics) { if (build == null) throw new ArgumentNullException(nameof(build)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); + if (context == null) throw new ArgumentNullException(nameof(context)); if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); var statementPos = node.Fragment.GetRange().Start; - var location = new QsLocation(DiagnosticTools.AsTuple(node.GetPositionRelativeToRoot()), node.Fragment.HeaderRange); - var (statement, messages) = build(location, symbolTracker); - diagnostics.AddRange(messages.Select(msg => Diagnostics.Generate(symbolTracker.SourceFile.Value, msg, statementPos))); + var location = new QsLocation(AsTuple(node.GetPositionRelativeToRoot()), node.Fragment.HeaderRange); + var (statement, messages) = build(location, context); + diagnostics.AddRange(messages.Select(msg => Diagnostics.Generate(context.Symbols.SourceFile.Value, msg, statementPos))); return statement; } /// /// If the current tree node of the given iterator is a using-block intro, - /// builds the corresponding using-statement updating the given symbolTracker in the process, + /// builds the corresponding using-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -523,22 +528,22 @@ private static T BuildStatement(FragmentTree.TreeNode node, /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildUsingStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.UsingBlockIntro allocate) { - symbolTracker.BeginScope(); // pushing a scope such that the declared variables are not available outside the body + context.Symbols.BeginScope(); // pushing a scope such that the declared variables are not available outside the body var allocationScope = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewAllocateScope(nodes.Current.Fragment.Comments, relPos, symbols, allocate.Item1, allocate.Item2), - symbolTracker, diagnostics); - var body = BuildScope(nodes.Current.Children, symbolTracker, diagnostics); + (relPos, ctx) => Statements.NewAllocateScope(nodes.Current.Fragment.Comments, relPos, ctx, allocate.Item1, allocate.Item2), + context, diagnostics); + var body = BuildScope(nodes.Current.Children, context, diagnostics); statement = allocationScope(body); - symbolTracker.EndScope(); + context.Symbols.EndScope(); proceed = nodes.MoveNext(); return true; } @@ -548,7 +553,7 @@ private static bool TryBuildUsingStatement(IEnumerator no /// /// If the current tree node of the given iterator is a borrowing-block intro, - /// builds the corresponding borrowing-statement updating the given symbolTracker in the process, + /// builds the corresponding borrowing-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -561,22 +566,22 @@ private static bool TryBuildUsingStatement(IEnumerator no /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildBorrowStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.BorrowingBlockIntro borrow) { - symbolTracker.BeginScope(); // pushing a scope such that the declared variables are not available outside the body + context.Symbols.BeginScope(); // pushing a scope such that the declared variables are not available outside the body var borrowingScope = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewBorrowScope(nodes.Current.Fragment.Comments, relPos, symbols, borrow.Item1, borrow.Item2), - symbolTracker, diagnostics); - var body = BuildScope(nodes.Current.Children, symbolTracker, diagnostics); + (relPos, ctx) => Statements.NewBorrowScope(nodes.Current.Fragment.Comments, relPos, ctx, borrow.Item1, borrow.Item2), + context, diagnostics); + var body = BuildScope(nodes.Current.Children, context, diagnostics); statement = borrowingScope(body); - symbolTracker.EndScope(); + context.Symbols.EndScope(); proceed = nodes.MoveNext(); return true; } @@ -586,7 +591,7 @@ private static bool TryBuildBorrowStatement(IEnumerator n /// /// If the current tree node of the given iterator is a repeat-until-success (RUS) intro, - /// builds the corresponding RUS-statement updating the given symbolTracker in the process, + /// builds the corresponding RUS-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -600,39 +605,39 @@ private static bool TryBuildBorrowStatement(IEnumerator n /// or if the repeat header is not followed by a until-success clause. /// private static bool TryBuildRepeatStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind.IsRepeatIntro) { - symbolTracker.BeginScope(); + context.Symbols.BeginScope(); var headerNode = nodes.Current; - var inheritedSymbols = symbolTracker.CurrentDeclarations; - var repeatBody = new QsScope(BuildStatements(nodes.Current.Children.GetEnumerator(), symbolTracker, diagnostics), inheritedSymbols); + var inheritedSymbols = context.Symbols.CurrentDeclarations; + var repeatBody = new QsScope(BuildStatements(nodes.Current.Children.GetEnumerator(), context, diagnostics), inheritedSymbols); if (nodes.MoveNext() && nodes.Current.Fragment.Kind is QsFragmentKind.UntilSuccess untilCond) { - inheritedSymbols = symbolTracker.CurrentDeclarations; + inheritedSymbols = context.Symbols.CurrentDeclarations; var conditionalBlock = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewConditionalBlock(nodes.Current.Fragment.Comments, relPos, symbols, untilCond.Item1), - symbolTracker, diagnostics); + (relPos, ctx) => Statements.NewConditionalBlock(nodes.Current.Fragment.Comments, relPos, ctx, untilCond.Item1), + context, diagnostics); var fixupBody = untilCond.Item2 - ? new QsScope(BuildStatements(nodes.Current.Children.GetEnumerator(), symbolTracker, diagnostics), inheritedSymbols) + ? new QsScope(BuildStatements(nodes.Current.Children.GetEnumerator(), context, diagnostics), inheritedSymbols) : new QsScope(ImmutableArray.Empty, inheritedSymbols); var (successCondition, fixupBlock) = conditionalBlock(fixupBody); // here, condition = true implies the block is *not* executed - symbolTracker.EndScope(); + context.Symbols.EndScope(); statement = BuildStatement(headerNode, - (relPos, symbols) => + (relPos, ctx) => { var repeatBlock = new QsPositionedBlock(repeatBody, QsNullable.NewValue(relPos), headerNode.Fragment.Comments); - return Statements.NewRepeatStatement(symbols, repeatBlock, successCondition, fixupBlock); - } - , symbolTracker, diagnostics); + return Statements.NewRepeatStatement(ctx.Symbols, repeatBlock, successCondition, fixupBlock); + }, + context, diagnostics); proceed = nodes.MoveNext(); return true; } @@ -644,7 +649,7 @@ private static bool TryBuildRepeatStatement(IEnumerator n /// /// If the current tree node of the given iterator is a for-loop intro, - /// builds the corresponding for-statement updating the given symbolTracker in the process, + /// builds the corresponding for-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -657,22 +662,22 @@ private static bool TryBuildRepeatStatement(IEnumerator n /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildForStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.ForLoopIntro forStatement) { - symbolTracker.BeginScope(); // pushing a scope such that the declared variables are not available outside the body + context.Symbols.BeginScope(); // pushing a scope such that the declared variables are not available outside the body var forLoop = BuildStatement(nodes.Current, (relPos, symbols) => Statements.NewForStatement(nodes.Current.Fragment.Comments, relPos, symbols, forStatement.Item1, forStatement.Item2), - symbolTracker, diagnostics); - var body = BuildScope(nodes.Current.Children, symbolTracker, diagnostics); + context, diagnostics); + var body = BuildScope(nodes.Current.Children, context, diagnostics); statement = forLoop(body); - symbolTracker.EndScope(); + context.Symbols.EndScope(); proceed = nodes.MoveNext(); return true; } @@ -682,7 +687,7 @@ private static bool TryBuildForStatement(IEnumerator node /// /// If the current tree node of the given iterator is a while-loop intro, - /// builds the corresponding while-statement updating the given symbolTracker in the process, + /// builds the corresponding while-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -695,22 +700,22 @@ private static bool TryBuildForStatement(IEnumerator node /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildWhileStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.WhileLoopIntro whileStatement) { - symbolTracker.BeginScope(); // pushing a scope such that the declared variables are not available outside the body + context.Symbols.BeginScope(); // pushing a scope such that the declared variables are not available outside the body var whileLoop = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewWhileStatement(nodes.Current.Fragment.Comments, relPos, symbols, whileStatement.Item), - symbolTracker, diagnostics); - var body = BuildScope(nodes.Current.Children, symbolTracker, diagnostics); + (relPos, ctx) => Statements.NewWhileStatement(nodes.Current.Fragment.Comments, relPos, ctx, whileStatement.Item), + context, diagnostics); + var body = BuildScope(nodes.Current.Children, context, diagnostics); statement = whileLoop(body); - symbolTracker.EndScope(); + context.Symbols.EndScope(); proceed = nodes.MoveNext(); return true; } @@ -720,7 +725,7 @@ private static bool TryBuildWhileStatement(IEnumerator no /// /// If the current tree node of the given iterator is an if-statement into, - /// builds the corresponding if-statement updating the given symbolTracker in the process, + /// builds the corresponding if-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -733,20 +738,20 @@ private static bool TryBuildWhileStatement(IEnumerator no /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildIfStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.IfClause ifCond) { // if block var buildClause = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewConditionalBlock(nodes.Current.Fragment.Comments, relPos, symbols, ifCond.Item), - symbolTracker, diagnostics); - var ifBlock = buildClause(BuildScope(nodes.Current.Children, symbolTracker, diagnostics)); + (relPos, ctx) => Statements.NewConditionalBlock(nodes.Current.Fragment.Comments, relPos, ctx, ifCond.Item), + context, diagnostics); + var ifBlock = buildClause(BuildScope(nodes.Current.Children, context, diagnostics)); // elif blocks proceed = nodes.MoveNext(); @@ -754,9 +759,9 @@ private static bool TryBuildIfStatement(IEnumerator nodes while (proceed && nodes.Current.Fragment.Kind is QsFragmentKind.ElifClause elifCond) { buildClause = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewConditionalBlock(nodes.Current.Fragment.Comments, relPos, symbols, elifCond.Item), - symbolTracker, diagnostics); - elifBlocks.Add(buildClause(BuildScope(nodes.Current.Children, symbolTracker, diagnostics))); + (relPos, ctx) => Statements.NewConditionalBlock(nodes.Current.Fragment.Comments, relPos, ctx, elifCond.Item), + context, diagnostics); + elifBlocks.Add(buildClause(BuildScope(nodes.Current.Children, context, diagnostics))); proceed = nodes.MoveNext(); } @@ -764,8 +769,8 @@ private static bool TryBuildIfStatement(IEnumerator nodes var elseBlock = QsNullable.Null; if (proceed && nodes.Current.Fragment.Kind.IsElseClause) { - var scope = BuildScope(nodes.Current.Children, symbolTracker, diagnostics); - var elseLocation = new QsLocation(DiagnosticTools.AsTuple(nodes.Current.GetPositionRelativeToRoot()), nodes.Current.Fragment.HeaderRange); + var scope = BuildScope(nodes.Current.Children, context, diagnostics); + var elseLocation = new QsLocation(AsTuple(nodes.Current.GetPositionRelativeToRoot()), nodes.Current.Fragment.HeaderRange); elseBlock = QsNullable.NewValue( new QsPositionedBlock(scope, QsNullable.NewValue(elseLocation), nodes.Current.Fragment.Comments)); proceed = nodes.MoveNext(); @@ -780,7 +785,7 @@ private static bool TryBuildIfStatement(IEnumerator nodes /// /// If the current tree node of the given iterator is a within-block intro, - /// builds the corresponding conjugation updating the given symbolTracker in the process, + /// builds the corresponding conjugation updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -793,30 +798,30 @@ private static bool TryBuildIfStatement(IEnumerator nodes /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildConjugationStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); QsNullable RelativeLocation(FragmentTree.TreeNode node) => - QsNullable.NewValue(new QsLocation(DiagnosticTools.AsTuple(node.GetPositionRelativeToRoot()), node.Fragment.HeaderRange)); + QsNullable.NewValue(new QsLocation(AsTuple(node.GetPositionRelativeToRoot()), node.Fragment.HeaderRange)); if (nodes.Current.Fragment.Kind.IsWithinBlockIntro) { // The requirement for outer blocks in conjugations is always that an adjoint can be auto-generated for them, // independent on what functor specializations need to be auto-generated for the containing operation. var requiredFunctorSupport = ImmutableHashSet.Create(QsFunctor.Adjoint); - var outerTranformation = BuildScope(nodes.Current.Children, symbolTracker, diagnostics, requiredFunctorSupport); + var outerTranformation = BuildScope(nodes.Current.Children, context, diagnostics, requiredFunctorSupport); var outer = new QsPositionedBlock(outerTranformation, RelativeLocation(nodes.Current), nodes.Current.Fragment.Comments); if (nodes.MoveNext() && nodes.Current.Fragment.Kind.IsApplyBlockIntro) { - var innerTransformation = BuildScope(nodes.Current.Children, symbolTracker, diagnostics); + var innerTransformation = BuildScope(nodes.Current.Children, context, diagnostics); var inner = new QsPositionedBlock(innerTransformation, RelativeLocation(nodes.Current), nodes.Current.Fragment.Comments); var built = Statements.NewConjugation(outer, inner); - diagnostics.AddRange(built.Item2.Select(msg => Diagnostics.Generate(symbolTracker.SourceFile.Value, msg.Item2, nodes.Current.GetRootPosition()))); + diagnostics.AddRange(built.Item2.Select(msg => Diagnostics.Generate(context.Symbols.SourceFile.Value, msg.Item2, nodes.Current.GetRootPosition()))); statement = built.Item1; proceed = nodes.MoveNext(); @@ -830,7 +835,7 @@ QsNullable RelativeLocation(FragmentTree.TreeNode node) => /// /// If the current tree node of the given iterator is a let-statement, - /// builds the corresponding let-statement updating the given symbolTracker in the process, + /// builds the corresponding let-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -843,18 +848,18 @@ QsNullable RelativeLocation(FragmentTree.TreeNode node) => /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildLetStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.ImmutableBinding letStatement) { statement = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewImmutableBinding(nodes.Current.Fragment.Comments, relPos, symbols, letStatement.Item1, letStatement.Item2), - symbolTracker, diagnostics); + (relPos, ctx) => Statements.NewImmutableBinding(nodes.Current.Fragment.Comments, relPos, ctx, letStatement.Item1, letStatement.Item2), + context, diagnostics); proceed = nodes.MoveNext(); return true; } @@ -864,7 +869,7 @@ private static bool TryBuildLetStatement(IEnumerator node /// /// If the current tree node of the given iterator is a mutable-statement, - /// builds the corresponding mutable-statement updating the given symbolTracker in the process, + /// builds the corresponding mutable-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -877,18 +882,18 @@ private static bool TryBuildLetStatement(IEnumerator node /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildMutableStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.MutableBinding mutableStatement) { statement = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewMutableBinding(nodes.Current.Fragment.Comments, relPos, symbols, mutableStatement.Item1, mutableStatement.Item2), - symbolTracker, diagnostics); + (relPos, ctx) => Statements.NewMutableBinding(nodes.Current.Fragment.Comments, relPos, ctx, mutableStatement.Item1, mutableStatement.Item2), + context, diagnostics); proceed = nodes.MoveNext(); return true; } @@ -898,7 +903,7 @@ private static bool TryBuildMutableStatement(IEnumerator /// /// If the current tree node of the given iterator is a set-statement, - /// builds the corresponding set-statement updating the given symbolTracker in the process, + /// builds the corresponding set-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -911,18 +916,18 @@ private static bool TryBuildMutableStatement(IEnumerator /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildSetStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.ValueUpdate setStatement) { statement = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewValueUpdate(nodes.Current.Fragment.Comments, relPos, symbols, setStatement.Item1, setStatement.Item2), - symbolTracker, diagnostics); + (relPos, ctx) => Statements.NewValueUpdate(nodes.Current.Fragment.Comments, relPos, ctx, setStatement.Item1, setStatement.Item2), + context, diagnostics); proceed = nodes.MoveNext(); return true; } @@ -932,7 +937,7 @@ private static bool TryBuildSetStatement(IEnumerator node /// /// If the current tree node of the given iterator is a fail-statement, - /// builds the corresponding fail-statement updating the given symbolTracker in the process, + /// builds the corresponding fail-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -945,18 +950,18 @@ private static bool TryBuildSetStatement(IEnumerator node /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildFailStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.FailStatement failStatement) { statement = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewFailStatement(nodes.Current.Fragment.Comments, relPos, symbols, failStatement.Item), - symbolTracker, diagnostics); + (relPos, ctx) => Statements.NewFailStatement(nodes.Current.Fragment.Comments, relPos, ctx, failStatement.Item), + context, diagnostics); proceed = nodes.MoveNext(); return true; } @@ -966,7 +971,7 @@ private static bool TryBuildFailStatement(IEnumerator nod /// /// If the current tree node of the given iterator is a return-statement, - /// builds the corresponding return-statement updating the given symbolTracker in the process, + /// builds the corresponding return-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -979,18 +984,18 @@ private static bool TryBuildFailStatement(IEnumerator nod /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildReturnStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.ReturnStatement returnStatement) { statement = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewReturnStatement(nodes.Current.Fragment.Comments, relPos, symbols, returnStatement.Item), - symbolTracker, diagnostics); + (relPos, ctx) => Statements.NewReturnStatement(nodes.Current.Fragment.Comments, relPos, ctx, returnStatement.Item), + context, diagnostics); proceed = nodes.MoveNext(); return true; } @@ -1000,7 +1005,7 @@ private static bool TryBuildReturnStatement(IEnumerator n /// /// If the current tree node of the given iterator is an expression-statement, - /// builds the corresponding expression-statement updating the given symbolTracker in the process, + /// builds the corresponding expression-statement updating the given symbol tracker in the process, /// and moves the iterator to the next node. /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. @@ -1013,18 +1018,18 @@ private static bool TryBuildReturnStatement(IEnumerator n /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. /// private static bool TryBuildExpressionStatement(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics, out bool proceed, out QsStatement statement) + ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.ExpressionStatement expressionStatement) { statement = BuildStatement(nodes.Current, - (relPos, symbols) => Statements.NewExpressionStatement(nodes.Current.Fragment.Comments, relPos, symbols, expressionStatement.Item), - symbolTracker, diagnostics); + (relPos, ctx) => Statements.NewExpressionStatement(nodes.Current.Fragment.Comments, relPos, ctx, expressionStatement.Item), + context, diagnostics); proceed = nodes.MoveNext(); return true; } @@ -1041,12 +1046,12 @@ private static bool TryBuildExpressionStatement(IEnumerator - private static ImmutableArray BuildStatements(IEnumerator nodes, - SymbolTracker symbolTracker, List diagnostics) + private static ImmutableArray BuildStatements( + IEnumerator nodes, ResolutionContext context, List diagnostics) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); - if (symbolTracker.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); var proceed = nodes.MoveNext(); @@ -1056,43 +1061,43 @@ private static ImmutableArray BuildStatements(IEnumerator BuildStatements(IEnumerator private static SpecializationImplementation BuildUserDefinedImplementation( - FragmentTree.TreeNode root, NonNullable sourceFile, + FragmentTree.TreeNode root, + NonNullable sourceFile, QsTuple> argTuple, ImmutableHashSet requiredFunctorSupport, - SymbolTracker symbolTracker, List diagnostics) + ResolutionContext context, + List diagnostics) { if (argTuple == null) throw new ArgumentNullException(nameof(argTuple)); - if (symbolTracker == null) throw new ArgumentNullException(nameof(symbolTracker)); + if (context == null) throw new ArgumentNullException(nameof(context)); if (requiredFunctorSupport == null) throw new ArgumentNullException(nameof(requiredFunctorSupport)); if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); @@ -1129,27 +1136,29 @@ private static SpecializationImplementation BuildUserDefinedImplementation( // -> the position information is set to null (only) for variables defined in the declaration var (variablesOnDeclation, variablesOnSpecialization) = SyntaxGenerator.ExtractItems(argTuple).Partition(decl => decl.Position.IsNull); - symbolTracker.BeginScope(requiredFunctorSupport); + context.Symbols.BeginScope(requiredFunctorSupport); foreach (var decl in variablesOnDeclation) - { symbolTracker.TryAddVariableDeclartion(decl); } + { + context.Symbols.TryAddVariableDeclartion(decl); + } var specPos = root.Fragment.GetRange().Start; foreach (var decl in variablesOnSpecialization) { - var msgs = symbolTracker.TryAddVariableDeclartion(decl).Item2; - var position = specPos.Add(DiagnosticTools.AsPosition(decl.Position.Item)); + var msgs = context.Symbols.TryAddVariableDeclartion(decl).Item2; + var position = specPos.Add(AsPosition(decl.Position.Item)); diagnostics.AddRange(msgs.Select(msg => Diagnostics.Generate(sourceFile.Value, msg, position))); } - var implementation = BuildScope(root.Children, symbolTracker, diagnostics); - symbolTracker.EndScope(); + var implementation = BuildScope(root.Children, context, diagnostics); + context.Symbols.EndScope(); // verify that all paths return a value if needed (or fail) var (allPathsReturn, messages) = SyntaxProcessing.SyntaxTree.AllPathsReturnValueOrFail(implementation); var rootPosition = root.Fragment.GetRange().Start; - Position AsAbsolutePosition(Tuple statementPos) => rootPosition.Add(DiagnosticTools.AsPosition(statementPos)); + Position AsAbsolutePosition(Tuple statementPos) => rootPosition.Add(AsPosition(statementPos)); diagnostics.AddRange(messages.Select(msg => Diagnostics.Generate(sourceFile.Value, msg.Item2, AsAbsolutePosition(msg.Item1)))); - if (!(symbolTracker.ExpectedReturnType.Resolution.IsUnitType || symbolTracker.ExpectedReturnType.Resolution.IsInvalidType) && !allPathsReturn) + if (!(context.Symbols.ExpectedReturnType.Resolution.IsUnitType || context.Symbols.ExpectedReturnType.Resolution.IsInvalidType) && !allPathsReturn) { var errRange = Parsing.HeaderDelimiters(root.Fragment.Kind.IsControlledAdjointDeclaration ? 2 : 1).Invoke(root.Fragment.Text); var missingReturn = new QsCompilerDiagnostic(DiagnosticItem.NewError(ErrorCode.MissingReturnOrFailStatement), Enumerable.Empty(), errRange); @@ -1205,21 +1214,25 @@ private static IEnumerable RequiredFunctorSupport(QsSpecializationKin /// provided the children are exclusively valid statements. Fails with the corresponding exception otherwise. /// Adds the generated diagnostics to the given list of diagnostics. /// Throws an ArgumentNullException if the parent signature, its argument tuple, - /// the NamespaceManager containing all global declarations, or the given list of diagnostics is null. + /// the compilation unit, or the given list of diagnostics is null. /// Throws an ArgumentException if the given root is neither a specialization declaration, nor a callable declaration, /// or if the callable the specialization belongs to does not support that specialization according to the given NamespaceManager. /// - private static ImmutableArray BuildSpecializations - (FragmentTree specsRoot, ResolvedSignature parentSignature, QsTuple> argTuple, - NamespaceManager symbols, List diagnostics, CancellationToken cancellationToken) + private static ImmutableArray BuildSpecializations( + FragmentTree specsRoot, + ResolvedSignature parentSignature, + QsTuple> argTuple, + CompilationUnit compilation, + List diagnostics, + CancellationToken cancellationToken) { if (parentSignature == null) throw new ArgumentNullException(nameof(parentSignature)); if (argTuple == null) throw new ArgumentNullException(nameof(argTuple)); - if (symbols == null) throw new ArgumentNullException(nameof(symbols)); + if (compilation == null) throw new ArgumentNullException(nameof(compilation)); if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); if (cancellationToken.IsCancellationRequested) return ImmutableArray.Empty; - var definedSpecs = symbols.DefinedSpecializations(new QsQualifiedName(specsRoot.Namespace, specsRoot.Callable)) + var definedSpecs = compilation.GlobalSymbols.DefinedSpecializations(new QsQualifiedName(specsRoot.Namespace, specsRoot.Callable)) .ToLookup(s => s.Item2.Kind).ToImmutableDictionary( specs => specs.Key, specs => QsCompilerError.RaiseOnFailure(specs.Single, "more than one specialization of the same kind exists")); // currently not supported @@ -1235,8 +1248,6 @@ QsSpecialization BuildSpecialization(QsSpecializationKind kind, ResolvedSignatur if (!definedSpecs.TryGetValue(kind, out var defined)) throw new ArgumentException($"missing entry for {kind} specialization of {specsRoot.Namespace}.{specsRoot.Callable}"); var (directive, spec) = defined; - - var symbolTracker = new SymbolTracker(symbols, spec.SourceFile, spec.Parent); var implementation = directive.IsValue ? SpecializationImplementation.NewGenerated(directive.Item) : null; // a user defined implementation is ignored if it is invalid to specify such (e.g. for self-adjoint or intrinsic operations) @@ -1248,7 +1259,11 @@ QsSpecialization BuildSpecialization(QsSpecializationKind kind, ResolvedSignatur QsGeneratorDirective GetDirective(QsSpecializationKind k) => definedSpecs.TryGetValue(k, out defined) && defined.Item1.IsValue ? defined.Item1.Item : null; var requiredFunctorSupport = RequiredFunctorSupport(kind, GetDirective).ToImmutableHashSet(); - implementation = BuildUserDefinedImplementation(root, spec.SourceFile, arg, requiredFunctorSupport, symbolTracker, diagnostics); + var symbolTracker = + new SymbolTracker(compilation.GlobalSymbols, spec.SourceFile, spec.Parent); + var context = new ResolutionContext(symbolTracker, compilation.RuntimeCapabilities); + implementation = BuildUserDefinedImplementation( + root, spec.SourceFile, arg, requiredFunctorSupport, context, diagnostics); QsCompilerError.Verify(symbolTracker.AllScopesClosed, "all scopes should be closed"); } implementation = implementation ?? SpecializationImplementation.Intrinsic; @@ -1257,7 +1272,7 @@ QsSpecialization BuildSpecialization(QsSpecializationKind kind, ResolvedSignatur var parentCharacteristics = parentSignature.Information.Characteristics; // note that if this one is invalid, the corresponding specializations are still compiled var supportedFunctors = parentCharacteristics.SupportedFunctors.ValueOr(ImmutableHashSet.Empty); - var ctlArgPos = QsNullable>.NewValue(DiagnosticTools.AsTuple(new Position())); // position relative to the start of the specialization, i.e. zero-position + var ctlArgPos = QsNullable>.NewValue(AsTuple(new Position())); // position relative to the start of the specialization, i.e. zero-position var controlledSignature = SyntaxGenerator.BuildControlled(parentSignature); QsSpecialization BuildSpec (FragmentTree.TreeNode root) @@ -1265,7 +1280,7 @@ QsSpecialization BuildSpec (FragmentTree.TreeNode root) if (cancellationToken.IsCancellationRequested) return null; bool InvalidCharacteristicsOrSupportedFunctors(params QsFunctor[] functors) => parentCharacteristics.AreInvalid || !functors.Any(f => !supportedFunctors.Contains(f)); - if (!definedSpecs.Values.Any(d => d.Item2.Position is DeclarationHeader.Offset.Defined pos && DiagnosticTools.AsPosition(pos.Item) == root.Fragment.GetRange().Start)) return null; // only process specializations that are valid + if (!definedSpecs.Values.Any(d => d.Item2.Position is DeclarationHeader.Offset.Defined pos && AsPosition(pos.Item) == root.Fragment.GetRange().Start)) return null; // only process specializations that are valid if (FileHeader.IsCallableDeclaration(root.Fragment)) // no specializations have been defined -> one default body { @@ -1369,7 +1384,7 @@ internal static List RunTypeChecking (CompilationUnit compilation, (KeyValuePair specsRoot) { var info = callableDeclarations[specsRoot.Key]; - var specs = BuildSpecializations(specsRoot.Value.Item2, info.Signature, info.ArgumentTuple, compilation.GlobalSymbols, diagnostics, cancellationToken); + var specs = BuildSpecializations(specsRoot.Value.Item2, info.Signature, info.ArgumentTuple, compilation, diagnostics, cancellationToken); return (specsRoot.Key, specs); } @@ -1388,7 +1403,7 @@ QsCallable GetCallable((QsQualifiedName, ImmutableArray) specI var offset = info.Position is DeclarationHeader.Offset.Defined pos ? pos.Item : null; QsCompilerError.Verify(offset != null, "missing position information for built callable"); var msgs = symbolTracker.TryAddVariableDeclartion(decl).Item2 - .Select(msg => Diagnostics.Generate(info.SourceFile.Value, msg, DiagnosticTools.AsPosition(offset))); + .Select(msg => Diagnostics.Generate(info.SourceFile.Value, msg, AsPosition(offset))); diagnostics.AddRange(msgs); } symbolTracker.EndScope(); @@ -1457,7 +1472,7 @@ private static (LocalDeclarations, IEnumerable) StatementsAfterAndL LocalDeclarations Concat(LocalDeclarations fst, LocalDeclarations snd) => new LocalDeclarations(fst.Variables.Concat(snd.Variables).ToImmutableArray()); bool BeforePosition(QsNullable location) => - location.IsValue && DiagnosticTools.AsPosition(location.Item.Offset).IsSmallerThan(relativePosition); + location.IsValue && AsPosition(location.Item.Offset).IsSmallerThan(relativePosition); bool StartsBeforePosition(QsScope body) => body.Statements.Any() && BeforePosition(body.Statements.First().Location); var precedingStatements = scope.Statements.TakeWhile(stm => BeforePosition(stm.Location)).ToImmutableArray(); diff --git a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs index 62410c1e63..9057619232 100644 --- a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs @@ -10,6 +10,7 @@ open System.Linq open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics +open Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants open Microsoft.Quantum.QsCompiler.SymbolTracker open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxGenerator @@ -37,6 +38,13 @@ type private Variance = | Contravariant | Invariant +/// The context used for symbol resolution and type checking. +type ResolutionContext<'a> = + { /// The symbol tracker for the parent callable. + Symbols : SymbolTracker<'a> + /// The runtime capabilities for the compilation unit. + Capabilities : RuntimeCapabilities } + let private invalid = InvalidType |> ResolvedType.New let private ExprWithoutTypeArgs isMutable (ex, t, dep, range) = let inferred = InferredExpressionInformation.New (isMutable = isMutable, quantumDep = dep) @@ -246,11 +254,16 @@ let private VerifyConcatenation parent addError (lhsType : ResolvedType, lhsRang /// If a common base type exists, verifies that this base type supports equality comparison, /// adding the corresponding error otherwise. /// If one of the given types is a missing type, also adds the corresponding ExpressionOfUnknownType error(s). -let private VerifyEqualityComparison parent addError (lhsType : ResolvedType, lhsRange) (rhsType : ResolvedType, rhsRange) = - // NOTE: this may not be the behavior that we want (right now it does not matter, since we don't support equality comparison for any derived type) - let baseType = CommonBaseType addError (ErrorCode.ArgumentMismatchInBinaryOp, [lhsType |> toString; rhsType |> toString]) parent (lhsType, lhsRange) (rhsType, rhsRange) - let expected (t : ResolvedType) = t.supportsEqualityComparison - VerifyIsOneOf expected (ErrorCode.InvalidTypeInEqualityComparison, [baseType |> toString]) addError (baseType, rhsRange) |> ignore +let private VerifyEqualityComparison context addError (lhsType, lhsRange) (rhsType, rhsRange) = + // NOTE: this may not be the behavior that we want (right now it does not matter, since we don't support equality + // comparison for any derived type). + let argumentError = ErrorCode.ArgumentMismatchInBinaryOp, [toString lhsType; toString rhsType] + let baseType = CommonBaseType addError argumentError context.Symbols.Parent (lhsType, lhsRange) (rhsType, rhsRange) + let unsupportedError = ErrorCode.InvalidTypeInEqualityComparison, [toString baseType] + VerifyIsOneOf + (fun t -> t.supportsEqualityComparison context.Capabilities) + unsupportedError addError (baseType, rhsRange) + |> ignore /// Given a list of all item types and there corresponding ranges, verifies that a value array literal can be built from them. /// Adds a MissingExprInArray error with the corresponding range using addError if one of the given types is missing. @@ -547,10 +560,10 @@ type QsExpression with /// recursively computes the corresponding typed expression for a Q# expression. /// Calls addDiagnostic on each diagnostic generated during the resolution. /// Returns the computed typed expression. - member this.Resolve (symbols : SymbolTracker<_>) addDiagnostic : TypedExpression = - + member this.Resolve ({ Symbols = symbols } as context) addDiagnostic : TypedExpression = + /// Calls Resolve on the given Q# expression. - let InnerExpression (item : QsExpression) = item.Resolve symbols addDiagnostic + let InnerExpression (item : QsExpression) = item.Resolve context addDiagnostic /// Builds a QsCompilerDiagnostic with the given error code and range. let addError code range = range |> QsCompilerDiagnostic.Error code |> addDiagnostic /// Builds a QsCompilerDiagnostic with the given warning code and range. @@ -797,7 +810,7 @@ type QsExpression with /// Resolves and verifies the given left hand side and right hand side of a call expression, /// and returns the corresponding expression as typed expression. let buildCall (method, arg) = - let getType (ex : QsExpression) = (ex.Resolve symbols (fun _ -> ())).ResolvedType // don't push resolution errors when tuple matching arguments + let getType (ex : QsExpression) = (ex.Resolve context (fun _ -> ())).ResolvedType // don't push resolution errors when tuple matching arguments let (resolvedMethod, resolvedArg) = (InnerExpression method, InnerExpression arg) let locQdepClassicalEx = resolvedMethod.InferredInformation.HasLocalQuantumDependency || resolvedArg.InferredInformation.HasLocalQuantumDependency let exprKind = CallLikeExpression (resolvedMethod, resolvedArg) @@ -869,10 +882,8 @@ type QsExpression with | BXOR (lhs,rhs) -> buildIntegralOp BXOR (lhs, rhs) | AND (lhs,rhs) -> buildBooleanOpWith VerifyAreBooleans true AND (lhs, rhs) | OR (lhs,rhs) -> buildBooleanOpWith VerifyAreBooleans true OR (lhs, rhs) - | EQ (lhs,rhs) -> buildBooleanOpWith (VerifyEqualityComparison symbols.Parent) false EQ (lhs, rhs) - | NEQ (lhs,rhs) -> buildBooleanOpWith (VerifyEqualityComparison symbols.Parent) false NEQ (lhs, rhs) + | EQ (lhs,rhs) -> buildBooleanOpWith (VerifyEqualityComparison context) false EQ (lhs, rhs) + | NEQ (lhs,rhs) -> buildBooleanOpWith (VerifyEqualityComparison context) false NEQ (lhs, rhs) | NEG ex -> verifyAndBuildWith NEG VerifySupportsArithmetic ex | BNOT ex -> verifyAndBuildWith BNOT VerifyIsIntegral ex | NOT ex -> verifyAndBuildWith NOT (fun log arg -> VerifyIsBoolean log arg; ResolvedType.New Bool) ex - - diff --git a/src/QsCompiler/SyntaxProcessor/StatementVerification.fs b/src/QsCompiler/SyntaxProcessor/StatementVerification.fs index 98b7ef89cb..5e0117f832 100644 --- a/src/QsCompiler/SyntaxProcessor/StatementVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/StatementVerification.fs @@ -21,21 +21,21 @@ open Microsoft.Quantum.QsCompiler.Transformations.SearchAndReplace // some utils for type checking statements -/// Given a verication function that takes an error logging function as well as an expression type and its range, +/// Given a verification function that takes an error logging function as well as an expression type and its range, /// first resolves the given expression, and then verifies its resolved type with the verification function. /// Returns the typed expression built upon resolution, the return value of the verification function, /// and an array with the diagnostics generated during resolution and verification. -let private VerifyWith verification symbols (expr : QsExpression) = +let private VerifyWith verification context (expr : QsExpression) = let accumulatedDiagnostics = new List() let addError code = QsCompilerDiagnostic.Error code >> accumulatedDiagnostics.Add - let typedExpr = expr.Resolve symbols accumulatedDiagnostics.Add + let typedExpr = expr.Resolve context accumulatedDiagnostics.Add let resVerification = verification addError (typedExpr.ResolvedType, expr.RangeOrDefault) typedExpr, resVerification, accumulatedDiagnostics |> Seq.toArray /// Resolves the given expression, and returns the typed expression built upon resolution, /// as well as an array with the diagnostics generated during resolution. -let private Verify symbols expr = - VerifyWith (fun _ _ _ -> ()) symbols expr |> fun (ex,_,err) -> ex, err +let private Verify context expr = + VerifyWith (fun _ _ _ -> ()) context expr |> fun (ex,_,err) -> ex, err /// If the given SymbolTracker specifies that an auto-inversion of the routine is requested, /// checks if the given typed expression has any local quantum dependencies. @@ -66,26 +66,34 @@ let NewExpressionStatement comments location symbols expr = let verifiedExpr, _, diagnostics = VerifyWith VerifyIsUnit symbols expr verifiedExpr |> QsExpressionStatement |> asStatement comments location LocalDeclarations.Empty, diagnostics -/// Resolves and verifies the given Q# expression given a symbol tracker containing all currently declared symbols, -/// verifies that the resolved expression is indeed of type String, and builds a Q# fail-statement at the given location from it. +/// Resolves and verifies the given Q# expression given the resolution context, verifies that the resolved expression is +/// indeed of type String, and builds a Q# fail-statement at the given location from it. +/// /// Returns the built statement as well as an array of diagnostics generated during resolution and verification. -let NewFailStatement comments location symbols expr = - let verifiedExpr, _, diagnostics = VerifyWith VerifyIsString symbols expr - let autoGenErrs = (verifiedExpr, expr.RangeOrDefault) |> onAutoInvertCheckQuantumDependency symbols +let NewFailStatement comments location context expr = + let verifiedExpr, _, diagnostics = VerifyWith VerifyIsString context expr + let autoGenErrs = (verifiedExpr, expr.RangeOrDefault) |> onAutoInvertCheckQuantumDependency context.Symbols verifiedExpr |> QsFailStatement |> asStatement comments location LocalDeclarations.Empty, Array.concat [diagnostics; autoGenErrs] -/// Resolves and verifies the given Q# expression given a symbol tracker containing all currently declared symbols. +/// Resolves and verifies the given Q# expression using the resolution context. /// Verifies that the type of the resolved expression is indeed compatible with the expected return type associated with the symbol tracker. /// Uses VerifyAssignment to verify that this is indeed the case. /// Builds the corresponding Q# return-statement at the given location, /// and returns it along with an array of diagnostics generated during resolution and verification. /// Errors due to the statement not satisfying the necessary conditions for the required auto-generation of specializations /// (specified by the given SymbolTracker) are also included in the returned diagnostics. -let NewReturnStatement comments (location : QsLocation) (symbols : SymbolTracker<_>) expr = - let VerifyIsReturnType = VerifyAssignment symbols.ExpectedReturnType symbols.Parent ErrorCode.TypeMismatchInReturn - let verifiedExpr, _, diagnostics = VerifyWith VerifyIsReturnType symbols expr - let autoGenErrs = symbols |> onAutoInvertGenerateError ((ErrorCode.ReturnStatementWithinAutoInversion, []), location.Range) - verifiedExpr |> QsReturnStatement |> asStatement comments location LocalDeclarations.Empty, Array.concat [diagnostics; autoGenErrs] +let NewReturnStatement comments (location : QsLocation) context expr = + let verifyIsReturnType = + VerifyAssignment context.Symbols.ExpectedReturnType context.Symbols.Parent ErrorCode.TypeMismatchInReturn + let verifiedExpr, _, diagnostics = VerifyWith verifyIsReturnType context expr + let autoGenErrs = + context.Symbols + |> onAutoInvertGenerateError ((ErrorCode.ReturnStatementWithinAutoInversion, []), location.Range) + let statement = + verifiedExpr + |> QsReturnStatement + |> asStatement comments location LocalDeclarations.Empty + statement, Array.append diagnostics autoGenErrs /// Given a Q# symbol, as well as the resolved type of the right hand side that is assigned to it, /// shape matches the symbol tuple with the type to determine whether the assignment is valid, and @@ -127,30 +135,39 @@ let private VerifyBinding tryBuildDeclaration (qsSym, (rhsType, rhsRange)) warnO symbolTuple symItems, Array.concat declarations, Array.concat errs GetBindings (qsSym, rhsType) -/// Resolves and verifies the given Q# expressions given a symbol tracker containing all currently declared symbols. +/// Resolves and verifies the given Q# expressions using the resolution context. /// Verifies that the type of the resolved right hand side expression is indeed compatible with the resolved type of the left hand side expression. /// Uses VerifyAssignment to verify that this is indeed the case. /// Builds the corresponding Q# set-statement at the given location, /// and returns it along with an array of diagnostics generated during resolution and verification. /// Errors due to the statement not satisfying the necessary conditions for the required auto-generation of specializations /// (specified by the given SymbolTracker) are also included in the returned diagnostics. -let NewValueUpdate comments (location : QsLocation) symbols (lhs : QsExpression, rhs : QsExpression) = - let verifiedLhs, lhsErrs = Verify symbols lhs - let VerifyIsCorrectType = VerifyAssignment verifiedLhs.ResolvedType symbols.Parent ErrorCode.TypeMismatchInValueUpdate - let verifiedRhs, _, rhsErrs = VerifyWith VerifyIsCorrectType symbols rhs +let NewValueUpdate comments (location : QsLocation) context (lhs : QsExpression, rhs : QsExpression) = + let verifiedLhs, lhsErrs = Verify context lhs + let VerifyIsCorrectType = + VerifyAssignment verifiedLhs.ResolvedType context.Symbols.Parent ErrorCode.TypeMismatchInValueUpdate + let verifiedRhs, _, rhsErrs = VerifyWith VerifyIsCorrectType context rhs let localQdep = verifiedRhs.InferredInformation.HasLocalQuantumDependency let rec VerifyMutability = function | Tuple (exs : TypedExpression list) -> exs |> Seq.collect VerifyMutability |> Seq.toArray - | Item (ex : TypedExpression) when ex.InferredInformation.IsMutable -> ex.Expression |> function - | Identifier (LocalVariable id, Null) -> symbols.UpdateQuantumDependency id localQdep; [||] - | _ -> [||] + | Item (ex : TypedExpression) when ex.InferredInformation.IsMutable -> + match ex.Expression with + | Identifier (LocalVariable id, Null) -> context.Symbols.UpdateQuantumDependency id localQdep + | _ -> () + [||] | Item (ex : TypedExpression) -> let range = ex.Range.ValueOr QsCompilerDiagnostic.DefaultRange [| range |> QsCompilerDiagnostic.Error (ErrorCode.UpdateOfImmutableIdentifier, []) |] | _ -> [||] // both missing and invalid expressions on the lhs are fine let refErrs = verifiedLhs |> VerifyMutability - let autoGenErrs = symbols |> onAutoInvertGenerateError ((ErrorCode.ValueUpdateWithinAutoInversion, []), location.Range) - QsValueUpdate.New(verifiedLhs, verifiedRhs) |> QsValueUpdate |> asStatement comments location LocalDeclarations.Empty, Array.concat [lhsErrs; refErrs; rhsErrs; autoGenErrs] + let autoGenErrs = + context.Symbols + |> onAutoInvertGenerateError ((ErrorCode.ValueUpdateWithinAutoInversion, []), location.Range) + let statement = + QsValueUpdate.New(verifiedLhs, verifiedRhs) + |> QsValueUpdate + |> asStatement comments location LocalDeclarations.Empty + statement, Array.concat [lhsErrs; refErrs; rhsErrs; autoGenErrs] /// Adds a variable declaration with the given name, quantum dependency, and type at the given location to the given symbol tracker. /// Generates the corresponding error(s) if a symbol with the same name is already visible on that scope and/or the given name is not a valid variable name. @@ -167,19 +184,19 @@ let private TryAddDeclaration isMutable (symbols : SymbolTracker<_>) (name : Non (if added then Some decl else None), errs |> Array.append tpErr /// Given a Q# symbol, as well as the expression on the right hand side that is assigned to it, -/// resolves and verifies the assignment using VerifyBinding and the given symbol tracker that contains all currently declared symbols. +/// resolves and verifies the assignment using VerifyBinding and the resolution context. /// Pushes all determined variable declarations into the current scope of the symbol tracker, /// generating the corresponding error(s) if a symbol with the same name is already visible on that scope and/or the symbol is not a valid variable name. /// Builds the corresponding Q# let- or mutable-statement (depending on the given kind) at the given location, /// and returns it along with an array of all generated diagnostics. -let private NewBinding kind comments (location : QsLocation) (symbols : SymbolTracker<_>) (qsSym : QsSymbol, qsExpr : QsExpression) = - let rhs, rhsErrs = Verify symbols qsExpr +let private NewBinding kind comments (location : QsLocation) context (qsSym : QsSymbol, qsExpr : QsExpression) = + let rhs, rhsErrs = Verify context qsExpr let symTuple, varDeclarations, errs = let isMutable = kind |> function | MutableBinding -> true | ImmutableBinding -> false let localQdep = rhs.InferredInformation.HasLocalQuantumDependency - let addDeclaration (name, range) = TryAddDeclaration isMutable symbols (name, (Value location.Offset, range), localQdep) + let addDeclaration (name, range) = TryAddDeclaration isMutable context.Symbols (name, (Value location.Offset, range), localQdep) VerifyBinding addDeclaration (qsSym, (rhs.ResolvedType, qsExpr.RangeOrDefault)) false - let autoGenErrs = (rhs, qsExpr.RangeOrDefault) |> onAutoInvertCheckQuantumDependency symbols + let autoGenErrs = (rhs, qsExpr.RangeOrDefault) |> onAutoInvertCheckQuantumDependency context.Symbols let binding = QsBinding.New kind (symTuple, rhs) |> QsVariableDeclaration binding |> asStatement comments location (LocalDeclarations.New varDeclarations), Array.concat [rhsErrs; errs; autoGenErrs] @@ -198,7 +215,7 @@ let NewMutableBinding comments location symbols (qsSym, qsExpr) = type BlockStatement<'T> = delegate of QsScope -> 'T -/// Given the location of the statement header as well as a symbol tracker containing all currently declared symbols, +/// Given the location of the statement header and the resolution context, /// builds the Q# for-statement at the given location, that iterates over the given expression, with the given symbol as loop variable(s). /// In order to do so, verifies the expression is indeed iterable, /// and verifies that the shape of the symbol tuple is compatible with the type of the iteration item using VerifyBinding. @@ -206,32 +223,32 @@ type BlockStatement<'T> = delegate of QsScope -> 'T /// Returns an array with all generated diagnostics, /// as well as a delegate that given a Q# scope returns the built for-statement with the given scope as the body. /// NOTE: the declared loop variables are *not* visible after the statements ends, hence they are *not* attaches as local declarations to the statement! -let NewForStatement comments (location : QsLocation) (symbols : SymbolTracker<_>) (qsSym : QsSymbol, qsExpr : QsExpression) = - let iterExpr, itemT, iterErrs = VerifyWith VerifyIsIterable symbols qsExpr +let NewForStatement comments (location : QsLocation) context (qsSym : QsSymbol, qsExpr : QsExpression) = + let iterExpr, itemT, iterErrs = VerifyWith VerifyIsIterable context qsExpr let symTuple, _, varErrs = let localQdep = iterExpr.InferredInformation.HasLocalQuantumDependency - let addDeclaration (name, range) = TryAddDeclaration false symbols (name, (Value location.Offset, range), localQdep) + let addDeclaration (name, range) = TryAddDeclaration false context.Symbols (name, (Value location.Offset, range), localQdep) VerifyBinding addDeclaration (qsSym, (itemT, qsExpr.RangeOrDefault)) false - let autoGenErrs = (iterExpr, qsExpr.RangeOrDefault) |> onAutoInvertCheckQuantumDependency symbols + let autoGenErrs = (iterExpr, qsExpr.RangeOrDefault) |> onAutoInvertCheckQuantumDependency context.Symbols let forLoop body = QsForStatement.New ((symTuple, itemT), iterExpr, body) |> QsForStatement new BlockStatement<_>(forLoop >> asStatement comments location LocalDeclarations.Empty), Array.concat [iterErrs; varErrs; autoGenErrs] -/// Given the location of the statement header as well as a symbol tracker containing all currently declared symbols, +/// Given the location of the statement header and the resolution context, /// builds the Q# while-statement at the given location with the given expression as condition. /// Verifies the expression is indeed of type Bool, and returns an array with all generated diagnostics, /// as well as a delegate that given a Q# scope returns the built while-statement with the given scope as the body. -let NewWhileStatement comments (location : QsLocation) (symbols : SymbolTracker<_>) (qsExpr : QsExpression) = - let cond, _, errs = VerifyWith VerifyIsBoolean symbols qsExpr +let NewWhileStatement comments (location : QsLocation) context (qsExpr : QsExpression) = + let cond, _, errs = VerifyWith VerifyIsBoolean context qsExpr let whileLoop body = QsWhileStatement.New (cond, body) |> QsWhileStatement new BlockStatement<_>(whileLoop >> asStatement comments location LocalDeclarations.Empty), errs -/// Resolves and verifies the given Q# expression given a symbol tracker containing all currently declared symbols. +/// Resolves and verifies the given Q# expression using the resolution context. /// Verifies that the type of the resolved expression is indeed of kind Bool. /// Returns an array of all diagnostics generated during resolution and verification, /// as well as a delegate that given a positioned block of Q# statements returns the corresponding conditional block. -let NewConditionalBlock comments location (symbols : SymbolTracker<_>) (qsExpr : QsExpression) = - let condition, _, errs = VerifyWith VerifyIsBoolean symbols qsExpr - let autoGenErrs = (condition, qsExpr.RangeOrDefault) |> onAutoInvertCheckQuantumDependency symbols +let NewConditionalBlock comments location context (qsExpr : QsExpression) = + let condition, _, errs = VerifyWith VerifyIsBoolean context qsExpr + let autoGenErrs = (condition, qsExpr.RangeOrDefault) |> onAutoInvertCheckQuantumDependency context.Symbols let block body = condition, QsPositionedBlock.New comments (Value location) body new BlockStatement<_>(block), Array.concat [errs; autoGenErrs] @@ -278,7 +295,7 @@ let NewConjugation (outer : QsPositionedBlock, inner : QsPositionedBlock) = |> Seq.map (fun loc -> (loc.Offset, loc.Range |> QsCompilerDiagnostic.Error (ErrorCode.InvalidReassignmentInApplyBlock, []))) |> Seq.toArray QsConjugation.New (outer, inner) |> QsConjugation |> asStatement QsComments.Empty location LocalDeclarations.Empty, updateErrs -/// Given the location of the statement header as well as a symbol tracker containing all currently declared symbols, +/// Given the location of the statement header and the resolution context, /// builds the Q# using- or borrowing-statement (depending on the given kind) at the given location /// that initializes the qubits in the given initializer expression and assigns them to the given symbol. /// In order to do so, resolves and verifies the initializer expression, as well as its binding to the given symbol using VerifyBinding. @@ -286,12 +303,12 @@ let NewConjugation (outer : QsPositionedBlock, inner : QsPositionedBlock) = /// Returns an array with all generated diagnostics, /// as well as a delegate that given a Q# scope returns the built using- or borrowing-statement with the given scope as the body. /// NOTE: the declared variables are *not* visible after the statements ends, hence they are *not* attaches as local declarations to the statement! -let private NewBindingScope kind comments (location : QsLocation) (symbols : SymbolTracker<_>) (qsSym : QsSymbol, qsInit : QsInitializer) = +let private NewBindingScope kind comments (location : QsLocation) context (qsSym : QsSymbol, qsInit : QsInitializer) = let rec VerifyInitializer (init : QsInitializer) = init.Initializer |> function | SingleQubitAllocation -> SingleQubitAllocation |> ResolvedInitializer.New, [||] | QubitRegisterAllocation nr -> - let verifiedNr, _, err = VerifyWith VerifyIsInteger symbols nr - let autoGenErrs = (verifiedNr, nr.RangeOrDefault) |> onAutoInvertCheckQuantumDependency symbols + let verifiedNr, _, err = VerifyWith VerifyIsInteger context nr + let autoGenErrs = (verifiedNr, nr.RangeOrDefault) |> onAutoInvertCheckQuantumDependency context.Symbols QubitRegisterAllocation verifiedNr |> ResolvedInitializer.New, Array.concat [err; autoGenErrs] | QubitTupleAllocation is -> let items, errs = is |> Seq.map VerifyInitializer |> Seq.toList |> List.unzip @@ -301,10 +318,12 @@ let private NewBindingScope kind comments (location : QsLocation) (symbols : Sym let initializer, initErrs = VerifyInitializer qsInit let symTuple, _, varErrs = let rhsRange = qsInit.Range.ValueOr QsCompilerDiagnostic.DefaultRange - let addDeclaration (name, range) = TryAddDeclaration false symbols (name, (Value location.Offset, range), false) + let addDeclaration (name, range) = + TryAddDeclaration false context.Symbols (name, (Value location.Offset, range), false) VerifyBinding addDeclaration (qsSym, (initializer.Type, rhsRange)) true let bindingScope body = QsQubitScope.New kind ((symTuple, initializer), body) |> QsQubitScope - new BlockStatement<_>(bindingScope >> asStatement comments location LocalDeclarations.Empty), Array.concat [initErrs; varErrs] + let statement = BlockStatement<_>(bindingScope >> asStatement comments location LocalDeclarations.Empty) + statement, Array.append initErrs varErrs /// Resolves, verifies and builds the Q# using-statement at the given location binding the given initializer to the given symbol. /// Adds the corresponding local variable declarations to the given symbol tracker. @@ -315,4 +334,3 @@ let NewAllocateScope comments location symbols (qsSym, qsInit) = NewBindingScope /// Adds the corresponding local variable declarations to the given symbol tracker. /// Returns the built statement along with an array containing all diagnostics generated in the process. let NewBorrowScope comments location symbols (qsSym, qsInit) = NewBindingScope QsQubitScopeKind.Borrow comments location symbols (qsSym, qsInit) - diff --git a/src/QsCompiler/SyntaxProcessor/VerificationTools.fs b/src/QsCompiler/SyntaxProcessor/VerificationTools.fs index 4407a9f73e..1f5c37d340 100644 --- a/src/QsCompiler/SyntaxProcessor/VerificationTools.fs +++ b/src/QsCompiler/SyntaxProcessor/VerificationTools.fs @@ -5,6 +5,7 @@ module Microsoft.Quantum.QsCompiler.SyntaxProcessing.VerificationTools open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.DataTypes +open Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTokens open Microsoft.Quantum.QsCompiler.SyntaxTree @@ -74,12 +75,13 @@ type ResolvedType with | _ -> false this.Exists condition - /// If the given type supports equality comparison, + /// If the given type supports equality comparison within the capabilities of the runtime, /// returns the type of an equality comparison expression as Some (which is always Bool). /// Returns None otherwise. - member this.supportsEqualityComparison = + member this.supportsEqualityComparison capabilities = match this.Resolution with - | Int | BigInt | Double | Bool | Qubit | String | Result | Pauli -> Some (Bool |> ResolvedType.New) // excludes Range + | Int | BigInt | Double | Bool | Qubit | String | Pauli -> Some (ResolvedType.New Bool) + | Result when capabilities <> RuntimeCapabilities.QPRGen0 -> Some (ResolvedType.New Bool) | _ -> None /// If the given type supports arithmetic operations, @@ -107,5 +109,3 @@ type ResolvedType with | Range -> Some (Int |> ResolvedType.New) | ArrayType bt -> Some bt | _ -> None - - From 4bf68c544cb02b528dce515139018cfd05c306c3 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 4 Jun 2020 13:31:57 -0700 Subject: [PATCH 02/20] Move more fields from SymbolTracker to ResolutionContext --- .../CompilationManager/TypeChecking.cs | 9 ++++--- .../SyntaxProcessor/ExpressionVerification.fs | 18 +++++++++++++- .../SyntaxProcessor/StatementVerification.fs | 5 ++-- .../SyntaxProcessor/SymbolTracker.fs | 24 +++++++++---------- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index 2b19931272..4aff29626a 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -1158,7 +1158,7 @@ private static SpecializationImplementation BuildUserDefinedImplementation( var rootPosition = root.Fragment.GetRange().Start; Position AsAbsolutePosition(Tuple statementPos) => rootPosition.Add(AsPosition(statementPos)); diagnostics.AddRange(messages.Select(msg => Diagnostics.Generate(sourceFile.Value, msg.Item2, AsAbsolutePosition(msg.Item1)))); - if (!(context.Symbols.ExpectedReturnType.Resolution.IsUnitType || context.Symbols.ExpectedReturnType.Resolution.IsInvalidType) && !allPathsReturn) + if (!(context.ReturnType.Resolution.IsUnitType || context.ReturnType.Resolution.IsInvalidType) && !allPathsReturn) { var errRange = Parsing.HeaderDelimiters(root.Fragment.Kind.IsControlledAdjointDeclaration ? 2 : 1).Invoke(root.Fragment.Text); var missingReturn = new QsCompilerDiagnostic(DiagnosticItem.NewError(ErrorCode.MissingReturnOrFailStatement), Enumerable.Empty(), errRange); @@ -1259,12 +1259,11 @@ QsSpecialization BuildSpecialization(QsSpecializationKind kind, ResolvedSignatur QsGeneratorDirective GetDirective(QsSpecializationKind k) => definedSpecs.TryGetValue(k, out defined) && defined.Item1.IsValue ? defined.Item1.Item : null; var requiredFunctorSupport = RequiredFunctorSupport(kind, GetDirective).ToImmutableHashSet(); - var symbolTracker = - new SymbolTracker(compilation.GlobalSymbols, spec.SourceFile, spec.Parent); - var context = new ResolutionContext(symbolTracker, compilation.RuntimeCapabilities); + var context = CreateResolutionContext( + compilation.GlobalSymbols, compilation.RuntimeCapabilities, spec); implementation = BuildUserDefinedImplementation( root, spec.SourceFile, arg, requiredFunctorSupport, context, diagnostics); - QsCompilerError.Verify(symbolTracker.AllScopesClosed, "all scopes should be closed"); + QsCompilerError.Verify(context.Symbols.AllScopesClosed, "all scopes should be closed"); } implementation = implementation ?? SpecializationImplementation.Intrinsic; return GetSpecialization(spec, signature, implementation, comments); diff --git a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs index 9057619232..01247a84fe 100644 --- a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs @@ -11,6 +11,7 @@ open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants +open Microsoft.Quantum.QsCompiler.SymbolManagement open Microsoft.Quantum.QsCompiler.SymbolTracker open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxGenerator @@ -42,9 +43,24 @@ type private Variance = type ResolutionContext<'a> = { /// The symbol tracker for the parent callable. Symbols : SymbolTracker<'a> + /// True if the parent callable for the current scope is an operation. + IsInOperation : bool + /// The return type of the parent callable for the current scope. + ReturnType : ResolvedType /// The runtime capabilities for the compilation unit. Capabilities : RuntimeCapabilities } +/// Creates a resolution context for the specialization. +/// Thrown if the specialization's parent does not exist. +let CreateResolutionContext (nsManager : NamespaceManager) capabilities (spec : SpecializationDeclarationHeader) = + match nsManager.TryGetCallable spec.Parent (spec.Parent.Namespace, spec.SourceFile) with + | Found declaration -> + { Symbols = SymbolTracker (nsManager, spec.SourceFile, spec.Parent) + IsInOperation = declaration.Kind = Operation + ReturnType = StripPositionInfo.Apply declaration.Signature.ReturnType + Capabilities = capabilities } + | _ -> failwith "The specialization's parent does not exist." + let private invalid = InvalidType |> ResolvedType.New let private ExprWithoutTypeArgs isMutable (ex, t, dep, range) = let inferred = InferredExpressionInformation.New (isMutable = isMutable, quantumDep = dep) @@ -837,7 +853,7 @@ type QsExpression with let localQDependency = if isPartialApplication then locQdepClassicalEx else true let exInfo = InferredExpressionInformation.New (isMutable = false, quantumDep = localQDependency) let typeParamResolutions, exType = (argT, resT) |> callTypeOrPartial (fun (i,o) -> QsTypeKind.Operation ((i,o), characteristics)) - if not (symbols.WithinOperation || isPartialApplication) then method.RangeOrDefault |> addError (ErrorCode.OperationCallOutsideOfOperation, []); invalidEx + if not (context.IsInOperation || isPartialApplication) then method.RangeOrDefault |> addError (ErrorCode.OperationCallOutsideOfOperation, []); invalidEx else TypedExpression.New (exprKind, typeParamResolutions, exType, exInfo, this.Range) | _ -> method.RangeOrDefault |> addError (ErrorCode.ExpectingCallableExpr, [resolvedMethod.ResolvedType |> toString]); invalidEx diff --git a/src/QsCompiler/SyntaxProcessor/StatementVerification.fs b/src/QsCompiler/SyntaxProcessor/StatementVerification.fs index 5e0117f832..7c9089202b 100644 --- a/src/QsCompiler/SyntaxProcessor/StatementVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/StatementVerification.fs @@ -82,9 +82,8 @@ let NewFailStatement comments location context expr = /// and returns it along with an array of diagnostics generated during resolution and verification. /// Errors due to the statement not satisfying the necessary conditions for the required auto-generation of specializations /// (specified by the given SymbolTracker) are also included in the returned diagnostics. -let NewReturnStatement comments (location : QsLocation) context expr = - let verifyIsReturnType = - VerifyAssignment context.Symbols.ExpectedReturnType context.Symbols.Parent ErrorCode.TypeMismatchInReturn +let NewReturnStatement comments (location : QsLocation) (context : ResolutionContext<_>) expr = + let verifyIsReturnType = VerifyAssignment context.ReturnType context.Symbols.Parent ErrorCode.TypeMismatchInReturn let verifiedExpr, _, diagnostics = VerifyWith verifyIsReturnType context expr let autoGenErrs = context.Symbols diff --git a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs index 3b3aec8f50..cc32c1593b 100644 --- a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs +++ b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs @@ -65,17 +65,17 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif InvalidOperationException "the content of the namespace manager associated with this symbol tracker has changed" |> raise globals - /// Denotes a property of the parent callable associated with this symbol tracker, - /// such as information about the kind, the type parameters, the returns type, and the source file it is declared in. - /// IMPORTANT: these need to be adapted if we want to support type specializations and/or external specializations! - let parentIsOperation, typeParameters, expectedReturnType = + /// The type parameters of the parent callable associated with this symbol tracker. + /// + /// IMPORTANT: This needs to be adapted if we want to support type specializations and/or external specializations! + let typeParameters = match GlobalSymbols().TryGetCallable parent (parent.Namespace, sourceFile) with | 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 + decl.Signature.TypeParameters + |> Seq.choose (function | ValidName name -> Some name | InvalidName -> None) + |> fun valid -> valid.ToImmutableArray () | _ -> 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. /// Returns Null otherwise. @@ -104,12 +104,10 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif /// the namespace and callable declaration within which the symbols tracked by this SymbolTracker instance are used member this.Parent = parent - /// set to true if the parent callable associated with this symbol tracker is an operation - member this.WithinOperation = parentIsOperation + /// the source file within which the Parent (a callable declaration) associated with this SymbolTracker instance is declared member this.SourceFile = sourceFile - /// the expected type for all values returned within the scope this symbol tracker is used for (contains no range information) - member this.ExpectedReturnType = expectedReturnType + /// returns true if no scope is currently open member this.AllScopesClosed = pushedScopes.Length = 0 @@ -246,7 +244,7 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif /// For each diagnostic generated during the resolution, calls the given addDiagnostics function on it. /// Returns the resolved type, *including* its range information if applicable. member internal this.ResolveType addDiagnostic (qsType : QsType) = - let resolved, errs = GlobalSymbols().ResolveType (this.Parent, typeParameters, this.SourceFile) qsType + let resolved, errs = GlobalSymbols().ResolveType (parent, typeParameters, sourceFile) qsType for err in errs do addDiagnostic err resolved From 7589685cdde5ae4af84fd6173fc293b435c83dd1 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 4 Jun 2020 14:13:03 -0700 Subject: [PATCH 03/20] Undo some changes to TypeChecking.cs --- .../CompilationManager/TypeChecking.cs | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index 4aff29626a..af5458042f 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -9,7 +9,6 @@ using Microsoft.Quantum.QsCompiler.CompilationBuilder.DataStructures; using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.Diagnostics; -using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.Quantum.QsCompiler.SymbolManagement; using Microsoft.Quantum.QsCompiler.SymbolTracker; using Microsoft.Quantum.QsCompiler.SyntaxProcessing; @@ -18,8 +17,6 @@ using Microsoft.Quantum.QsCompiler.TextProcessing; using Microsoft.VisualStudio.LanguageServer.Protocol; using static Microsoft.Quantum.QsCompiler.SyntaxProcessing.Expressions; -using static Microsoft.Quantum.QsCompiler.CompilationBuilder.DiagnosticTools; -using Statements = Microsoft.Quantum.QsCompiler.SyntaxProcessing.Statements; namespace Microsoft.Quantum.QsCompiler.CompilationBuilder { @@ -46,7 +43,7 @@ internal static class TypeChecking precedingFragment = preceding.GetFragment(); if (precedingFragment.IncludeInCompilation && precedingFragment.Kind is QsFragmentKind.DeclarationAttribute att) { - var offset = AsTuple(precedingFragment.GetRange().Start); + var offset = DiagnosticTools.AsTuple(precedingFragment.GetRange().Start); attributes.Add(new AttributeAnnotation(att.Item1, att.Item2, offset, precedingFragment.Comments)); } else break; @@ -90,7 +87,7 @@ internal static ImmutableArray DocumentingComments(this FileContentManag /// For namespaces with an invalid namespace name the symbol name in the header item will be set to an UnknownNamespace. /// private static IEnumerable<(CodeFragment.TokenIndex, HeaderEntry)> GetNamespaceHeaderItems(this FileContentManager file) => - file.GetHeaderItems(file?.NamespaceDeclarationTokens(), frag => frag.Kind.DeclaredNamespace(), InternalUse.UnknownNamespace); + file.GetHeaderItems(file?.NamespaceDeclarationTokens(), frag => frag.Kind.DeclaredNamespace(), ReservedKeywords.InternalUse.UnknownNamespace); /// /// Returns the HeaderItems corresponding to all open directives with a valid name in the given file, or null if the given file is null. @@ -214,7 +211,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position try { compilation.GlobalSymbols.RemoveSource(file.FileName); - QsLocation Location(Position pos, Tuple range) => new QsLocation(AsTuple(pos), range); + QsLocation Location(Position pos, Tuple range) => new QsLocation(DiagnosticTools.AsTuple(pos), range); // add all namespace declarations var namespaceHeaders = file.GetNamespaceHeaderItems().ToImmutableArray(); @@ -290,7 +287,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position { var position = headerItem.GetPosition(); var (specKind, generator, introRange) = headerItem.Declaration; - var location = new QsLocation(AsTuple(position), introRange); + var location = new QsLocation(DiagnosticTools.AsTuple(position), introRange); var messages = ns.TryAddCallableSpecialization(specKind, file.FileName, location, parentName, generator, headerItem.Attributes, headerItem.Documentation); if (!messages.Any(msg => msg.Diagnostic.IsError)) contentToCompile.Add(tIndex); foreach (var msg in messages) @@ -313,7 +310,7 @@ private static T ContainingParent(Position pos, IReadOnlyCollection<(Position // if the declaration directly contains statements and no specialization, auto-generate a default body for the callable if (!specializations.Any()) { - var location = new QsLocation(AsTuple(parent.Item2.GetPosition()), parentName.Item2); + var location = new QsLocation(DiagnosticTools.AsTuple(parent.Item2.GetPosition()), parentName.Item2); var genRange = QsRangeInfo.NewValue(location.Range); // set to the range of the callable name var omittedSymbol = new QsSymbol(QsSymbolKind.OmittedSymbols, QsRangeInfo.Null); var generatorKind = QsSpecializationGeneratorKind.NewUserDefinedImplementation(omittedSymbol); @@ -343,7 +340,7 @@ internal static void ResolveGlobalSymbols(NamespaceManager symbols, List source, IEnumerable, QsCompilerDiagnostic>> msgs) => - diagnostics.AddRange(msgs.Select(msg => Diagnostics.Generate(source.Value, msg.Item2, AsPosition(msg.Item1)))); + diagnostics.AddRange(msgs.Select(msg => Diagnostics.Generate(source.Value, msg.Item2, DiagnosticTools.AsPosition(msg.Item1)))); if (fileName != null) { @@ -507,7 +504,7 @@ private static T BuildStatement(FragmentTree.TreeNode node, if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); var statementPos = node.Fragment.GetRange().Start; - var location = new QsLocation(AsTuple(node.GetPositionRelativeToRoot()), node.Fragment.HeaderRange); + var location = new QsLocation(DiagnosticTools.AsTuple(node.GetPositionRelativeToRoot()), node.Fragment.HeaderRange); var (statement, messages) = build(location, context); diagnostics.AddRange(messages.Select(msg => Diagnostics.Generate(context.Symbols.SourceFile.Value, msg, statementPos))); return statement; @@ -770,7 +767,7 @@ private static bool TryBuildIfStatement(IEnumerator nodes if (proceed && nodes.Current.Fragment.Kind.IsElseClause) { var scope = BuildScope(nodes.Current.Children, context, diagnostics); - var elseLocation = new QsLocation(AsTuple(nodes.Current.GetPositionRelativeToRoot()), nodes.Current.Fragment.HeaderRange); + var elseLocation = new QsLocation(DiagnosticTools.AsTuple(nodes.Current.GetPositionRelativeToRoot()), nodes.Current.Fragment.HeaderRange); elseBlock = QsNullable.NewValue( new QsPositionedBlock(scope, QsNullable.NewValue(elseLocation), nodes.Current.Fragment.Comments)); proceed = nodes.MoveNext(); @@ -806,7 +803,7 @@ private static bool TryBuildConjugationStatement(IEnumerator RelativeLocation(FragmentTree.TreeNode node) => - QsNullable.NewValue(new QsLocation(AsTuple(node.GetPositionRelativeToRoot()), node.Fragment.HeaderRange)); + QsNullable.NewValue(new QsLocation(DiagnosticTools.AsTuple(node.GetPositionRelativeToRoot()), node.Fragment.HeaderRange)); if (nodes.Current.Fragment.Kind.IsWithinBlockIntro) { @@ -1146,7 +1143,7 @@ private static SpecializationImplementation BuildUserDefinedImplementation( foreach (var decl in variablesOnSpecialization) { var msgs = context.Symbols.TryAddVariableDeclartion(decl).Item2; - var position = specPos.Add(AsPosition(decl.Position.Item)); + var position = specPos.Add(DiagnosticTools.AsPosition(decl.Position.Item)); diagnostics.AddRange(msgs.Select(msg => Diagnostics.Generate(sourceFile.Value, msg, position))); } @@ -1156,7 +1153,7 @@ private static SpecializationImplementation BuildUserDefinedImplementation( // verify that all paths return a value if needed (or fail) var (allPathsReturn, messages) = SyntaxProcessing.SyntaxTree.AllPathsReturnValueOrFail(implementation); var rootPosition = root.Fragment.GetRange().Start; - Position AsAbsolutePosition(Tuple statementPos) => rootPosition.Add(AsPosition(statementPos)); + Position AsAbsolutePosition(Tuple statementPos) => rootPosition.Add(DiagnosticTools.AsPosition(statementPos)); diagnostics.AddRange(messages.Select(msg => Diagnostics.Generate(sourceFile.Value, msg.Item2, AsAbsolutePosition(msg.Item1)))); if (!(context.ReturnType.Resolution.IsUnitType || context.ReturnType.Resolution.IsInvalidType) && !allPathsReturn) { @@ -1271,7 +1268,7 @@ QsSpecialization BuildSpecialization(QsSpecializationKind kind, ResolvedSignatur var parentCharacteristics = parentSignature.Information.Characteristics; // note that if this one is invalid, the corresponding specializations are still compiled var supportedFunctors = parentCharacteristics.SupportedFunctors.ValueOr(ImmutableHashSet.Empty); - var ctlArgPos = QsNullable>.NewValue(AsTuple(new Position())); // position relative to the start of the specialization, i.e. zero-position + var ctlArgPos = QsNullable>.NewValue(DiagnosticTools.AsTuple(new Position())); // position relative to the start of the specialization, i.e. zero-position var controlledSignature = SyntaxGenerator.BuildControlled(parentSignature); QsSpecialization BuildSpec (FragmentTree.TreeNode root) @@ -1279,7 +1276,7 @@ QsSpecialization BuildSpec (FragmentTree.TreeNode root) if (cancellationToken.IsCancellationRequested) return null; bool InvalidCharacteristicsOrSupportedFunctors(params QsFunctor[] functors) => parentCharacteristics.AreInvalid || !functors.Any(f => !supportedFunctors.Contains(f)); - if (!definedSpecs.Values.Any(d => d.Item2.Position is DeclarationHeader.Offset.Defined pos && AsPosition(pos.Item) == root.Fragment.GetRange().Start)) return null; // only process specializations that are valid + if (!definedSpecs.Values.Any(d => d.Item2.Position is DeclarationHeader.Offset.Defined pos && DiagnosticTools.AsPosition(pos.Item) == root.Fragment.GetRange().Start)) return null; // only process specializations that are valid if (FileHeader.IsCallableDeclaration(root.Fragment)) // no specializations have been defined -> one default body { @@ -1402,7 +1399,7 @@ QsCallable GetCallable((QsQualifiedName, ImmutableArray) specI var offset = info.Position is DeclarationHeader.Offset.Defined pos ? pos.Item : null; QsCompilerError.Verify(offset != null, "missing position information for built callable"); var msgs = symbolTracker.TryAddVariableDeclartion(decl).Item2 - .Select(msg => Diagnostics.Generate(info.SourceFile.Value, msg, AsPosition(offset))); + .Select(msg => Diagnostics.Generate(info.SourceFile.Value, msg, DiagnosticTools.AsPosition(offset))); diagnostics.AddRange(msgs); } symbolTracker.EndScope(); @@ -1471,7 +1468,7 @@ private static (LocalDeclarations, IEnumerable) StatementsAfterAndL LocalDeclarations Concat(LocalDeclarations fst, LocalDeclarations snd) => new LocalDeclarations(fst.Variables.Concat(snd.Variables).ToImmutableArray()); bool BeforePosition(QsNullable location) => - location.IsValue && AsPosition(location.Item.Offset).IsSmallerThan(relativePosition); + location.IsValue && DiagnosticTools.AsPosition(location.Item.Offset).IsSmallerThan(relativePosition); bool StartsBeforePosition(QsScope body) => body.Statements.Any() && BeforePosition(body.Statements.First().Location); var precedingStatements = scope.Statements.TakeWhile(stm => BeforePosition(stm.Location)).ToImmutableArray(); From 2064cd4c29b51bcd87e61fa37b27b95f05b90594 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 4 Jun 2020 14:30:46 -0700 Subject: [PATCH 04/20] Move ResolutionContext up a bit --- .../SyntaxProcessor/ExpressionVerification.fs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs index 01247a84fe..0360c91bef 100644 --- a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs @@ -25,20 +25,6 @@ open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput // utils for verifying types in expressions -type private StripInferredInfoFromType () = - inherit TypeTransformationBase() - default this.OnCallableInformation opInfo = - let characteristics = this.OnCharacteristicsExpression opInfo.Characteristics - CallableInformation.New (characteristics, InferredCallableInformation.NoInformation) - override this.OnRangeInformation _ = QsRangeInfo.Null -let private StripInferredInfoFromType = (new StripInferredInfoFromType()).OnType - -/// used for type matching arguments in call-like expressions -type private Variance = -| Covariant -| Contravariant -| Invariant - /// The context used for symbol resolution and type checking. type ResolutionContext<'a> = { /// The symbol tracker for the parent callable. @@ -61,6 +47,20 @@ let CreateResolutionContext (nsManager : NamespaceManager) capabilities (spec : Capabilities = capabilities } | _ -> failwith "The specialization's parent does not exist." +type private StripInferredInfoFromType () = + inherit TypeTransformationBase() + default this.OnCallableInformation opInfo = + let characteristics = this.OnCharacteristicsExpression opInfo.Characteristics + CallableInformation.New (characteristics, InferredCallableInformation.NoInformation) + override this.OnRangeInformation _ = QsRangeInfo.Null +let private StripInferredInfoFromType = (new StripInferredInfoFromType()).OnType + +/// used for type matching arguments in call-like expressions +type private Variance = +| Covariant +| Contravariant +| Invariant + let private invalid = InvalidType |> ResolvedType.New let private ExprWithoutTypeArgs isMutable (ex, t, dep, range) = let inferred = InferredExpressionInformation.New (isMutable = isMutable, quantumDep = dep) From 400a7f888b35e3e1bcab6b49f9c2fa10e0c96be5 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 4 Jun 2020 16:32:52 -0700 Subject: [PATCH 05/20] Better error message --- src/QsCompiler/DataStructures/Diagnostics.fs | 4 +++- .../SyntaxProcessor/ExpressionVerification.fs | 10 +++++----- src/QsCompiler/SyntaxProcessor/VerificationTools.fs | 10 ---------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 58f961968e..8af4576729 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -166,6 +166,7 @@ type ErrorCode = | ExpectingIterableExpr = 5020 | ExpectingCallableExpr = 5021 | UnknownIdentifier = 5022 + | UnsupportedResultComparison = 5023 | CallableRedefinition = 6001 | CallableOverlapWithTypeConstructor = 6002 @@ -543,7 +544,8 @@ type DiagnosticItem = | ErrorCode.ExpectingIterableExpr -> "The type {0} does not support iteration. Expecting an expression of array type or of type Range." | ErrorCode.ExpectingCallableExpr -> "The type of the expression must be a function or operation type. The given expression is of type {0}." | ErrorCode.UnknownIdentifier -> "No identifier with the name \"{0}\" exists." - + | ErrorCode.UnsupportedResultComparison -> "Measurement results cannot be compared because the execution target supports only {0} runtime capabilities." + | ErrorCode.CallableRedefinition -> "Invalid callable declaration. A function or operation with the name \"{0}\" already exists." | ErrorCode.CallableOverlapWithTypeConstructor -> "Invalid callable declaration. A type constructor with the name \"{0}\" already exists." | ErrorCode.TypeRedefinition -> "Invalid type declaration. A type with the name \"{0}\" already exists." diff --git a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs index 0360c91bef..167d25065e 100644 --- a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs @@ -275,11 +275,11 @@ let private VerifyEqualityComparison context addError (lhsType, lhsRange) (rhsTy // comparison for any derived type). let argumentError = ErrorCode.ArgumentMismatchInBinaryOp, [toString lhsType; toString rhsType] let baseType = CommonBaseType addError argumentError context.Symbols.Parent (lhsType, lhsRange) (rhsType, rhsRange) - let unsupportedError = ErrorCode.InvalidTypeInEqualityComparison, [toString baseType] - VerifyIsOneOf - (fun t -> t.supportsEqualityComparison context.Capabilities) - unsupportedError addError (baseType, rhsRange) - |> ignore + match baseType.Resolution with + | Result when context.Capabilities = RuntimeCapabilities.QPRGen0 -> + addError (ErrorCode.UnsupportedResultComparison, [context.Capabilities.ToString ()]) rhsRange + | BigInt | Bool | Double | Int | Pauli | Qubit | Result | String -> () + | _ -> addError (ErrorCode.InvalidTypeInEqualityComparison, [toString baseType]) rhsRange /// Given a list of all item types and there corresponding ranges, verifies that a value array literal can be built from them. /// Adds a MissingExprInArray error with the corresponding range using addError if one of the given types is missing. diff --git a/src/QsCompiler/SyntaxProcessor/VerificationTools.fs b/src/QsCompiler/SyntaxProcessor/VerificationTools.fs index 1f5c37d340..6211d6278c 100644 --- a/src/QsCompiler/SyntaxProcessor/VerificationTools.fs +++ b/src/QsCompiler/SyntaxProcessor/VerificationTools.fs @@ -5,7 +5,6 @@ module Microsoft.Quantum.QsCompiler.SyntaxProcessing.VerificationTools open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.DataTypes -open Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTokens open Microsoft.Quantum.QsCompiler.SyntaxTree @@ -75,15 +74,6 @@ type ResolvedType with | _ -> false this.Exists condition - /// If the given type supports equality comparison within the capabilities of the runtime, - /// returns the type of an equality comparison expression as Some (which is always Bool). - /// Returns None otherwise. - member this.supportsEqualityComparison capabilities = - match this.Resolution with - | Int | BigInt | Double | Bool | Qubit | String | Pauli -> Some (ResolvedType.New Bool) - | Result when capabilities <> RuntimeCapabilities.QPRGen0 -> Some (ResolvedType.New Bool) - | _ -> None - /// If the given type supports arithmetic operations, /// returns the type of an arithmetic expression as Some. /// Returns None otherwise. From 38fb16abcfa195469efd1ae638e6624a1c4109a5 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 4 Jun 2020 18:26:45 -0700 Subject: [PATCH 06/20] Add basic tests --- .../Tests.Compiler/AccessModifierTests.fs | 5 +-- .../Tests.Compiler/AutoGenerationTests.fs | 5 +-- .../CapabilityVerificationTests.fs | 44 +++++++++++++++++++ .../Tests.Compiler/GlobalVerificationTests.fs | 5 +-- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 7 ++- .../Tests.Compiler/LocalVerificationTests.fs | 7 ++- .../TestCases/CapabilityVerification.qs | 9 ++++ .../TestUtils/SetupVerificationTests.fs | 10 +++-- .../Tests.Compiler/Tests.Compiler.fsproj | 16 ++++--- .../Tests.Compiler/TypeCheckingTests.fs | 5 +-- 10 files changed, 83 insertions(+), 30 deletions(-) create mode 100644 src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs create mode 100644 src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs diff --git a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs index 72e8ddd8c9..50fb03c085 100644 --- a/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs +++ b/src/QsCompiler/Tests.Compiler/AccessModifierTests.fs @@ -12,10 +12,9 @@ open Microsoft.Quantum.QsCompiler.SyntaxTree open Xunit -type AccessModifierTests (output) = +type AccessModifierTests () = inherit CompilerTests - (CompilerTests.Compile "TestCases" ["AccessModifiers.qs"] [File.ReadAllLines("ReferenceTargets.txt").[1]], - output) + (CompilerTests.Compile ("TestCases", ["AccessModifiers.qs"], [File.ReadAllLines("ReferenceTargets.txt").[1]])) member private this.Expect name (diagnostics : IEnumerable) = let ns = "Microsoft.Quantum.Testing.AccessModifiers" |> NonNullable<_>.New diff --git a/src/QsCompiler/Tests.Compiler/AutoGenerationTests.fs b/src/QsCompiler/Tests.Compiler/AutoGenerationTests.fs index 2ae27951e9..087c786ebf 100644 --- a/src/QsCompiler/Tests.Compiler/AutoGenerationTests.fs +++ b/src/QsCompiler/Tests.Compiler/AutoGenerationTests.fs @@ -9,11 +9,10 @@ open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTree open Xunit -open Xunit.Abstractions -type FunctorAutoGenTests (output:ITestOutputHelper) = - inherit CompilerTests(CompilerTests.Compile "TestCases" ["General.qs"; "FunctorGeneration.qs"] [], output) +type FunctorAutoGenTests () = + inherit CompilerTests(CompilerTests.Compile ("TestCases", ["General.qs"; "FunctorGeneration.qs"])) member private this.Expect name (diag : IEnumerable) = let ns = "Microsoft.Quantum.Testing.FunctorGeneration" |> NonNullable<_>.New diff --git a/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs b/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs new file mode 100644 index 0000000000..416e280e0e --- /dev/null +++ b/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module Microsoft.Quantum.QsCompiler.Testing.CapabilityVerificationTests + +open Microsoft.Quantum.QsCompiler.DataTypes +open Microsoft.Quantum.QsCompiler.Diagnostics +open Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants +open Microsoft.Quantum.QsCompiler.SyntaxExtensions +open Microsoft.Quantum.QsCompiler.SyntaxTree +open Xunit + +/// Compiles the capability verification test cases using the given capability. +let private compile capabilities = + CompilerTests.Compile ("TestCases", ["CapabilityVerification.qs"], capabilities = capabilities) + +/// The unknown capability tester. +let private unknown = CompilerTests (compile RuntimeCapabilities.Unknown) + +/// The QPRGen0 capability tester. +let private gen0 = CompilerTests (compile RuntimeCapabilities.QPRGen0) + +/// The QPRGen1 capability tester. +let private gen1 = CompilerTests (compile RuntimeCapabilities.QPRGen1) + +/// The qualified name for the test case name. +let private testName name = + QsQualifiedName.New (NonNullable<_>.New "Microsoft.Quantum.Testing.CapabilityVerification", + NonNullable<_>.New name) + +/// The empty diagnostics list. +let private success = List.empty + +[] +let ``Unknown supports result comparison in functions`` () = + unknown.Verify (testName "ResultAsBool", success) + +[] +let ``QPRGen0 does not support result comparison in functions`` () = + gen0.Verify (testName "ResultAsBool", [Error ErrorCode.UnsupportedResultComparison]) + +[] +let ``QPRGen1 does not support result comparison in functions`` () = + gen1.Verify (testName "ResultAsBool", [Error ErrorCode.UnsupportedResultComparison]) diff --git a/src/QsCompiler/Tests.Compiler/GlobalVerificationTests.fs b/src/QsCompiler/Tests.Compiler/GlobalVerificationTests.fs index ed81cadd5b..4e2efe2af6 100644 --- a/src/QsCompiler/Tests.Compiler/GlobalVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/GlobalVerificationTests.fs @@ -9,11 +9,10 @@ open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTree open Xunit -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) +type GlobalVerificationTests () = + inherit CompilerTests(CompilerTests.Compile ("TestCases", ["General.qs"; "GlobalVerification.qs"; "Types.qs"; System.IO.Path.Join("LinkingTests", "Core.qs")])) 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 c907697921..71aee9cf46 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -22,11 +22,10 @@ open Microsoft.Quantum.QsCompiler.Transformations.Monomorphization.Validation open Microsoft.Quantum.QsCompiler.Transformations.SearchAndReplace open Microsoft.VisualStudio.LanguageServer.Protocol open Xunit -open Xunit.Abstractions -type LinkingTests (output:ITestOutputHelper) = - inherit CompilerTests(CompilerTests.Compile (Path.Combine ("TestCases", "LinkingTests" )) ["Core.qs"; "InvalidEntryPoints.qs"] [], output) +type LinkingTests () = + inherit CompilerTests(CompilerTests.Compile (Path.Combine ("TestCases", "LinkingTests"), ["Core.qs"; "InvalidEntryPoints.qs"])) let compilationManager = new CompilationUnitManager(new Action (fun ex -> failwith ex.Message), isExecutable = true) @@ -78,7 +77,7 @@ type LinkingTests (output:ITestOutputHelper) = compilationManager.AddOrUpdateSourceFileAsync(file) |> ignore let built = compilationManager.Build() compilationManager.TryRemoveSourceFileAsync(fileId, false) |> ignore - let tests = new CompilerTests(built, output) + let tests = CompilerTests built for callable in built.Callables.Values |> Seq.filter inFile do tests.Verify (callable.FullName, diag) diff --git a/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs b/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs index 5e08e5305f..ea8dae81f3 100644 --- a/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/LocalVerificationTests.fs @@ -10,15 +10,14 @@ open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTree open Xunit -open Xunit.Abstractions -type LocalVerificationTests (output:ITestOutputHelper) = +type LocalVerificationTests () = inherit CompilerTests( - CompilerTests.Compile "TestCases" [ + 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/CapabilityVerification.qs b/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs new file mode 100644 index 0000000000..4f407e4252 --- /dev/null +++ b/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/// Test cases for verification of execution target runtime capabilities. +namespace Microsoft.Quantum.Testing.CapabilityVerification { + function ResultAsBool(result : Result) : Bool { + return result == Zero ? false | true; + } +} diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs index eb03d7079a..2187681451 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs @@ -12,13 +12,13 @@ open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.CompilationBuilder open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics +open Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants open Microsoft.Quantum.QsCompiler.SyntaxTree open Microsoft.VisualStudio.LanguageServer.Protocol open Xunit -open Xunit.Abstractions -type CompilerTests (compilation : CompilationUnitManager.Compilation, output:ITestOutputHelper) = +type CompilerTests (compilation : CompilationUnitManager.Compilation) = let syntaxTree = let mutable compilation = compilation.BuiltCompilation @@ -90,9 +90,11 @@ type CompilerTests (compilation : CompilationUnitManager.Compilation, output:ITe if other.Any() then NotImplementedException "unknown diagnostics item to verify" |> raise - static member Compile srcFolder files references = + static member Compile (srcFolder, files, ?references, ?capabilities) = + let references = defaultArg references [] + let capabilities = defaultArg capabilities RuntimeCapabilities.Unknown let compileFiles (files : IEnumerable<_>) = - let mgr = new CompilationUnitManager(fun ex -> failwith ex.Message) + let mgr = new CompilationUnitManager((fun ex -> failwith ex.Message), capabilities = capabilities) files.ToImmutableDictionary(Path.GetFullPath >> Uri, File.ReadAllText) |> CompilationUnitManager.InitializeFileManagers |> mgr.AddOrUpdateSourceFilesAsync diff --git a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj index 94e620420f..79cf170615 100644 --- a/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj +++ b/src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj @@ -105,19 +105,22 @@ PreserveNewest - + PreserveNewest PreserveNewest - + PreserveNewest - + PreserveNewest - + + PreserveNewest + + PreserveNewest @@ -129,8 +132,8 @@ PreserveNewest - - PreserveNewest + + PreserveNewest @@ -138,6 +141,7 @@ + diff --git a/src/QsCompiler/Tests.Compiler/TypeCheckingTests.fs b/src/QsCompiler/Tests.Compiler/TypeCheckingTests.fs index cdd8592016..320d449d51 100644 --- a/src/QsCompiler/Tests.Compiler/TypeCheckingTests.fs +++ b/src/QsCompiler/Tests.Compiler/TypeCheckingTests.fs @@ -9,11 +9,10 @@ open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTree open Xunit -open Xunit.Abstractions -type TypeCheckingTests (output:ITestOutputHelper) = - inherit CompilerTests(CompilerTests.Compile "TestCases" ["General.qs"; "TypeChecking.qs"; "Types.qs"] [], output) +type TypeCheckingTests () = + inherit CompilerTests(CompilerTests.Compile ("TestCases", ["General.qs"; "TypeChecking.qs"; "Types.qs"])) member private this.Expect name (diag : IEnumerable) = let ns = "Microsoft.Quantum.Testing.TypeChecking" |> NonNullable<_>.New From ded4328f1f5bad679595a0ec1841102446677545 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 4 Jun 2020 20:18:32 -0700 Subject: [PATCH 07/20] Add more tests (but not working right now...) --- .../CapabilityVerificationTests.fs | 41 ++++++++++++------- .../TestCases/CapabilityVerification.qs | 38 +++++++++++++++++ 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs b/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs index 416e280e0e..3d2ff1032a 100644 --- a/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs @@ -28,17 +28,30 @@ let private testName name = QsQualifiedName.New (NonNullable<_>.New "Microsoft.Quantum.Testing.CapabilityVerification", NonNullable<_>.New name) -/// The empty diagnostics list. -let private success = List.empty - -[] -let ``Unknown supports result comparison in functions`` () = - unknown.Verify (testName "ResultAsBool", success) - -[] -let ``QPRGen0 does not support result comparison in functions`` () = - gen0.Verify (testName "ResultAsBool", [Error ErrorCode.UnsupportedResultComparison]) - -[] -let ``QPRGen1 does not support result comparison in functions`` () = - gen1.Verify (testName "ResultAsBool", [Error ErrorCode.UnsupportedResultComparison]) +/// Asserts that the tester allows the given test case. +let private allows (tester : CompilerTests) name = tester.Verify (testName name, List.empty) + +/// Asserts that the tester disallows the given test case. +let private restricts (tester : CompilerTests) name = + tester.Verify (testName name, [Error ErrorCode.UnsupportedResultComparison]) + +/// The names of all test cases. +let private all = + [ "ResultAsBool" + "ResultAsBoolOp" + "ResultAsBoolOpReturnIf" + "ResultAsBoolOpSetIf" + "EmptyIf" + "Reset" ] + + +let [] ``Unknown allows all Result comparison`` () = List.iter (allows unknown) all +let [] ``QPRGen0 restricts all Result comparison`` () = List.iter (restricts gen0) all + +// [] +let [] ``QPRGen1 restricts Result comparison in functions`` () = restricts gen1 "ResultAsBool" +let [] ``QPRGen1 restricts non-if Result comparison in operations`` () = restricts gen1 "ResultAsBoolOp" +let [] ``QPRGen1 restricts return from Result if`` () = restricts gen1 "ResultAsBoolOpReturnIf" +let [] ``QPRGen1 restricts mutable set from Result if`` () = restricts gen1 "ResultAsBoolOpSetIf" +let [] ``QPRGen1 allows empty Result if`` () = allows gen1 "EmptyIf" +let [] ``QPRGen1 allows operation call from Result if`` () = allows gen1 "Reset" diff --git a/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs b/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs index 4f407e4252..435dfd862c 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs @@ -3,7 +3,45 @@ /// Test cases for verification of execution target runtime capabilities. namespace Microsoft.Quantum.Testing.CapabilityVerification { + internal operation X(q : Qubit) : Unit { + body intrinsic; + } + + internal operation M(q : Qubit) : Result { + body intrinsic; + } + function ResultAsBool(result : Result) : Bool { return result == Zero ? false | true; } + + operation ResultAsBoolOp(result : Result) : Bool { + return result == Zero ? false | true; + } + + operation ResultAsBoolOpReturnIf(result : Result) : Bool { + if (result == Zero) { + return false; + } else { + return true; + } + } + + operation ResultAsBoolOpSetIf(result : Result) : Bool { + mutable bool = false; + if (result == One) { + set bool = true; + } + return bool; + } + + operation EmptyIf(result : Result) : Unit { + if (result == Zero) { } + } + + operation Reset(q : Qubit) : Unit { + if (M(q) == One) { + X(q); + } + } } From a66da5156f6be551486835d94384545eab8a2b95 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 4 Jun 2020 20:54:32 -0700 Subject: [PATCH 08/20] Fix tests --- .../Tests.Compiler/CapabilityVerificationTests.fs | 12 +++++++----- .../TestCases/CapabilityVerification.qs | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs b/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs index 3d2ff1032a..80cc2cb4c5 100644 --- a/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs @@ -48,10 +48,12 @@ let private all = let [] ``Unknown allows all Result comparison`` () = List.iter (allows unknown) all let [] ``QPRGen0 restricts all Result comparison`` () = List.iter (restricts gen0) all -// [] -let [] ``QPRGen1 restricts Result comparison in functions`` () = restricts gen1 "ResultAsBool" -let [] ``QPRGen1 restricts non-if Result comparison in operations`` () = restricts gen1 "ResultAsBoolOp" -let [] ``QPRGen1 restricts return from Result if`` () = restricts gen1 "ResultAsBoolOpReturnIf" -let [] ``QPRGen1 restricts mutable set from Result if`` () = restricts gen1 "ResultAsBoolOpSetIf" +/// The reason for skipping tests for QPRGen1 restrictions. +let [] private gen1Todo = "QPRGen1 verification is not implemented yet" + +let [] ``QPRGen1 restricts Result comparison in functions`` () = restricts gen1 "ResultAsBool" +let [] ``QPRGen1 restricts non-if Result comparison in operations`` () = restricts gen1 "ResultAsBoolOp" +let [] ``QPRGen1 restricts return from Result if`` () = restricts gen1 "ResultAsBoolOpReturnIf" +let [] ``QPRGen1 restricts mutable set from Result if`` () = restricts gen1 "ResultAsBoolOpSetIf" let [] ``QPRGen1 allows empty Result if`` () = allows gen1 "EmptyIf" let [] ``QPRGen1 allows operation call from Result if`` () = allows gen1 "Reset" diff --git a/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs b/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs index 435dfd862c..b67729256b 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs @@ -28,11 +28,11 @@ namespace Microsoft.Quantum.Testing.CapabilityVerification { } operation ResultAsBoolOpSetIf(result : Result) : Bool { - mutable bool = false; + mutable b = false; if (result == One) { - set bool = true; + set b = true; } - return bool; + return b; } operation EmptyIf(result : Result) : Unit { From cbff168489c709e84cea6920ac91f38195185092 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 5 Jun 2020 09:37:27 -0700 Subject: [PATCH 09/20] Add tests for not equals --- .../CapabilityVerificationTests.fs | 45 ++++++++++++++----- .../TestCases/CapabilityVerification.qs | 34 ++++++++++++++ 2 files changed, 69 insertions(+), 10 deletions(-) diff --git a/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs b/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs index 80cc2cb4c5..e2c5c20ca8 100644 --- a/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/CapabilityVerificationTests.fs @@ -38,22 +38,47 @@ let private restricts (tester : CompilerTests) name = /// The names of all test cases. let private all = [ "ResultAsBool" + "ResultAsBoolNeq" "ResultAsBoolOp" + "ResultAsBoolNeqOp" "ResultAsBoolOpReturnIf" + "ResultAsBoolNeqOpReturnIf" "ResultAsBoolOpSetIf" + "ResultAsBoolNeqOpSetIf" "EmptyIf" - "Reset" ] - + "EmptyIfNeq" + "Reset" + "ResetNeq" ] let [] ``Unknown allows all Result comparison`` () = List.iter (allows unknown) all let [] ``QPRGen0 restricts all Result comparison`` () = List.iter (restricts gen0) all -/// The reason for skipping tests for QPRGen1 restrictions. -let [] private gen1Todo = "QPRGen1 verification is not implemented yet" +[] +let ``QPRGen1 restricts Result comparison in functions`` () = + restricts gen1 "ResultAsBool" + restricts gen1 "ResultAsBoolNeq" + +[] +let ``QPRGen1 restricts non-if Result comparison in operations`` () = + restricts gen1 "ResultAsBoolOp" + restricts gen1 "ResultAsBoolNeqOp" + +[] +let ``QPRGen1 restricts return from Result if`` () = + restricts gen1 "ResultAsBoolOpReturnIf" + restricts gen1 "ResultAsBoolNeqOpReturnIf" + +[] +let ``QPRGen1 restricts mutable set from Result if`` () = + restricts gen1 "ResultAsBoolOpSetIf" + restricts gen1 "ResultAsBoolNeqOpSetIf" + +[] +let ``QPRGen1 allows empty Result if`` () = + allows gen1 "EmptyIf" + allows gen1 "EmptyIfNeq" -let [] ``QPRGen1 restricts Result comparison in functions`` () = restricts gen1 "ResultAsBool" -let [] ``QPRGen1 restricts non-if Result comparison in operations`` () = restricts gen1 "ResultAsBoolOp" -let [] ``QPRGen1 restricts return from Result if`` () = restricts gen1 "ResultAsBoolOpReturnIf" -let [] ``QPRGen1 restricts mutable set from Result if`` () = restricts gen1 "ResultAsBoolOpSetIf" -let [] ``QPRGen1 allows empty Result if`` () = allows gen1 "EmptyIf" -let [] ``QPRGen1 allows operation call from Result if`` () = allows gen1 "Reset" +[] +let ``QPRGen1 allows operation call from Result if`` () = + allows gen1 "Reset" + allows gen1 "ResetNeq" diff --git a/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs b/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs index b67729256b..000161c30a 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/CapabilityVerification.qs @@ -14,10 +14,18 @@ namespace Microsoft.Quantum.Testing.CapabilityVerification { function ResultAsBool(result : Result) : Bool { return result == Zero ? false | true; } + + function ResultAsBoolNeq(result : Result) : Bool { + return result != One ? false | true; + } operation ResultAsBoolOp(result : Result) : Bool { return result == Zero ? false | true; } + + function ResultAsBoolNeqOp(result : Result) : Bool { + return result != One ? false | true; + } operation ResultAsBoolOpReturnIf(result : Result) : Bool { if (result == Zero) { @@ -26,6 +34,14 @@ namespace Microsoft.Quantum.Testing.CapabilityVerification { return true; } } + + operation ResultAsBoolNeqOpReturnIf(result : Result) : Bool { + if (result != One) { + return false; + } else { + return true; + } + } operation ResultAsBoolOpSetIf(result : Result) : Bool { mutable b = false; @@ -34,14 +50,32 @@ namespace Microsoft.Quantum.Testing.CapabilityVerification { } return b; } + + operation ResultAsBoolNeqOpSetIf(result : Result) : Bool { + mutable b = false; + if (result != Zero) { + set b = true; + } + return b; + } operation EmptyIf(result : Result) : Unit { if (result == Zero) { } } + operation EmptyIfNeq(result : Result) : Unit { + if (result != Zero) { } + } + operation Reset(q : Qubit) : Unit { if (M(q) == One) { X(q); } } + + operation ResetNeq(q : Qubit) : Unit { + if (M(q) != Zero) { + X(q); + } + } } From be436923ae76a2bfb41c562728af1cc80a673bf8 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 11 Jun 2020 11:07:18 -0700 Subject: [PATCH 10/20] Use static member for Create --- .../CompilationManager/TypeChecking.cs | 2 +- .../SyntaxProcessor/ExpressionVerification.fs | 25 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index af5458042f..2eaa3319d9 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -1256,7 +1256,7 @@ QsSpecialization BuildSpecialization(QsSpecializationKind kind, ResolvedSignatur QsGeneratorDirective GetDirective(QsSpecializationKind k) => definedSpecs.TryGetValue(k, out defined) && defined.Item1.IsValue ? defined.Item1.Item : null; var requiredFunctorSupport = RequiredFunctorSupport(kind, GetDirective).ToImmutableHashSet(); - var context = CreateResolutionContext( + var context = ResolutionContext.Create( compilation.GlobalSymbols, compilation.RuntimeCapabilities, spec); implementation = BuildUserDefinedImplementation( root, spec.SourceFile, arg, requiredFunctorSupport, context, diagnostics); diff --git a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs index 167d25065e..a541c5a8bc 100644 --- a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs @@ -35,17 +35,20 @@ type ResolutionContext<'a> = ReturnType : ResolvedType /// The runtime capabilities for the compilation unit. Capabilities : RuntimeCapabilities } - -/// Creates a resolution context for the specialization. -/// Thrown if the specialization's parent does not exist. -let CreateResolutionContext (nsManager : NamespaceManager) capabilities (spec : SpecializationDeclarationHeader) = - match nsManager.TryGetCallable spec.Parent (spec.Parent.Namespace, spec.SourceFile) with - | Found declaration -> - { Symbols = SymbolTracker (nsManager, spec.SourceFile, spec.Parent) - IsInOperation = declaration.Kind = Operation - ReturnType = StripPositionInfo.Apply declaration.Signature.ReturnType - Capabilities = capabilities } - | _ -> failwith "The specialization's parent does not exist." + with + + /// + /// Creates a resolution context for the specialization. + /// + /// Thrown if the specialization's parent does not exist. + static member Create (nsManager : NamespaceManager) capabilities (spec : SpecializationDeclarationHeader) = + match nsManager.TryGetCallable spec.Parent (spec.Parent.Namespace, spec.SourceFile) with + | Found declaration -> + { Symbols = SymbolTracker<'a> (nsManager, spec.SourceFile, spec.Parent) + IsInOperation = declaration.Kind = Operation + ReturnType = StripPositionInfo.Apply declaration.Signature.ReturnType + Capabilities = capabilities } + | _ -> failwith "The specialization's parent does not exist." type private StripInferredInfoFromType () = inherit TypeTransformationBase() From a470a0f90bc743495abfa2939692ee814af7049f Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 11 Jun 2020 11:24:25 -0700 Subject: [PATCH 11/20] Move ResolutionContext to SymbolTracker.fs --- .../CompilationManager/TypeChecking.cs | 2 -- .../SyntaxProcessor/ExpressionVerification.fs | 27 ------------------ .../SyntaxProcessor/StatementVerification.fs | 3 +- .../SyntaxProcessor/SymbolTracker.fs | 28 ++++++++++++++++++- 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index 2eaa3319d9..b1a3d71e7e 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -10,13 +10,11 @@ using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.Diagnostics; using Microsoft.Quantum.QsCompiler.SymbolManagement; -using Microsoft.Quantum.QsCompiler.SymbolTracker; using Microsoft.Quantum.QsCompiler.SyntaxProcessing; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; using Microsoft.Quantum.QsCompiler.TextProcessing; using Microsoft.VisualStudio.LanguageServer.Protocol; -using static Microsoft.Quantum.QsCompiler.SyntaxProcessing.Expressions; namespace Microsoft.Quantum.QsCompiler.CompilationBuilder { diff --git a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs index a541c5a8bc..0c0b9e070f 100644 --- a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs @@ -11,8 +11,6 @@ open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants -open Microsoft.Quantum.QsCompiler.SymbolManagement -open Microsoft.Quantum.QsCompiler.SymbolTracker open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxGenerator open Microsoft.Quantum.QsCompiler.SyntaxProcessing.VerificationTools @@ -25,31 +23,6 @@ open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput // utils for verifying types in expressions -/// The context used for symbol resolution and type checking. -type ResolutionContext<'a> = - { /// The symbol tracker for the parent callable. - Symbols : SymbolTracker<'a> - /// True if the parent callable for the current scope is an operation. - IsInOperation : bool - /// The return type of the parent callable for the current scope. - ReturnType : ResolvedType - /// The runtime capabilities for the compilation unit. - Capabilities : RuntimeCapabilities } - with - - /// - /// Creates a resolution context for the specialization. - /// - /// Thrown if the specialization's parent does not exist. - static member Create (nsManager : NamespaceManager) capabilities (spec : SpecializationDeclarationHeader) = - match nsManager.TryGetCallable spec.Parent (spec.Parent.Namespace, spec.SourceFile) with - | Found declaration -> - { Symbols = SymbolTracker<'a> (nsManager, spec.SourceFile, spec.Parent) - IsInOperation = declaration.Kind = Operation - ReturnType = StripPositionInfo.Apply declaration.Signature.ReturnType - Capabilities = capabilities } - | _ -> failwith "The specialization's parent does not exist." - type private StripInferredInfoFromType () = inherit TypeTransformationBase() default this.OnCallableInformation opInfo = diff --git a/src/QsCompiler/SyntaxProcessor/StatementVerification.fs b/src/QsCompiler/SyntaxProcessor/StatementVerification.fs index 7c9089202b..3a92c0fc0f 100644 --- a/src/QsCompiler/SyntaxProcessor/StatementVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/StatementVerification.fs @@ -6,15 +6,14 @@ module Microsoft.Quantum.QsCompiler.SyntaxProcessing.Statements 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 open Microsoft.Quantum.QsCompiler.SyntaxExtensions +open Microsoft.Quantum.QsCompiler.SyntaxProcessing open Microsoft.Quantum.QsCompiler.SyntaxProcessing.Expressions open Microsoft.Quantum.QsCompiler.SyntaxProcessing.VerificationTools open Microsoft.Quantum.QsCompiler.SyntaxTokens -open Microsoft.Quantum.QsCompiler.SymbolTracker open Microsoft.Quantum.QsCompiler.SyntaxTree open Microsoft.Quantum.QsCompiler.Transformations.SearchAndReplace diff --git a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs index cc32c1593b..3dffed3899 100644 --- a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs +++ b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -namespace Microsoft.Quantum.QsCompiler.SymbolTracker +namespace Microsoft.Quantum.QsCompiler.SyntaxProcessing open System open System.Collections.Generic @@ -9,6 +9,7 @@ open System.Collections.Immutable open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics +open Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SymbolManagement open Microsoft.Quantum.QsCompiler.SyntaxProcessing.VerificationTools @@ -278,3 +279,28 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif | LocalVariable name -> decl.TypeItems.Items |> Seq.choose (namedWithName name.Value) |> Seq.toList |> function | [itemDecl] -> itemDecl.Type |> StripPositionInfo.Apply | _ -> addError (ErrorCode.UnknownItemName, [udt.Name.Value; name.Value]); InvalidType |> ResolvedType.New + +/// The context used for symbol resolution and type checking. +type ResolutionContext<'a> = + { /// The symbol tracker for the parent callable. + Symbols : SymbolTracker<'a> + /// True if the parent callable for the current scope is an operation. + IsInOperation : bool + /// The return type of the parent callable for the current scope. + ReturnType : ResolvedType + /// The runtime capabilities for the compilation unit. + Capabilities : RuntimeCapabilities } + with + + /// + /// Creates a resolution context for the specialization. + /// + /// Thrown if the specialization's parent does not exist. + static member Create (nsManager : NamespaceManager) capabilities (spec : SpecializationDeclarationHeader) = + match nsManager.TryGetCallable spec.Parent (spec.Parent.Namespace, spec.SourceFile) with + | Found declaration -> + { Symbols = SymbolTracker<'a> (nsManager, spec.SourceFile, spec.Parent) + IsInOperation = declaration.Kind = Operation + ReturnType = StripPositionInfo.Apply declaration.Signature.ReturnType + Capabilities = capabilities } + | _ -> failwith "The specialization's parent does not exist." From daf37f956a2514bd008dfff59f735c3a66cd6906 Mon Sep 17 00:00:00 2001 From: Sarah Marshall <33814365+samarsha@users.noreply.github.com> Date: Thu, 11 Jun 2020 11:33:36 -0700 Subject: [PATCH 12/20] Update doc comment to ArgumentException Co-authored-by: bettinaheim <34236215+bettinaheim@users.noreply.github.com> --- src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs index 167d25065e..b4f79eab15 100644 --- a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs @@ -37,7 +37,10 @@ type ResolutionContext<'a> = Capabilities : RuntimeCapabilities } /// Creates a resolution context for the specialization. -/// Thrown if the specialization's parent does not exist. +/// +/// Thrown if the given namespace manager does not contain all resolutions, +/// or if the specialization's parent does not exist in the given namespace manager. +/// let CreateResolutionContext (nsManager : NamespaceManager) capabilities (spec : SpecializationDeclarationHeader) = match nsManager.TryGetCallable spec.Parent (spec.Parent.Namespace, spec.SourceFile) with | Found declaration -> From 67785a2b144e5ce74a0ef8d11de41b65e4d6a4f1 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 11 Jun 2020 11:47:46 -0700 Subject: [PATCH 13/20] Replace failwith with ArgumentException --- src/QsCompiler/SyntaxProcessor/SymbolTracker.fs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs index 3dffed3899..893e5083a1 100644 --- a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs +++ b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs @@ -295,7 +295,10 @@ type ResolutionContext<'a> = /// /// Creates a resolution context for the specialization. /// - /// Thrown if the specialization's parent does not exist. + /// + /// Thrown if the given namespace manager does not contain all resolutions or if the specialization's parent does + /// not exist in the given namespace manager. + /// static member Create (nsManager : NamespaceManager) capabilities (spec : SpecializationDeclarationHeader) = match nsManager.TryGetCallable spec.Parent (spec.Parent.Namespace, spec.SourceFile) with | Found declaration -> @@ -303,4 +306,4 @@ type ResolutionContext<'a> = IsInOperation = declaration.Kind = Operation ReturnType = StripPositionInfo.Apply declaration.Signature.ReturnType Capabilities = capabilities } - | _ -> failwith "The specialization's parent does not exist." + | _ -> raise <| ArgumentException "The specialization's parent does not exist." From e8cedce18f9de8bc06a192b00b9182e8aac4bce8 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 11 Jun 2020 12:18:47 -0700 Subject: [PATCH 14/20] Mention SymbolTracker versioning in ResolutionContext --- src/QsCompiler/SyntaxProcessor/SymbolTracker.fs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs index 893e5083a1..88d0105b58 100644 --- a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs +++ b/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs @@ -294,6 +294,10 @@ type ResolutionContext<'a> = /// /// Creates a resolution context for the specialization. + /// + /// The symbol tracker in the context does not make a copy of the given namespace manager. Instead, it throws an + /// if the namespace manager has been modified (i.e. the version number of + /// the namespace manager has changed). /// /// /// Thrown if the given namespace manager does not contain all resolutions or if the specialization's parent does From 43524a02866e87ae9544144758dbb009b9522927 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 11 Jun 2020 12:42:53 -0700 Subject: [PATCH 15/20] Restore supportsEqualityComparison and use VerifyIsOneOf --- .../SyntaxProcessor/ExpressionVerification.fs | 5 +++-- src/QsCompiler/SyntaxProcessor/VerificationTools.fs | 10 ++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs index 0c0b9e070f..6669307dae 100644 --- a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs @@ -254,8 +254,9 @@ let private VerifyEqualityComparison context addError (lhsType, lhsRange) (rhsTy match baseType.Resolution with | Result when context.Capabilities = RuntimeCapabilities.QPRGen0 -> addError (ErrorCode.UnsupportedResultComparison, [context.Capabilities.ToString ()]) rhsRange - | BigInt | Bool | Double | Int | Pauli | Qubit | Result | String -> () - | _ -> addError (ErrorCode.InvalidTypeInEqualityComparison, [toString baseType]) rhsRange + | _ -> + let unsupportedError = ErrorCode.InvalidTypeInEqualityComparison, [toString baseType] + VerifyIsOneOf (fun t -> t.supportsEqualityComparison) unsupportedError addError (baseType, rhsRange) |> ignore /// Given a list of all item types and there corresponding ranges, verifies that a value array literal can be built from them. /// Adds a MissingExprInArray error with the corresponding range using addError if one of the given types is missing. diff --git a/src/QsCompiler/SyntaxProcessor/VerificationTools.fs b/src/QsCompiler/SyntaxProcessor/VerificationTools.fs index 6211d6278c..5f0719793f 100644 --- a/src/QsCompiler/SyntaxProcessor/VerificationTools.fs +++ b/src/QsCompiler/SyntaxProcessor/VerificationTools.fs @@ -74,6 +74,14 @@ type ResolvedType with | _ -> false this.Exists condition + /// If the given type supports equality comparison, + /// returns the type of an equality comparison expression as Some (which is always Bool). + /// Returns None otherwise. + member this.supportsEqualityComparison = + match this.Resolution with + | Int | BigInt | Double | Bool | Qubit | String | Result | Pauli -> Some (Bool |> ResolvedType.New) // excludes Range + | _ -> None + /// If the given type supports arithmetic operations, /// returns the type of an arithmetic expression as Some. /// Returns None otherwise. @@ -99,3 +107,5 @@ type ResolvedType with | Range -> Some (Int |> ResolvedType.New) | ArrayType bt -> Some bt | _ -> None + + From ecb5814cb8471a0115d796f989418e5a15613683 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 11 Jun 2020 12:48:01 -0700 Subject: [PATCH 16/20] Undo changes to VerificationTools.fs --- src/QsCompiler/SyntaxProcessor/VerificationTools.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/SyntaxProcessor/VerificationTools.fs b/src/QsCompiler/SyntaxProcessor/VerificationTools.fs index 5f0719793f..4407a9f73e 100644 --- a/src/QsCompiler/SyntaxProcessor/VerificationTools.fs +++ b/src/QsCompiler/SyntaxProcessor/VerificationTools.fs @@ -74,12 +74,12 @@ type ResolvedType with | _ -> false this.Exists condition - /// If the given type supports equality comparison, - /// returns the type of an equality comparison expression as Some (which is always Bool). + /// If the given type supports equality comparison, + /// returns the type of an equality comparison expression as Some (which is always Bool). /// Returns None otherwise. - member this.supportsEqualityComparison = + member this.supportsEqualityComparison = match this.Resolution with - | Int | BigInt | Double | Bool | Qubit | String | Result | Pauli -> Some (Bool |> ResolvedType.New) // excludes Range + | Int | BigInt | Double | Bool | Qubit | String | Result | Pauli -> Some (Bool |> ResolvedType.New) // excludes Range | _ -> None /// If the given type supports arithmetic operations, From 81033740d97bb6d5c8a2f03672055a7a8d7a1ad9 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 15 Jun 2020 11:53:07 -0700 Subject: [PATCH 17/20] Rename ResolutionContext to ScopeContext --- .../CompilationManager/TypeChecking.cs | 304 +++++++++--------- .../{SymbolTracker.fs => ScopeTools.fs} | 8 +- .../SyntaxProcessor/StatementVerification.fs | 4 +- .../SyntaxProcessor/SyntaxProcessor.fsproj | 2 +- 4 files changed, 159 insertions(+), 159 deletions(-) rename src/QsCompiler/SyntaxProcessor/{SymbolTracker.fs => ScopeTools.fs} (99%) diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index b1a3d71e7e..9cf4f237cf 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -464,15 +464,15 @@ internal static void ImportGlobalSymbols(this FileContentManager file, Compilati // routines for building statements /// - /// Builds the QsScope containing the given list of tree nodes, - /// calling BuildStatement for each of them, and using the given symbol tracker to verify and track all symbols. - /// The declarations the scope inherits from its parents are assumed to be the current declarations in the given symbol tacker. - /// If a required set of functors are specified, then each operation called within the built scope needs to support these functors. - /// If the set of required functors is unspecified or null, then the functors to support are determined by the parent scope. - /// Throws an ArgumentNullException if the given list of tree nodes, symbol tracker or diagnostics are null. + /// Builds the QsScope containing the given list of tree nodes, + /// calling BuildStatement for each of them, and using the given scope context to verify and track all symbols. + /// The declarations the scope inherits from its parents are assumed to be the current declarations in the given scope context. + /// If a required set of functors are specified, then each operation called within the built scope needs to support these functors. + /// If the set of required functors is unspecified or null, then the functors to support are determined by the parent scope. + /// Throws an ArgumentNullException if the given list of tree nodes, scope context or diagnostics are null. /// private static QsScope BuildScope(IReadOnlyList nodeContent, - ResolutionContext context, List diagnostics, ImmutableHashSet requiredFunctorSupport = null) + ScopeContext context, List diagnostics, ImmutableHashSet requiredFunctorSupport = null) { if (nodeContent == null) throw new ArgumentNullException(nameof(nodeContent)); if (context == null) throw new ArgumentNullException(nameof(context)); @@ -486,16 +486,16 @@ private static QsScope BuildScope(IReadOnlyList nodeConte } /// - /// Applies the given build function to the position relative to the tree root and the given resolution context + /// Applies the given build function to the position relative to the tree root and the given scope context /// to get the desired object as well as a list of diagnostics. /// Adds the generated diagnostics to the given list of diagnostics, and returns the build object. /// /// - /// Thrown if the given build function, symbolTracker, or diagnostics are null. + /// Thrown if the given build function, scope context, or diagnostics are null. /// private static T BuildStatement(FragmentTree.TreeNode node, - Func, Tuple> build, - ResolutionContext context, List diagnostics) + Func, Tuple> build, + ScopeContext context, List diagnostics) { if (build == null) throw new ArgumentNullException(nameof(build)); if (context == null) throw new ArgumentNullException(nameof(context)); @@ -510,24 +510,24 @@ private static T BuildStatement(FragmentTree.TreeNode node, /// /// If the current tree node of the given iterator is a using-block intro, - /// builds the corresponding using-statement updating the given symbol tracker in the process, + /// builds the corresponding using-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildUsingStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.UsingBlockIntro allocate) @@ -548,24 +548,24 @@ private static bool TryBuildUsingStatement(IEnumerator no /// /// If the current tree node of the given iterator is a borrowing-block intro, - /// builds the corresponding borrowing-statement updating the given symbol tracker in the process, + /// builds the corresponding borrowing-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildBorrowStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.BorrowingBlockIntro borrow) @@ -586,25 +586,25 @@ private static bool TryBuildBorrowStatement(IEnumerator n /// /// If the current tree node of the given iterator is a repeat-until-success (RUS) intro, - /// builds the corresponding RUS-statement updating the given symbol tracker in the process, + /// builds the corresponding RUS-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope, + /// Throws an ArgumentException if the given scope context does not currently contain an open scope, /// or if the repeat header is not followed by a until-success clause. /// private static bool TryBuildRepeatStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind.IsRepeatIntro) @@ -644,24 +644,24 @@ private static bool TryBuildRepeatStatement(IEnumerator n /// /// If the current tree node of the given iterator is a for-loop intro, - /// builds the corresponding for-statement updating the given symbol tracker in the process, + /// builds the corresponding for-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildForStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.ForLoopIntro forStatement) @@ -682,24 +682,24 @@ private static bool TryBuildForStatement(IEnumerator node /// /// If the current tree node of the given iterator is a while-loop intro, - /// builds the corresponding while-statement updating the given symbol tracker in the process, + /// builds the corresponding while-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildWhileStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.WhileLoopIntro whileStatement) @@ -720,24 +720,24 @@ private static bool TryBuildWhileStatement(IEnumerator no /// /// If the current tree node of the given iterator is an if-statement into, - /// builds the corresponding if-statement updating the given symbol tracker in the process, + /// builds the corresponding if-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildIfStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.IfClause ifCond) @@ -780,24 +780,24 @@ private static bool TryBuildIfStatement(IEnumerator nodes /// /// If the current tree node of the given iterator is a within-block intro, - /// builds the corresponding conjugation updating the given symbol tracker in the process, + /// builds the corresponding conjugation updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildConjugationStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); QsNullable RelativeLocation(FragmentTree.TreeNode node) => @@ -830,24 +830,24 @@ QsNullable RelativeLocation(FragmentTree.TreeNode node) => /// /// If the current tree node of the given iterator is a let-statement, - /// builds the corresponding let-statement updating the given symbol tracker in the process, + /// builds the corresponding let-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildLetStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.ImmutableBinding letStatement) @@ -864,24 +864,24 @@ private static bool TryBuildLetStatement(IEnumerator node /// /// If the current tree node of the given iterator is a mutable-statement, - /// builds the corresponding mutable-statement updating the given symbol tracker in the process, + /// builds the corresponding mutable-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildMutableStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.MutableBinding mutableStatement) @@ -898,24 +898,24 @@ private static bool TryBuildMutableStatement(IEnumerator /// /// If the current tree node of the given iterator is a set-statement, - /// builds the corresponding set-statement updating the given symbol tracker in the process, + /// builds the corresponding set-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildSetStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.ValueUpdate setStatement) @@ -932,24 +932,24 @@ private static bool TryBuildSetStatement(IEnumerator node /// /// If the current tree node of the given iterator is a fail-statement, - /// builds the corresponding fail-statement updating the given symbol tracker in the process, + /// builds the corresponding fail-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildFailStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.FailStatement failStatement) @@ -966,24 +966,24 @@ private static bool TryBuildFailStatement(IEnumerator nod /// /// If the current tree node of the given iterator is a return-statement, - /// builds the corresponding return-statement updating the given symbol tracker in the process, + /// builds the corresponding return-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildReturnStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.ReturnStatement returnStatement) @@ -1000,24 +1000,24 @@ private static bool TryBuildReturnStatement(IEnumerator n /// /// If the current tree node of the given iterator is an expression-statement, - /// builds the corresponding expression-statement updating the given symbol tracker in the process, + /// builds the corresponding expression-statement updating the given scope context in the process, /// and moves the iterator to the next node. - /// Adds the diagnostics generated during the building to the given list of diagnostics. + /// Adds the diagnostics generated during the building to the given list of diagnostics. /// Returns the built statement as out parameter, and returns true if the statement has been built. - /// Sets the out parameter to null and returns false otherwise. - /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - - /// i.e. it is set to true if either the iterator has not been moved (no statement built), - /// or if the last MoveNext() returned true, and is otherwise set to false. - /// This routine will fail if accessing the current iterator item fails. + /// Sets the out parameter to null and returns false otherwise. + /// Sets the boolean out parameter to true, if the iterator contains another node at the end of the routine - + /// i.e. it is set to true if either the iterator has not been moved (no statement built), + /// or if the last MoveNext() returned true, and is otherwise set to false. + /// This routine will fail if accessing the current iterator item fails. /// Throws an ArgumentNullException if any of the given arguments is null. - /// Throws an ArgumentException if the given symbol tracker does not currently contain an open scope. + /// Throws an ArgumentException if the given scope context does not currently contain an open scope. /// private static bool TryBuildExpressionStatement(IEnumerator nodes, - ResolutionContext context, List diagnostics, out bool proceed, out QsStatement statement) + ScopeContext context, List diagnostics, out bool proceed, out QsStatement statement) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); if (nodes.Current.Fragment.Kind is QsFragmentKind.ExpressionStatement expressionStatement) @@ -1033,20 +1033,20 @@ private static bool TryBuildExpressionStatement(IEnumerator - /// Given a sequence of tree nodes, builds the corrsponding array of Q# statements (ignoring invalid fragments) - /// using and updating the given symbol tracker and adding the generated diagnostics to the given list of diagnostics, - /// provided each statement consists of a suitable statement header followed by the required continuation(s), if any. - /// Throws an ArgumentException if this is not the case, - /// or if the given symbol tracker does not currently contain an open scope. + /// Given a sequence of tree nodes, builds the corrsponding array of Q# statements (ignoring invalid fragments) + /// using and updating the given scope context and adding the generated diagnostics to the given list of diagnostics, + /// provided each statement consists of a suitable statement header followed by the required continuation(s), if any. + /// Throws an ArgumentException if this is not the case, + /// or if the given scope context does not currently contain an open scope. /// Throws an ArgumentNullException if any of the given arguments is null, /// or if any of the fragments contained in the given nodes is null. /// private static ImmutableArray BuildStatements( - IEnumerator nodes, ResolutionContext context, List diagnostics) + IEnumerator nodes, ScopeContext context, List diagnostics) { if (nodes == null) throw new ArgumentNullException(nameof(nodes)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid symbol tracker state - statements may only occur within a scope"); + if (context.Symbols.AllScopesClosed) throw new ArgumentException("invalid scope context state - statements may only occur within a scope"); if (diagnostics == null) throw new ArgumentException(nameof(diagnostics)); var proceed = nodes.MoveNext(); @@ -1106,19 +1106,19 @@ private static ImmutableArray BuildStatements( /// /// Builds the user defined implementation based on the (children of the) given specialization root. /// The implementation takes the given argument tuple as argument and needs to support auto-generation for the specified set of functors. - /// Uses the given SymbolTracker to resolve the symbols used within the implementation, and generates suitable diagnostics in the process. + /// Uses the given scope context to resolve the symbols used within the implementation, and generates suitable diagnostics in the process. /// If necessary, generates suitable diagnostics for functor arguments (only!), which are discriminated by the missing position information /// for argument variables defined in the callable declaration). /// If the expected return type for the specialization is not Unit, verifies that all paths return a value or fail, generating suitable diagnostics. /// Adds the generated diagnostics to the given list of diagnostics. - /// Throws an ArgumentNullException if the given argument, the symbol tracker, or diagnostics are null. + /// Throws an ArgumentNullException if the given argument, the scope context, or diagnostics are null. /// private static SpecializationImplementation BuildUserDefinedImplementation( FragmentTree.TreeNode root, NonNullable sourceFile, QsTuple> argTuple, ImmutableHashSet requiredFunctorSupport, - ResolutionContext context, + ScopeContext context, List diagnostics) { if (argTuple == null) throw new ArgumentNullException(nameof(argTuple)); @@ -1254,7 +1254,7 @@ QsSpecialization BuildSpecialization(QsSpecializationKind kind, ResolvedSignatur QsGeneratorDirective GetDirective(QsSpecializationKind k) => definedSpecs.TryGetValue(k, out defined) && defined.Item1.IsValue ? defined.Item1.Item : null; var requiredFunctorSupport = RequiredFunctorSupport(kind, GetDirective).ToImmutableHashSet(); - var context = ResolutionContext.Create( + var context = ScopeContext.Create( compilation.GlobalSymbols, compilation.RuntimeCapabilities, spec); implementation = BuildUserDefinedImplementation( root, spec.SourceFile, arg, requiredFunctorSupport, context, diagnostics); diff --git a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs b/src/QsCompiler/SyntaxProcessor/ScopeTools.fs similarity index 99% rename from src/QsCompiler/SyntaxProcessor/SymbolTracker.fs rename to src/QsCompiler/SyntaxProcessor/ScopeTools.fs index 88d0105b58..eafb735573 100644 --- a/src/QsCompiler/SyntaxProcessor/SymbolTracker.fs +++ b/src/QsCompiler/SyntaxProcessor/ScopeTools.fs @@ -280,8 +280,8 @@ type SymbolTracker<'P>(globals : NamespaceManager, sourceFile, parent : QsQualif | [itemDecl] -> itemDecl.Type |> StripPositionInfo.Apply | _ -> addError (ErrorCode.UnknownItemName, [udt.Name.Value; name.Value]); InvalidType |> ResolvedType.New -/// The context used for symbol resolution and type checking. -type ResolutionContext<'a> = +/// The context used for symbol resolution and type checking within the scope of a callable. +type ScopeContext<'a> = { /// The symbol tracker for the parent callable. Symbols : SymbolTracker<'a> /// True if the parent callable for the current scope is an operation. @@ -293,7 +293,7 @@ type ResolutionContext<'a> = with /// - /// Creates a resolution context for the specialization. + /// Creates a scope context for the specialization. /// /// The symbol tracker in the context does not make a copy of the given namespace manager. Instead, it throws an /// if the namespace manager has been modified (i.e. the version number of @@ -310,4 +310,4 @@ type ResolutionContext<'a> = IsInOperation = declaration.Kind = Operation ReturnType = StripPositionInfo.Apply declaration.Signature.ReturnType Capabilities = capabilities } - | _ -> raise <| ArgumentException "The specialization's parent does not exist." + | _ -> raise <| ArgumentException "The specialization's parent callable does not exist." diff --git a/src/QsCompiler/SyntaxProcessor/StatementVerification.fs b/src/QsCompiler/SyntaxProcessor/StatementVerification.fs index 3a92c0fc0f..569e285e2e 100644 --- a/src/QsCompiler/SyntaxProcessor/StatementVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/StatementVerification.fs @@ -75,13 +75,13 @@ let NewFailStatement comments location context expr = verifiedExpr |> QsFailStatement |> asStatement comments location LocalDeclarations.Empty, Array.concat [diagnostics; autoGenErrs] /// Resolves and verifies the given Q# expression using the resolution context. -/// Verifies that the type of the resolved expression is indeed compatible with the expected return type associated with the symbol tracker. +/// Verifies that the type of the resolved expression is indeed compatible with the expected return type of the parent callable. /// Uses VerifyAssignment to verify that this is indeed the case. /// Builds the corresponding Q# return-statement at the given location, /// and returns it along with an array of diagnostics generated during resolution and verification. /// Errors due to the statement not satisfying the necessary conditions for the required auto-generation of specializations /// (specified by the given SymbolTracker) are also included in the returned diagnostics. -let NewReturnStatement comments (location : QsLocation) (context : ResolutionContext<_>) expr = +let NewReturnStatement comments (location : QsLocation) (context : ScopeContext<_>) expr = let verifyIsReturnType = VerifyAssignment context.ReturnType context.Symbols.Parent ErrorCode.TypeMismatchInReturn let verifiedExpr, _, diagnostics = VerifyWith verifyIsReturnType context expr let autoGenErrs = diff --git a/src/QsCompiler/SyntaxProcessor/SyntaxProcessor.fsproj b/src/QsCompiler/SyntaxProcessor/SyntaxProcessor.fsproj index 8dbffc6716..ee8e14a0ae 100644 --- a/src/QsCompiler/SyntaxProcessor/SyntaxProcessor.fsproj +++ b/src/QsCompiler/SyntaxProcessor/SyntaxProcessor.fsproj @@ -13,7 +13,7 @@ DelaySign.fs - + From 5b066ea45df67a6d7d5a9f2ac7b7a9b340b36ca8 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 15 Jun 2020 19:18:51 -0700 Subject: [PATCH 18/20] Include execution target in error message --- .../CompilationManager/CompilationUnit.cs | 10 +++- .../CompilationUnitManager.cs | 17 ++++-- .../CompilationManager/ProjectManager.cs | 54 ++++++++++++++----- .../CompilationManager/TypeChecking.cs | 5 +- src/QsCompiler/Compiler/CompilationLoader.cs | 6 ++- src/QsCompiler/DataStructures/DataTypes.fs | 11 ++-- src/QsCompiler/DataStructures/Diagnostics.fs | 6 ++- src/QsCompiler/LanguageServer/EditorState.cs | 12 ++++- .../SyntaxProcessor/ExpressionVerification.fs | 2 +- src/QsCompiler/SyntaxProcessor/ScopeTools.fs | 12 +++-- 10 files changed, 106 insertions(+), 29 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 31c3fbe7b5..3827ec9696 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -229,6 +229,7 @@ public class CompilationUnit : IReaderWriterLock, IDisposable internal readonly RuntimeCapabilities RuntimeCapabilities; internal readonly bool IsExecutable; + internal readonly string ExecutionTarget; public void Dispose() { this.SyncRoot.Dispose(); } @@ -238,8 +239,12 @@ public void Dispose() /// with the given sequence of locks registered as dependent locks if the sequence is not null. /// Throws an ArgumentNullException if any of the given locks is. /// - internal CompilationUnit(RuntimeCapabilities capabilities, bool isExecutable, - References externals = null, IEnumerable dependentLocks = null) + internal CompilationUnit( + RuntimeCapabilities capabilities, + bool isExecutable, + string executionTarget, + References externals = null, + IEnumerable dependentLocks = null) { this.SyncRoot = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); this.DependentLocks = dependentLocks == null @@ -249,6 +254,7 @@ internal CompilationUnit(RuntimeCapabilities capabilities, bool isExecutable, this.RuntimeCapabilities = capabilities; this.IsExecutable = isExecutable; + this.ExecutionTarget = executionTarget; this.CompiledCallables = new Dictionary(); this.CompiledTypes = new Dictionary(); diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 5bdfcd1899..4fcbacf3f8 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -64,11 +64,15 @@ public class CompilationUnitManager : IDisposable /// that action is called whenever diagnostics within a file have changed and are ready for publishing. /// public CompilationUnitManager( - Action exceptionLogger = null, Action publishDiagnostics = null, bool syntaxCheckOnly = false, - AssemblyConstants.RuntimeCapabilities capabilities = AssemblyConstants.RuntimeCapabilities.Unknown, bool isExecutable = false) + Action exceptionLogger = null, + Action publishDiagnostics = null, + bool syntaxCheckOnly = false, + AssemblyConstants.RuntimeCapabilities capabilities = AssemblyConstants.RuntimeCapabilities.Unknown, + bool isExecutable = false, + string executionTarget = null) { this.EnableVerification = !syntaxCheckOnly; - this.CompilationUnit = new CompilationUnit(capabilities, isExecutable); + this.CompilationUnit = new CompilationUnit(capabilities, isExecutable, executionTarget); this.FileContentManagers = new ConcurrentDictionary, FileContentManager>(); this.ChangedFiles = new ManagedHashSet>(new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion)); this.PublishDiagnostics = publishDiagnostics ?? (_ => { }); @@ -440,7 +444,12 @@ private Task SpawnGlobalTypeCheckingAsync(bool runSynchronously = false) // work with a separate compilation unit instance such that processing of all further edits can go on in parallel var sourceFiles = this.FileContentManagers.Values.OrderBy(m => m.FileName); this.ChangedFiles.RemoveAll(f => sourceFiles.Any(m => m.FileName.Value == f.Value)); - var compilation = new CompilationUnit(this.CompilationUnit.RuntimeCapabilities, this.CompilationUnit.IsExecutable, this.CompilationUnit.Externals, sourceFiles.Select(file => file.SyncRoot)); + var compilation = new CompilationUnit( + this.CompilationUnit.RuntimeCapabilities, + this.CompilationUnit.IsExecutable, + this.CompilationUnit.ExecutionTarget, + this.CompilationUnit.Externals, + sourceFiles.Select(file => file.SyncRoot)); var content = compilation.UpdateGlobalSymbolsFor(sourceFiles); foreach (var file in sourceFiles) this.PublishDiagnostics(file.Diagnostics()); diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index 564e489e63..67362f1ea0 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -23,17 +23,22 @@ internal class ProjectProperties public readonly string OutputPath; public readonly AssemblyConstants.RuntimeCapabilities RuntimeCapabilities; public readonly bool IsExecutable; + public readonly string ExecutionTarget; public readonly bool ExposeReferencesViaTestNames; - internal static ProjectProperties Default => - new ProjectProperties("Latest", "", AssemblyConstants.RuntimeCapabilities.Unknown, false, false); - - public ProjectProperties(string version, string outputPath, AssemblyConstants.RuntimeCapabilities runtimeCapabilities, bool isExecutable, bool loadTestNames) + public ProjectProperties( + string version, + string outputPath, + AssemblyConstants.RuntimeCapabilities runtimeCapabilities, + bool isExecutable, + string executionTarget, + bool loadTestNames) { this.Version = version ?? ""; this.OutputPath = outputPath ?? throw new ArgumentNullException(nameof(outputPath)); this.RuntimeCapabilities = runtimeCapabilities; this.IsExecutable = isExecutable; + this.ExecutionTarget = executionTarget; this.ExposeReferencesViaTestNames = loadTestNames; } } @@ -47,13 +52,34 @@ public class ProjectInformation public readonly ImmutableArray ProjectReferences; public readonly ImmutableArray References; - internal static ProjectInformation Empty(string version, string outputPath, AssemblyConstants.RuntimeCapabilities runtimeCapabilities) => - new ProjectInformation(version, outputPath, runtimeCapabilities, false, false, Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()); - - public ProjectInformation(string version, string outputPath, AssemblyConstants.RuntimeCapabilities runtimeCapabilities, bool isExecutable, bool loadTestNames, - IEnumerable sourceFiles, IEnumerable projectReferences, IEnumerable references) + internal static ProjectInformation Empty( + string version, + string outputPath, + AssemblyConstants.RuntimeCapabilities runtimeCapabilities) => + new ProjectInformation( + version, + outputPath, + runtimeCapabilities, + false, + null, + false, + Enumerable.Empty(), + Enumerable.Empty(), + Enumerable.Empty()); + + public ProjectInformation( + string version, + string outputPath, + AssemblyConstants.RuntimeCapabilities runtimeCapabilities, + bool isExecutable, + string executionTarget, + bool loadTestNames, + IEnumerable sourceFiles, + IEnumerable projectReferences, + IEnumerable references) { - this.Properties = new ProjectProperties(version, outputPath, runtimeCapabilities, isExecutable, loadTestNames); + this.Properties = new ProjectProperties( + version, outputPath, runtimeCapabilities, isExecutable, executionTarget, loadTestNames); this.SourceFiles = sourceFiles?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(sourceFiles)); this.ProjectReferences = projectReferences?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(projectReferences)); this.References = references?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(references)); @@ -146,8 +172,12 @@ internal Project(Uri projectFile, ProjectInformation projectInfo, // but we don't do any semantic verification, and we don't publish diagnostics for them. this.Processing = new ProcessingQueue(onException); this.Manager = new CompilationUnitManager( - onException, ignore ? null : publishDiagnostics, syntaxCheckOnly: ignore, - this.Properties.RuntimeCapabilities, this.Properties.IsExecutable); + onException, + ignore ? null : publishDiagnostics, + syntaxCheckOnly: ignore, + this.Properties.RuntimeCapabilities, + this.Properties.IsExecutable, + this.Properties.ExecutionTarget); this.Log = log ?? ((msg, severity) => Console.WriteLine($"{severity}: {msg}")); this.LoadedSourceFiles = ImmutableHashSet.Empty; diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index 9cf4f237cf..41e0a2c8f5 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -1255,7 +1255,10 @@ QsSpecialization BuildSpecialization(QsSpecializationKind kind, ResolvedSignatur QsGeneratorDirective GetDirective(QsSpecializationKind k) => definedSpecs.TryGetValue(k, out defined) && defined.Item1.IsValue ? defined.Item1.Item : null; var requiredFunctorSupport = RequiredFunctorSupport(kind, GetDirective).ToImmutableHashSet(); var context = ScopeContext.Create( - compilation.GlobalSymbols, compilation.RuntimeCapabilities, spec); + compilation.GlobalSymbols, + compilation.RuntimeCapabilities, + QsNullable.FromReference(compilation.ExecutionTarget), + spec); implementation = BuildUserDefinedImplementation( root, spec.SourceFile, arg, requiredFunctorSupport, context, diagnostics); QsCompilerError.Verify(context.Symbols.AllScopesClosed, "all scopes should be closed"); diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 8b39c05fe6..8cb3a60c58 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -441,7 +441,11 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference RaiseCompilationTaskStart("OverallCompilation", "Build"); this.CompilationStatus.Validation = Status.Succeeded; var files = CompilationUnitManager.InitializeFileManagers(sourceFiles, null, this.OnCompilerException); // do *not* live track (i.e. use publishing) here! - var compilationManager = new CompilationUnitManager(this.OnCompilerException, capabilities: this.Config.RuntimeCapabilities, isExecutable: this.Config.IsExecutable); + var compilationManager = new CompilationUnitManager( + this.OnCompilerException, + capabilities: this.Config.RuntimeCapabilities, + isExecutable: this.Config.IsExecutable, + executionTarget: this.Config.AssemblyConstants.GetValueOrDefault(ExecutionTarget)); compilationManager.UpdateReferencesAsync(references); compilationManager.AddOrUpdateSourceFilesAsync(files); this.VerifiedCompilation = compilationManager.Build(); diff --git a/src/QsCompiler/DataStructures/DataTypes.fs b/src/QsCompiler/DataStructures/DataTypes.fs index 1bbe527321..d89625e1bb 100644 --- a/src/QsCompiler/DataStructures/DataTypes.fs +++ b/src/QsCompiler/DataStructures/DataTypes.fs @@ -53,6 +53,14 @@ type QsNullable<'T> = // to avoid having to include the F# core in the C# part o | Some v -> Value v | None -> Null +/// Factories for . +module QsNullable = + /// Creates a from a nullable reference type. + let FromReference reference = + match reference with + | null -> Null + | value -> Value value + [] type NonNullable<'T> = private Item of 'T with @@ -116,6 +124,3 @@ type IReaderWriterLock = abstract member ExitReadLock : unit -> unit abstract member EnterWriteLock : unit -> unit abstract member ExitWriteLock : unit -> unit - - - diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index c8f98d8d06..f8bf4d8d2a 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -545,7 +545,11 @@ type DiagnosticItem = | ErrorCode.ExpectingIterableExpr -> "The type {0} does not support iteration. Expecting an expression of array type or of type Range." | ErrorCode.ExpectingCallableExpr -> "The type of the expression must be a function or operation type. The given expression is of type {0}." | ErrorCode.UnknownIdentifier -> "No identifier with the name \"{0}\" exists." - | ErrorCode.UnsupportedResultComparison -> "Measurement results cannot be compared because the execution target supports only {0} runtime capabilities." + | ErrorCode.UnsupportedResultComparison -> + // TODO: When the names of the runtime capabilities are finalized, they can be included in the error + // message. + "The execution target {0} does not support comparing measurement results. " + + "Choose an execution target with additional capabilities or use a controlled operation instead." | ErrorCode.CallableRedefinition -> "Invalid callable declaration. A function or operation with the name \"{0}\" already exists." | ErrorCode.CallableOverlapWithTypeConstructor -> "Invalid callable declaration. A type constructor with the name \"{0}\" already exists." diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index 1297609b14..029923ce6d 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -105,6 +105,7 @@ internal bool QsProjectLoader(Uri projectFile, out ProjectInformation info) var targetFile = projectInstance.GetPropertyValue("TargetFileName"); var outputPath = Path.Combine(projectInstance.Directory, outputDir, targetFile); + var executionTarget = projectInstance.GetProperty("ResolvedQsharpExecutionTarget")?.EvaluatedValue; var resRuntimeCapability = projectInstance.GetPropertyValue("ResolvedRuntimeCapabilities"); var runtimeCapabilities = Enum.TryParse(resRuntimeCapability, out AssemblyConstants.RuntimeCapabilities capability) ? capability @@ -125,7 +126,16 @@ internal bool QsProjectLoader(Uri projectFile, out ProjectInformation info) telemetryMeas["csharpfiles"] = csharpFiles.Count(); telemetryProps["defaultSimulator"] = defaultSimulator; this.SendTelemetry("project-load", telemetryProps, telemetryMeas); // does not send anything unless the corresponding flag is defined upon compilation - info = new ProjectInformation(version, outputPath, runtimeCapabilities, isExecutable, loadTestNames, sourceFiles, projectReferences, references); + info = new ProjectInformation( + version, + outputPath, + runtimeCapabilities, + isExecutable, + executionTarget, + loadTestNames, + sourceFiles, + projectReferences, + references); return true; } diff --git a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs index 6669307dae..f2a1508c59 100644 --- a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs @@ -253,7 +253,7 @@ let private VerifyEqualityComparison context addError (lhsType, lhsRange) (rhsTy let baseType = CommonBaseType addError argumentError context.Symbols.Parent (lhsType, lhsRange) (rhsType, rhsRange) match baseType.Resolution with | Result when context.Capabilities = RuntimeCapabilities.QPRGen0 -> - addError (ErrorCode.UnsupportedResultComparison, [context.Capabilities.ToString ()]) rhsRange + addError (ErrorCode.UnsupportedResultComparison, [context.ExecutionTarget.ValueOr "Unknown"]) rhsRange | _ -> let unsupportedError = ErrorCode.InvalidTypeInEqualityComparison, [toString baseType] VerifyIsOneOf (fun t -> t.supportsEqualityComparison) unsupportedError addError (baseType, rhsRange) |> ignore diff --git a/src/QsCompiler/SyntaxProcessor/ScopeTools.fs b/src/QsCompiler/SyntaxProcessor/ScopeTools.fs index eafb735573..885e1a205e 100644 --- a/src/QsCompiler/SyntaxProcessor/ScopeTools.fs +++ b/src/QsCompiler/SyntaxProcessor/ScopeTools.fs @@ -289,7 +289,9 @@ type ScopeContext<'a> = /// The return type of the parent callable for the current scope. ReturnType : ResolvedType /// The runtime capabilities for the compilation unit. - Capabilities : RuntimeCapabilities } + Capabilities : RuntimeCapabilities + /// The name of the execution target for the compilation unit. + ExecutionTarget : QsNullable } with /// @@ -303,11 +305,15 @@ type ScopeContext<'a> = /// Thrown if the given namespace manager does not contain all resolutions or if the specialization's parent does /// not exist in the given namespace manager. /// - static member Create (nsManager : NamespaceManager) capabilities (spec : SpecializationDeclarationHeader) = + static member Create (nsManager : NamespaceManager) + capabilities + executionTarget + (spec : SpecializationDeclarationHeader) = match nsManager.TryGetCallable spec.Parent (spec.Parent.Namespace, spec.SourceFile) with | Found declaration -> { Symbols = SymbolTracker<'a> (nsManager, spec.SourceFile, spec.Parent) IsInOperation = declaration.Kind = Operation ReturnType = StripPositionInfo.Apply declaration.Signature.ReturnType - Capabilities = capabilities } + Capabilities = capabilities + ExecutionTarget = executionTarget } | _ -> raise <| ArgumentException "The specialization's parent callable does not exist." From 907dda891b43e80ff2df3f99e3613c8b5aca2ede Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Mon, 15 Jun 2020 20:53:54 -0700 Subject: [PATCH 19/20] Make ScopeContext.ExecutionTarget non-nullable --- src/QsCompiler/CompilationManager/CompilationUnit.cs | 4 ++-- .../CompilationManager/CompilationUnitManager.cs | 2 +- src/QsCompiler/CompilationManager/ProjectManager.cs | 8 ++++---- src/QsCompiler/CompilationManager/TypeChecking.cs | 2 +- src/QsCompiler/Compiler/CompilationLoader.cs | 5 ++++- src/QsCompiler/DataStructures/DataTypes.fs | 8 -------- src/QsCompiler/LanguageServer/EditorState.cs | 5 +++-- src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs | 2 +- src/QsCompiler/SyntaxProcessor/ScopeTools.fs | 2 +- 9 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnit.cs b/src/QsCompiler/CompilationManager/CompilationUnit.cs index 3827ec9696..534a5ee812 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnit.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnit.cs @@ -229,7 +229,7 @@ public class CompilationUnit : IReaderWriterLock, IDisposable internal readonly RuntimeCapabilities RuntimeCapabilities; internal readonly bool IsExecutable; - internal readonly string ExecutionTarget; + internal readonly NonNullable ExecutionTarget; public void Dispose() { this.SyncRoot.Dispose(); } @@ -242,7 +242,7 @@ public void Dispose() internal CompilationUnit( RuntimeCapabilities capabilities, bool isExecutable, - string executionTarget, + NonNullable executionTarget, References externals = null, IEnumerable dependentLocks = null) { diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 4fcbacf3f8..7690b200bc 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -69,7 +69,7 @@ public CompilationUnitManager( bool syntaxCheckOnly = false, AssemblyConstants.RuntimeCapabilities capabilities = AssemblyConstants.RuntimeCapabilities.Unknown, bool isExecutable = false, - string executionTarget = null) + NonNullable executionTarget = default) { this.EnableVerification = !syntaxCheckOnly; this.CompilationUnit = new CompilationUnit(capabilities, isExecutable, executionTarget); diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index 67362f1ea0..e5855fdffe 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -23,7 +23,7 @@ internal class ProjectProperties public readonly string OutputPath; public readonly AssemblyConstants.RuntimeCapabilities RuntimeCapabilities; public readonly bool IsExecutable; - public readonly string ExecutionTarget; + public readonly NonNullable ExecutionTarget; public readonly bool ExposeReferencesViaTestNames; public ProjectProperties( @@ -31,7 +31,7 @@ public ProjectProperties( string outputPath, AssemblyConstants.RuntimeCapabilities runtimeCapabilities, bool isExecutable, - string executionTarget, + NonNullable executionTarget, bool loadTestNames) { this.Version = version ?? ""; @@ -61,7 +61,7 @@ internal static ProjectInformation Empty( outputPath, runtimeCapabilities, false, - null, + NonNullable.New("Unspecified"), false, Enumerable.Empty(), Enumerable.Empty(), @@ -72,7 +72,7 @@ public ProjectInformation( string outputPath, AssemblyConstants.RuntimeCapabilities runtimeCapabilities, bool isExecutable, - string executionTarget, + NonNullable executionTarget, bool loadTestNames, IEnumerable sourceFiles, IEnumerable projectReferences, diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index 41e0a2c8f5..9caeeaf924 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -1257,7 +1257,7 @@ QsSpecialization BuildSpecialization(QsSpecializationKind kind, ResolvedSignatur var context = ScopeContext.Create( compilation.GlobalSymbols, compilation.RuntimeCapabilities, - QsNullable.FromReference(compilation.ExecutionTarget), + compilation.ExecutionTarget, spec); implementation = BuildUserDefinedImplementation( root, spec.SourceFile, arg, requiredFunctorSupport, context, diagnostics); diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 8cb3a60c58..f5002a3c03 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -441,11 +441,14 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference RaiseCompilationTaskStart("OverallCompilation", "Build"); this.CompilationStatus.Validation = Status.Succeeded; var files = CompilationUnitManager.InitializeFileManagers(sourceFiles, null, this.OnCompilerException); // do *not* live track (i.e. use publishing) here! + var executionTarget = this.Config.AssemblyConstants.GetValueOrDefault(ExecutionTarget); var compilationManager = new CompilationUnitManager( this.OnCompilerException, capabilities: this.Config.RuntimeCapabilities, isExecutable: this.Config.IsExecutable, - executionTarget: this.Config.AssemblyConstants.GetValueOrDefault(ExecutionTarget)); + executionTarget: NonNullable.New(string.IsNullOrWhiteSpace(executionTarget) + ? "Unspecified" + : executionTarget)); compilationManager.UpdateReferencesAsync(references); compilationManager.AddOrUpdateSourceFilesAsync(files); this.VerifiedCompilation = compilationManager.Build(); diff --git a/src/QsCompiler/DataStructures/DataTypes.fs b/src/QsCompiler/DataStructures/DataTypes.fs index d89625e1bb..fee665728f 100644 --- a/src/QsCompiler/DataStructures/DataTypes.fs +++ b/src/QsCompiler/DataStructures/DataTypes.fs @@ -53,14 +53,6 @@ type QsNullable<'T> = // to avoid having to include the F# core in the C# part o | Some v -> Value v | None -> Null -/// Factories for . -module QsNullable = - /// Creates a from a nullable reference type. - let FromReference reference = - match reference with - | null -> Null - | value -> Value value - [] type NonNullable<'T> = private Item of 'T with diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index 029923ce6d..7c8e5a1fb2 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.Build.Execution; using Microsoft.Quantum.QsCompiler.CompilationBuilder; +using Microsoft.Quantum.QsCompiler.DataTypes; using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -105,7 +106,7 @@ internal bool QsProjectLoader(Uri projectFile, out ProjectInformation info) var targetFile = projectInstance.GetPropertyValue("TargetFileName"); var outputPath = Path.Combine(projectInstance.Directory, outputDir, targetFile); - var executionTarget = projectInstance.GetProperty("ResolvedQsharpExecutionTarget")?.EvaluatedValue; + var executionTarget = projectInstance.GetPropertyValue("ResolvedQsharpExecutionTarget"); var resRuntimeCapability = projectInstance.GetPropertyValue("ResolvedRuntimeCapabilities"); var runtimeCapabilities = Enum.TryParse(resRuntimeCapability, out AssemblyConstants.RuntimeCapabilities capability) ? capability @@ -131,7 +132,7 @@ internal bool QsProjectLoader(Uri projectFile, out ProjectInformation info) outputPath, runtimeCapabilities, isExecutable, - executionTarget, + NonNullable.New(string.IsNullOrWhiteSpace(executionTarget) ? "Unspecified" : executionTarget), loadTestNames, sourceFiles, projectReferences, diff --git a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs index f2a1508c59..4c62ae08f6 100644 --- a/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs +++ b/src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs @@ -253,7 +253,7 @@ let private VerifyEqualityComparison context addError (lhsType, lhsRange) (rhsTy let baseType = CommonBaseType addError argumentError context.Symbols.Parent (lhsType, lhsRange) (rhsType, rhsRange) match baseType.Resolution with | Result when context.Capabilities = RuntimeCapabilities.QPRGen0 -> - addError (ErrorCode.UnsupportedResultComparison, [context.ExecutionTarget.ValueOr "Unknown"]) rhsRange + addError (ErrorCode.UnsupportedResultComparison, [context.ExecutionTarget.Value]) rhsRange | _ -> let unsupportedError = ErrorCode.InvalidTypeInEqualityComparison, [toString baseType] VerifyIsOneOf (fun t -> t.supportsEqualityComparison) unsupportedError addError (baseType, rhsRange) |> ignore diff --git a/src/QsCompiler/SyntaxProcessor/ScopeTools.fs b/src/QsCompiler/SyntaxProcessor/ScopeTools.fs index 885e1a205e..c838e10fa4 100644 --- a/src/QsCompiler/SyntaxProcessor/ScopeTools.fs +++ b/src/QsCompiler/SyntaxProcessor/ScopeTools.fs @@ -291,7 +291,7 @@ type ScopeContext<'a> = /// The runtime capabilities for the compilation unit. Capabilities : RuntimeCapabilities /// The name of the execution target for the compilation unit. - ExecutionTarget : QsNullable } + ExecutionTarget : NonNullable } with /// From 576afe1841dfffef0c0d67da7bfc7ebe45f74496 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Fri, 19 Jun 2020 14:00:59 -0700 Subject: [PATCH 20/20] Update error message --- src/QsCompiler/DataStructures/Diagnostics.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index f8bf4d8d2a..98770b5f7f 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -549,7 +549,7 @@ type DiagnosticItem = // TODO: When the names of the runtime capabilities are finalized, they can be included in the error // message. "The execution target {0} does not support comparing measurement results. " + - "Choose an execution target with additional capabilities or use a controlled operation instead." + "Choose an execution target with additional capabilities or avoid result comparisons." | ErrorCode.CallableRedefinition -> "Invalid callable declaration. A function or operation with the name \"{0}\" already exists." | ErrorCode.CallableOverlapWithTypeConstructor -> "Invalid callable declaration. A type constructor with the name \"{0}\" already exists."