Implement default definitions for associated types.

Addresses <rdar://problem/14292873>.


Swift SVN r11422
This commit is contained in:
Doug Gregor
2013-12-18 06:24:53 +00:00
parent fdd16143a8
commit ec4913b0ea
13 changed files with 172 additions and 31 deletions

View File

@@ -1301,7 +1301,7 @@ an lvalue unless it has a <code>var-get</code> clause but not
<h4 id="protocol-member-typealias">'typealias' protocol elements (associated types)</h4> <h4 id="protocol-member-typealias">'typealias' protocol elements (associated types)</h4>
<pre class="grammar"> <pre class="grammar">
protocol-member ::= <a href="#typealias-head">typealias-head</a> protocol-member ::= <a href="#typealias-head">typealias-head</a> ('=' <a href="#type">type</a>)?
</pre> </pre>
<p>'typealias' members of a protocol define associated types, which <p>'typealias' members of a protocol define associated types, which
@@ -1310,14 +1310,17 @@ an lvalue unless it has a <code>var-get</code> clause but not
conforming type to another. When an associated type has an <a conforming type to another. When an associated type has an <a
href="#inheritance">inheritance</a> clause, any type meant to href="#inheritance">inheritance</a> clause, any type meant to
satisfy the associated type requirement must conform to each of the satisfy the associated type requirement must conform to each of the
protocols specified within that inheritance clause.</p> protocols specified within that inheritance clause. If a type is
provided after the '=', it is a default definition for the
associated type that will be used as the type witness if the type
witness cannot be determined in any other way.</p>
<pre class="example"> <pre class="example">
protocol Enumerable { protocol Enumerable {
typename EnumeratorType : Enumerator typename EnumeratorType : Enumerator
func getElements() -> EnumeratorType func getElements() -> EnumeratorType
} }
</pre> </pre>
<!-- ===================================================================== --> <!-- ===================================================================== -->
<h3 id="decl-subscript">subscript Declarations</h3> <h3 id="decl-subscript">subscript Declarations</h3>

View File

