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>
<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>
<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
href="#inheritance">inheritance</a> clause, any type meant to
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">
protocol Enumerable {
typename EnumeratorType : Enumerator
func getElements() -> EnumeratorType
}
</pre>
</pre>
<!-- ===================================================================== -->
<h3 id="decl-subscript">subscript Declarations</h3>

View File

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

View File

@@ -225,8 +225,6 @@ ERROR(expected_equal_in_typealias,decl_parsing,PointsToFirstBadToken,
"expected '=' in typealias declaration", ())
ERROR(expected_type_in_typealias,decl_parsing,PointsToFirstBadToken,
"expected type in typealias declaration", ())
ERROR(associated_type_def,decl_parsing,none,
"typealias %0 in protocol cannot have a definition", (Identifier))
// Func
ERROR(func_decl_nonglobal_operator,decl_parsing,none,
@@ -1139,6 +1137,9 @@ NOTE(ambiguous_witnesses,sema_tcd,none,
"with type %2", (int, Identifier, Type))
NOTE(no_witnesses_type,sema_tcd,none,
"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,
"multiple matching types named %0", (Identifier))
NOTE(protocol_witness_exact_match,sema_tcd,none,

View File

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

View File

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

View File

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

View File

@@ -986,17 +986,14 @@ ParserResult<TypeDecl> Parser::parseDeclTypeAlias(bool WantDefinition,
Status |= UnderlyingTy;
if (UnderlyingTy.isNull())
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 (isAssociatedType) {
auto assocType = new (Context) AssociatedTypeDecl(CurDeclContext,
TypeAliasLoc, Id, IdLoc);
auto assocType = new (Context) AssociatedTypeDecl(
CurDeclContext,
TypeAliasLoc, Id, IdLoc,
UnderlyingTy.getPtrOrNull());
if (!Inherited.empty())
assocType->setInherited(Context.AllocateCopy(Inherited));
addToScope(assocType);

View File

@@ -1235,6 +1235,16 @@ public:
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
// auto-incremented raw value for the subsequent case, or returns null if
// the value is not auto-incrementable.
@@ -2186,8 +2196,13 @@ void TypeChecker::validateDecl(ValueDecl *D, bool resolveTypeParams) {
case DeclKind::GenericTypeParam:
case DeclKind::AssociatedType: {
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;
}
// FIXME: Avoid full check in these cases?
DeclContext *DC = typeParam->getDeclContext();

View File

@@ -124,8 +124,6 @@ namespace {
}
}
/// FIXME: Generic substitutions here.
/// \brief Associated types determined by matching this requirement.
SmallVector<std::pair<AssociatedTypeDecl *, Type>, 2>
AssociatedTypeDeductions;
@@ -751,7 +749,53 @@ static ResolveWitnessResult resolveTypeWitnessViaLookup(
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(
TypeChecker &tc,
ProtocolDecl *proto,
@@ -1101,12 +1145,16 @@ checkConformsToProtocol(TypeChecker &TC, Type T, ProtocolDecl *Proto,
if (Complained || invalid)
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 {
switch (resolveTypeWitnessViaDerivation(TC, Proto, T, DC, ComplainLoc,
// Default implementations.
switch (resolveTypeWitnessViaDefault(TC, Proto, T, DC, ComplainLoc,
assocType, TypeWitnesses,
Complained)) {
case ResolveWitnessResult::Success:
deducedAssocTypes.push_back(
{assocType, TypeWitnesses[assocType].Replacement});
return true;
case ResolveWitnessResult::ExplicitFailed:
@@ -1117,7 +1165,23 @@ checkConformsToProtocol(TypeChecker &TC, Type T, ProtocolDecl *Proto,
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;
};
unresolvedAssocTypes.erase(std::remove_if(unresolvedAssocTypes.begin(),
@@ -1131,6 +1195,11 @@ checkConformsToProtocol(TypeChecker &TC, Type T, ProtocolDecl *Proto,
// Diagnose all missing associated types.
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) {
TC.diagnose(ComplainLoc, diag::type_does_not_conform,
T, Proto->getDeclaredType());

View File

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

View File

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

View File

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

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'}}