Exemplo n.º 1
0
 internal static IAttributeBuilder <T> WithAttribute <T>(
     this IAttributeBuilder <T> builder,
     string @namespace,
     string name,
     TypedExpression input) =>
 builder.AddAttribute(
     AttributeUtils.BuildAttribute(
         new QsQualifiedName(@namespace, name), input));
Exemplo n.º 2
0
        /// <summary>
        /// Adds a TargetInstruction attribute to each intrinsic callable that doesn't have one,
        /// unless the automatically determined target instruction name conflicts with another target instruction name.
        /// The automatically determined name of the target instruction is the lower case version of the unqualified callable name.
        /// Type constructors and generic callables are left unchanged.
        /// </summary>
        /// <returns>Returns true if all missing attributes have been successfully added and false if some attributes could not be added.</returns>
        /// <exception cref="InvalidOperationException">All intrinsic callables need to have exactly one body specialization.</exception>
        public static bool TryAddMissingTargetInstructionAttributes(QsCompilation compilation, out QsCompilation transformed)
        {
            var           success          = true;
            List <string> instructionNames = new List <string>();

            // populate the intruction names with all manually specified ones first
            foreach (var callable in compilation.Namespaces.Callables())
            {
                var manuallySpecified = SymbolResolution.TryGetTargetInstructionName(callable.Attributes);
                if (manuallySpecified.IsValue)
                {
                    instructionNames.Add(manuallySpecified.Item);
                }
            }

            QsCallable AddAttribute(QsCallable callable)
            {
                if (callable.Specializations.Length != 1)
                {
                    throw new InvalidOperationException("intrinsic callable needs to have exactly one body specialization");
                }

                var instructionName = callable.FullName.Name.ToLowerInvariant();

                if (instructionNames.Contains(instructionName))
                {
                    success = false;
                    return(callable);
                }
                return(callable.AddAttribute(
                           AttributeUtils.BuildAttribute(
                               BuiltIn.TargetInstruction.FullName,
                               AttributeUtils.StringArgument(instructionName))));
            }

            QsNamespace AddAttributes(QsNamespace ns)
            {
                var elements = ns.Elements.Select(element =>
                                                  element is QsNamespaceElement.QsCallable callable &&
                                                  callable.Item.IsIntrinsic &&
                                                  callable.Item.Signature.TypeParameters.Length == 0 &&
                                                  !NameDecorator.IsAutoGeneratedName(callable.Item.FullName) &&
                                                  !callable.Item.Kind.IsTypeConstructor &&
                                                  !callable.Item.Attributes.Any(BuiltIn.DefinesTargetInstruction)
                    ? QsNamespaceElement.NewQsCallable(AddAttribute(callable.Item))
                    : element);

                return(new QsNamespace(ns.Name, elements.ToImmutableArray(), ns.Documentation));
            }

            var namespaces = compilation.Namespaces.Select(AddAttributes).ToImmutableArray();

            transformed = new QsCompilation(namespaces, compilation.EntryPoints);
            return(success);
        }
        /// <summary>
        /// Creates a separate callable for each intrinsic specialization,
        /// and replaces the specialization implementations of the original callable with a call to these.
        /// Self adjoint generation directives in intrinsic callables are replaced by a provided implementation.
        /// Type constructors and generic callables or callables that already define a target instruction name are left unchanged.
        /// </summary>
        /// <exception cref="ArgumentException">
        /// An intrinsic callable contains non-intrinsic specializations
        /// or a non-intrinsic callable contains intrinsic specializations,
        /// or the a callable doesn't have a body specialization.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// A specialization has explicit type arguments;
        /// Monomorphization needs to run before separating target instructions.
        /// </exception>
        private static QsNamespace LiftIntrinsicSpecializations(QsNamespace ns)
        {
            var elements = ImmutableArray.CreateBuilder <QsNamespaceElement>();

            foreach (var element in ns.Elements)
            {
                if (element is QsNamespaceElement.QsCallable c &&
                    c.Item.Signature.TypeParameters.Length == 0 &&
                    !c.Item.Kind.IsTypeConstructor)
                {
                    if (c.Item.IsIntrinsic)
                    {
                        QsCallable callable = c.Item;
                        if (!callable.Specializations.Any(spec => spec.Kind.IsQsBody))
                        {
                            throw new ArgumentException("missing body specialization");
                        }
                        else if (callable.Specializations.Any(spec => spec.TypeArguments.IsValue))
                        {
                            throw new InvalidOperationException("specialization with type arguments");
                        }
                        else if (callable.Specializations.Length == 1 && callable.Attributes.Any(BuiltIn.DefinesTargetInstruction))
                        {
                            elements.Add(element);
                        }
                        else
                        {
                            QsQualifiedName GeneratedName(QsSpecializationKind kind) =>
                            new QsQualifiedName(callable.FullName.Namespace, $"{callable.FullName.Name}{SpecializationSuffix(kind)}");

                            var specializations = ImmutableArray.CreateRange(callable.Specializations.Select(spec =>
                            {
                                var inferredInfo = spec.Signature.Information.InferredInformation;
                                if (!inferredInfo.IsIntrinsic && !inferredInfo.IsSelfAdjoint)
                                {
                                    throw new ArgumentException("non-intrinsic specialization for intrinsic callable");
                                }

                                // Get the correct argument tuple both for the added intrinsic callable
                                // and the generated provided specialization that replaces the intrinsic one.
                                var argTuple = BuildSpecArgTuple(callable.ArgumentTuple, spec.Kind);

                                // Create a separate callable for that specialization,
                                // unless the specialization is not needed for a self-adjoint callable.

                                var genCallableSignature = new ResolvedSignature(
                                    ImmutableArray <QsLocalSymbol> .Empty,
                                    spec.Signature.ArgumentType,
                                    spec.Signature.ReturnType,
                                    new CallableInformation(
                                        ResolvedCharacteristics.Empty,
                                        new InferredCallableInformation(isIntrinsic: true, isSelfAdjoint: false)));
                                var genCallableName = GeneratedName(
                                    inferredInfo.IsSelfAdjoint && spec.Kind.IsQsAdjoint ? QsSpecializationKind.QsBody :
                                    inferredInfo.IsSelfAdjoint && spec.Kind.IsQsControlledAdjoint ? QsSpecializationKind.QsControlled :
                                    spec.Kind);

                                if (!inferredInfo.IsSelfAdjoint || spec.Kind.IsQsBody || spec.Kind.IsQsControlled)
                                {
                                    var genCallableBody = new QsSpecialization(
                                        QsSpecializationKind.QsBody,
                                        genCallableName,
                                        spec.Attributes,
                                        spec.Source,
                                        QsNullable <QsLocation> .Null,
                                        spec.TypeArguments,
                                        genCallableSignature,
                                        SpecializationImplementation.Intrinsic,
                                        spec.Documentation,
                                        spec.Comments);
                                    var genCallable = new QsCallable(
                                        callable.Kind,
                                        genCallableName,
                                        callable.Attributes,
                                        callable.Access,
                                        spec.Source,
                                        spec.Location,
                                        genCallableSignature,
                                        argTuple,
                                        ImmutableArray.Create(genCallableBody),
                                        ImmutableArray <string> .Empty,
                                        QsComments.Empty);
                                    elements.Add(QsNamespaceElement.NewQsCallable(genCallable));
                                }

                                // Create a specialization that calls into the generated callable,
                                // or the corresponding callable no callable for the specialization
                                // has been added due to hte operation being self-adjoint.

                                var genCallableType =
                                    callable.Kind == QsCallableKind.Operation
                                    ? OperationTypeFromSignature(genCallableSignature)
                                    : TypeKind.NewFunction(genCallableSignature.ArgumentType, genCallableSignature.ReturnType);
                                var call = SyntaxGenerator.CallNonGeneric(
                                    IdentifierForCallable(genCallableName, genCallableType),
                                    SyntaxGenerator.ArgumentTupleAsExpression(argTuple));
                                var statement = new QsStatement(
                                    QsStatementKind.NewQsReturnStatement(call),
                                    LocalDeclarations.Empty,
                                    QsNullable <QsLocation> .Null,
                                    QsComments.Empty);
                                var localDeclarations = new LocalDeclarations(
                                    SyntaxGenerator.ValidDeclarations(SyntaxGenerator.ExtractItems(argTuple)));

                                return(spec.WithImplementation(SpecializationImplementation.NewProvided(
                                                                   argTuple,
                                                                   new QsScope(ImmutableArray.Create(statement), localDeclarations))));
                            }));

                            // Create a callable that contains all specializations that
                            // call into the generated callables for each specialization.

                            var inlineAttribute = AttributeUtils.BuildAttribute(BuiltIn.Inline.FullName, SyntaxGenerator.UnitValue);
                            var signature       = new ResolvedSignature(
                                ImmutableArray <QsLocalSymbol> .Empty,
                                callable.Signature.ArgumentType,
                                callable.Signature.ReturnType,
                                new CallableInformation(
                                    callable.Signature.Information.Characteristics,
                                    new InferredCallableInformation(isSelfAdjoint: callable.IsSelfAdjoint, isIntrinsic: false)));
                            var redirect = new QsCallable(
                                callable.Kind,
                                callable.FullName,
                                ImmutableArray.Create(inlineAttribute),
                                callable.Access,
                                callable.Source,
                                callable.Location,
                                signature,
                                callable.ArgumentTuple,
                                specializations,
                                callable.Documentation,
                                callable.Comments);
                            elements.Add(QsNamespaceElement.NewQsCallable(redirect));
                        }
                    }
                    else if (c.Item.Specializations.Any(spec => spec.Implementation.IsIntrinsic))
                    {
                        throw new ArgumentException("intrinsic specialization for non-intrinsic callable");
                    }
                    else
                    {
                        elements.Add(element);
                    }
                }
Exemplo n.º 4
0
        // interface methods

        public bool Transformation(QsCompilation compilation, out QsCompilation transformed)
        {
            transformed = FilterSourceFiles.Apply(compilation);
            var manager = new CompilationUnitManager();

            // get source code from examples

            var fileManagers = ExamplesInDocs.Extract(transformed)
                               .Select(g => InitializeFileManager(g, compilation, g.Key))
                               .Where(m => m != null).ToImmutableHashSet();

            manager.AddOrUpdateSourceFilesAsync(fileManagers, suppressVerification: true);
            var sourceFiles = fileManagers.Select(m => m.FileName).ToImmutableHashSet();

            bool IsGeneratedSourceFile(NonNullable <string> source) => sourceFiles.Contains(source);

            // get everything contained in the compilation as references

            var refName = NonNullable <string> .New(Path.GetFullPath(ReferenceSource));

            var refHeaders = new References.Headers(refName, DllToQs.Rename(compilation).Namespaces);
            var refDict    = new Dictionary <NonNullable <string>, References.Headers> {
                { refName, refHeaders }
            };
            var references = new References(refDict.ToImmutableDictionary());

            manager.UpdateReferencesAsync(references);

            // compile the examples in the doc comments and add any diagnostics to the list of generated diagnostics

            var built       = manager.Build();
            var diagnostics = built.Diagnostics();

            this.Diagnostics.AddRange(diagnostics.Select(d => IRewriteStep.Diagnostic.Create(d, IRewriteStep.Stage.Transformation)));
            if (diagnostics.Any(d => d.Severity == VS.DiagnosticSeverity.Error))
            {
                return(false);
            }

            // add the extracted namespace elements from doc comments to the transformed compilation

            var toBeAdded = built.BuiltCompilation.Namespaces.ToImmutableDictionary(
                ns => ns.Name,
                ns => FilterBySourceFile.Apply(ns, IsGeneratedSourceFile));
            var namespaces = compilation.Namespaces.Select(ns =>
                                                           toBeAdded.TryGetValue(ns.Name, out var add)
                ? new QsNamespace(ns.Name, ns.Elements.AddRange(add.Elements), ns.Documentation)
                : ns);
            var addedNamespaces = toBeAdded.Values.Where(add => !compilation.Namespaces.Any(ns => ns.Name.Value == add.Name.Value));

            transformed = new QsCompilation(namespaces.Concat(addedNamespaces).ToImmutableArray(), compilation.EntryPoints);

            // mark all newly created callables that take unit as argument as unit tests to run on the QuantumSimulator and ResourcesEstimator

            bool IsSuitableForUnitTest(QsCallable c) => IsGeneratedSourceFile(c.SourceFile) && c.Signature.ArgumentType.Resolution.IsUnitType;

            var qsimAtt = AttributeUtils.BuildAttribute(BuiltIn.Test.FullName, AttributeUtils.StringArgument(Constants.QuantumSimulator));
            var restAtt = AttributeUtils.BuildAttribute(BuiltIn.Test.FullName, AttributeUtils.StringArgument(Constants.ResourcesEstimator));

            transformed = AttributeUtils.AddToCallables(transformed, (qsimAtt, IsSuitableForUnitTest), (restAtt, IsSuitableForUnitTest));
            return(true);
        }