mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Implement default definitions for associated types.
Addresses <rdar://problem/14292873>. Swift SVN r11422
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 << ")";
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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).
|
||||
>;
|
||||
|
||||
@@ -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(),
|
||||
|
||||
30
test/decl/protocol/req/associated_type_default.swift
Normal file
30
test/decl/protocol/req/associated_type_default.swift
Normal 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'}}
|
||||
Reference in New Issue
Block a user