@@ -1508,15 +1508,23 @@ class AssociatedTypeDecl : public AbstractTypeParamDecl {
/// The location of the initial keyword. /// The location of the initial keyword.
SourceLoc KeywordLoc; SourceLoc KeywordLoc;
/// The default definition.
TypeLoc DefaultDefinition;
public: public:
AssociatedTypeDecl(DeclContext *dc, SourceLoc keywordLoc, Identifier name, AssociatedTypeDecl(DeclContext *dc, SourceLoc keywordLoc, Identifier name,
SourceLoc nameLoc); SourceLoc nameLoc, TypeLoc defaultDefinition);
/// Get the protocol in which this associated type is declared. /// Get the protocol in which this associated type is declared.
ProtocolDecl *getProtocol() const { ProtocolDecl *getProtocol() const {
return cast<ProtocolDecl>(getDeclContext()); return cast<ProtocolDecl>(getDeclContext());
} }
/// Retrieve the default definition type.
Type getDefaultDefinitionType() const { return DefaultDefinition.getType(); }
TypeLoc &getDefaultDefinitionLoc() { return DefaultDefinition; }
SourceLoc getStartLoc() const { return KeywordLoc; } SourceLoc getStartLoc() const { return KeywordLoc; }
SourceRange getSourceRange() const; SourceRange getSourceRange() const;

View File

@@ -225,8 +225,6 @@ ERROR(expected_equal_in_typealias,decl_parsing,PointsToFirstBadToken,
"expected '=' in typealias declaration", ()) "expected '=' in typealias declaration", ())
ERROR(expected_type_in_typealias,decl_parsing,PointsToFirstBadToken, ERROR(expected_type_in_typealias,decl_parsing,PointsToFirstBadToken,
"expected type in typealias declaration", ()) "expected type in typealias declaration", ())
ERROR(associated_type_def,decl_parsing,none,
"typealias %0 in protocol cannot have a definition", (Identifier))
// Func // Func
ERROR(func_decl_nonglobal_operator,decl_parsing,none, ERROR(func_decl_nonglobal_operator,decl_parsing,none,
@@ -1139,6 +1137,9 @@ NOTE(ambiguous_witnesses,sema_tcd,none,
"with type %2", (int, Identifier, Type)) "with type %2", (int, Identifier, Type))
NOTE(no_witnesses_type,sema_tcd,none, NOTE(no_witnesses_type,sema_tcd,none,
"protocol requires nested type %0", (Identifier)) "protocol requires nested type %0", (Identifier))
NOTE(default_assocated_type_req_fail,sema_tcd,none,
"default associated type definition %0 does not conform to %1",
(Type, Type))
NOTE(ambiguous_witnesses_type,sema_tcd,none, NOTE(ambiguous_witnesses_type,sema_tcd,none,
"multiple matching types named %0", (Identifier)) "multiple matching types named %0", (Identifier))
NOTE(protocol_witness_exact_match,sema_tcd,none, NOTE(protocol_witness_exact_match,sema_tcd,none,

View File

@@ -334,6 +334,10 @@ namespace {
void visitAssociatedTypeDecl(AssociatedTypeDecl *decl) { void visitAssociatedTypeDecl(AssociatedTypeDecl *decl) {
printCommon(decl, "associated_type_decl"); printCommon(decl, "associated_type_decl");
if (auto defaultDef = decl->getDefaultDefinitionType()) {
OS << " default=";
defaultDef.print(OS);
}
OS << ")"; OS << ")";
} }

View File

@@ -568,6 +568,11 @@ void PrintAST::visitAssociatedTypeDecl(AssociatedTypeDecl *decl) {
recordDeclLoc(decl); recordDeclLoc(decl);
Printer << decl->getName().str(); Printer << decl->getName().str();
printInheritedWithSuperclass(decl); printInheritedWithSuperclass(decl);
if (!decl->getDefaultDefinitionLoc().isNull()) {
Printer << " = ";
decl->getDefaultDefinitionLoc().getType().print(Printer, Options);
}
} }
void PrintAST::visitEnumDecl(EnumDecl *decl) { void PrintAST::visitEnumDecl(EnumDecl *decl) {

View File

@@ -722,9 +722,10 @@ SourceRange GenericTypeParamDecl::getSourceRange() const {
} }
AssociatedTypeDecl::AssociatedTypeDecl(DeclContext *dc, SourceLoc keywordLoc, AssociatedTypeDecl::AssociatedTypeDecl(DeclContext *dc, SourceLoc keywordLoc,
Identifier name, SourceLoc nameLoc) Identifier name, SourceLoc nameLoc,
TypeLoc defaultDefinition)
: AbstractTypeParamDecl(DeclKind::AssociatedType, dc, name, nameLoc), : AbstractTypeParamDecl(DeclKind::AssociatedType, dc, name, nameLoc),
KeywordLoc(keywordLoc) KeywordLoc(keywordLoc), DefaultDefinition(defaultDefinition)
{ {
auto &ctx = dc->getASTContext(); auto &ctx = dc->getASTContext();
auto type = new (ctx, AllocationArena::Permanent) AssociatedTypeType(this); auto type = new (ctx, AllocationArena::Permanent) AssociatedTypeType(this);

View File

@@ -986,17 +986,14 @@ ParserResult<TypeDecl> Parser::parseDeclTypeAlias(bool WantDefinition,
Status |= UnderlyingTy; Status |= UnderlyingTy;
if (UnderlyingTy.isNull()) if (UnderlyingTy.isNull())
return Status; return Status;
if (!WantDefinition) {
diagnose(IdLoc, diag::associated_type_def, Id);
UnderlyingTy = nullptr;
}
} }
// If this is an associated type, build the AST for it. // If this is an associated type, build the AST for it.
if (isAssociatedType) { if (isAssociatedType) {
auto assocType = new (Context) AssociatedTypeDecl(CurDeclContext, auto assocType = new (Context) AssociatedTypeDecl(
TypeAliasLoc, Id, IdLoc); CurDeclContext,
TypeAliasLoc, Id, IdLoc,
UnderlyingTy.getPtrOrNull());
if (!Inherited.empty()) if (!Inherited.empty())
assocType->setInherited(Context.AllocateCopy(Inherited)); assocType->setInherited(Context.AllocateCopy(Inherited));
addToScope(assocType); addToScope(assocType);

View File

@@ -1235,6 +1235,16 @@ public:
checkExplicitConformance(TAD, TAD->getDeclaredType()); checkExplicitConformance(TAD, TAD->getDeclaredType());
} }
void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) {
// Check the default definition, if there is one.
TypeLoc &defaultDefinition = assocType->getDefaultDefinitionLoc();
if (!defaultDefinition.isNull() &&
TC.validateType(defaultDefinition, assocType->getDeclContext(),
/*allowUnboundGenerics=*/false)) {
defaultDefinition.setInvalidType(TC.Context);
}
}
// Given the raw value literal expression for an enum case, produces the // Given the raw value literal expression for an enum case, produces the
// auto-incremented raw value for the subsequent case, or returns null if // auto-incremented raw value for the subsequent case, or returns null if
// the value is not auto-incrementable. // the value is not auto-incrementable.
@@ -2186,8 +2196,13 @@ void TypeChecker::validateDecl(ValueDecl *D, bool resolveTypeParams) {
case DeclKind::GenericTypeParam: case DeclKind::GenericTypeParam:
case DeclKind::AssociatedType: { case DeclKind::AssociatedType: {
auto typeParam = cast<AbstractTypeParamDecl>(D); auto typeParam = cast<AbstractTypeParamDecl>(D);
if (!resolveTypeParams || typeParam->getArchetype()) if (!resolveTypeParams || typeParam->getArchetype()) {
if (auto assocType = dyn_cast<AssociatedTypeDecl>(typeParam)) {
DeclChecker(*this, false, false).visitAssociatedTypeDecl(assocType);
}
break; break;
}
// FIXME: Avoid full check in these cases? // FIXME: Avoid full check in these cases?
DeclContext *DC = typeParam->getDeclContext(); DeclContext *DC = typeParam->getDeclContext();

View File

@@ -124,8 +124,6 @@ namespace {
} }
} }
/// FIXME: Generic substitutions here.
/// \brief Associated types determined by matching this requirement. /// \brief Associated types determined by matching this requirement.
SmallVector<std::pair<AssociatedTypeDecl *, Type>, 2> SmallVector<std::pair<AssociatedTypeDecl *, Type>, 2>
AssociatedTypeDeductions; AssociatedTypeDeductions;
@@ -751,7 +749,53 @@ static ResolveWitnessResult resolveTypeWitnessViaLookup(
return ResolveWitnessResult::ExplicitFailed; return ResolveWitnessResult::ExplicitFailed;
} }
/// Attempt to resolve a type witness via member name lookup. /// Attempt to resolve a type witness via a default definition.
static ResolveWitnessResult resolveTypeWitnessViaDefault(
TypeChecker &tc,
ProtocolDecl *proto,
Type type,
DeclContext *dc,
SourceLoc loc,
AssociatedTypeDecl *assocType,
TypeWitnessMap &typeWitnesses,
bool alreadyComplained) {
// If we don't have a default definition, we're done.
if (assocType->getDefaultDefinitionLoc().isNull())
return ResolveWitnessResult::Missing;
// Create a set of type substitutions for all known associated type.
// FIXME: Base this on dependent types rather than archetypes?
TypeSubstitutionMap substitutions;
substitutions[proto->getSelf()->getArchetype()] = type;
for (const auto &witness: typeWitnesses) {
substitutions[witness.second.Archetype] = witness.second.Replacement;
}
auto defaultType = tc.substType(dc->getParentModule(),
assocType->getDefaultDefinitionLoc().getType(),
substitutions,
/*IgnoreMissing=*/true);
if (!defaultType)
return ResolveWitnessResult::Missing;
if (auto checkResult = checkTypeWitness(tc, dc, assocType, defaultType)) {
if (!alreadyComplained)
tc.diagnose(loc, diag::type_does_not_conform, type,
proto->getDeclaredType());
tc.diagnose(assocType, diag::default_assocated_type_req_fail,
defaultType, checkResult.getProtocol()->getDeclaredType());
return ResolveWitnessResult::ExplicitFailed;
}
// Fill in the type witness and declare success.
auto archetype = assocType->getArchetype();
typeWitnesses[assocType] = getArchetypeSubstitution(tc, dc, archetype,
defaultType);
return ResolveWitnessResult::Success;
}
/// Attempt to resolve a type witness via derivation.
static ResolveWitnessResult resolveTypeWitnessViaDerivation( static ResolveWitnessResult resolveTypeWitnessViaDerivation(
TypeChecker &tc, TypeChecker &tc,
ProtocolDecl *proto, ProtocolDecl *proto,
@@ -1101,12 +1145,16 @@ checkConformsToProtocol(TypeChecker &TC, Type T, ProtocolDecl *Proto,
if (Complained || invalid) if (Complained || invalid)
return nullptr; return nullptr;
// If any associated types were left unresolved, try to derive them. // If any associated types were left unresolved, try default types
// or compiler-supported derivation.
auto resolveAssocType = [&](AssociatedTypeDecl *assocType) -> bool { auto resolveAssocType = [&](AssociatedTypeDecl *assocType) -> bool {
switch (resolveTypeWitnessViaDerivation(TC, Proto, T, DC, ComplainLoc, // Default implementations.
assocType, TypeWitnesses, switch (resolveTypeWitnessViaDefault(TC, Proto, T, DC, ComplainLoc,
Complained)) { assocType, TypeWitnesses,
Complained)) {
case ResolveWitnessResult::Success: case ResolveWitnessResult::Success:
deducedAssocTypes.push_back(
{assocType, TypeWitnesses[assocType].Replacement});
return true; return true;
case ResolveWitnessResult::ExplicitFailed: case ResolveWitnessResult::ExplicitFailed:
@@ -1117,7 +1165,23 @@ checkConformsToProtocol(TypeChecker &TC, Type T, ProtocolDecl *Proto,
break; break;
} }
// FIXME: Default implementation switch (resolveTypeWitnessViaDerivation(TC, Proto, T, DC, ComplainLoc,
assocType, TypeWitnesses,
Complained)) {
case ResolveWitnessResult::Success:
deducedAssocTypes.push_back(
{assocType, TypeWitnesses[assocType].Replacement});
return true;
case ResolveWitnessResult::ExplicitFailed:
Complained = true;
return false;
case ResolveWitnessResult::Missing:
break;
}
// No other options.
return false; return false;
}; };
unresolvedAssocTypes.erase(std::remove_if(unresolvedAssocTypes.begin(), unresolvedAssocTypes.erase(std::remove_if(unresolvedAssocTypes.begin(),
@@ -1131,6 +1195,11 @@ checkConformsToProtocol(TypeChecker &TC, Type T, ProtocolDecl *Proto,
// Diagnose all missing associated types. // Diagnose all missing associated types.
for (auto assocType : unresolvedAssocTypes) { for (auto assocType : unresolvedAssocTypes) {
// If we had a default that didn't work out, we already
// complained about it.
if (!assocType->getDefaultDefinitionLoc().isNull())
continue;
if (!Complained) { if (!Complained) {
TC.diagnose(ComplainLoc, diag::type_does_not_conform, TC.diagnose(ComplainLoc, diag::type_does_not_conform,
T, Proto->getDeclaredType()); T, Proto->getDeclaredType());

View File

@@ -1274,12 +1274,14 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext,
DeclID contextID; DeclID contextID;
TypeID superclassID; TypeID superclassID;
TypeID archetypeID; TypeID archetypeID;
TypeID defaultDefinitionID;
bool isImplicit; bool isImplicit;
decls_block::AssociatedTypeDeclLayout::readRecord(scratch, nameID, decls_block::AssociatedTypeDeclLayout::readRecord(scratch, nameID,
contextID, contextID,
superclassID, superclassID,
archetypeID, archetypeID,
defaultDefinitionID,
isImplicit); isImplicit);
auto DC = ForcedContext ? *ForcedContext : getDeclContext(contextID); auto DC = ForcedContext ? *ForcedContext : getDeclContext(contextID);
@@ -1287,9 +1289,12 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext,
if (declOrOffset.isComplete()) if (declOrOffset.isComplete())
break; break;
TypeLoc defaultDefinitionType =
TypeLoc::withoutLoc(getType(defaultDefinitionID));
auto assocType = new (ctx) AssociatedTypeDecl(DC, SourceLoc(), auto assocType = new (ctx) AssociatedTypeDecl(DC, SourceLoc(),
getIdentifier(nameID), getIdentifier(nameID),
SourceLoc()); SourceLoc(),
defaultDefinitionType);
declOrOffset = assocType; declOrOffset = assocType;
assocType->setSuperclass(getType(superclassID)); assocType->setSuperclass(getType(superclassID));

View File

@@ -503,6 +503,7 @@ namespace decls_block {
DeclIDField, // context decl DeclIDField, // context decl
TypeIDField, // underlying type TypeIDField, // underlying type
TypeIDField, // archetype type TypeIDField, // archetype type
TypeIDField, // default definition
BCFixed<1> // implicit flag BCFixed<1> // implicit flag
// Trailed by the conformance info (if any). // Trailed by the conformance info (if any).
>; >;

View File

@@ -1130,12 +1130,14 @@ void Serializer::writeDecl(const Decl *D) {
const Decl *DC = getDeclForContext(assocType->getDeclContext()); const Decl *DC = getDeclForContext(assocType->getDeclContext());
unsigned abbrCode = DeclTypeAbbrCodes[AssociatedTypeDeclLayout::Code]; unsigned abbrCode = DeclTypeAbbrCodes[AssociatedTypeDeclLayout::Code];
AssociatedTypeDeclLayout::emitRecord(Out, ScratchRecord, abbrCode, AssociatedTypeDeclLayout::emitRecord(
addIdentifierRef(assocType->getName()), Out, ScratchRecord, abbrCode,
addDeclRef(DC), addIdentifierRef(assocType->getName()),
addTypeRef(assocType->getSuperclass()), addDeclRef(DC),
addTypeRef(assocType->getArchetype()), addTypeRef(assocType->getSuperclass()),
assocType->isImplicit()); addTypeRef(assocType->getArchetype()),
addTypeRef(assocType->getDefaultDefinitionType()),
assocType->isImplicit());
writeConformances(assocType->getProtocols(), writeConformances(assocType->getProtocols(),
assocType->getConformances(), assocType->getConformances(),

View File

@@ -0,0 +1,30 @@
// RUN: %swift -parse %s -verify
struct X { }
// Simple default definition for associated types.
protocol P1 {
typealias AssocType1 = Int
}
extension X : P1 { }
var i: X.AssocType1 = 17
// Dependent default definition for associated types
protocol P2 {
typealias AssocType2 = Self
}
extension X : P2 { }
var xAssoc2: X.AssocType2 = X()
// Dependent default definition for associated types that doesn't meet
// requirements.
protocol P3 {
typealias AssocType3 : P1 = Self // expected-note{{default associated type definition 'X2' does not conform to 'P1'}}
}
extension X : P3 { } // okay
struct X2 : P3 { } // expected-error{{type 'X2' does not conform to protocol 'P3'}}