diff --git a/src/QsCompiler/Transformations/ClassicallyControlled.cs b/src/QsCompiler/Transformations/ClassicallyControlled.cs index ad96e0cdc3..3dfe372774 100644 --- a/src/QsCompiler/Transformations/ClassicallyControlled.cs +++ b/src/QsCompiler/Transformations/ClassicallyControlled.cs @@ -766,14 +766,19 @@ public static QsCompilation Apply(QsCompilation compilation) return new QsCompilation(compilation.Namespaces.Select(ns => filter.Namespaces.OnNamespace(ns)).ToImmutableArray(), compilation.EntryPoints); } - private class LiftContent : ContentLifting.LiftContent + private class LiftContent : ContentLifting.LiftContent { - public LiftContent() : base() + internal class TransformationState : ContentLifting.LiftContent.TransformationState + { + // here is where additional handles would go + } + + public LiftContent() : base(new TransformationState()) { this.StatementKinds = new StatementKindTransformation(this); } - private new class StatementKindTransformation : ContentLifting.LiftContent.StatementKindTransformation + private new class StatementKindTransformation : ContentLifting.LiftContent.StatementKindTransformation { public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } diff --git a/src/QsCompiler/Transformations/ContentLifting.cs b/src/QsCompiler/Transformations/ContentLifting.cs index ec8e146a94..47da4ddd2b 100644 --- a/src/QsCompiler/Transformations/ContentLifting.cs +++ b/src/QsCompiler/Transformations/ContentLifting.cs @@ -19,19 +19,9 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ContentLifting using TypeArgsResolution = ImmutableArray, ResolvedType>>; /// - /// This transformation handles the task of lifting the contents of code blocks into generated operations. - /// The transformation provides validation to see if any given block can safely be lifted into its own operation. - /// Validation requirements are that there are no return statements and that there are no set statements - /// on mutables declared outside the block. Setting mutables declared inside the block is valid. - /// A block can be checked by setting the SharedState.IsValidScope to true before traversing the scope, - /// then checking the SharedState.IsValidScope after traversal. Blocks should be validated before calling - /// the SharedState.LiftBody function, which will generate a new operation with the block's contents. - /// All the known variables at the start of the block will become parameters to the new operation, and - /// the operation will have all the valid type parameters of the calling context as type parameters. - /// A call to the new operation is also returned with all the valid type parameters and known variables - /// being forwarded to the new operation as arguments. + /// Static class to accumulate all type parameter independent subclasses used by LiftContent. /// - public class LiftContent : SyntaxTreeTransformation + public static class LiftContent { internal class CallableDetails { @@ -310,22 +300,149 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature } } - protected LiftContent() : base(new TransformationState()) + /// + /// Transformation that updates the contents of newly generated operations by: + /// 1. Rerouting the origins of type parameter references to the new operation + /// 2. Changes the IsMutable and HasLocalQuantumDependency info on parameter references to be false + /// + private class UpdateGeneratedOp : SyntaxTreeTransformation + { + public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + { + var filter = new UpdateGeneratedOp(parameters, oldName, newName); + + return filter.Namespaces.OnCallableDeclaration(qsCallable); + } + + public class TransformationState + { + public bool IsRecursiveIdentifier = false; + public readonly ImmutableArray>> Parameters; + public readonly QsQualifiedName OldName; + public readonly QsQualifiedName NewName; + + public TransformationState(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + { + Parameters = parameters; + OldName = oldName; + NewName = newName; + } + } + + private UpdateGeneratedOp(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) + : base(new TransformationState(parameters, oldName, newName)) + { + this.Expressions = new ExpressionTransformation(this); + this.ExpressionKinds = new ExpressionKindTransformation(this); + this.Types = new TypeTransformation(this); + } + + private class ExpressionTransformation : ExpressionTransformation + { + public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override ImmutableDictionary>, ResolvedType> OnTypeParamResolutions(ImmutableDictionary>, ResolvedType> typeParams) + { + // Prevent keys from having their names updated + return typeParams.ToImmutableDictionary(kvp => kvp.Key, kvp => this.Types.OnType(kvp.Value)); + } + + public override TypedExpression OnTypedExpression(TypedExpression ex) + { + // Checks if expression is mutable identifier that is in parameter list + if ((ex.InferredInformation.IsMutable || ex.InferredInformation.HasLocalQuantumDependency) + && ex.Expression is ExpressionKind.Identifier id + && id.Item1 is Identifier.LocalVariable variable + && SharedState.Parameters.Any(x => x.VariableName.Equals(variable))) + { + // Set the mutability to false + ex = new TypedExpression( + ex.Expression, + ex.TypeArguments, + ex.ResolvedType, + new InferredExpressionInformation(false, false), // parameter references cannot be mutable or have local quantum dependency + ex.Range); + } + + // Prevent IsRecursiveIdentifier from propagating beyond the typed expression it is referring to + var isRecursiveIdentifier = SharedState.IsRecursiveIdentifier; + var rtrn = base.OnTypedExpression(ex); + SharedState.IsRecursiveIdentifier = isRecursiveIdentifier; + return rtrn; + } + } + + private class ExpressionKindTransformation : ExpressionKindTransformation + { + public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) + { + var rtrn = base.OnIdentifier(sym, tArgs); + + // Check if this is a recursive identifier + // In this context, that is a call back to the original callable from the newly generated operation + if (sym is Identifier.GlobalCallable callable && SharedState.OldName.Equals(callable.Item)) + { + // Setting this flag will prevent the rerouting logic from processing the resolved type of the recursive identifier expression. + // This is necessary because we don't want any type parameters from the original callable from being rerouted to the new generated + // operation's type parameters in the definition of the identifier. + SharedState.IsRecursiveIdentifier = true; + } + return rtrn; + } + } + + private class TypeTransformation : TypeTransformation + { + public TypeTransformation(SyntaxTreeTransformation parent) : base(parent) { } + + public override ResolvedTypeKind OnTypeParameter(QsTypeParameter tp) + { + // Reroute a type parameter's origin to the newly generated operation + if (!SharedState.IsRecursiveIdentifier && SharedState.OldName.Equals(tp.Origin)) + { + tp = new QsTypeParameter(SharedState.NewName, tp.TypeName, tp.Range); + } + + return base.OnTypeParameter(tp); + } + } + } + } + + /// + /// This transformation handles the task of lifting the contents of code blocks into generated operations. + /// The transformation provides validation to see if any given block can safely be lifted into its own operation. + /// Validation requirements are that there are no return statements and that there are no set statements + /// on mutables declared outside the block. Setting mutables declared inside the block is valid. + /// A block can be checked by setting the SharedState.IsValidScope to true before traversing the scope, + /// then checking the SharedState.IsValidScope after traversal. Blocks should be validated before calling + /// the SharedState.LiftBody function, which will generate a new operation with the block's contents. + /// All the known variables at the start of the block will become parameters to the new operation, and + /// the operation will have all the valid type parameters of the calling context as type parameters. + /// A call to the new operation is also returned with all the valid type parameters and known variables + /// being forwarded to the new operation as arguments. + /// + public class LiftContent : SyntaxTreeTransformation + where T : LiftContent.TransformationState + { + protected LiftContent(T state) : base(state) { this.Namespaces = new NamespaceTransformation(this); this.StatementKinds = new StatementKindTransformation(this); this.Expressions = new ExpressionTransformation(this); this.ExpressionKinds = new ExpressionKindTransformation(this); - this.Types = new TypeTransformation(this, TransformationOptions.Disabled); + this.Types = new TypeTransformation(this, TransformationOptions.Disabled); } - protected class NamespaceTransformation : NamespaceTransformation + protected class NamespaceTransformation : NamespaceTransformation { - public NamespaceTransformation(SyntaxTreeTransformation parent) : base(parent) { } + public NamespaceTransformation(SyntaxTreeTransformation parent) : base(parent) { } public override QsCallable OnCallableDeclaration(QsCallable c) { - SharedState.CurrentCallable = new CallableDetails(c); + SharedState.CurrentCallable = new LiftContent.CallableDetails(c); return base.OnCallableDeclaration(c); } @@ -372,9 +489,9 @@ public override QsNamespace OnNamespace(QsNamespace ns) } } - protected class StatementKindTransformation : StatementKindTransformation + protected class StatementKindTransformation : StatementKindTransformation { - public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + public StatementKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } public override QsStatementKind OnConjugation(QsConjugation stm) { @@ -415,9 +532,9 @@ public override QsStatementKind OnStatementKind(QsStatementKind kind) } } - protected class ExpressionTransformation : ExpressionTransformation + protected class ExpressionTransformation : ExpressionTransformation { - public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } + public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } public override TypedExpression OnTypedExpression(TypedExpression ex) { @@ -433,9 +550,9 @@ public override TypedExpression OnTypedExpression(TypedExpression ex) } } - protected class ExpressionKindTransformation : ExpressionKindTransformation + protected class ExpressionKindTransformation : ExpressionKindTransformation { - public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } + public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) { @@ -447,115 +564,5 @@ public override ExpressionKind OnIdentifier(Identifier sym, QsNullable - /// Transformation that updates the contents of newly generated operations by: - /// 1. Rerouting the origins of type parameter references to the new operation - /// 2. Changes the IsMutable and HasLocalQuantumDependency info on parameter references to be false - /// - private class UpdateGeneratedOp : SyntaxTreeTransformation - { - public static QsCallable Apply(QsCallable qsCallable, ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) - { - var filter = new UpdateGeneratedOp(parameters, oldName, newName); - - return filter.Namespaces.OnCallableDeclaration(qsCallable); - } - - public class TransformationState - { - public bool IsRecursiveIdentifier = false; - public readonly ImmutableArray>> Parameters; - public readonly QsQualifiedName OldName; - public readonly QsQualifiedName NewName; - - public TransformationState(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) - { - Parameters = parameters; - OldName = oldName; - NewName = newName; - } - } - - private UpdateGeneratedOp(ImmutableArray>> parameters, QsQualifiedName oldName, QsQualifiedName newName) - : base(new TransformationState(parameters, oldName, newName)) - { - this.Expressions = new ExpressionTransformation(this); - this.ExpressionKinds = new ExpressionKindTransformation(this); - this.Types = new TypeTransformation(this); - } - - private class ExpressionTransformation : ExpressionTransformation - { - public ExpressionTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override ImmutableDictionary>, ResolvedType> OnTypeParamResolutions(ImmutableDictionary>, ResolvedType> typeParams) - { - // Prevent keys from having their names updated - return typeParams.ToImmutableDictionary(kvp => kvp.Key, kvp => this.Types.OnType(kvp.Value)); - } - - public override TypedExpression OnTypedExpression(TypedExpression ex) - { - // Checks if expression is mutable identifier that is in parameter list - if ((ex.InferredInformation.IsMutable || ex.InferredInformation.HasLocalQuantumDependency) - && ex.Expression is ExpressionKind.Identifier id - && id.Item1 is Identifier.LocalVariable variable - && SharedState.Parameters.Any(x => x.VariableName.Equals(variable))) - { - // Set the mutability to false - ex = new TypedExpression( - ex.Expression, - ex.TypeArguments, - ex.ResolvedType, - new InferredExpressionInformation(false, false), // parameter references cannot be mutable or have local quantum dependency - ex.Range); - } - - // Prevent IsRecursiveIdentifier from propagating beyond the typed expression it is referring to - var isRecursiveIdentifier = SharedState.IsRecursiveIdentifier; - var rtrn = base.OnTypedExpression(ex); - SharedState.IsRecursiveIdentifier = isRecursiveIdentifier; - return rtrn; - } - } - - private class ExpressionKindTransformation : ExpressionKindTransformation - { - public ExpressionKindTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override ExpressionKind OnIdentifier(Identifier sym, QsNullable> tArgs) - { - var rtrn = base.OnIdentifier(sym, tArgs); - - // Check if this is a recursive identifier - // In this context, that is a call back to the original callable from the newly generated operation - if (sym is Identifier.GlobalCallable callable && SharedState.OldName.Equals(callable.Item)) - { - // Setting this flag will prevent the rerouting logic from processing the resolved type of the recursive identifier expression. - // This is necessary because we don't want any type parameters from the original callable from being rerouted to the new generated - // operation's type parameters in the definition of the identifier. - SharedState.IsRecursiveIdentifier = true; - } - return rtrn; - } - } - - private class TypeTransformation : TypeTransformation - { - public TypeTransformation(SyntaxTreeTransformation parent) : base(parent) { } - - public override ResolvedTypeKind OnTypeParameter(QsTypeParameter tp) - { - // Reroute a type parameter's origin to the newly generated operation - if (!SharedState.IsRecursiveIdentifier && SharedState.OldName.Equals(tp.Origin)) - { - tp = new QsTypeParameter(SharedState.NewName, tp.TypeName, tp.Range); - } - - return base.OnTypeParameter(tp); - } - } - } } }