[Type checker] Introduce value witness constraints.

Introduce a new kind of constraint, the "value witness" constraint,
which captures a reference to a witness for a specific protocol
conformance. It otherwise acts like a more restricted form of a "value
member" constraint, where the specific member is known (as a
ValueDecl*) in advance.

The constraint is effectively dependent on the protocol
conformance itself; if that conformance fails, mark the type variables
in the resolved member type as "holes", so that the conformance
failure does not cascade.

Note that the resolved overload for this constraint always refers to
the requirement, rather than the witness, so we will end up recording
witness-method references in the AST rather than concrete references,
and leave it up to the optimizers to perform devirtualization. This is
demonstrated by the SIL changes needed in tests, and is part of the
wider resilience issue with conformances described by
rdar://problem/22708391.
This commit is contained in:
Doug Gregor
2020-01-02 11:51:05 -08:00
parent c5a655e35b
commit bbcaf8c669
13 changed files with 263 additions and 28 deletions

View File

@@ -1252,6 +1252,7 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::ValueWitness:
case ConstraintKind::BridgingConversion:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
@@ -1316,6 +1317,7 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::ValueWitness:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
case ConstraintKind::OneWayEqual:
@@ -1594,6 +1596,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::ValueWitness:
case ConstraintKind::BridgingConversion:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
@@ -3863,6 +3866,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::ValueWitness:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
case ConstraintKind::OneWayEqual:
@@ -6079,7 +6083,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
markMemberTypeAsPotentialHole(memberTy);
return SolutionKind::Solved;
} else if (kind == ConstraintKind::ValueMember && baseObjTy->isHole()) {
} else if ((kind == ConstraintKind::ValueMember ||
kind == ConstraintKind::ValueWitness) &&
baseObjTy->isHole()) {
// If base type is a "hole" there is no reason to record any
// more "member not found" fixes for chained member references.
increaseScore(SK_Fix);
@@ -6354,6 +6360,72 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
return SolutionKind::Error;
}
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyValueWitnessConstraint(
ConstraintKind kind, Type baseType, ValueDecl *requirement, Type memberType,
DeclContext *useDC, FunctionRefKind functionRefKind,
TypeMatchOptions flags, ConstraintLocatorBuilder locator) {
// We'd need to record original base type because it might be a type
// variable representing another missing member.
auto origBaseType = baseType;
auto formUnsolved = [&] {
// If requested, generate a constraint.
if (flags.contains(TMF_GenerateConstraints)) {
auto *witnessConstraint = Constraint::createValueWitness(
*this, kind, origBaseType, memberType, requirement, useDC,
functionRefKind, getConstraintLocator(locator));
addUnsolvedConstraint(witnessConstraint);
return SolutionKind::Solved;
}
return SolutionKind::Unsolved;
};
// Resolve the base type, if we can. If we can't resolve the base type,
// then we can't solve this constraint.
Type baseObjectType = getFixedTypeRecursive(
baseType, flags, /*wantRValue=*/true);
if (baseObjectType->isTypeVariableOrMember()) {
return formUnsolved();
}
// Check conformance to the protocol. If it doesn't conform, this constraint
// fails. Don't attempt to fix it.
// FIXME: Look in the constraint system to see if we've resolved the
// conformance already?
auto proto = requirement->getDeclContext()->getSelfProtocolDecl();
assert(proto && "Value witness constraint for a non-requirement");
auto conformance = TypeChecker::conformsToProtocol(
baseObjectType, proto, useDC,
(ConformanceCheckFlags::InExpression |
ConformanceCheckFlags::SkipConditionalRequirements));
if (!conformance) {
// The conformance failed, so mark the member type as a "hole". We cannot
// do anything further here.
if (!shouldAttemptFixes())
return SolutionKind::Error;
memberType.visit([&](Type type) {
if (auto *typeVar = type->getAs<TypeVariableType>())
recordPotentialHole(typeVar);
});
return SolutionKind::Solved;
}
// Reference the requirement.
Type resolvedBaseType = simplifyType(baseType, flags);
if (resolvedBaseType->isTypeVariableOrMember())
return formUnsolved();
auto choice = OverloadChoice(resolvedBaseType, requirement, functionRefKind);
resolveOverload(getConstraintLocator(locator), memberType, choice,
useDC);
return SolutionKind::Solved;
}
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyDefaultableConstraint(
Type first, Type second,
@@ -8671,6 +8743,7 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first,
case ConstraintKind::ValueMember:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueWitness:
case ConstraintKind::BindOverload:
case ConstraintKind::Disjunction:
case ConstraintKind::KeyPath:
@@ -9058,6 +9131,16 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
TMF_GenerateConstraints,
constraint.getLocator());
case ConstraintKind::ValueWitness:
return simplifyValueWitnessConstraint(constraint.getKind(),
constraint.getFirstType(),
constraint.getRequirement(),
constraint.getSecondType(),
constraint.getMemberUseDC(),
constraint.getFunctionRefKind(),
TMF_GenerateConstraints,
constraint.getLocator());
case ConstraintKind::Defaultable:
return simplifyDefaultableConstraint(constraint.getFirstType(),
constraint.getSecondType(),