mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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:
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user