Permalink
Browse files

[Type checker] Teach conformsToProtocol() to check conditional requir…

…ements.

conformsToProtocol() is the main way in which we check whether a given type
conforms to a given protocol. Extend it to check conditional requirements by
default, so that an unmodified caller will get the "does not conform" result
(with diagnostics when a location is present) rather than simply ignoring
the conditional requirements.

Some callers take responsibility for conditional requirements, e.g., to
push them into the constraint system. Allow those callers to opt out of
this checking, and do so wherever appropriate.

Fixes rdar://problem/35518088, where we were ignoring the conditional
requirements needed to verify that Equatable synthesis could be performed.
  • Loading branch information...
DougGregor committed Nov 22, 2017
1 parent 33810d9 commit e3a5318b97c1f1b62bec5eb1ad591ce344d8bed5
@@ -160,20 +160,6 @@ class alignas(1 << TypeAlignInBits) GenericSignature final
/// array of the generic parameters for the innermost generic type.
ArrayRef<GenericTypeParamType *> getInnermostGenericParams() const;
/// Create a text string that describes the bindings of generic parameters
/// that are relevant to the given set of types, e.g.,
/// "[with T = Bar, U = Wibble]".
///
/// \param types The types that will be scanned for generic type parameters,
/// which will be used in the resulting type.
///
/// \param substitutions The generic parameter -> generic argument
/// substitutions that will have been applied to these types.
/// These are used to produce the "parameter = argument" bindings in the test.
std::string
gatherGenericParamBindingsText(ArrayRef<Type> types,
TypeSubstitutionFn substitutions) const;
/// Retrieve the requirements.
ArrayRef<Requirement> getRequirements() const {
return const_cast<GenericSignature *>(this)->getRequirementsBuffer();
@@ -110,45 +110,6 @@ GenericSignature::getSubstitutableParams() const {
return result;
}
std::string GenericSignature::gatherGenericParamBindingsText(
ArrayRef<Type> types, TypeSubstitutionFn substitutions) const {
llvm::SmallPtrSet<GenericTypeParamType *, 2> knownGenericParams;
for (auto type : types) {
type.visit([&](Type type) {
if (auto gp = type->getAs<GenericTypeParamType>()) {
knownGenericParams.insert(
gp->getCanonicalType()->castTo<GenericTypeParamType>());
}
});
}
if (knownGenericParams.empty())
return "";
SmallString<128> result;
for (auto gp : this->getGenericParams()) {
auto canonGP = gp->getCanonicalType()->castTo<GenericTypeParamType>();
if (!knownGenericParams.count(canonGP))
continue;
if (result.empty())
result += " [with ";
else
result += ", ";
result += gp->getName().str();
result += " = ";
auto type = substitutions(canonGP);
if (!type)
return "";
result += type.getString();
}
result += "]";
return result.str().str();
}
ASTContext &GenericSignature::getASTContext(
ArrayRef<swift::GenericTypeParamType *> params,
ArrayRef<swift::Requirement> requirements) {
@@ -471,9 +471,10 @@ namespace {
if (!baseTy->is<ArchetypeType>() && !baseTy->isAnyExistentialType()) {
auto &tc = cs.getTypeChecker();
auto conformance =
tc.conformsToProtocol(baseTy, proto, cs.DC,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::Used));
tc.conformsToProtocol(
baseTy, proto, cs.DC,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::Used));
if (conformance && conformance->isConcrete()) {
if (auto witness =
conformance->getConcrete()->getWitnessDecl(decl, &tc)) {
@@ -2261,8 +2262,6 @@ namespace {
tc.conformsToProtocol(conformingType, proto, cs.DC,
ConformanceCheckFlags::InExpression);
assert(conformance && "object literal type conforms to protocol");
assert(conformance->getConditionalRequirements().empty() &&
"unhandled conditional conformance");
Expr *base = TypeExpr::createImplicitHack(expr->getLoc(), conformingType,
ctx);
@@ -2713,8 +2712,6 @@ namespace {
tc.conformsToProtocol(arrayTy, arrayProto, cs.DC,
ConformanceCheckFlags::InExpression);
assert(conformance && "Type does not conform to protocol?");
assert(conformance->getConditionalRequirements().empty() &&
"unhandled conditional conformance");
// Call the witness that builds the array literal.
// FIXME: callWitness() may end up re-doing some work we already did
@@ -2797,9 +2794,6 @@ namespace {
if (!conformance)
return nullptr;
assert(conformance->getConditionalRequirements().empty() &&
"unhandled conditional conformance");
// Call the witness that builds the dictionary literal.
// FIXME: callWitness() may end up re-doing some work we already did
// to convert the dictionary literal elements to the (key, value) tuple.
@@ -4295,8 +4289,9 @@ namespace {
for (auto indexType : indexTypes) {
auto conformance =
cs.TC.conformsToProtocol(indexType.getType(), hashable,
cs.DC, ConformanceCheckFlags::Used
|ConformanceCheckFlags::InExpression);
cs.DC,
(ConformanceCheckFlags::Used|
ConformanceCheckFlags::InExpression));
if (!conformance) {
cs.TC.diagnose(component.getIndexExpr()->getLoc(),
diag::expr_keypath_subscript_index_not_hashable,
@@ -6122,12 +6117,11 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
// Find the conformance of the source type to Hashable.
auto hashable = tc.Context.getProtocol(KnownProtocolKind::Hashable);
auto conformance =
tc.conformsToProtocol(cs.getType(expr), hashable, cs.DC,
(ConformanceCheckFlags::InExpression |
ConformanceCheckFlags::Used));
tc.conformsToProtocol(
cs.getType(expr), hashable, cs.DC,
(ConformanceCheckFlags::InExpression |
ConformanceCheckFlags::Used));
assert(conformance && "must conform to Hashable");
assert(conformance->getConditionalRequirements().empty() &&
"unhandled conditional conformance");
return cs.cacheType(
new (tc.Context) AnyHashableErasureExpr(expr, toType, *conformance));
@@ -6556,10 +6550,9 @@ Expr *ExprRewriter::convertLiteral(Expr *literal,
Optional<ProtocolConformanceRef> builtinConformance;
if (builtinProtocol &&
(builtinConformance =
tc.conformsToProtocol(type, builtinProtocol, cs.DC,
ConformanceCheckFlags::InExpression))) {
assert(builtinConformance->getConditionalRequirements().empty() &&
"conditional conformance to builtins not handled");
tc.conformsToProtocol(
type, builtinProtocol, cs.DC,
(ConformanceCheckFlags::InExpression)))) {
// Find the builtin argument type we'll use.
Type argType;
@@ -6710,8 +6703,7 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal,
(builtinConformance =
tc.conformsToProtocol(type, builtinProtocol, cs.DC,
ConformanceCheckFlags::InExpression))) {
assert(builtinConformance->getConditionalRequirements().empty() &&
"conditional conformance to builtins not handled");
// Find the witness that we'll use to initialize the type via a builtin
// literal.
auto witness = findNamedWitnessImpl<AbstractFunctionDecl>(
@@ -517,8 +517,10 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) {
for (auto proto : literalProtocols) {
do {
// If the type conforms to this protocol, we're covered.
if (tc.conformsToProtocol(testType, proto, DC,
ConformanceCheckFlags::InExpression)) {
if (tc.conformsToProtocol(
testType, proto, DC,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::SkipConditionalRequirements))) {
coveredLiteralProtocols.insert(proto);
break;
}
@@ -4975,8 +4975,10 @@ bool FailureDiagnosis::diagnoseArgumentGenericRequirements(
TC.diagnose(Candidate, note, first, second,
rawFirstType, rawSecondType,
genericSig->gatherGenericParamBindingsText(
{rawFirstType, rawSecondType}, Substitutions));
TypeChecker::gatherGenericParamBindingsText(
{rawFirstType, rawSecondType},
genericSig->getGenericParams(),
Substitutions));
ParentConditionalConformance::diagnoseConformanceStack(
TC.Diags, Candidate->getLoc(), parents);
@@ -4992,7 +4994,9 @@ bool FailureDiagnosis::diagnoseArgumentGenericRequirements(
auto result = TC.checkGenericArguments(
dc, callExpr->getLoc(), fnExpr->getLoc(), AFD->getInterfaceType(),
env->getGenericSignature(), substitutionFn,
env->getGenericSignature()->getGenericParams(),
env->getGenericSignature()->getRequirements(),
substitutionFn,
LookUpConformanceInModule{dc->getParentModule()}, nullptr,
ConformanceCheckFlags::SuppressDependencyTracking, &genericReqListener);
@@ -232,8 +232,10 @@ computeSelfTypeRelationship(TypeChecker &tc, DeclContext *dc, DeclContext *dc1,
// If the model type does not conform to the protocol, the bases are
// unrelated.
auto conformance = tc.conformsToProtocol(modelTy, proto, dc,
ConformanceCheckFlags::InExpression);
auto conformance = tc.conformsToProtocol(
modelTy, proto, dc,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::SkipConditionalRequirements));
if (!conformance)
return {SelfTypeRelationship::Unrelated, None};
@@ -2618,8 +2618,10 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
case ConstraintKind::LiteralConformsTo: {
// Check whether this type conforms to the protocol.
if (auto conformance =
TC.conformsToProtocol(type, protocol, DC,
ConformanceCheckFlags::InExpression)) {
TC.conformsToProtocol(
type, protocol, DC,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::SkipConditionalRequirements))) {
return recordConformance(*conformance);
}
break;
@@ -8824,9 +8824,11 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
// complete.
auto evaluateTargetConformanceTo = [&](ProtocolDecl *protocol) {
auto targetType = target->getDeclaredInterfaceType();
if (auto ref = conformsToProtocol(targetType, protocol, target,
ConformanceCheckFlags::Used,
SourceLoc())) {
if (auto ref = conformsToProtocol(
targetType, protocol, target,
(ConformanceCheckFlags::Used|
ConformanceCheckFlags::SkipConditionalRequirements),
SourceLoc())) {
if (auto *conformance = ref->getConcrete()->getRootNormalConformance()) {
if (conformance->isIncomplete()) {
// Check conformance, forcing synthesis.
@@ -354,6 +354,48 @@ void TypeChecker::validateRequirements(
}
}
std::string
TypeChecker::gatherGenericParamBindingsText(
ArrayRef<Type> types,
ArrayRef<GenericTypeParamType *> genericParams,
TypeSubstitutionFn substitutions) {
llvm::SmallPtrSet<GenericTypeParamType *, 2> knownGenericParams;
for (auto type : types) {
type.visit([&](Type type) {
if (auto gp = type->getAs<GenericTypeParamType>()) {
knownGenericParams.insert(
gp->getCanonicalType()->castTo<GenericTypeParamType>());
}
});
}
if (knownGenericParams.empty())
return "";
SmallString<128> result;
for (auto gp : genericParams) {
auto canonGP = gp->getCanonicalType()->castTo<GenericTypeParamType>();
if (!knownGenericParams.count(canonGP))
continue;
if (result.empty())
result += " [with ";
else
result += ", ";
result += gp->getName().str();
result += " = ";
auto type = substitutions(canonGP);
if (!type)
return "";
result += type.getString();
}
result += "]";
return result.str().str();
}
void
TypeChecker::prepareGenericParamList(GenericParamList *gp,
DeclContext *dc) {
@@ -1216,21 +1258,26 @@ void TypeChecker::validateGenericTypeSignature(GenericTypeDecl *typeDecl) {
RequirementCheckResult TypeChecker::checkGenericArguments(
DeclContext *dc, SourceLoc loc, SourceLoc noteLoc, Type owner,
GenericSignature *genericSig, TypeSubstitutionFn substitutions,
ArrayRef<GenericTypeParamType *> genericParams,
ArrayRef<Requirement> requirements,
TypeSubstitutionFn substitutions,
LookupConformanceFn conformances,
UnsatisfiedDependency *unsatisfiedDependency,
ConformanceCheckOptions conformanceOptions,
GenericRequirementsCheckListener *listener,
SubstOptions options) {
bool valid = true;
// We handle any conditional requirements ourselves.
conformanceOptions |= ConformanceCheckFlags::SkipConditionalRequirements;
struct RequirementSet {
ArrayRef<Requirement> Requirements;
SmallVector<ParentConditionalConformance, 4> Parents;
};
SmallVector<RequirementSet, 8> pendingReqs;
pendingReqs.push_back({genericSig->getRequirements(), {}});
pendingReqs.push_back({requirements, {}});
while (!pendingReqs.empty()) {
auto current = pendingReqs.pop_back_val();
@@ -1352,9 +1399,15 @@ RequirementCheckResult TypeChecker::checkGenericArguments(
if (loc.isValid()) {
// FIXME: Poor source-location information.
diagnose(loc, diagnostic, owner, firstType, secondType);
std::string genericParamBindingsText;
if (!genericParams.empty()) {
genericParamBindingsText =
gatherGenericParamBindingsText(
{rawFirstType, rawSecondType}, genericParams, substitutions);
}
diagnose(noteLoc, diagnosticNote, rawFirstType, rawSecondType,
genericSig->gatherGenericParamBindingsText(
{rawFirstType, rawSecondType}, substitutions));
genericParamBindingsText);
ParentConditionalConformance::diagnoseConformanceStack(Diags, noteLoc,
current.Parents);
@@ -106,6 +106,7 @@ namespace {
ConformanceCheckOptions conformanceOptions;
if (Options.contains(NameLookupFlags::KnownPrivate))
conformanceOptions |= ConformanceCheckFlags::InExpression;
conformanceOptions |= ConformanceCheckFlags::SkipConditionalRequirements;
DeclContext *foundDC = found->getDeclContext();
@@ -430,6 +431,7 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc,
ConformanceCheckOptions conformanceOptions;
if (options.contains(NameLookupFlags::KnownPrivate))
conformanceOptions |= ConformanceCheckFlags::InExpression;
conformanceOptions |= ConformanceCheckFlags::SkipConditionalRequirements;
for (AssociatedTypeDecl *assocType : inferredAssociatedTypes) {
// If the type does not actually conform to the protocol, skip this
Oops, something went wrong.

0 comments on commit e3a5318

Please sign in to comment.