Skip to content

Speculatively revert "[Type checker] Eliminate the 'literalConformanceProto' state on type variables." #5254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
3 changes: 1 addition & 2 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2920,8 +2920,7 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
// If simplification has turned this into the same types, then this isn't the
// broken constraint that we're looking for.
if (fromType->isEqual(toType) &&
constraint->getKind() != ConstraintKind::ConformsTo &&
constraint->getKind() != ConstraintKind::LiteralConformsTo)
constraint->getKind() != ConstraintKind::ConformsTo)
return false;


Expand Down
108 changes: 57 additions & 51 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,47 +500,49 @@ namespace {
return false;
}

/// Determine whether the given parameter type and argument should be
/// Determine whether the given parameter and argument type should be
/// "favored" because they match exactly.
bool isFavoredParamAndArg(ConstraintSystem &CS,
Type paramTy,
Expr *arg,
Type argTy,
Expr *otherArg = nullptr,
Type otherArgTy = Type()) {
// Determine the argument type.
argTy = argTy->getLValueOrInOutObjectType();

Type otherArgTy) {
if (argTy->getAs<LValueType>())
argTy = argTy->getLValueOrInOutObjectType();

if (!otherArgTy.isNull() &&
otherArgTy->getAs<LValueType>())
otherArgTy = otherArgTy->getLValueOrInOutObjectType();

// Do the types match exactly?
if (paramTy->isEqual(argTy))
return true;

// If the argument is a literal, this is a favored param/arg pair if
// the parameter is of that default type.
auto &tc = CS.getTypeChecker();
auto literalProto = tc.getLiteralProtocol(arg->getSemanticsProvidingExpr());
if (!literalProto) return false;

// Dig out the second argument type.
if (otherArgTy)
otherArgTy = otherArgTy->getLValueOrInOutObjectType();

// If there is another, concrete argument, check whether it's type
// conforms to the literal protocol and test against it directly.
// This helps to avoid 'widening' the favored type to the default type for
// the literal.
if (otherArgTy && otherArgTy->getAnyNominal()) {
return otherArgTy->isEqual(paramTy) &&
tc.conformsToProtocol(otherArgTy, literalProto, CS.DC,
ConformanceCheckFlags::InExpression);

// If the argument is a type variable created for a literal that has a
// default type, this is a favored param/arg pair if the parameter is of
// that default type.
// Is the argument a type variable...
if (auto argTypeVar = argTy->getAs<TypeVariableType>()) {
if (auto proto = argTypeVar->getImpl().literalConformanceProto) {
// If it's a struct type associated with the literal conformance,
// test against it directly. This helps to avoid 'widening' the
// favored type to the default type for the literal.
if (!otherArgTy.isNull() &&
otherArgTy->getAs<StructType>()) {

if (CS.TC.conformsToProtocol(otherArgTy,
proto,
CS.DC,
ConformanceCheckFlags::InExpression)) {
return otherArgTy->isEqual(paramTy);
}
} 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;
}

Expand Down Expand Up @@ -740,6 +742,9 @@ namespace {
/// for the operand and contextual type.
void favorMatchingUnaryOperators(ApplyExpr *expr,
ConstraintSystem &CS) {
// Find the argument type.
auto argTy = expr->getArg()->getType()->getWithoutParens();

// Determine whether the given declaration is favored.
auto isFavoredDecl = [&](ValueDecl *value) -> bool {
auto valueTy = value->getType();
Expand All @@ -757,8 +762,7 @@ namespace {
auto resultTy = fnTy->getResult();
auto contextualTy = CS.getContextualType(expr);

return isFavoredParamAndArg(CS, paramTy, expr->getArg(),
expr->getArg()->getType()) &&
return isFavoredParamAndArg(CS, paramTy, argTy, Type()) &&
(!contextualTy || contextualTy->isEqual(resultTy));
};

Expand Down Expand Up @@ -877,10 +881,8 @@ namespace {
if (!fnTy)
return false;

Expr *firstArg = argTupleExpr->getElement(0);
auto firstFavoredTy = CS.getFavoredType(firstArg);
Expr *secondArg = argTupleExpr->getElement(1);
auto secondFavoredTy = CS.getFavoredType(secondArg);
auto firstFavoredTy = CS.getFavoredType(argTupleExpr->getElement(0));
auto secondFavoredTy = CS.getFavoredType(argTupleExpr->getElement(1));

auto favoredExprTy = CS.getFavoredType(expr);

Expand Down Expand Up @@ -924,10 +926,8 @@ namespace {
auto contextualTy = CS.getContextualType(expr);

return
(isFavoredParamAndArg(CS, firstParamTy, firstArg, firstArgTy,
secondArg, secondArgTy) ||
isFavoredParamAndArg(CS, secondParamTy, secondArg, secondArgTy,
firstArg, firstArgTy)) &&
(isFavoredParamAndArg(CS, firstParamTy, firstArgTy, secondArgTy) ||
isFavoredParamAndArg(CS, secondParamTy, secondArgTy, firstArgTy)) &&
firstParamTy->isEqual(secondParamTy) &&
(!contextualTy || contextualTy->isEqual(resultTy));
};
Expand Down Expand Up @@ -1083,7 +1083,7 @@ namespace {
auto keyTy = dictTy->first;
auto valueTy = dictTy->second;

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

if (isLValueBase)
Expand Down Expand Up @@ -1164,7 +1164,10 @@ namespace {

auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_PrefersSubtypeBinding);
CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,

tv->getImpl().literalConformanceProto = protocol;

CS.addConstraint(ConstraintKind::ConformsTo, tv,
protocol->getDeclaredType(),
CS.getConstraintLocator(expr));
return tv;
Expand All @@ -1187,7 +1190,8 @@ namespace {
// ExpressibleByStringInterpolation protocol.
auto locator = CS.getConstraintLocator(expr);
auto tv = CS.createTypeVariable(locator, TVO_PrefersSubtypeBinding);
CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,
tv->getImpl().literalConformanceProto = interpolationProto;
CS.addConstraint(ConstraintKind::ConformsTo, tv,
interpolationProto->getDeclaredType(),
locator);

Expand Down Expand Up @@ -1260,7 +1264,9 @@ namespace {
auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_PrefersSubtypeBinding);

CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,
tv->getImpl().literalConformanceProto = protocol;

CS.addConstraint(ConstraintKind::ConformsTo, tv,
protocol->getDeclaredType(),
CS.getConstraintLocator(expr));

Expand Down Expand Up @@ -1677,7 +1683,7 @@ namespace {
contextualArrayElementType =
CS.getBaseTypeForArrayType(contextualType.getPointer());

CS.addConstraint(ConstraintKind::LiteralConformsTo, contextualType,
CS.addConstraint(ConstraintKind::ConformsTo, contextualType,
arrayProto->getDeclaredType(),
locator);

Expand All @@ -1697,7 +1703,7 @@ namespace {
auto arrayTy = CS.createTypeVariable(locator, TVO_PrefersSubtypeBinding);

// The array must be an array literal type.
CS.addConstraint(ConstraintKind::LiteralConformsTo, arrayTy,
CS.addConstraint(ConstraintKind::ConformsTo, arrayTy,
arrayProto->getDeclaredType(),
locator);

Expand Down Expand Up @@ -1763,8 +1769,8 @@ namespace {
auto dictionaryTy = CS.createTypeVariable(locator,
TVO_PrefersSubtypeBinding);

// The dictionary must be a dictionary literal type.
CS.addConstraint(ConstraintKind::LiteralConformsTo, dictionaryTy,
// The array must be a dictionary literal type.
CS.addConstraint(ConstraintKind::ConformsTo, dictionaryTy,
dictionaryProto->getDeclaredType(),
locator);

Expand Down
3 changes: 0 additions & 3 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2288,7 +2288,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
return SolutionKind::Solved;
break;
case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
// Check whether this type conforms to the protocol.
if (TC.conformsToProtocol(type, protocol, DC,
ConformanceCheckFlags::InExpression))
Expand Down Expand Up @@ -3468,7 +3467,6 @@ static TypeMatchKind getTypeMatchKind(ConstraintKind kind) {
llvm_unreachable("Overload binding constraints don't involve type matches");

case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::SelfObjectOfProtocol:
llvm_unreachable("Conformance constraints don't involve type matches");

Expand Down Expand Up @@ -4128,7 +4126,6 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
return SolutionKind::Solved;

case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::SelfObjectOfProtocol:
return simplifyConformsToConstraint(
constraint.getFirstType(),
Expand Down
46 changes: 10 additions & 36 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,24 +62,14 @@ static Optional<Type> checkTypeOfBinding(ConstraintSystem &cs,
return None;

// 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 (isNilLiteral) {
*isNilLiteral = false;

// Look for a literal-conformance constraint on the type variable.
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;
}
}
}
if (isNilLiteral &&
bindingTypeVar->getImpl().literalConformanceProto &&
bindingTypeVar->getImpl().literalConformanceProto->isSpecificProtocol(
KnownProtocolKind::ExpressibleByNilLiteral))
*isNilLiteral = true;

return None;
}
Expand Down Expand Up @@ -677,7 +667,6 @@ static bool shouldBindToValueType(Constraint *constraint)
case ConstraintKind::Equal:
case ConstraintKind::BindParam:
case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::CheckedCast:
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::ApplicableFunction:
Expand Down Expand Up @@ -793,19 +782,8 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
result.InvolvesTypeVariables = true;
continue;

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::ConformsTo:
case ConstraintKind::SelfObjectOfProtocol: {
// FIXME: Only for LiteralConformsTo?

// If there is a default literal type for this protocol, it's a
// potential binding.
auto defaultType = tc.getDefaultType(constraint->getProtocol(), cs.DC);
Expand Down Expand Up @@ -930,17 +908,13 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
// Check whether we can perform this binding.
// FIXME: this has a super-inefficient extraneous simplifyType() in it.
bool isNilLiteral = false;
bool *isNilLiteralPtr = nullptr;
if (!addOptionalSupertypeBindings && kind == AllowedBindingKind::Supertypes)
isNilLiteralPtr = &isNilLiteral;
if (auto boundType = checkTypeOfBinding(cs, typeVar, type,
isNilLiteralPtr)) {
if (auto boundType = checkTypeOfBinding(cs, typeVar, type, &isNilLiteral)) {
type = *boundType;
if (type->hasTypeVariable())
result.InvolvesTypeVariables = true;
} else {
// If the bound is a 'nil' literal type, add optional supertype bindings.
if (isNilLiteral) {
if (isNilLiteral && kind == AllowedBindingKind::Supertypes) {
addOptionalSupertypeBindings = true;
continue;
}
Expand Down
5 changes: 0 additions & 5 deletions lib/Sema/Constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second,
case ConstraintKind::OperatorArgumentTupleConversion:
case ConstraintKind::OperatorArgumentConversion:
case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::CheckedCast:
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::DynamicTypeOf:
Expand Down Expand Up @@ -145,7 +144,6 @@ Constraint::Constraint(ConstraintKind kind, Fix fix,

ProtocolDecl *Constraint::getProtocol() const {
assert((Kind == ConstraintKind::ConformsTo ||
Kind == ConstraintKind::LiteralConformsTo ||
Kind == ConstraintKind::SelfObjectOfProtocol)
&& "Not a conformance constraint");
return Types.Second->castTo<ProtocolType>()->getDecl();
Expand All @@ -164,7 +162,6 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const {
case ConstraintKind::OperatorArgumentTupleConversion:
case ConstraintKind::OperatorArgumentConversion:
case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::CheckedCast:
case ConstraintKind::DynamicTypeOf:
case ConstraintKind::SelfObjectOfProtocol:
Expand Down Expand Up @@ -241,7 +238,6 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const {
case ConstraintKind::OperatorArgumentConversion:
Out << " operator arg conv "; 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::SelfObjectOfProtocol: Out << " Self type of "; break;
case ConstraintKind::ApplicableFunction: Out << " applicable fn "; break;
Expand Down Expand Up @@ -490,7 +486,6 @@ gatherReferencedTypeVars(Constraint *constraint,
case ConstraintKind::BindOverload:
case ConstraintKind::Class:
case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::SelfObjectOfProtocol:
constraint->getFirstType()->getTypeVariables(typeVars);

Expand Down
4 changes: 0 additions & 4 deletions lib/Sema/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,6 @@ enum class ConstraintKind : char {
/// \brief The first type must conform to the second type (which is a
/// protocol type).
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.
CheckedCast,
/// \brief The first type can act as the Self type of the second type (which
Expand Down Expand Up @@ -476,7 +473,6 @@ class Constraint final : public llvm::ilist_node<Constraint>,
case ConstraintKind::OperatorArgumentTupleConversion:
case ConstraintKind::OperatorArgumentConversion:
case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::CheckedCast:
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::ApplicableFunction:
Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ class TypeVariableType::Implementation {
friend class constraints::SavedTypeVariableBinding;

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,
unsigned options)
: Options(options), locator(locator),
Expand Down