Skip to content

Commit 6bdd9cf

Browse files
committed
[Type checker] Eliminate the 'literalConformanceProto' state on type variables.
The 'literalConformanceProto' field of TypeVariableType::Implementation didn't take into account equivalence classes of type variables. Eliminate it, and either look at the actual expressions (for optimizing constraints during constraint generation) or the actual constraints on a given type variable (for determining whether to include optionals in the set of potential type variable bindings).
1 parent 331937a commit 6bdd9cf

File tree

7 files changed

+101
-74
lines changed

7 files changed

+101
-74
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2920,7 +2920,8 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
29202920
// If simplification has turned this into the same types, then this isn't the
29212921
// broken constraint that we're looking for.
29222922
if (fromType->isEqual(toType) &&
2923-
constraint->getKind() != ConstraintKind::ConformsTo)
2923+
constraint->getKind() != ConstraintKind::ConformsTo &&
2924+
constraint->getKind() != ConstraintKind::LiteralConformsTo)
29242925
return false;
29252926

29262927

lib/Sema/CSGen.cpp

Lines changed: 51 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -500,49 +500,47 @@ namespace {
500500
return false;
501501
}
502502

503-
/// Determine whether the given parameter and argument type should be
503+
/// Determine whether the given parameter type and argument should be
504504
/// "favored" because they match exactly.
505505
bool isFavoredParamAndArg(ConstraintSystem &CS,
506506
Type paramTy,
507+
Expr *arg,
507508
Type argTy,
508-
Type otherArgTy) {
509-
if (argTy->getAs<LValueType>())
510-
argTy = argTy->getLValueOrInOutObjectType();
511-
512-
if (!otherArgTy.isNull() &&
513-
otherArgTy->getAs<LValueType>())
514-
otherArgTy = otherArgTy->getLValueOrInOutObjectType();
515-
509+
Expr *otherArg = nullptr,
510+
Type otherArgTy = Type()) {
511+
// Determine the argument type.
512+
argTy = argTy->getLValueOrInOutObjectType();
513+
516514
// Do the types match exactly?
517515
if (paramTy->isEqual(argTy))
518516
return true;
519-
520-
// If the argument is a type variable created for a literal that has a
521-
// default type, this is a favored param/arg pair if the parameter is of
522-
// that default type.
523-
// Is the argument a type variable...
524-
if (auto argTypeVar = argTy->getAs<TypeVariableType>()) {
525-
if (auto proto = argTypeVar->getImpl().literalConformanceProto) {
526-
// If it's a struct type associated with the literal conformance,
527-
// test against it directly. This helps to avoid 'widening' the
528-
// favored type to the default type for the literal.
529-
if (!otherArgTy.isNull() &&
530-
otherArgTy->getAs<StructType>()) {
531-
532-
if (CS.TC.conformsToProtocol(otherArgTy,
533-
proto,
534-
CS.DC,
535-
ConformanceCheckFlags::InExpression)) {
536-
return otherArgTy->isEqual(paramTy);
537-
}
538-
} else if (auto defaultTy = CS.TC.getDefaultType(proto, CS.DC)) {
539-
if (paramTy->isEqual(defaultTy)) {
540-
return true;
541-
}
542-
}
543-
}
517+
518+
// If the argument is a literal, this is a favored param/arg pair if
519+
// the parameter is of that default type.
520+
auto &tc = CS.getTypeChecker();
521+
auto literalProto = tc.getLiteralProtocol(arg->getSemanticsProvidingExpr());
522+
if (!literalProto) return false;
523+
524+
// Dig out the second argument type.
525+
if (otherArgTy)
526+
otherArgTy = otherArgTy->getLValueOrInOutObjectType();
527+
528+
// If there is another, concrete argument, check whether it's type
529+
// conforms to the literal protocol and test against it directly.
530+
// This helps to avoid 'widening' the favored type to the default type for
531+
// the literal.
532+
if (otherArgTy && otherArgTy->getAnyNominal()) {
533+
return otherArgTy->isEqual(paramTy) &&
534+
tc.conformsToProtocol(otherArgTy, literalProto, CS.DC,
535+
ConformanceCheckFlags::InExpression);
544536
}
545-
537+
538+
// If there is a default type for the literal protocol, check whether
539+
// it is the same as the parameter type.
540+
// Check whether there is a default type to compare against.
541+
if (Type defaultType = tc.getDefaultType(literalProto, CS.DC))
542+
return paramTy->isEqual(defaultType);
543+
546544
return false;
547545
}
548546

@@ -750,9 +748,6 @@ namespace {
750748
/// for the operand and contextual type.
751749
void favorMatchingUnaryOperators(ApplyExpr *expr,
752750
ConstraintSystem &CS) {
753-
// Find the argument type.
754-
auto argTy = getInnerParenType(expr->getArg()->getType());
755-
756751
// Determine whether the given declaration is favored.
757752
auto isFavoredDecl = [&](ValueDecl *value) -> bool {
758753
auto valueTy = value->getType();
@@ -770,7 +765,8 @@ namespace {
770765
auto resultTy = fnTy->getResult();
771766
auto contextualTy = CS.getContextualType(expr);
772767

773-
return isFavoredParamAndArg(CS, paramTy, argTy, Type()) &&
768+
return isFavoredParamAndArg(CS, paramTy, expr->getArg(),
769+
expr->getArg()->getType()) &&
774770
(!contextualTy || contextualTy->isEqual(resultTy));
775771
};
776772

@@ -889,8 +885,10 @@ namespace {
889885
if (!fnTy)
890886
return false;
891887

892-
auto firstFavoredTy = CS.getFavoredType(argTupleExpr->getElement(0));
893-
auto secondFavoredTy = CS.getFavoredType(argTupleExpr->getElement(1));
888+
Expr *firstArg = argTupleExpr->getElement(0);
889+
auto firstFavoredTy = CS.getFavoredType(firstArg);
890+
Expr *secondArg = argTupleExpr->getElement(1);
891+
auto secondFavoredTy = CS.getFavoredType(secondArg);
894892

895893
auto favoredExprTy = CS.getFavoredType(expr);
896894

@@ -934,8 +932,10 @@ namespace {
934932
auto contextualTy = CS.getContextualType(expr);
935933

936934
return
937-
(isFavoredParamAndArg(CS, firstParamTy, firstArgTy, secondArgTy) ||
938-
isFavoredParamAndArg(CS, secondParamTy, secondArgTy, firstArgTy)) &&
935+
(isFavoredParamAndArg(CS, firstParamTy, firstArg, firstArgTy,
936+
secondArg, secondArgTy) ||
937+
isFavoredParamAndArg(CS, secondParamTy, secondArg, secondArgTy,
938+
firstArg, firstArgTy)) &&
939939
firstParamTy->isEqual(secondParamTy) &&
940940
(!contextualTy || contextualTy->isEqual(resultTy));
941941
};
@@ -1091,7 +1091,7 @@ namespace {
10911091
auto keyTy = dictTy->first;
10921092
auto valueTy = dictTy->second;
10931093

1094-
if (isFavoredParamAndArg(CS, keyTy, index->getType(), Type())) {
1094+
if (isFavoredParamAndArg(CS, keyTy, index, index->getType())) {
10951095
outputTy = OptionalType::get(valueTy);
10961096

10971097
if (isLValueBase)
@@ -1172,10 +1172,7 @@ namespace {
11721172

11731173
auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr),
11741174
TVO_PrefersSubtypeBinding);
1175-
1176-
tv->getImpl().literalConformanceProto = protocol;
1177-
1178-
CS.addConstraint(ConstraintKind::ConformsTo, tv,
1175+
CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,
11791176
protocol->getDeclaredType(),
11801177
CS.getConstraintLocator(expr));
11811178
return tv;
@@ -1198,8 +1195,7 @@ namespace {
11981195
// ExpressibleByStringInterpolation protocol.
11991196
auto locator = CS.getConstraintLocator(expr);
12001197
auto tv = CS.createTypeVariable(locator, TVO_PrefersSubtypeBinding);
1201-
tv->getImpl().literalConformanceProto = interpolationProto;
1202-
CS.addConstraint(ConstraintKind::ConformsTo, tv,
1198+
CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,
12031199
interpolationProto->getDeclaredType(),
12041200
locator);
12051201

@@ -1272,9 +1268,7 @@ namespace {
12721268
auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr),
12731269
TVO_PrefersSubtypeBinding);
12741270

1275-
tv->getImpl().literalConformanceProto = protocol;
1276-
1277-
CS.addConstraint(ConstraintKind::ConformsTo, tv,
1271+
CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,
12781272
protocol->getDeclaredType(),
12791273
CS.getConstraintLocator(expr));
12801274

@@ -1691,7 +1685,7 @@ namespace {
16911685
contextualArrayElementType =
16921686
CS.getBaseTypeForArrayType(contextualType.getPointer());
16931687

1694-
CS.addConstraint(ConstraintKind::ConformsTo, contextualType,
1688+
CS.addConstraint(ConstraintKind::LiteralConformsTo, contextualType,
16951689
arrayProto->getDeclaredType(),
16961690
locator);
16971691

@@ -1711,7 +1705,7 @@ namespace {
17111705
auto arrayTy = CS.createTypeVariable(locator, TVO_PrefersSubtypeBinding);
17121706

17131707
// The array must be an array literal type.
1714-
CS.addConstraint(ConstraintKind::ConformsTo, arrayTy,
1708+
CS.addConstraint(ConstraintKind::LiteralConformsTo, arrayTy,
17151709
arrayProto->getDeclaredType(),
17161710
locator);
17171711

@@ -1777,8 +1771,8 @@ namespace {
17771771
auto dictionaryTy = CS.createTypeVariable(locator,
17781772
TVO_PrefersSubtypeBinding);
17791773

1780-
// The array must be a dictionary literal type.
1781-
CS.addConstraint(ConstraintKind::ConformsTo, dictionaryTy,
1774+
// The dictionary must be a dictionary literal type.
1775+
CS.addConstraint(ConstraintKind::LiteralConformsTo, dictionaryTy,
17821776
dictionaryProto->getDeclaredType(),
17831777
locator);
17841778

lib/Sema/CSSimplify.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2288,6 +2288,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
22882288
return SolutionKind::Solved;
22892289
break;
22902290
case ConstraintKind::ConformsTo:
2291+
case ConstraintKind::LiteralConformsTo:
22912292
// Check whether this type conforms to the protocol.
22922293
if (TC.conformsToProtocol(type, protocol, DC,
22932294
ConformanceCheckFlags::InExpression))
@@ -3473,6 +3474,7 @@ static TypeMatchKind getTypeMatchKind(ConstraintKind kind) {
34733474
llvm_unreachable("Overload binding constraints don't involve type matches");
34743475

34753476
case ConstraintKind::ConformsTo:
3477+
case ConstraintKind::LiteralConformsTo:
34763478
case ConstraintKind::SelfObjectOfProtocol:
34773479
llvm_unreachable("Conformance constraints don't involve type matches");
34783480

@@ -4132,6 +4134,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
41324134
return SolutionKind::Solved;
41334135

41344136
case ConstraintKind::ConformsTo:
4137+
case ConstraintKind::LiteralConformsTo:
41354138
case ConstraintKind::SelfObjectOfProtocol:
41364139
return simplifyConformsToConstraint(
41374140
constraint.getFirstType(),

lib/Sema/CSSolver.cpp

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,24 @@ static Optional<Type> checkTypeOfBinding(ConstraintSystem &cs,
6262
return None;
6363

6464
// If the type is a type variable itself, don't permit the binding.
65-
// FIXME: This is a hack. We need to be smarter about whether there's enough
66-
// structure in the type to produce an interesting binding, or not.
6765
if (auto bindingTypeVar = type->getRValueType()->getAs<TypeVariableType>()) {
68-
if (isNilLiteral &&
69-
bindingTypeVar->getImpl().literalConformanceProto &&
70-
bindingTypeVar->getImpl().literalConformanceProto->isSpecificProtocol(
71-
KnownProtocolKind::ExpressibleByNilLiteral))
72-
*isNilLiteral = true;
66+
if (isNilLiteral) {
67+
*isNilLiteral = false;
68+
69+
// Look for a literal-conformance constraint on the type variable.
70+
SmallVector<Constraint *, 8> constraints;
71+
cs.getConstraintGraph().gatherConstraints(bindingTypeVar, constraints);
72+
for (auto constraint : constraints) {
73+
if (constraint->getKind() == ConstraintKind::LiteralConformsTo &&
74+
constraint->getProtocol()->isSpecificProtocol(
75+
KnownProtocolKind::ExpressibleByNilLiteral) &&
76+
cs.simplifyType(constraint->getFirstType())
77+
->isEqual(bindingTypeVar)) {
78+
*isNilLiteral = true;
79+
break;
80+
}
81+
}
82+
}
7383

7484
return None;
7585
}
@@ -667,6 +677,7 @@ static bool shouldBindToValueType(Constraint *constraint)
667677
case ConstraintKind::Equal:
668678
case ConstraintKind::BindParam:
669679
case ConstraintKind::ConformsTo:
680+
case ConstraintKind::LiteralConformsTo:
670681
case ConstraintKind::CheckedCast:
671682
case ConstraintKind::SelfObjectOfProtocol:
672683
case ConstraintKind::ApplicableFunction:
@@ -782,8 +793,19 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
782793
result.InvolvesTypeVariables = true;
783794
continue;
784795

785-
case ConstraintKind::ConformsTo:
796+
case ConstraintKind::LiteralConformsTo:
797+
// If there is a 'nil' literal constraint, we might need optional
798+
// supertype bindings.
799+
if (constraint->getProtocol()->isSpecificProtocol(
800+
KnownProtocolKind::ExpressibleByNilLiteral))
801+
addOptionalSupertypeBindings = true;
802+
803+
SWIFT_FALLTHROUGH;
804+
805+
case ConstraintKind::ConformsTo:
786806
case ConstraintKind::SelfObjectOfProtocol: {
807+
// FIXME: Only for LiteralConformsTo?
808+
787809
// If there is a default literal type for this protocol, it's a
788810
// potential binding.
789811
auto defaultType = tc.getDefaultType(constraint->getProtocol(), cs.DC);
@@ -908,13 +930,17 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
908930
// Check whether we can perform this binding.
909931
// FIXME: this has a super-inefficient extraneous simplifyType() in it.
910932
bool isNilLiteral = false;
911-
if (auto boundType = checkTypeOfBinding(cs, typeVar, type, &isNilLiteral)) {
933+
bool *isNilLiteralPtr = nullptr;
934+
if (!addOptionalSupertypeBindings && kind == AllowedBindingKind::Supertypes)
935+
isNilLiteralPtr = &isNilLiteral;
936+
if (auto boundType = checkTypeOfBinding(cs, typeVar, type,
937+
isNilLiteralPtr)) {
912938
type = *boundType;
913939
if (type->hasTypeVariable())
914940
result.InvolvesTypeVariables = true;
915941
} else {
916942
// If the bound is a 'nil' literal type, add optional supertype bindings.
917-
if (isNilLiteral && kind == AllowedBindingKind::Supertypes) {
943+
if (isNilLiteral) {
918944
addOptionalSupertypeBindings = true;
919945
continue;
920946
}

lib/Sema/Constraint.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second,
6161
case ConstraintKind::OperatorArgumentTupleConversion:
6262
case ConstraintKind::OperatorArgumentConversion:
6363
case ConstraintKind::ConformsTo:
64+
case ConstraintKind::LiteralConformsTo:
6465
case ConstraintKind::CheckedCast:
6566
case ConstraintKind::SelfObjectOfProtocol:
6667
case ConstraintKind::DynamicTypeOf:
@@ -144,6 +145,7 @@ Constraint::Constraint(ConstraintKind kind, Fix fix,
144145

145146
ProtocolDecl *Constraint::getProtocol() const {
146147
assert((Kind == ConstraintKind::ConformsTo ||
148+
Kind == ConstraintKind::LiteralConformsTo ||
147149
Kind == ConstraintKind::SelfObjectOfProtocol)
148150
&& "Not a conformance constraint");
149151
return Types.Second->castTo<ProtocolType>()->getDecl();
@@ -162,6 +164,7 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const {
162164
case ConstraintKind::OperatorArgumentTupleConversion:
163165
case ConstraintKind::OperatorArgumentConversion:
164166
case ConstraintKind::ConformsTo:
167+
case ConstraintKind::LiteralConformsTo:
165168
case ConstraintKind::CheckedCast:
166169
case ConstraintKind::DynamicTypeOf:
167170
case ConstraintKind::SelfObjectOfProtocol:
@@ -238,6 +241,7 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const {
238241
case ConstraintKind::OperatorArgumentConversion:
239242
Out << " operator arg conv "; break;
240243
case ConstraintKind::ConformsTo: Out << " conforms to "; break;
244+
case ConstraintKind::LiteralConformsTo: Out << " literal conforms to "; break;
241245
case ConstraintKind::CheckedCast: Out << " checked cast to "; break;
242246
case ConstraintKind::SelfObjectOfProtocol: Out << " Self type of "; break;
243247
case ConstraintKind::ApplicableFunction: Out << " applicable fn "; break;
@@ -486,6 +490,7 @@ gatherReferencedTypeVars(Constraint *constraint,
486490
case ConstraintKind::BindOverload:
487491
case ConstraintKind::Class:
488492
case ConstraintKind::ConformsTo:
493+
case ConstraintKind::LiteralConformsTo:
489494
case ConstraintKind::SelfObjectOfProtocol:
490495
constraint->getFirstType()->getTypeVariables(typeVars);
491496

lib/Sema/Constraint.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ enum class ConstraintKind : char {
8181
/// \brief The first type must conform to the second type (which is a
8282
/// protocol type).
8383
ConformsTo,
84+
/// \brief The first type describes a literal that conforms to the second
85+
/// type, which is one of the known expressible-by-literal protocols.
86+
LiteralConformsTo,
8487
/// A checked cast from the first type to the second.
8588
CheckedCast,
8689
/// \brief The first type can act as the Self type of the second type (which
@@ -473,6 +476,7 @@ class Constraint final : public llvm::ilist_node<Constraint>,
473476
case ConstraintKind::OperatorArgumentTupleConversion:
474477
case ConstraintKind::OperatorArgumentConversion:
475478
case ConstraintKind::ConformsTo:
479+
case ConstraintKind::LiteralConformsTo:
476480
case ConstraintKind::CheckedCast:
477481
case ConstraintKind::SelfObjectOfProtocol:
478482
case ConstraintKind::ApplicableFunction:

lib/Sema/ConstraintSystem.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,6 @@ class TypeVariableType::Implementation {
158158
friend class constraints::SavedTypeVariableBinding;
159159

160160
public:
161-
162-
/// \brief If this type variable is an opened literal expression, keep track
163-
/// of the associated literal conformance for optimization and diagnostic
164-
/// purposes.
165-
ProtocolDecl *literalConformanceProto = nullptr;
166-
167161
explicit Implementation(constraints::ConstraintLocator *locator,
168162
unsigned options)
169163
: Options(options), locator(locator),

0 commit comments

Comments
 (0)