[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).

(cherry picked from commit 6bdd9cfae5)
This commit is contained in:
Doug Gregor
2016-10-11 16:57:53 -07:00
parent 053f3b4b48
commit 49b833b51a
7 changed files with 101 additions and 74 deletions

View File

@@ -2920,7 +2920,8 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
// If simplification has turned this into the same types, then this isn't the // If simplification has turned this into the same types, then this isn't the
// broken constraint that we're looking for. // broken constraint that we're looking for.
if (fromType->isEqual(toType) && if (fromType->isEqual(toType) &&
constraint->getKind() != ConstraintKind::ConformsTo) constraint->getKind() != ConstraintKind::ConformsTo &&
constraint->getKind() != ConstraintKind::LiteralConformsTo)
return false; return false;

View File

@@ -500,49 +500,47 @@ namespace {
return false; return false;
} }
/// Determine whether the given parameter and argument type should be /// Determine whether the given parameter type and argument should be
/// "favored" because they match exactly. /// "favored" because they match exactly.
bool isFavoredParamAndArg(ConstraintSystem &CS, bool isFavoredParamAndArg(ConstraintSystem &CS,
Type paramTy, Type paramTy,
Expr *arg,
Type argTy, Type argTy,
Type otherArgTy) { Expr *otherArg = nullptr,
if (argTy->getAs<LValueType>()) Type otherArgTy = Type()) {
argTy = argTy->getLValueOrInOutObjectType(); // Determine the argument type.
argTy = argTy->getLValueOrInOutObjectType();
if (!otherArgTy.isNull() &&
otherArgTy->getAs<LValueType>())
otherArgTy = otherArgTy->getLValueOrInOutObjectType();
// Do the types match exactly? // Do the types match exactly?
if (paramTy->isEqual(argTy)) if (paramTy->isEqual(argTy))
return true; return true;
// If the argument is a type variable created for a literal that has a // If the argument is a literal, this is a favored param/arg pair if
// default type, this is a favored param/arg pair if the parameter is of // the parameter is of that default type.
// that default type. auto &tc = CS.getTypeChecker();
// Is the argument a type variable... auto literalProto = tc.getLiteralProtocol(arg->getSemanticsProvidingExpr());
if (auto argTypeVar = argTy->getAs<TypeVariableType>()) { if (!literalProto) return false;
if (auto proto = argTypeVar->getImpl().literalConformanceProto) {
// If it's a struct type associated with the literal conformance, // Dig out the second argument type.
// test against it directly. This helps to avoid 'widening' the if (otherArgTy)
// favored type to the default type for the literal. otherArgTy = otherArgTy->getLValueOrInOutObjectType();
if (!otherArgTy.isNull() &&
otherArgTy->getAs<StructType>()) { // If there is another, concrete argument, check whether it's type
// conforms to the literal protocol and test against it directly.
if (CS.TC.conformsToProtocol(otherArgTy, // This helps to avoid 'widening' the favored type to the default type for
proto, // the literal.
CS.DC, if (otherArgTy && otherArgTy->getAnyNominal()) {
ConformanceCheckFlags::InExpression)) { return otherArgTy->isEqual(paramTy) &&
return otherArgTy->isEqual(paramTy); tc.conformsToProtocol(otherArgTy, literalProto, CS.DC,
} ConformanceCheckFlags::InExpression);
} else if (auto defaultTy = CS.TC.getDefaultType(proto, CS.DC)) {
if (paramTy->isEqual(defaultTy)) {
return true;
}
}
}
} }
// If there is a default type for the literal protocol, check whether
// it is the same as the parameter type.
// Check whether there is a default type to compare against.
if (Type defaultType = tc.getDefaultType(literalProto, CS.DC))
return paramTy->isEqual(defaultType);
return false; return false;
} }
@@ -742,9 +740,6 @@ namespace {
/// for the operand and contextual type. /// for the operand and contextual type.
void favorMatchingUnaryOperators(ApplyExpr *expr, void favorMatchingUnaryOperators(ApplyExpr *expr,
ConstraintSystem &CS) { ConstraintSystem &CS) {
// Find the argument type.
auto argTy = expr->getArg()->getType()->getWithoutParens();
// Determine whether the given declaration is favored. // Determine whether the given declaration is favored.
auto isFavoredDecl = [&](ValueDecl *value) -> bool { auto isFavoredDecl = [&](ValueDecl *value) -> bool {
auto valueTy = value->getType(); auto valueTy = value->getType();
@@ -762,7 +757,8 @@ namespace {
auto resultTy = fnTy->getResult(); auto resultTy = fnTy->getResult();
auto contextualTy = CS.getContextualType(expr); auto contextualTy = CS.getContextualType(expr);
return isFavoredParamAndArg(CS, paramTy, argTy, Type()) && return isFavoredParamAndArg(CS, paramTy, expr->getArg(),
expr->getArg()->getType()->getWithoutParens()) &&
(!contextualTy || contextualTy->isEqual(resultTy)); (!contextualTy || contextualTy->isEqual(resultTy));
}; };
@@ -881,8 +877,10 @@ namespace {
if (!fnTy) if (!fnTy)
return false; return false;
auto firstFavoredTy = CS.getFavoredType(argTupleExpr->getElement(0)); Expr *firstArg = argTupleExpr->getElement(0);
auto secondFavoredTy = CS.getFavoredType(argTupleExpr->getElement(1)); auto firstFavoredTy = CS.getFavoredType(firstArg);
Expr *secondArg = argTupleExpr->getElement(1);
auto secondFavoredTy = CS.getFavoredType(secondArg);
auto favoredExprTy = CS.getFavoredType(expr); auto favoredExprTy = CS.getFavoredType(expr);
@@ -926,8 +924,10 @@ namespace {
auto contextualTy = CS.getContextualType(expr); auto contextualTy = CS.getContextualType(expr);
return return
(isFavoredParamAndArg(CS, firstParamTy, firstArgTy, secondArgTy) || (isFavoredParamAndArg(CS, firstParamTy, firstArg, firstArgTy,
isFavoredParamAndArg(CS, secondParamTy, secondArgTy, firstArgTy)) && secondArg, secondArgTy) ||
isFavoredParamAndArg(CS, secondParamTy, secondArg, secondArgTy,
firstArg, firstArgTy)) &&
firstParamTy->isEqual(secondParamTy) && firstParamTy->isEqual(secondParamTy) &&
(!contextualTy || contextualTy->isEqual(resultTy)); (!contextualTy || contextualTy->isEqual(resultTy));
}; };
@@ -1083,7 +1083,7 @@ namespace {
auto keyTy = dictTy->first; auto keyTy = dictTy->first;
auto valueTy = dictTy->second; auto valueTy = dictTy->second;
if (isFavoredParamAndArg(CS, keyTy, index->getType(), Type())) { if (isFavoredParamAndArg(CS, keyTy, index, index->getType())) {
outputTy = OptionalType::get(valueTy); outputTy = OptionalType::get(valueTy);
if (isLValueBase) if (isLValueBase)
@@ -1164,10 +1164,7 @@ namespace {
auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr), auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_PrefersSubtypeBinding); TVO_PrefersSubtypeBinding);
CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,
tv->getImpl().literalConformanceProto = protocol;
CS.addConstraint(ConstraintKind::ConformsTo, tv,
protocol->getDeclaredType(), protocol->getDeclaredType(),
CS.getConstraintLocator(expr)); CS.getConstraintLocator(expr));
return tv; return tv;
@@ -1190,8 +1187,7 @@ namespace {
// ExpressibleByStringInterpolation protocol. // ExpressibleByStringInterpolation protocol.
auto locator = CS.getConstraintLocator(expr); auto locator = CS.getConstraintLocator(expr);
auto tv = CS.createTypeVariable(locator, TVO_PrefersSubtypeBinding); auto tv = CS.createTypeVariable(locator, TVO_PrefersSubtypeBinding);
tv->getImpl().literalConformanceProto = interpolationProto; CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,
CS.addConstraint(ConstraintKind::ConformsTo, tv,
interpolationProto->getDeclaredType(), interpolationProto->getDeclaredType(),
locator); locator);
@@ -1264,9 +1260,7 @@ namespace {
auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr), auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_PrefersSubtypeBinding); TVO_PrefersSubtypeBinding);
tv->getImpl().literalConformanceProto = protocol; CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,
CS.addConstraint(ConstraintKind::ConformsTo, tv,
protocol->getDeclaredType(), protocol->getDeclaredType(),
CS.getConstraintLocator(expr)); CS.getConstraintLocator(expr));
@@ -1683,7 +1677,7 @@ namespace {
contextualArrayElementType = contextualArrayElementType =
CS.getBaseTypeForArrayType(contextualType.getPointer()); CS.getBaseTypeForArrayType(contextualType.getPointer());
CS.addConstraint(ConstraintKind::ConformsTo, contextualType, CS.addConstraint(ConstraintKind::LiteralConformsTo, contextualType,
arrayProto->getDeclaredType(), arrayProto->getDeclaredType(),
locator); locator);
@@ -1703,7 +1697,7 @@ namespace {
auto arrayTy = CS.createTypeVariable(locator, TVO_PrefersSubtypeBinding); auto arrayTy = CS.createTypeVariable(locator, TVO_PrefersSubtypeBinding);
// The array must be an array literal type. // The array must be an array literal type.
CS.addConstraint(ConstraintKind::ConformsTo, arrayTy, CS.addConstraint(ConstraintKind::LiteralConformsTo, arrayTy,
arrayProto->getDeclaredType(), arrayProto->getDeclaredType(),
locator); locator);
@@ -1769,8 +1763,8 @@ namespace {
auto dictionaryTy = CS.createTypeVariable(locator, auto dictionaryTy = CS.createTypeVariable(locator,
TVO_PrefersSubtypeBinding); TVO_PrefersSubtypeBinding);
// The array must be a dictionary literal type. // The dictionary must be a dictionary literal type.
CS.addConstraint(ConstraintKind::ConformsTo, dictionaryTy, CS.addConstraint(ConstraintKind::LiteralConformsTo, dictionaryTy,
dictionaryProto->getDeclaredType(), dictionaryProto->getDeclaredType(),
locator); locator);

View File

@@ -2288,6 +2288,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
return SolutionKind::Solved; return SolutionKind::Solved;
break; break;
case ConstraintKind::ConformsTo: case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
// Check whether this type conforms to the protocol. // Check whether this type conforms to the protocol.
if (TC.conformsToProtocol(type, protocol, DC, if (TC.conformsToProtocol(type, protocol, DC,
ConformanceCheckFlags::InExpression)) ConformanceCheckFlags::InExpression))
@@ -3467,6 +3468,7 @@ static TypeMatchKind getTypeMatchKind(ConstraintKind kind) {
llvm_unreachable("Overload binding constraints don't involve type matches"); llvm_unreachable("Overload binding constraints don't involve type matches");
case ConstraintKind::ConformsTo: case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::SelfObjectOfProtocol:
llvm_unreachable("Conformance constraints don't involve type matches"); llvm_unreachable("Conformance constraints don't involve type matches");
@@ -4126,6 +4128,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
return SolutionKind::Solved; return SolutionKind::Solved;
case ConstraintKind::ConformsTo: case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::SelfObjectOfProtocol:
return simplifyConformsToConstraint( return simplifyConformsToConstraint(
constraint.getFirstType(), constraint.getFirstType(),

View File

@@ -62,14 +62,24 @@ static Optional<Type> checkTypeOfBinding(ConstraintSystem &cs,
return None; return None;
// If the type is a type variable itself, don't permit the binding. // If the type is a type variable itself, don't permit the binding.
// FIXME: This is a hack. We need to be smarter about whether there's enough
// structure in the type to produce an interesting binding, or not.
if (auto bindingTypeVar = type->getRValueType()->getAs<TypeVariableType>()) { if (auto bindingTypeVar = type->getRValueType()->getAs<TypeVariableType>()) {
if (isNilLiteral && if (isNilLiteral) {
bindingTypeVar->getImpl().literalConformanceProto && *isNilLiteral = false;
bindingTypeVar->getImpl().literalConformanceProto->isSpecificProtocol(
KnownProtocolKind::ExpressibleByNilLiteral)) // Look for a literal-conformance constraint on the type variable.
*isNilLiteral = true; SmallVector<Constraint *, 8> constraints;
cs.getConstraintGraph().gatherConstraints(bindingTypeVar, constraints);
for (auto constraint : constraints) {
if (constraint->getKind() == ConstraintKind::LiteralConformsTo &&
constraint->getProtocol()->isSpecificProtocol(
KnownProtocolKind::ExpressibleByNilLiteral) &&
cs.simplifyType(constraint->getFirstType())
->isEqual(bindingTypeVar)) {
*isNilLiteral = true;
break;
}
}
}
return None; return None;
} }
@@ -667,6 +677,7 @@ static bool shouldBindToValueType(Constraint *constraint)
case ConstraintKind::Equal: case ConstraintKind::Equal:
case ConstraintKind::BindParam: case ConstraintKind::BindParam:
case ConstraintKind::ConformsTo: case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::CheckedCast: case ConstraintKind::CheckedCast:
case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::ApplicableFunction: case ConstraintKind::ApplicableFunction:
@@ -782,8 +793,19 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
result.InvolvesTypeVariables = true; result.InvolvesTypeVariables = true;
continue; continue;
case ConstraintKind::ConformsTo: case ConstraintKind::LiteralConformsTo:
// If there is a 'nil' literal constraint, we might need optional
// supertype bindings.
if (constraint->getProtocol()->isSpecificProtocol(
KnownProtocolKind::ExpressibleByNilLiteral))
addOptionalSupertypeBindings = true;
SWIFT_FALLTHROUGH;
case ConstraintKind::ConformsTo:
case ConstraintKind::SelfObjectOfProtocol: { case ConstraintKind::SelfObjectOfProtocol: {
// FIXME: Only for LiteralConformsTo?
// If there is a default literal type for this protocol, it's a // If there is a default literal type for this protocol, it's a
// potential binding. // potential binding.
auto defaultType = tc.getDefaultType(constraint->getProtocol(), cs.DC); auto defaultType = tc.getDefaultType(constraint->getProtocol(), cs.DC);
@@ -908,13 +930,17 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
// Check whether we can perform this binding. // Check whether we can perform this binding.
// FIXME: this has a super-inefficient extraneous simplifyType() in it. // FIXME: this has a super-inefficient extraneous simplifyType() in it.
bool isNilLiteral = false; bool isNilLiteral = false;
if (auto boundType = checkTypeOfBinding(cs, typeVar, type, &isNilLiteral)) { bool *isNilLiteralPtr = nullptr;
if (!addOptionalSupertypeBindings && kind == AllowedBindingKind::Supertypes)
isNilLiteralPtr = &isNilLiteral;
if (auto boundType = checkTypeOfBinding(cs, typeVar, type,
isNilLiteralPtr)) {
type = *boundType; type = *boundType;
if (type->hasTypeVariable()) if (type->hasTypeVariable())
result.InvolvesTypeVariables = true; result.InvolvesTypeVariables = true;
} else { } else {
// If the bound is a 'nil' literal type, add optional supertype bindings. // If the bound is a 'nil' literal type, add optional supertype bindings.
if (isNilLiteral && kind == AllowedBindingKind::Supertypes) { if (isNilLiteral) {
addOptionalSupertypeBindings = true; addOptionalSupertypeBindings = true;
continue; continue;
} }

View File

@@ -61,6 +61,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second,
case ConstraintKind::OperatorArgumentTupleConversion: case ConstraintKind::OperatorArgumentTupleConversion:
case ConstraintKind::OperatorArgumentConversion: case ConstraintKind::OperatorArgumentConversion:
case ConstraintKind::ConformsTo: case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::CheckedCast: case ConstraintKind::CheckedCast:
case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::DynamicTypeOf: case ConstraintKind::DynamicTypeOf:
@@ -144,6 +145,7 @@ Constraint::Constraint(ConstraintKind kind, Fix fix,
ProtocolDecl *Constraint::getProtocol() const { ProtocolDecl *Constraint::getProtocol() const {
assert((Kind == ConstraintKind::ConformsTo || assert((Kind == ConstraintKind::ConformsTo ||
Kind == ConstraintKind::LiteralConformsTo ||
Kind == ConstraintKind::SelfObjectOfProtocol) Kind == ConstraintKind::SelfObjectOfProtocol)
&& "Not a conformance constraint"); && "Not a conformance constraint");
return Types.Second->castTo<ProtocolType>()->getDecl(); return Types.Second->castTo<ProtocolType>()->getDecl();
@@ -162,6 +164,7 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const {
case ConstraintKind::OperatorArgumentTupleConversion: case ConstraintKind::OperatorArgumentTupleConversion:
case ConstraintKind::OperatorArgumentConversion: case ConstraintKind::OperatorArgumentConversion:
case ConstraintKind::ConformsTo: case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::CheckedCast: case ConstraintKind::CheckedCast:
case ConstraintKind::DynamicTypeOf: case ConstraintKind::DynamicTypeOf:
case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::SelfObjectOfProtocol:
@@ -238,6 +241,7 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const {
case ConstraintKind::OperatorArgumentConversion: case ConstraintKind::OperatorArgumentConversion:
Out << " operator arg conv "; break; Out << " operator arg conv "; break;
case ConstraintKind::ConformsTo: Out << " conforms to "; break; case ConstraintKind::ConformsTo: Out << " conforms to "; break;
case ConstraintKind::LiteralConformsTo: Out << " literal conforms to "; break;
case ConstraintKind::CheckedCast: Out << " checked cast to "; break; case ConstraintKind::CheckedCast: Out << " checked cast to "; break;
case ConstraintKind::SelfObjectOfProtocol: Out << " Self type of "; break; case ConstraintKind::SelfObjectOfProtocol: Out << " Self type of "; break;
case ConstraintKind::ApplicableFunction: Out << " applicable fn "; break; case ConstraintKind::ApplicableFunction: Out << " applicable fn "; break;
@@ -486,6 +490,7 @@ gatherReferencedTypeVars(Constraint *constraint,
case ConstraintKind::BindOverload: case ConstraintKind::BindOverload:
case ConstraintKind::Class: case ConstraintKind::Class:
case ConstraintKind::ConformsTo: case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::SelfObjectOfProtocol:
constraint->getFirstType()->getTypeVariables(typeVars); constraint->getFirstType()->getTypeVariables(typeVars);

View File

@@ -81,6 +81,9 @@ enum class ConstraintKind : char {
/// \brief The first type must conform to the second type (which is a /// \brief The first type must conform to the second type (which is a
/// protocol type). /// protocol type).
ConformsTo, ConformsTo,
/// \brief The first type describes a literal that conforms to the second
/// type, which is one of the known expressible-by-literal protocols.
LiteralConformsTo,
/// A checked cast from the first type to the second. /// A checked cast from the first type to the second.
CheckedCast, CheckedCast,
/// \brief The first type can act as the Self type of the second type (which /// \brief The first type can act as the Self type of the second type (which
@@ -473,6 +476,7 @@ public:
case ConstraintKind::OperatorArgumentTupleConversion: case ConstraintKind::OperatorArgumentTupleConversion:
case ConstraintKind::OperatorArgumentConversion: case ConstraintKind::OperatorArgumentConversion:
case ConstraintKind::ConformsTo: case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::CheckedCast: case ConstraintKind::CheckedCast:
case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::ApplicableFunction: case ConstraintKind::ApplicableFunction:

View File

@@ -158,12 +158,6 @@ class TypeVariableType::Implementation {
friend class constraints::SavedTypeVariableBinding; friend class constraints::SavedTypeVariableBinding;
public: public:
/// \brief If this type variable is an opened literal expression, keep track
/// of the associated literal conformance for optimization and diagnostic
/// purposes.
ProtocolDecl *literalConformanceProto = nullptr;
explicit Implementation(constraints::ConstraintLocator *locator, explicit Implementation(constraints::ConstraintLocator *locator,
unsigned options) unsigned options)
: Options(options), locator(locator), : Options(options), locator(locator),