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
11 changes: 8 additions & 3 deletions src/QsCompiler/Transformations/ClassicallyControlled.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<LiftContent.TransformationState>
{
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<TransformationState>.StatementKindTransformation
{
public StatementKindTransformation(SyntaxTreeTransformation<TransformationState> parent) : base(parent) { }

Expand Down
273 changes: 140 additions & 133 deletions src/QsCompiler/Transformations/ContentLifting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,9 @@ namespace Microsoft.Quantum.QsCompiler.Transformations.ContentLifting
using TypeArgsResolution = ImmutableArray<Tuple<QsQualifiedName, NonNullable<string>, ResolvedType>>;

/// <summary>
/// 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<T>.
/// </summary>
public class LiftContent : SyntaxTreeTransformation<LiftContent.TransformationState>
public static class LiftContent
{
internal class CallableDetails
{
Expand Down Expand Up @@ -310,22 +300,149 @@ QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature
}
}

protected LiftContent() : base(new TransformationState())
/// <summary>
/// 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
/// </summary>
private class UpdateGeneratedOp : SyntaxTreeTransformation<UpdateGeneratedOp.TransformationState>
{
public static QsCallable Apply(QsCallable qsCallable, ImmutableArray<LocalVariableDeclaration<NonNullable<string>>> 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<LocalVariableDeclaration<NonNullable<string>>> Parameters;
public readonly QsQualifiedName OldName;
public readonly QsQualifiedName NewName;

public TransformationState(ImmutableArray<LocalVariableDeclaration<NonNullable<string>>> parameters, QsQualifiedName oldName, QsQualifiedName newName)
{
Parameters = parameters;
OldName = oldName;
NewName = newName;
}
}

private UpdateGeneratedOp(ImmutableArray<LocalVariableDeclaration<NonNullable<string>>> 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<TransformationState>
{
public ExpressionTransformation(SyntaxTreeTransformation<TransformationState> parent) : base(parent) { }

public override ImmutableDictionary<Tuple<QsQualifiedName, NonNullable<string>>, ResolvedType> OnTypeParamResolutions(ImmutableDictionary<Tuple<QsQualifiedName, NonNullable<string>>, 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<TransformationState>
{
public ExpressionKindTransformation(SyntaxTreeTransformation<TransformationState> parent) : base(parent) { }

public override ExpressionKind OnIdentifier(Identifier sym, QsNullable<ImmutableArray<ResolvedType>> 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<TransformationState>
{
public TypeTransformation(SyntaxTreeTransformation<TransformationState> 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);
}
}
}
}

/// <summary>
/// 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.
/// </summary>
public class LiftContent<T> : SyntaxTreeTransformation<T>
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<TransformationState>(this, TransformationOptions.Disabled);
this.Types = new TypeTransformation<T>(this, TransformationOptions.Disabled);
}

protected class NamespaceTransformation : NamespaceTransformation<TransformationState>
protected class NamespaceTransformation : NamespaceTransformation<T>
{
public NamespaceTransformation(SyntaxTreeTransformation<TransformationState> parent) : base(parent) { }
public NamespaceTransformation(SyntaxTreeTransformation<T> parent) : base(parent) { }

public override QsCallable OnCallableDeclaration(QsCallable c)
{
SharedState.CurrentCallable = new CallableDetails(c);
SharedState.CurrentCallable = new LiftContent.CallableDetails(c);
return base.OnCallableDeclaration(c);
}

Expand Down Expand Up @@ -372,9 +489,9 @@ public override QsNamespace OnNamespace(QsNamespace ns)
}
}

protected class StatementKindTransformation : StatementKindTransformation<TransformationState>
protected class StatementKindTransformation : StatementKindTransformation<T>
{
public StatementKindTransformation(SyntaxTreeTransformation<TransformationState> parent) : base(parent) { }
public StatementKindTransformation(SyntaxTreeTransformation<T> parent) : base(parent) { }

public override QsStatementKind OnConjugation(QsConjugation stm)
{
Expand Down Expand Up @@ -415,9 +532,9 @@ public override QsStatementKind OnStatementKind(QsStatementKind kind)
}
}

protected class ExpressionTransformation : ExpressionTransformation<TransformationState>
protected class ExpressionTransformation : ExpressionTransformation<T>
{
public ExpressionTransformation(SyntaxTreeTransformation<TransformationState> parent) : base(parent) { }
public ExpressionTransformation(SyntaxTreeTransformation<T> parent) : base(parent) { }

public override TypedExpression OnTypedExpression(TypedExpression ex)
{
Expand All @@ -433,9 +550,9 @@ public override TypedExpression OnTypedExpression(TypedExpression ex)
}
}

protected class ExpressionKindTransformation : ExpressionKindTransformation<TransformationState>
protected class ExpressionKindTransformation : ExpressionKindTransformation<T>
{
public ExpressionKindTransformation(SyntaxTreeTransformation<TransformationState> parent) : base(parent) { }
public ExpressionKindTransformation(SyntaxTreeTransformation<T> parent) : base(parent) { }

public override ExpressionKind OnIdentifier(Identifier sym, QsNullable<ImmutableArray<ResolvedType>> tArgs)
{
Expand All @@ -447,115 +564,5 @@ public override ExpressionKind OnIdentifier(Identifier sym, QsNullable<Immutable
return base.OnIdentifier(sym, tArgs);
}
}

/// <summary>
/// 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
/// </summary>
private class UpdateGeneratedOp : SyntaxTreeTransformation<UpdateGeneratedOp.TransformationState>
{
public static QsCallable Apply(QsCallable qsCallable, ImmutableArray<LocalVariableDeclaration<NonNullable<string>>> 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<LocalVariableDeclaration<NonNullable<string>>> Parameters;
public readonly QsQualifiedName OldName;
public readonly QsQualifiedName NewName;

public TransformationState(ImmutableArray<LocalVariableDeclaration<NonNullable<string>>> parameters, QsQualifiedName oldName, QsQualifiedName newName)
{
Parameters = parameters;
OldName = oldName;
NewName = newName;
}
}

private UpdateGeneratedOp(ImmutableArray<LocalVariableDeclaration<NonNullable<string>>> 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<TransformationState>
{
public ExpressionTransformation(SyntaxTreeTransformation<TransformationState> parent) : base(parent) { }

public override ImmutableDictionary<Tuple<QsQualifiedName, NonNullable<string>>, ResolvedType> OnTypeParamResolutions(ImmutableDictionary<Tuple<QsQualifiedName, NonNullable<string>>, 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<TransformationState>
{
public ExpressionKindTransformation(SyntaxTreeTransformation<TransformationState> parent) : base(parent) { }

public override ExpressionKind OnIdentifier(Identifier sym, QsNullable<ImmutableArray<ResolvedType>> 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<TransformationState>
{
public TypeTransformation(SyntaxTreeTransformation<TransformationState> 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);
}
}
}
}
}