Skip to content

Commit 51ff12d

Browse files
committed
[ConstraintLocator] Augment ContextualType element to carry its purpose
Having purpose attached to the contextual type element makes it much easier to diagnose contextual mismatches without involving constraint system state.
1 parent 9df8944 commit 51ff12d

File tree

10 files changed

+95
-71
lines changed

10 files changed

+95
-71
lines changed

include/swift/Sema/ConstraintLocator.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,46 @@ class Pattern;
4141
class SourceManager;
4242
class ProtocolConformance;
4343

44+
/// This specifies the purpose of the contextual type, when specified to
45+
/// typeCheckExpression. This is used for diagnostic generation to produce more
46+
/// specified error messages when the conversion fails.
47+
///
48+
enum ContextualTypePurpose : uint8_t {
49+
CTP_Unused, ///< No contextual type is specified.
50+
CTP_Initialization, ///< Pattern binding initialization.
51+
CTP_ReturnStmt, ///< Value specified to a 'return' statement.
52+
CTP_ReturnSingleExpr, ///< Value implicitly returned from a function.
53+
CTP_YieldByValue, ///< By-value yield operand.
54+
CTP_YieldByReference, ///< By-reference yield operand.
55+
CTP_ThrowStmt, ///< Value specified to a 'throw' statement.
56+
CTP_EnumCaseRawValue, ///< Raw value specified for "case X = 42" in enum.
57+
CTP_DefaultParameter, ///< Default value in parameter 'foo(a : Int = 42)'.
58+
59+
/// Default value in @autoclosure parameter
60+
/// 'foo(a : @autoclosure () -> Int = 42)'.
61+
CTP_AutoclosureDefaultParameter,
62+
63+
CTP_CalleeResult, ///< Constraint is placed on the result of a callee.
64+
CTP_CallArgument, ///< Call to function or operator requires type.
65+
CTP_ClosureResult, ///< Closure result expects a specific type.
66+
CTP_ArrayElement, ///< ArrayExpr wants elements to have a specific type.
67+
CTP_DictionaryKey, ///< DictionaryExpr keys should have a specific type.
68+
CTP_DictionaryValue, ///< DictionaryExpr values should have a specific type.
69+
CTP_CoerceOperand, ///< CoerceExpr operand coerced to specific type.
70+
CTP_AssignSource, ///< AssignExpr source operand coerced to result type.
71+
CTP_SubscriptAssignSource, ///< AssignExpr source operand coerced to subscript
72+
///< result type.
73+
CTP_Condition, ///< Condition expression of various statements e.g.
74+
///< `if`, `for`, `while` etc.
75+
CTP_ForEachStmt, ///< "expression/sequence" associated with 'for-in' loop
76+
///< is expected to conform to 'Sequence' protocol.
77+
CTP_WrappedProperty, ///< Property type expected to match 'wrappedValue' type
78+
CTP_ComposedPropertyWrapper, ///< Composed wrapper type expected to match
79+
///< former 'wrappedValue' type
80+
81+
CTP_CannotFail, ///< Conversion can never fail. abort() if it does.
82+
};
83+
4484
namespace constraints {
4585

4686
class ConstraintSystem;
@@ -851,6 +891,25 @@ class LocatorPathElt::ImplicitConversion final
851891
}
852892
};
853893

