Skip to content

Commit

Permalink
Merge pull request #12698 from slavapestov/anyobject-assoc-type-const…
Browse files Browse the repository at this point in the history
…raint

Sema: Check AnyObject constraint on associated type witnesses
  • Loading branch information
slavapestov committed Nov 1, 2017
2 parents 484c7ad + 398f76d commit e6dc458
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 23 deletions.
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'}}

0 comments on commit e6dc458

Please sign in to comment.