Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/QsCompiler/CompilationManager/CompilationUnit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,16 @@ internal Headers(string source,
?? ImmutableArray<(SpecializationDeclarationHeader, SpecializationImplementation)>.Empty;
}

internal Headers(NonNullable<string> source, IEnumerable<QsNamespace> syntaxTree) : this (
/// <summary>
/// Initializes a set of reference headers based on the given syntax tree loaded from the specified source.
/// The source is expected to be the path to the dll from which the syntax has been loaded.
/// Returns an empty set of headers if the given syntax tree is null.
/// </summary>
public Headers(NonNullable<string> source, IEnumerable<QsNamespace> syntaxTree) : this (
source.Value,
syntaxTree.Callables().Where(c => c.SourceFile.Value.EndsWith(".qs")).Select(CallableDeclarationHeader.New),
syntaxTree.Specializations().Where(c => c.SourceFile.Value.EndsWith(".qs")).Select(s => (SpecializationDeclarationHeader.New(s), s.Implementation)),
syntaxTree.Types().Where(c => c.SourceFile.Value.EndsWith(".qs")).Select(TypeDeclarationHeader.New))
syntaxTree?.Callables().Where(c => c.SourceFile.Value.EndsWith(".qs")).Select(CallableDeclarationHeader.New),
syntaxTree?.Specializations().Where(c => c.SourceFile.Value.EndsWith(".qs")).Select(s => (SpecializationDeclarationHeader.New(s), s.Implementation)),
syntaxTree?.Types().Where(c => c.SourceFile.Value.EndsWith(".qs")).Select(TypeDeclarationHeader.New))
{ }

internal Headers(NonNullable<string> source, IEnumerable<(string, string)> attributes) : this(
Expand Down
4 changes: 2 additions & 2 deletions src/QsCompiler/CompilationManager/DiagnosticTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static class DiagnosticTools
/// Returns the line and character of the given position as tuple without verifying them.
/// Throws an ArgumentNullException if the given position is null.
/// </summary>
internal static Tuple<int, int> AsTuple(Position position) =>
public static Tuple<int, int> AsTuple(Position position) =>
position != null
? new Tuple<int, int>(position.Line, position.Character)
: throw new ArgumentNullException(nameof(position));
Expand All @@ -28,7 +28,7 @@ internal static Tuple<int, int> AsTuple(Position position) =>
/// Returns a Position with the line and character given as tuple (inverse function for AsTuple).
/// Throws an ArgumentNullException if the given tuple is null.
/// </summary>
internal static Position AsPosition(Tuple<int, int> position) =>
public static Position AsPosition(Tuple<int, int> position) =>
position != null
? new Position(position.Item1, position.Item2)
: throw new ArgumentNullException(nameof(position));
Expand Down
23 changes: 23 additions & 0 deletions src/QsCompiler/Compiler/PluginInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.Quantum.QsCompiler.CompilationBuilder;
using Microsoft.Quantum.QsCompiler.SyntaxTree;
using VS = Microsoft.VisualStudio.LanguageServer.Protocol;


namespace Microsoft.Quantum.QsCompiler
Expand Down Expand Up @@ -82,6 +84,27 @@ public struct Diagnostic
/// The position is null if the diagnostic is not caused by a piece of source code.
/// </summary>
public Tuple<int, int> End { get; set; }

/// <summary>
/// Initializes a new diagnostic.
/// If a diagnostic generated by the Q# compiler is given as argument, the values are initialized accordingly.
/// </summary>
public static Diagnostic Create(VS.Diagnostic d = null, Stage stage = Stage.Unknown) =>
d == null ? new Diagnostic() : new Diagnostic
{
Severity = d.Severity switch
{
VS.DiagnosticSeverity.Error => DiagnosticSeverity.Error,
VS.DiagnosticSeverity.Warning => DiagnosticSeverity.Warning,
VS.DiagnosticSeverity.Information => DiagnosticSeverity.Info,
_ => DiagnosticSeverity.Hidden
},
Message = d.Message,
Source = d.Source,
Stage = stage,
Start = d.Range?.Start == null ? null : DiagnosticTools.AsTuple(d.Range.Start),
End = d.Range?.End == null ? null : DiagnosticTools.AsTuple(d.Range.End)
};
}