894+
class LocatorPathElt::ContextualType final : public StoredIntegerElement<1> {
895+
public:
896+
ContextualType(ContextualTypePurpose purpose)
897+
: StoredIntegerElement(ConstraintLocator::ContextualType,
898+
static_cast<unsigned>(purpose)) {}
899+
900+
ContextualTypePurpose getPurpose() const {
901+
return static_cast<ContextualTypePurpose>(getValue());
902+
}
903+
904+
bool isFor(ContextualTypePurpose purpose) const {
905+
return getPurpose() == purpose;
906+
}
907+
908+
static bool classof(const LocatorPathElt *elt) {
909+
return elt->getKind() == ConstraintLocator::ContextualType;
910+
}
911+
};
912+
854913
namespace details {
855914
template <typename CustomPathElement>
856915
class PathElement {

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ CUSTOM_LOCATOR_PATH_ELT(ClosureBody)
6161
SIMPLE_LOCATOR_PATH_ELT(ConstructorMember)
6262

6363
/// The desired contextual type passed in to the constraint system.
64-
SIMPLE_LOCATOR_PATH_ELT(ContextualType)
64+
CUSTOM_LOCATOR_PATH_ELT(ContextualType)
6565

6666
/// A result of an expression involving dynamic lookup.
6767
SIMPLE_LOCATOR_PATH_ELT(DynamicLookupResult)

include/swift/Sema/ConstraintSystem.h

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -90,46 +90,6 @@ void *operator new(size_t bytes, swift::constraints::ConstraintSystem& cs,
9090

9191
namespace swift {
9292

93-
/// This specifies the purpose of the contextual type, when specified to
94-
/// typeCheckExpression. This is used for diagnostic generation to produce more
95-
/// specified error messages when the conversion fails.
96-
///
97-
enum ContextualTypePurpose {
98-
CTP_Unused, ///< No contextual type is specified.
99-
CTP_Initialization, ///< Pattern binding initialization.
100-
CTP_ReturnStmt, ///< Value specified to a 'return' statement.
101-
CTP_ReturnSingleExpr, ///< Value implicitly returned from a function.
102-
CTP_YieldByValue, ///< By-value yield operand.
103-
CTP_YieldByReference, ///< By-reference yield operand.
104-
CTP_ThrowStmt, ///< Value specified to a 'throw' statement.
105-
CTP_EnumCaseRawValue, ///< Raw value specified for "case X = 42" in enum.
106-
CTP_DefaultParameter, ///< Default value in parameter 'foo(a : Int = 42)'.
107-
108-
/// Default value in @autoclosure parameter
109-
/// 'foo(a : @autoclosure () -> Int = 42)'.
110-
CTP_AutoclosureDefaultParameter,
111-
112-
CTP_CalleeResult, ///< Constraint is placed on the result of a callee.
113-
CTP_CallArgument, ///< Call to function or operator requires type.
114-
CTP_ClosureResult, ///< Closure result expects a specific type.
115-
CTP_ArrayElement, ///< ArrayExpr wants elements to have a specific type.
116-
CTP_DictionaryKey, ///< DictionaryExpr keys should have a specific type.
117-
CTP_DictionaryValue, ///< DictionaryExpr values should have a specific type.
118-
CTP_CoerceOperand, ///< CoerceExpr operand coerced to specific type.
119-
CTP_AssignSource, ///< AssignExpr source operand coerced to result type.
120-
CTP_SubscriptAssignSource, ///< AssignExpr source operand coerced to subscript
121-
///< result type.
122-
CTP_Condition, ///< Condition expression of various statements e.g.
123-
///< `if`, `for`, `while` etc.
124-
CTP_ForEachStmt, ///< "expression/sequence" associated with 'for-in' loop
125-
///< is expected to conform to 'Sequence' protocol.
126-
CTP_WrappedProperty, ///< Property type expected to match 'wrappedValue' type
127-
CTP_ComposedPropertyWrapper, ///< Composed wrapper type expected to match
128-
///< former 'wrappedValue' type
129-
130-
CTP_CannotFail, ///< Conversion can never fail. abort() if it does.
131-
};
132-
13393
/// Specify how we handle the binding of underconstrained (free) type variables
13494
/// within a solution to a constraint system.
13595
enum class FreeTypeVariableBinding {

lib/Sema/BuilderTransform.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ class BuilderClosureVisitor
767767
// If needed, generate constraints for everything in the case statement.
768768
if (cs) {
769769
auto locator = cs->getConstraintLocator(
770-
subjectExpr, LocatorPathElt::ContextualType());
770+
subjectExpr, LocatorPathElt::ContextualType(CTP_Initialization));
771771
Type subjectType = cs->getType(subjectExpr);
772772

773773
if (cs->generateConstraints(caseStmt, dc, subjectType, locator)) {
@@ -851,7 +851,7 @@ class BuilderClosureVisitor
851851
cs->addConstraint(
852852
ConstraintKind::Equal, cs->getType(arrayInitExpr), arrayType,
853853
cs->getConstraintLocator(
854-
arrayInitExpr, LocatorPathElt::ContextualType()));
854+
arrayInitExpr, LocatorPathElt::ContextualType(CTP_Initialization)));
855855

856856
// Form a call to Array.append(_:) to add the result of executing each
857857
// iteration of the loop body to the array formed above.

lib/Sema/CSApply.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8336,8 +8336,8 @@ static Optional<SolutionApplicationTarget> applySolutionToInitialization(
83368336

83378337
// Convert the initializer to the type of the pattern.
83388338
auto &cs = solution.getConstraintSystem();
8339-
auto locator =
8340-
cs.getConstraintLocator(initializer, LocatorPathElt::ContextualType());
8339+
auto locator = cs.getConstraintLocator(
8340+
initializer, LocatorPathElt::ContextualType(CTP_Initialization));
83418341
initializer = solution.coerceToType(initializer, initType, locator);
83428342
if (!initializer)
83438343
return None;

lib/Sema/CSDiagnostics.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6692,8 +6692,9 @@ bool UnableToInferClosureParameterType::diagnoseAsError() {
66926692

66936693
// If there is a contextual mismatch associated with this
66946694
// closure, let's not diagnose any parameter type issues.
6695-
if (hasFixFor(solution, getConstraintLocator(
6696-
closure, LocatorPathElt::ContextualType())))
6695+
if (hasFixFor(solution,
6696+
getConstraintLocator(closure, LocatorPathElt::ContextualType(
6697+
CTP_Initialization))))
66976698
return false;
66986699

66996700
if (auto *parentExpr = findParentExpr(closure)) {

lib/Sema/CSFix.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1340,7 +1340,8 @@ bool IgnoreAssignmentDestinationType::diagnose(const Solution &solution,
13401340

13411341
AssignmentTypeMismatchFailure failure(
13421342
solution, CTP, getFromType(), getToType(),
1343-
cs.getConstraintLocator(AE->getSrc(), LocatorPathElt::ContextualType()));
1343+
cs.getConstraintLocator(AE->getSrc(),
1344+
LocatorPathElt::ContextualType(CTP)));
13441345
return failure.diagnose(asNote);
13451346
}
13461347

lib/Sema/CSGen.cpp

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3626,8 +3626,10 @@ static bool generateWrappedPropertyTypeConstraints(
36263626
}
36273627

36283628
// The property type must be equal to the wrapped value type
3629-
cs.addConstraint(ConstraintKind::Equal, propertyType, wrappedValueType,
3630-
cs.getConstraintLocator(wrappedVar, LocatorPathElt::ContextualType()));
3629+
cs.addConstraint(
3630+
ConstraintKind::Equal, propertyType, wrappedValueType,
3631+
cs.getConstraintLocator(
3632+
wrappedVar, LocatorPathElt::ContextualType(CTP_WrappedProperty)));
36313633
cs.setContextualType(wrappedVar, TypeLoc::withoutLoc(wrappedValueType),
36323634
CTP_WrappedProperty);
36333635
return false;
@@ -3637,8 +3639,8 @@ static bool generateWrappedPropertyTypeConstraints(
36373639
static bool generateInitPatternConstraints(
36383640
ConstraintSystem &cs, SolutionApplicationTarget target, Expr *initializer) {
36393641
auto pattern = target.getInitializationPattern();
3640-
auto locator =
3641-
cs.getConstraintLocator(initializer, LocatorPathElt::ContextualType());
3642+
auto locator = cs.getConstraintLocator(
3643+
initializer, LocatorPathElt::ContextualType(CTP_Initialization));
36423644
Type patternType = cs.generateConstraints(
36433645
pattern, locator, target.shouldBindPatternVarsOneWay(),
36443646
target.getInitializationPatternBindingDecl(),
@@ -3669,8 +3671,8 @@ generateForEachStmtConstraints(
36693671
bool isAsync = stmt->getAwaitLoc().isValid();
36703672

36713673
auto locator = cs.getConstraintLocator(sequence);
3672-
auto contextualLocator =
3673-
cs.getConstraintLocator(sequence, LocatorPathElt::ContextualType());
3674+
auto contextualLocator = cs.getConstraintLocator(
3675+
sequence, LocatorPathElt::ContextualType(CTP_ForEachStmt));
36743676

36753677
// The expression type must conform to the Sequence protocol.
36763678
auto sequenceProto = TypeChecker::getProtocol(
@@ -3787,8 +3789,9 @@ bool ConstraintSystem::generateConstraints(
37873789
if (target.isOptionalSomePatternInit()) {
37883790
assert(!target.getExprContextualType() &&
37893791
"some pattern cannot have contextual type pre-configured");
3790-
auto *convertTypeLocator =
3791-
getConstraintLocator(expr, LocatorPathElt::ContextualType());
3792+
auto *convertTypeLocator = getConstraintLocator(
3793+
expr, LocatorPathElt::ContextualType(
3794+
target.getExprContextualTypePurpose()));
37923795
Type var = createTypeVariable(convertTypeLocator, TVO_CanBindToNoEscape);
37933796
target.setExprConversionType(TypeChecker::getOptionalType(expr->getLoc(), var));
37943797
}
@@ -3810,7 +3813,7 @@ bool ConstraintSystem::generateConstraints(
38103813
ContextualTypePurpose ctp = target.getExprContextualTypePurpose();
38113814
bool isOpaqueReturnType = target.infersOpaqueReturnType();
38123815
auto *convertTypeLocator =
3813-
getConstraintLocator(expr, LocatorPathElt::ContextualType());
3816+
getConstraintLocator(expr, LocatorPathElt::ContextualType(ctp));
38143817

38153818
auto getLocator = [&](Type ty) -> ConstraintLocator * {
38163819
// If we have a placeholder originating from a PlaceholderTypeRepr,
@@ -3973,11 +3976,10 @@ bool ConstraintSystem::generateConstraints(StmtCondition condition,
39733976
return true;
39743977
}
39753978

3976-
addConstraint(ConstraintKind::Conversion,
3977-
getType(condExpr),
3978-
boolTy,
3979-
getConstraintLocator(condExpr,
3980-
LocatorPathElt::ContextualType()));
3979+
addConstraint(
3980+
ConstraintKind::Conversion, getType(condExpr), boolTy,
3981+
getConstraintLocator(condExpr,
3982+
LocatorPathElt::ContextualType(CTP_Condition)));
39813983
continue;
39823984
}
39833985

lib/Sema/CSSimplify.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4784,14 +4784,14 @@ bool ConstraintSystem::repairFailures(
47844784
// truth and produce a contextual mismatch instead of per-branch failure,
47854785
// because it's a better pointer than potential then-to-else type mismatch.
47864786
if (auto contextualType = getContextualType(anchor)) {
4787+
auto purpose = getContextualTypePurpose(anchor);
47874788
if (contextualType->isEqual(rhs)) {
4788-
auto *loc =
4789-
getConstraintLocator(anchor, LocatorPathElt::ContextualType());
4789+
auto *loc = getConstraintLocator(
4790+
anchor, LocatorPathElt::ContextualType(purpose));
47904791
if (hasFixFor(loc, FixKind::ContextualMismatch))
47914792
return true;
47924793

4793-
if (contextualType->isVoid() &&
4794-
getContextualTypePurpose(anchor) == CTP_ReturnStmt) {
4794+
if (contextualType->isVoid() && purpose == CTP_ReturnStmt) {
47954795
conversionsOrFixes.push_back(RemoveReturn::create(*this, lhs, loc));
47964796
break;
47974797
}
@@ -6135,7 +6135,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
61356135
if (auto *Nil = getAsExpr<NilLiteralExpr>(anchor)) {
61366136
auto *fixLocator = getConstraintLocator(
61376137
getContextualType(Nil)
6138-
? locator.withPathElement(LocatorPathElt::ContextualType())
6138+
? locator.withPathElement(LocatorPathElt::ContextualType(
6139+
getContextualTypePurpose(Nil)))
61396140
: locator);
61406141

61416142
// Here the roles are reversed - `nil` is something we are trying to
@@ -11788,7 +11789,7 @@ void ConstraintSystem::addContextualConversionConstraint(
1178811789

1178911790
// Add the constraint.
1179011791
auto *convertTypeLocator =
11791-
getConstraintLocator(expr, LocatorPathElt::ContextualType());
11792+
getConstraintLocator(expr, LocatorPathElt::ContextualType(purpose));
1179211793
addConstraint(constraintKind, getType(expr), conversionType,
1179311794
convertTypeLocator, /*isFavored*/ true);
1179411795
}

unittests/Sema/BindingInferenceTests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,9 @@ TEST_F(SemaTest, TestTransitiveProtocolInference) {
187187
auto *typeVar = cs.createTypeVariable(cs.getConstraintLocator({}),
188188
/*options=*/0);
189189

190-
cs.addConstraint(
191-
ConstraintKind::Conversion, typeVar, GPT1,
192-
cs.getConstraintLocator({}, LocatorPathElt::ContextualType()));
190+
cs.addConstraint(ConstraintKind::Conversion, typeVar, GPT1,
191+
cs.getConstraintLocator({}, LocatorPathElt::ContextualType(
192+
CTP_Initialization)));
193193

194194
auto bindings = inferBindings(cs, typeVar);
195195
ASSERT_TRUE(bindings.getConformanceRequirements().empty());

0 commit comments

Comments
 (0)