Skip to content
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

Sema: Check AnyObject constraint on associated type witnesses #12698

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1543,7 +1543,7 @@ NOTE(bad_associated_type_deduction,none,
NOTE(associated_type_deduction_witness_failed,none,
"inferred type %1 (by matching requirement %0) is invalid: "
"does not %select{inherit from|conform to}3 %2",
(DeclName, Type, DeclName, bool))
(DeclName, Type, Type, bool))
NOTE(ambiguous_associated_type_deduction,none,
"ambiguous inference of associated type %0: %1 vs. %2",
(DeclName, Type, Type))
Expand Down
47 changes: 25 additions & 22 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1766,19 +1766,18 @@ namespace {
///
/// This class evaluates true if an error occurred.
class CheckTypeWitnessResult {
NominalTypeDecl *Nominal = nullptr;
Type Requirement;

public:
CheckTypeWitnessResult() { }
CheckTypeWitnessResult(Type reqt) : Requirement(reqt) {}

CheckTypeWitnessResult(NominalTypeDecl *nominal) : Nominal(nominal) {
assert(isa<ProtocolDecl>(nominal) || isa<ClassDecl>(nominal));
Type getRequirement() const { return Requirement; }
bool isConformanceRequirement() const {
return Requirement->isExistentialType();
}

NominalTypeDecl *getProtocolOrClass() const { return Nominal; }
bool isProtocol() const { return isa<ProtocolDecl>(Nominal); }

explicit operator bool() const { return Nominal != nullptr; }
explicit operator bool() const { return !Requirement.isNull(); }
};

/// The set of associated types that have been inferred by matching
Expand Down Expand Up @@ -1814,8 +1813,8 @@ namespace {
out.indent(indent + 2);
out << std::get<0>(inferred)->getName() << " := "
<< std::get<1>(inferred).getString();
if (auto nominal = std::get<2>(inferred).getProtocolOrClass())
out << " [failed constraint " << nominal->getName() << "]";
auto type = std::get<2>(inferred).getRequirement();
out << " [failed constraint " << type.getString() << "]";
}

out << ")";
Expand Down Expand Up @@ -3558,13 +3557,13 @@ static CheckTypeWitnessResult checkTypeWitness(TypeChecker &tc, DeclContext *dc,

if (auto superclass = genericSig->getSuperclassBound(depTy)) {
if (!superclass->isExactSuperclassOf(type))
return superclass->getAnyNominal();
return superclass;
}

// Check protocol conformances.
for (auto reqProto : genericSig->getConformsTo(depTy)) {
if (!tc.conformsToProtocol(type, reqProto, dc, None))
return reqProto;
return CheckTypeWitnessResult(reqProto->getDeclaredType());

// FIXME: Why is conformsToProtocol() not enough? The stdlib doesn't
// build unless we fail here while inferring an associated type
Expand All @@ -3580,10 +3579,16 @@ static CheckTypeWitnessResult checkTypeWitness(TypeChecker &tc, DeclContext *dc,
return t.subst(subMap, SubstFlags::UseErrorType)->hasError();
});
if (result)
return reqProto;
return CheckTypeWitnessResult(reqProto->getDeclaredType());
}
}

if (genericSig->requiresClass(depTy)) {
if (!type->isObjCExistentialType() &&
!type->mayHaveSuperclass())
return CheckTypeWitnessResult(tc.Context.getAnyObjectType());
}

// Success!
return CheckTypeWitnessResult();
}
Expand Down Expand Up @@ -3616,7 +3621,7 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(

// Determine which of the candidates is viable.
SmallVector<std::pair<TypeDecl *, Type>, 2> viable;
SmallVector<std::pair<TypeDecl *, NominalTypeDecl *>, 2> nonViable;
SmallVector<std::pair<TypeDecl *, CheckTypeWitnessResult>, 2> nonViable;
for (auto candidate : candidates) {
// Skip nested generic types.
if (auto *genericDecl = dyn_cast<GenericTypeDecl>(candidate.first))
Expand All @@ -3626,8 +3631,7 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
// Check this type against the protocol requirements.
if (auto checkResult = checkTypeWitness(TC, DC, Proto, assocType,
candidate.second)) {
auto reqProto = checkResult.getProtocolOrClass();
nonViable.push_back({candidate.first, reqProto});
nonViable.push_back({candidate.first, checkResult});
} else {
viable.push_back(candidate);
}
Expand Down Expand Up @@ -3671,8 +3675,8 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
candidate.first,
diag::protocol_witness_nonconform_type,
candidate.first->getDeclaredInterfaceType(),
candidate.second->getDeclaredInterfaceType(),
candidate.second->getDeclaredInterfaceType()->is<ProtocolType>());
candidate.second.getRequirement(),
candidate.second.isConformanceRequirement());
}
});

Expand Down Expand Up @@ -5077,9 +5081,8 @@ void ConformanceChecker::resolveTypeWitnesses() {
failedDefaultedWitness,
failedDefaultedAssocType->getFullName(),
proto->getDeclaredType(),
failedDefaultedResult.getProtocolOrClass()
->getDeclaredType(),
failedDefaultedResult.isProtocol());
failedDefaultedResult.getRequirement(),
failedDefaultedResult.isConformanceRequirement());
});
return;
}
Expand Down Expand Up @@ -5117,8 +5120,8 @@ void ConformanceChecker::resolveTypeWitnesses() {
diag::associated_type_deduction_witness_failed,
failed.Requirement->getFullName(),
failed.TypeWitness,
failed.Result.getProtocolOrClass()->getFullName(),
failed.Result.isProtocol());
failed.Result.getRequirement(),
failed.Result.isConformanceRequirement());
}
});

Expand Down
2 changes: 2 additions & 0 deletions test/decl/protocol/conforms/associated_type.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ class C { }

protocol P {
associatedtype AssocP : C // expected-note{{protocol requires nested type 'AssocP'; do you want to add it?}}
associatedtype AssocA : AnyObject // expected-note{{protocol requires nested type 'AssocA'; do you want to add it?}}
}

struct X : P { // expected-error{{type 'X' does not conform to protocol 'P'}}
typealias AssocP = Int // expected-note{{possibly intended match 'X.AssocP' (aka 'Int') does not inherit from 'C'}}
typealias AssocA = Int // expected-note{{possibly intended match 'X.AssocA' (aka 'Int') does not conform to 'AnyObject'}}
}

// SR-5166
Expand Down
6 changes: 6 additions & 0 deletions test/decl/protocol/req/associated_type_default.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ protocol P3 {
extension X : P3 { } // okay

struct X2 : P3 { } // expected-error{{type 'X2' does not conform to protocol 'P3'}}

protocol P4 {
associatedtype AssocType4 : AnyObject = Int // expected-note{{default type 'Int' for associated type 'AssocType4' (from protocol 'P4') does not conform to 'AnyObject'}}
}

struct X4 : P4 {} // expected-error{{type 'X4' does not conform to protocol 'P4'}}