/// <summary>
Expand Down
3 changes: 3 additions & 0 deletions src/QsCompiler/Core/SymbolResolution.fs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ module SymbolResolution =
/// or if the resolved argument type does not match the expected argument type.
/// The TypeId in the resolved attribute is set to Null if the unresolved Id is not a valid identifier
/// or if the correct attribute cannot be determined, and is set to the corresponding type identifier otherwise.
/// Throws an ArgumentException if a tuple-valued attribute argument does not contain at least one item.
let internal ResolveAttribute getAttribute (attribute : AttributeAnnotation) =
let asTypedExression range (exKind, exType) = {
Expression = exKind
Expand Down Expand Up @@ -460,7 +461,9 @@ module SymbolResolution =
| StringLiteral (s, exs) ->
if exs.Length <> 0 then invalidExpr ex.Range, [| ex.Range |> diagnostic ErrorCode.InterpolatedStringInAttribute |]
else (StringLiteral (s, ImmutableArray.Empty), String) |> asTypedExression ex.Range, [||]
| ValueTuple vs when vs.Length = 1 -> ArgExression (vs.First())
| ValueTuple vs ->
if vs.Length = 0 then ArgumentException "tuple valued attribute argument requires at least one tuple item" |> raise
let innerExs, errs = aggregateInner vs
let types = (innerExs |> Seq.map (fun ex -> ex.ResolvedType)).ToImmutableArray()
(ValueTuple innerExs, TupleType types) |> asTypedExression ex.Range, errs
Expand Down
24 changes: 16 additions & 8 deletions src/QsCompiler/Core/SyntaxGenerator.fs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,13 @@ module SyntaxGenerator =
/// setting the quantum dependency to the given value and assuming no type parameter resolutions.
/// Sets the range information for the built expression to Null.
let private AutoGeneratedExpression kind exTypeKind qDep =
let noInferredInfo = InferredExpressionInformation.New (false, quantumDep = qDep)
TypedExpression.New (kind, ImmutableDictionary.Empty, exTypeKind |> ResolvedType.New, noInferredInfo, QsRangeInfo.Null)
let inferredInfo = InferredExpressionInformation.New (false, quantumDep = qDep)
TypedExpression.New (kind, ImmutableDictionary.Empty, exTypeKind |> ResolvedType.New, inferredInfo, QsRangeInfo.Null)

/// Creates a typed expression that represents an invalid expression of invalid type.
/// Sets the range information for the built expression to Null.
let InvalidExpression =
AutoGeneratedExpression InvalidExpr QsTypeKind.InvalidType false

/// Creates a typed expression that corresponds to a Unit value.
/// Sets the range information for the built expression to Null.
Expand Down Expand Up @@ -97,6 +102,14 @@ module SyntaxGenerator =
let RangeLiteral (lhs, rhs) =
AutoGeneratedExpression (RangeLiteral (lhs, rhs)) QsTypeKind.Range false

/// Creates a typed expression that corresponds to a value tuple with the given items.
/// Sets the range information for the built expression to Null.
/// Does *not* strip positional information from the given items; the responsibility to do so is with the caller.
let TupleLiteral (items : TypedExpression seq) =
let qdep = items |> Seq.exists (fun item -> item.InferredInformation.HasLocalQuantumDependency)
let tupleType = items |> Seq.map (fun item -> item.ResolvedType) |> ImmutableArray.CreateRange |> TupleType
AutoGeneratedExpression (ValueTuple (items.ToImmutableArray())) tupleType qdep


// utils to for building typed expressions and iterable inversions

Expand Down Expand Up @@ -250,12 +263,7 @@ module SyntaxGenerator =
| _ -> true
if ctlQs.ResolvedType.Resolution <> QubitArray.Resolution && not (ctlQs.ResolvedType |> isInvalid) then
new ArgumentException "expression for the control qubits is valid but not of type Qubit[]" |> raise
let buildControlledArgument orig =
let kind = QsExpressionKind.ValueTuple ([ctlQs; orig].ToImmutableArray())
let quantumDep = orig.InferredInformation.HasLocalQuantumDependency || ctlQs.InferredInformation.HasLocalQuantumDependency
let exInfo = InferredExpressionInformation.New (isMutable = false, quantumDep = quantumDep)
TypedExpression.New (kind, orig.TypeParameterResolutions, AddControlQubits orig.ResolvedType, exInfo, QsRangeInfo.Null)
buildControlledArgument arg
TupleLiteral [ctlQs; arg]

/// Returns the name of the control qubits
/// if the given argument tuple is consistent with the argument tuple of a controlled specialization.
Expand Down
3 changes: 3 additions & 0 deletions src/QsCompiler/DataStructures/SyntaxTree.fs
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ type QsSpecialization = {
}
with
member this.AddAttribute att = {this with Attributes = this.Attributes.Add att}
member this.AddAttributes (att : _ seq) = {this with Attributes = this.Attributes.AddRange att}
member this.WithImplementation impl = {this with Implementation = impl}
member this.WithParent (getName : Func<_,_>) = {this with Parent = getName.Invoke(this.Parent)}

Expand Down Expand Up @@ -699,6 +700,7 @@ type QsCallable = {
}
with
member this.AddAttribute att = {this with Attributes = this.Attributes.Add att}
member this.AddAttributes (att : _ seq) = {this with Attributes = this.Attributes.AddRange att}
member this.WithSpecializations (getSpecs : Func<_,_>) = {this with Specializations = getSpecs.Invoke(this.Specializations)}
member this.WithFullName (getName : Func<_,_>) = {this with FullName = getName.Invoke(this.FullName)}

Expand Down Expand Up @@ -740,6 +742,7 @@ type QsCustomType = {
}
with
member this.AddAttribute att = {this with Attributes = this.Attributes.Add att}
member this.AddAttributes (att : _ seq) = {this with Attributes = this.Attributes.AddRange att}
member this.WithFullName (getName : Func<_,_>) = {this with FullName = getName.Invoke(this.FullName)}


Expand Down
21 changes: 21 additions & 0 deletions src/QsCompiler/Tests.Compiler/TestCases/AttributeGeneration.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Testing.AttributeGeneration {

open Microsoft.Quantum.Arrays;

function DefaultArray<'A>(size : Int) : 'A[] {
mutable arr = new 'A[size];
for (i in IndexRange(arr)) {
set arr w/= i <- Default<'A>();
}
return arr;
}

operation CallDefaultArray<'A>(size : Int) : 'A[] {
return DefaultArray(size);
}

}

3 changes: 3 additions & 0 deletions src/QsCompiler/Tests.Compiler/Tests.Compiler.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@
<None Include="TestCases\ExecutionTests\LoggingBasedTests.qs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="TestCases\AttributeGeneration.qs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="TestCases\Transformation.qs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
94 changes: 86 additions & 8 deletions src/QsCompiler/Tests.Compiler/TransformationTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,44 @@ open Microsoft.Quantum.QsCompiler
open Microsoft.Quantum.QsCompiler.CompilationBuilder
open Microsoft.Quantum.QsCompiler.DataTypes
open Microsoft.Quantum.QsCompiler.SyntaxTree
open Microsoft.Quantum.QsCompiler.Transformations
open Microsoft.Quantum.QsCompiler.Transformations.Core
open Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput
open Xunit


// utils for testing syntax tree transformations and the corresponding infrastructure

type private GlobalDeclarations(parent : CheckDeclarations) =
inherit NamespaceTransformation(parent, TransformationOptions.NoRebuild)

override this.OnCallableDeclaration c =
parent.CheckCallableDeclaration c

override this.OnTypeDeclaration t =
parent.CheckTypeDeclaration t

override this.OnSpecializationDeclaration s =
parent.CheckSpecializationDeclaration s

and private CheckDeclarations private (_internal_, onTypeDecl, onCallableDecl, onSpecDecl) =
inherit SyntaxTreeTransformation()

member internal this.CheckTypeDeclaration = onTypeDecl
member internal this.CheckCallableDeclaration = onCallableDecl
member internal this.CheckSpecializationDeclaration = onSpecDecl

new (?onTypeDecl, ?onCallableDecl, ?onSpecDecl) as this =
let onTypeDecl = defaultArg onTypeDecl id
let onCallableDecl = defaultArg onCallableDecl id
let onSpecDecl = defaultArg onSpecDecl id
CheckDeclarations("_internal_", onTypeDecl, onCallableDecl, onSpecDecl) then
this.Types <- new TypeTransformation(this, TransformationOptions.Disabled)
this.Expressions <- new ExpressionTransformation(this, TransformationOptions.Disabled)
this.Statements <- new StatementTransformation(this, TransformationOptions.Disabled)
this.Namespaces <- new GlobalDeclarations(this)


type private Counter () =
member val callsCount = 0 with get, set
member val opsCount = 0 with get, set
Expand Down Expand Up @@ -78,16 +109,16 @@ let private buildSyntaxTree code =
compilationUnit.AddOrUpdateSourceFileAsync file |> ignore // spawns a task that modifies the current compilation
let mutable syntaxTree = compilationUnit.Build().BuiltCompilation // will wait for any current tasks to finish
CodeGeneration.GenerateFunctorSpecializations(syntaxTree, &syntaxTree) |> ignore
syntaxTree.Namespaces
syntaxTree


//////////////////////////////// tests //////////////////////////////////

[<Fact>]
let ``basic walk`` () =
let tree = Path.Combine(Path.GetFullPath ".", "TestCases", "Transformation.qs") |> File.ReadAllText |> buildSyntaxTree
let compilation = Path.Combine(Path.GetFullPath ".", "TestCases", "Transformation.qs") |> File.ReadAllText |> buildSyntaxTree
let walker = new SyntaxCounter(TransformationOptions.NoRebuild)
tree |> Seq.iter (walker.Namespaces.OnNamespace >> ignore)
compilation.Namespaces |> Seq.iter (walker.Namespaces.OnNamespace >> ignore)

Assert.Equal (4, walker.Counter.udtCount)
Assert.Equal (1, walker.Counter.funCount)
Expand All @@ -96,11 +127,12 @@ let ``basic walk`` () =
Assert.Equal (6, walker.Counter.ifsCount)
Assert.Equal (20, walker.Counter.callsCount)


[<Fact>]
let ``basic transformation`` () =
let tree = Path.Combine(Path.GetFullPath ".", "TestCases", "Transformation.qs") |> File.ReadAllText |> buildSyntaxTree
let compilation = Path.Combine(Path.GetFullPath ".", "TestCases", "Transformation.qs") |> File.ReadAllText |> buildSyntaxTree
let walker = new SyntaxCounter()
tree |> Seq.iter (walker.Namespaces.OnNamespace >> ignore)
compilation.Namespaces |> Seq.iter (walker.Namespaces.OnNamespace >> ignore)

Assert.Equal (4, walker.Counter.udtCount)
Assert.Equal (1, walker.Counter.funCount)
Expand All @@ -109,14 +141,60 @@ let ``basic transformation`` () =
Assert.Equal (6, walker.Counter.ifsCount)
Assert.Equal (20, walker.Counter.callsCount)


[<Fact>]
let ``attaching attributes to callables`` () =
let WithinNamespace nsName (c : QsNamespaceElement) = c.GetFullName().Namespace.Value = nsName
let attGenNs = "Microsoft.Quantum.Testing.AttributeGeneration"
let predicate = QsCallable >> WithinNamespace attGenNs
let sources = [
Path.Combine(Path.GetFullPath ".", "TestCases", "LinkingTests", "Core.qs")
Path.Combine(Path.GetFullPath ".", "TestCases", "AttributeGeneration.qs")
]
let compilation = sources |> Seq.map File.ReadAllText |> String.Concat |> buildSyntaxTree
let testAttribute = AttributeUtils.BuildAttribute(BuiltIn.Test.FullName, AttributeUtils.StringArgument "QuantumSimulator")

let checkSpec (spec : QsSpecialization) = Assert.Empty spec.Attributes; spec
let checkType (customType : QsCustomType) =
if customType |> QsCustomType |> WithinNamespace attGenNs then Assert.Empty customType.Attributes;
customType
let checkCallable limitedToNs nrAtts (callable : QsCallable) =
if limitedToNs = null || callable |> QsCallable |> WithinNamespace limitedToNs then
Assert.Equal(nrAtts, callable.Attributes.Length)
for att in callable.Attributes do
Assert.Equal(testAttribute, att)
else Assert.Empty callable.Attributes
callable

let transformed = AttributeUtils.AddToCallables(compilation, testAttribute, predicate)
let checker = new CheckDeclarations(checkType, checkCallable attGenNs 1, checkSpec)
checker.Apply transformed |> ignore

let transformed = AttributeUtils.AddToCallables(compilation, testAttribute, null)
let checker = new CheckDeclarations(checkType, checkCallable null 1, checkSpec)
checker.Apply transformed |> ignore

let transformed = AttributeUtils.AddToCallables(compilation, testAttribute)
let checker = new CheckDeclarations(checkType, checkCallable null 1, checkSpec)
checker.Apply transformed |> ignore

let transformed = AttributeUtils.AddToCallables(compilation, struct (testAttribute, new Func<_,_>(predicate)), struct(testAttribute, new Func<_,_>(predicate)))
let checker = new CheckDeclarations(checkType, checkCallable attGenNs 2, checkSpec)
checker.Apply transformed |> ignore

let transformed = AttributeUtils.AddToCallables(compilation, testAttribute, testAttribute)
let checker = new CheckDeclarations(checkType, checkCallable null 2, checkSpec)
checker.Apply transformed |> ignore


[<Fact>]
let ``generation of open statements`` () =
let tree = buildSyntaxTree @"
let compilation = buildSyntaxTree @"
namespace Microsoft.Quantum.Testing {
operation emptyOperation () : Unit {}
}"

let ns = tree |> Seq.head
let ns = compilation.Namespaces |> Seq.head
let source = ns.Elements.Single() |> function
| QsCallable callable -> callable.SourceFile
| QsCustomType t -> t.SourceFile
Expand All @@ -133,7 +211,7 @@ let ``generation of open statements`` () =
let imports = ImmutableDictionary.Empty.Add(ns.Name, openDirectives)

let codeOutput = ref null
SyntaxTreeToQsharp.Apply (codeOutput, tree, struct (source, imports)) |> Assert.True
SyntaxTreeToQsharp.Apply (codeOutput, compilation.Namespaces, struct (source, imports)) |> Assert.True
let lines = Utils.SplitLines (codeOutput.Value.Single().[ns.Name])
Assert.Equal(13, lines.Count())

Expand Down
4 changes: 2 additions & 2 deletions src/QsCompiler/TextProcessor/QsFragmentParsing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ let private namespaceDeclaration =
buildFragment namespaceDeclHeader.parse (expectedNamespaceName eof) invalid (fun _ -> NamespaceDeclaration) eof

/// Uses buildFragment to parse a Q# DeclarationAttribute as QsFragment.
let private attributeDeclaration =
let private attributeAnnotation =
let invalid = DeclarationAttribute (invalidSymbol, unknownExpr)
let attributeId = multiSegmentSymbol ErrorCode.InvalidIdentifierName |>> asQualifiedSymbol
let expectedArgs =
Expand Down Expand Up @@ -500,6 +500,6 @@ let private expressionStatement =
let internal codeFragment =
let validFragment =
choice (fragments |> List.map snd)
<|> attributeDeclaration
<|> attributeAnnotation
<|> expressionStatement // the expressionStatement needs to be last
attempt validFragment <|> buildInvalidFragment (preturn ())
Loading