Skip to content
This repository has been archived by the owner on Jan 12, 2024. It is now read-only.

Commit

Permalink
Support class constraints with multiple dependencies (#1402)
Browse files Browse the repository at this point in the history
  • Loading branch information
bamarsha authored Apr 7, 2022
1 parent c1f2925 commit 59be764
Show file tree
Hide file tree
Showing 13 changed files with 550 additions and 419 deletions.
8 changes: 7 additions & 1 deletion src/QsCompiler/DataStructures/Diagnostics.fs
Original file line number Diff line number Diff line change
Expand Up @@ -794,8 +794,14 @@ type DiagnosticItem =
"The type of the given argument does not match the expected type. Got an argument of type {0}, expecting one of type {1} instead."
| ErrorCode.UnexpectedTupleArgument -> "Unexpected argument tuple. Expecting an argument of type {0}."
| ErrorCode.AmbiguousTypeParameterResolution ->
let note =
if Seq.item 1 args |> String.IsNullOrWhiteSpace then
""
else
Environment.NewLine + "Note: Relevant unsolved constraints: {1}"

"The type parameter {0} is ambiguous. More type annotations or usage context may be necessary."
+ if Seq.item 1 args |> String.IsNullOrWhiteSpace then "" else " Note: {0} has constraints {1}."
+ note
| ErrorCode.ConstrainsTypeParameter -> "The given expression constrains the type parameter(s) {0}."
| ErrorCode.GlobalTypeAlreadyExists -> "A type with the name \"{0}\" already exists."
| ErrorCode.GlobalCallableAlreadyExists -> "A callable with the name \"{0}\" already exists."
Expand Down
70 changes: 37 additions & 33 deletions src/QsCompiler/SyntaxProcessor/ExpressionVerification.fs
Original file line number Diff line number Diff line change
Expand Up @@ -129,22 +129,22 @@ let private verifyUdtWith processUdt (resolvedType: ResolvedType) range =
/// Verifies that <paramref name="lhs"/> and <paramref name="rhs"/> have type Bool.
/// </summary>
let private verifyAreBooleans (inference: InferenceContext) lhs rhs =
inference.Match(ResolvedType.New Bool .> lhs.ResolvedType)
@ inference.Match(ResolvedType.New Bool .> rhs.ResolvedType)
inference.Constrain(ResolvedType.New Bool .> lhs.ResolvedType)
@ inference.Constrain(ResolvedType.New Bool .> rhs.ResolvedType)

/// <summary>
/// Verifies that <paramref name="lhs"/> and <paramref name="rhs"/> have type Int.
/// </summary>
let private verifyAreIntegers (inference: InferenceContext) lhs rhs =
inference.Match(ResolvedType.New Int .> lhs.ResolvedType)
@ inference.Match(ResolvedType.New Int .> rhs.ResolvedType)
inference.Constrain(ResolvedType.New Int .> lhs.ResolvedType)
@ inference.Constrain(ResolvedType.New Int .> rhs.ResolvedType)

/// <summary>
/// Verifies that <paramref name="expr"/> has type Int or BigInt.
/// </summary>
/// <returns>The type of <paramref name="expr"/> and the diagnostics.</returns>
let private verifyIsIntegral (inference: InferenceContext) expr =
expr.ResolvedType, inference.Constrain(expr.ResolvedType, Integral)
expr.ResolvedType, Integral expr.ResolvedType |> Class |> inference.Constrain

/// <summary>
/// Verifies that <paramref name="lhs"/> and <paramref name="rhs"/> have an intersecting integral type.
Expand All @@ -153,15 +153,15 @@ let private verifyIsIntegral (inference: InferenceContext) expr =
let private verifyIntegralOp (inference: InferenceContext) range lhs rhs =
let exType, intersectDiagnostics = inference.Intersect(lhs.ResolvedType, rhs.ResolvedType)
let exType = exType |> ResolvedType.withAllRanges (TypeRange.inferred range)
let constrainDiagnostics = inference.Constrain(exType, Integral)
let constrainDiagnostics = Integral exType |> Class |> inference.Constrain
exType, intersectDiagnostics @ constrainDiagnostics

/// <summary>
/// Verifies that <paramref name="expr"/> has a numeric type.
/// </summary>
/// <returns>The type of <paramref name="expr"/> and the diagnostics.</returns>
let private verifySupportsArithmetic (inference: InferenceContext) expr =
expr.ResolvedType, inference.Constrain(expr.ResolvedType, Numeric)
expr.ResolvedType, Num expr.ResolvedType |> Class |> inference.Constrain

/// <summary>
/// Verifies that <paramref name="lhs"/> and <paramref name="rhs"/> have an intersecting numeric type.
Expand All @@ -170,7 +170,7 @@ let private verifySupportsArithmetic (inference: InferenceContext) expr =
let private verifyArithmeticOp (inference: InferenceContext) range lhs rhs =
let exType, intersectDiagnostics = inference.Intersect(lhs.ResolvedType, rhs.ResolvedType)
let exType = exType |> ResolvedType.withAllRanges (TypeRange.inferred range)
let constrainDiagnostics = inference.Constrain(exType, Numeric)
let constrainDiagnostics = Num exType |> Class |> inference.Constrain
exType, intersectDiagnostics @ constrainDiagnostics

/// <summary>
Expand All @@ -180,7 +180,7 @@ let private verifyArithmeticOp (inference: InferenceContext) range lhs rhs =
let internal verifyIsIterable (inference: InferenceContext) expr =
let range = rangeOrDefault expr
let item = inference.Fresh range
item, inference.Constrain(expr.ResolvedType, Iterable item)
item, Iterable(expr.ResolvedType, item) |> Class |> inference.Constrain

/// <summary>
/// Verifies that <paramref name="lhs"/> and <paramref name="rhs"/> have an intersecting semigroup type.
Expand All @@ -189,7 +189,7 @@ let internal verifyIsIterable (inference: InferenceContext) expr =
let private verifySemigroup (inference: InferenceContext) range lhs rhs =
let exType, intersectDiagnostics = inference.Intersect(lhs.ResolvedType, rhs.ResolvedType)
let exType = exType |> ResolvedType.withAllRanges (TypeRange.inferred range)
let constrainDiagnostics = inference.Constrain(exType, Semigroup)
let constrainDiagnostics = Semigroup exType |> Class |> inference.Constrain
exType, intersectDiagnostics @ constrainDiagnostics

/// <summary>
Expand All @@ -199,7 +199,7 @@ let private verifySemigroup (inference: InferenceContext) range lhs rhs =
let private verifyEqualityComparison (inference: InferenceContext) range lhs rhs =
let exType, intersectDiagnostics = inference.Intersect(lhs.ResolvedType, rhs.ResolvedType)
let exType = exType |> ResolvedType.withAllRanges (TypeRange.inferred range)
let constrainDiagnostics = inference.Constrain(exType, Equatable)
let constrainDiagnostics = Eq exType |> Class |> inference.Constrain
intersectDiagnostics @ constrainDiagnostics

/// <summary>
Expand Down Expand Up @@ -232,14 +232,14 @@ let private verifyValueArray (inference: InferenceContext) range exprs =
let private verifyIndexedItem (inference: InferenceContext) container indexType =
let range = rangeOrDefault container
let itemType = inference.Fresh range
itemType, inference.Constrain(container.ResolvedType, Indexed(indexType, itemType))
itemType, Index(container.ResolvedType, indexType, itemType) |> Class |> inference.Constrain

/// <summary>
/// Verifies that <paramref name="expr"/> has an adjointable type.
/// </summary>
/// <returns>The type of <paramref name="expr"/> and the diagnostics.</returns>
let private verifyAdjointApplication (inference: InferenceContext) expr =
expr.ResolvedType, inference.Constrain(expr.ResolvedType, Constraint.Adjointable)
expr.ResolvedType, ClassConstraint.Adjointable expr.ResolvedType |> Class |> inference.Constrain

/// <summary>
/// Verifies that <paramref name="expr"/> has a controllable type.
Expand All @@ -248,7 +248,7 @@ let private verifyAdjointApplication (inference: InferenceContext) expr =
let private verifyControlledApplication (inference: InferenceContext) expr =
let range = rangeOrDefault expr
let controlled = inference.Fresh range
controlled, inference.Constrain(expr.ResolvedType, Constraint.Controllable controlled)
controlled, ClassConstraint.Controllable(expr.ResolvedType, controlled) |> Class |> inference.Constrain

// utils for verifying identifiers, call expressions, and resolving type parameters

Expand Down Expand Up @@ -333,7 +333,7 @@ let private verifyIdentifier (inference: InferenceContext) (symbols: SymbolTrack
/// IMPORTANT: ignores any external type parameter occuring in expectedType without raising an error!
let internal verifyAssignment (inference: InferenceContext) expectedType mismatchErr rhs =
[
if inference.Match(expectedType .> rhs.ResolvedType) |> List.isEmpty |> not then
if inference.Constrain(expectedType .> rhs.ResolvedType) |> List.isEmpty |> not then
QsCompilerDiagnostic.Error
(mismatchErr, [ inference.Resolve rhs.ResolvedType |> showType; showType expectedType ])
(rangeOrDefault rhs)
Expand Down Expand Up @@ -374,7 +374,7 @@ let rec internal verifyBinding (inference: InferenceContext) tryBuildDeclaration
if List.isEmpty types then UnitType else ImmutableArray.CreateRange types |> TupleType
|> ResolvedType.create (TypeRange.inferred symbol.Range)

let unifyDiagnostics = inference.Match(tupleType .> rhsType)
let unifyDiagnostics = inference.Constrain(tupleType .> rhsType)

let verify symbol symbolType =
verifyBinding inference tryBuildDeclaration (symbol, symbolType) warnOnDiscard
Expand Down Expand Up @@ -499,7 +499,7 @@ type QsExpression with
let resolveSlicingRange start step end' =
let integerExpr ex =
let ex = resolve context ex
inference.Match(ResolvedType.New Int .> ex.ResolvedType) |> List.iter diagnose
inference.Constrain(ResolvedType.New Int .> ex.ResolvedType) |> List.iter diagnose
ex

let resolvedStep = step |> Option.map integerExpr
Expand Down Expand Up @@ -566,7 +566,7 @@ type QsExpression with
/// and returns the corrsponding NewArray expression as typed expression
let buildNewArray (bType, ex) =
let ex = resolve context ex
inference.Match(ResolvedType.New Int .> ex.ResolvedType) |> List.iter diagnose
inference.Constrain(ResolvedType.New Int .> ex.ResolvedType) |> List.iter diagnose

let resolvedBase = symbols.ResolveType diagnose bType
let arrType = resolvedBase |> ArrayType |> ResolvedType.create (TypeRange.inferred this.Range)
Expand All @@ -589,7 +589,7 @@ type QsExpression with
let value = resolve context value
let arrayType = ArrayType value.ResolvedType |> ResolvedType.create (TypeRange.inferred this.Range)
let size = resolve context size
inference.Match(ResolvedType.New Int .> size.ResolvedType) |> List.iter diagnose
inference.Constrain(ResolvedType.New Int .> size.ResolvedType) |> List.iter diagnose

let quantumDependency =
value.InferredInformation.HasLocalQuantumDependency
Expand Down Expand Up @@ -696,7 +696,7 @@ type QsExpression with
/// *under the assumption* that the range operator is left associative.
let buildRange (lhs: QsExpression, rhs) =
let rhs = resolve context rhs
inference.Match(ResolvedType.New Int .> rhs.ResolvedType) |> List.iter diagnose
inference.Constrain(ResolvedType.New Int .> rhs.ResolvedType) |> List.iter diagnose

let lhs =
match lhs.Expression with
Expand All @@ -714,7 +714,7 @@ type QsExpression with
| _ ->
resolve context lhs
|> (fun resStart ->
inference.Match(ResolvedType.New Int .> resStart.ResolvedType) |> List.iter diagnose
inference.Constrain(ResolvedType.New Int .> resStart.ResolvedType) |> List.iter diagnose
resStart)

let localQdependency =
Expand Down Expand Up @@ -773,7 +773,7 @@ type QsExpression with

let resolvedType =
if inference.Resolve(lhs.ResolvedType).Resolution = BigInt then
inference.Match(ResolvedType.New Int .> rhs.ResolvedType) |> List.iter diagnose
inference.Constrain(ResolvedType.New Int .> rhs.ResolvedType) |> List.iter diagnose
lhs.ResolvedType
else
verifyArithmeticOp inference this.Range lhs rhs |> takeDiagnostics
Expand Down Expand Up @@ -804,7 +804,7 @@ type QsExpression with
let lhs = resolve context lhs
let rhs = resolve context rhs
let resolvedType = verifyIsIntegral inference lhs |> takeDiagnostics
inference.Match(ResolvedType.New Int .> rhs.ResolvedType) |> List.iter diagnose
inference.Constrain(ResolvedType.New Int .> rhs.ResolvedType) |> List.iter diagnose

let localQdependency =
lhs.InferredInformation.HasLocalQuantumDependency
Expand Down Expand Up @@ -835,7 +835,7 @@ type QsExpression with
let cond = resolve context cond
let ifTrue = resolve context ifTrue
let ifFalse = resolve context ifFalse
inference.Match(ResolvedType.New Bool .> cond.ResolvedType) |> List.iter diagnose
inference.Constrain(ResolvedType.New Bool .> cond.ResolvedType) |> List.iter diagnose
verifyConditionalExecution ifTrue |> List.iter diagnose
verifyConditionalExecution ifFalse |> List.iter diagnose

Expand All @@ -855,7 +855,7 @@ type QsExpression with
let buildUnwrap ex =
let ex = resolve context ex
let exType = inference.Fresh this.RangeOrDefault
inference.Constrain(ex.ResolvedType, Wrapped exType) |> List.iter diagnose
Unwrap(ex.ResolvedType, exType) |> Class |> inference.Constrain |> List.iter diagnose

(UnwrapApplication ex, exType)
|> exprWithoutTypeArgs this.Range (inferred false ex.InferredInformation.HasLocalQuantumDependency)
Expand All @@ -869,19 +869,21 @@ type QsExpression with
let argType, partialType = partialArgType inference arg.ResolvedType

if Option.isNone partialType then
inference.Constrain(
callable.ResolvedType,
Set.ofSeq symbols.RequiredFunctorSupport |> CanGenerateFunctors
)
HasFunctorsIfOperation(callable.ResolvedType, Set.ofSeq symbols.RequiredFunctorSupport)
|> Class
|> inference.Constrain
|> List.iter diagnose

let output = inference.Fresh this.RangeOrDefault

if Option.isSome partialType || context.IsInOperation then
inference.Constrain(callable.ResolvedType, Callable(argType, output)) |> List.iter diagnose
Callable(callable.ResolvedType, argType, output)
|> Class
|> inference.Constrain
|> List.iter diagnose
else
let diagnostics =
inference.Match(callable.ResolvedType <. ResolvedType.New(QsTypeKind.Function(argType, output)))
inference.Constrain(callable.ResolvedType <. ResolvedType.New(QsTypeKind.Function(argType, output)))

if inference.Resolve callable.ResolvedType |> isOperation then
QsCompilerDiagnostic.Error(ErrorCode.OperationCallOutsideOfOperation, []) this.RangeOrDefault
Expand All @@ -894,7 +896,9 @@ type QsExpression with
| Some missing ->
let result = inference.Fresh this.RangeOrDefault

inference.Constrain(callable.ResolvedType, HasPartialApplication(missing, result))
HasPartialApplication(callable.ResolvedType, missing, result)
|> Class
|> inference.Constrain
|> List.iter diagnose

result
Expand Down Expand Up @@ -1049,6 +1053,6 @@ type QsExpression with
NOT
(fun ex' ->
Bool |> ResolvedType.create (TypeRange.inferred this.Range),
inference.Match(ResolvedType.New Bool .> ex'.ResolvedType))
inference.Constrain(ResolvedType.New Bool .> ex'.ResolvedType))
ex
| Lambda lambda -> buildLambda lambda
10 changes: 5 additions & 5 deletions src/QsCompiler/SyntaxProcessor/StatementVerification.fs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ let private asStatement comments location vars kind =
let NewExpressionStatement comments location context expr =
let expr, diagnostics = resolveExpr context expr

if context.Inference.Match(ResolvedType.New UnitType .> expr.ResolvedType) |> List.isEmpty |> not then
if context.Inference.Constrain(ResolvedType.New UnitType .> expr.ResolvedType) |> List.isEmpty |> not then
let type_ = context.Inference.Resolve expr.ResolvedType |> SyntaxTreeToQsharp.Default.ToCode
let range = QsNullable.defaultValue Range.Zero expr.Range
QsCompilerDiagnostic.Error(ErrorCode.ValueImplicitlyIgnored, [ type_ ]) range |> diagnostics.Add
Expand All @@ -70,7 +70,7 @@ let NewExpressionStatement comments location context expr =
/// Returns the built statement as well as an array of diagnostics generated during resolution and verification.
let NewFailStatement comments location context expr =
let expr, diagnostics = resolveExpr context expr
context.Inference.Match(ResolvedType.New String .> expr.ResolvedType) |> diagnostics.AddRange
context.Inference.Constrain(ResolvedType.New String .> expr.ResolvedType) |> diagnostics.AddRange
onAutoInvertCheckQuantumDependency context.Symbols expr |> diagnostics.AddRange
QsFailStatement expr |> asStatement comments location LocalDeclarations.Empty, diagnostics.ToArray()

Expand Down Expand Up @@ -217,7 +217,7 @@ let NewForStatement comments (location: QsLocation) context (symbol, expr) =
/// 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) context condition =
let condition, diagnostics = resolveExpr context condition
context.Inference.Match(ResolvedType.New Bool .> condition.ResolvedType) |> diagnostics.AddRange
context.Inference.Constrain(ResolvedType.New Bool .> condition.ResolvedType) |> diagnostics.AddRange

let whileLoop body =
QsWhileStatement.New(condition, body) |> QsWhileStatement
Expand All @@ -230,7 +230,7 @@ let NewWhileStatement comments (location: QsLocation) context condition =
/// as well as a delegate that given a positioned block of Q# statements returns the corresponding conditional block.
let NewConditionalBlock comments location context condition =
let condition, diagnostics = resolveExpr context condition
context.Inference.Match(ResolvedType.New Bool .> condition.ResolvedType) |> diagnostics.AddRange
context.Inference.Constrain(ResolvedType.New Bool .> condition.ResolvedType) |> diagnostics.AddRange
onAutoInvertCheckQuantumDependency context.Symbols condition |> diagnostics.AddRange
BlockStatement(fun body -> condition, QsPositionedBlock.New comments (Value location) body), diagnostics.ToArray()

Expand Down Expand Up @@ -322,7 +322,7 @@ let private NewBindingScope kind comments (location: QsLocation) context (symbol
SingleQubitAllocation |> ResolvedInitializer.create (TypeRange.inferred init.Range), Seq.empty
| QubitRegisterAllocation size ->
let size, diagnostics = resolveExpr context size
context.Inference.Match(ResolvedType.New Int .> size.ResolvedType) |> diagnostics.AddRange
context.Inference.Constrain(ResolvedType.New Int .> size.ResolvedType) |> diagnostics.AddRange
onAutoInvertCheckQuantumDependency context.Symbols size |> diagnostics.AddRange

QubitRegisterAllocation size |> ResolvedInitializer.create (TypeRange.inferred init.Range),
Expand Down
5 changes: 3 additions & 2 deletions src/QsCompiler/SyntaxProcessor/SyntaxProcessor.fsproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netstandard2.1</TargetFramework>
Expand All @@ -12,6 +11,8 @@
<Content Include="TypeInference\README.md" />
<Compile Include="TypeInference\Constraint.fsi" />
<Compile Include="TypeInference\Constraint.fs" />
<Compile Include="TypeInference\Diagnostic.fsi" />
<Compile Include="TypeInference\Diagnostic.fs" />
<Compile Include="TypeInference\InferenceContext.fsi" />
<Compile Include="TypeInference\InferenceContext.fs" />
<Compile Include="ScopeContext.fs" />
Expand All @@ -35,7 +36,7 @@

<ItemGroup>
<PackageReference Update="FSharp.Core" Version="4.7.0" />
<PackageReference Include="Microsoft.Experimental.Collections" Version="1.0.6-e190117-3" />
<PackageReference Update="System.ValueTuple" Version="4.4.0" />
</ItemGroup>

</Project>
Loading

0 comments on commit 59be764

Please sign in to comment.