diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 4f6553bfca009..8a7fcbd7da934 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1434,10 +1434,11 @@ NOTE(optional_req_near_match_accessibility,none, // Protocols and existentials ERROR(assoc_type_outside_of_protocol,none, - "cannot use associated type %0 outside of its protocol", (Identifier)) -ERROR(typealias_to_assoc_type_outside_of_protocol,none, - "cannot use typealias %0 of associated type %1 outside of its protocol", - (Identifier, TypeLoc)) + "associated type %0 can only be used with a concrete type or " + "generic parameter base", (Identifier)) +ERROR(typealias_outside_of_protocol,none, + "typealias %0 can only be used with a concrete type or " + "generic parameter base", (Identifier)) ERROR(circular_protocol_def,none, "circular protocol inheritance %0", (StringRef)) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index dfca15e69ad12..6863e71f9b173 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -2349,14 +2349,32 @@ diagnoseTypeMemberOnInstanceLookup(Type baseObjTy, SourceLoc loc) { SourceRange baseRange = baseExpr ? baseExpr->getSourceRange() : SourceRange(); + Optional Diag; + // If the base of the lookup is a protocol metatype, suggest // to replace the metatype with 'Self' // error saying the lookup cannot be on a protocol metatype if (auto metatypeTy = baseObjTy->getAs()) { - auto Diag = diagnose(loc, - diag::could_not_use_type_member_on_protocol_metatype, - baseObjTy, memberName); - Diag.highlight(baseRange).highlight(nameLoc.getSourceRange()); + assert(metatypeTy->getInstanceType()->isExistentialType()); + + // Give a customized message if we're accessing a member type + // of a protocol -- otherwise a diagnostic talking about + // static members doesn't make a whole lot of sense + if (isa(member)) { + Diag.emplace(diagnose(loc, + diag::typealias_outside_of_protocol, + memberName.getBaseName())); + } else if (isa(member)) { + Diag.emplace(diagnose(loc, + diag::assoc_type_outside_of_protocol, + memberName.getBaseName())); + } else { + Diag.emplace(diagnose(loc, + diag::could_not_use_type_member_on_protocol_metatype, + baseObjTy, memberName)); + } + + Diag->highlight(baseRange).highlight(nameLoc.getSourceRange()); // See through function decl context if (auto parent = CS->DC->getInnermostTypeContext()) { @@ -2365,7 +2383,7 @@ diagnoseTypeMemberOnInstanceLookup(Type baseObjTy, if (auto extensionContext = parent->getAsProtocolExtensionContext()) { if (extensionContext->getDeclaredType()->getCanonicalType() == metatypeTy->getInstanceType()->getCanonicalType()) { - Diag.fixItReplace(baseRange, "Self"); + Diag->fixItReplace(baseRange, "Self"); } } } @@ -2373,10 +2391,6 @@ diagnoseTypeMemberOnInstanceLookup(Type baseObjTy, return; } - // Otherwise the static member lookup was invalid because it was - // called on an instance - Optional Diag; - if (isa(member)) Diag.emplace(diagnose(loc, diag::could_not_use_enum_element_on_instance, memberName)); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index ea0a169ddf877..945fb0eb46bf4 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2962,7 +2962,16 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, result.addUnviable(cand, MemberLookupResult::UR_InstanceMemberOnType); return; } - + + // If the underlying type of a typealias is fully concrete, it is legal + // to access the type with a protocol metatype base. + } else if (isExistential && + isa(cand) && + !cast(cand)->getInterfaceType()->getCanonicalType() + ->hasTypeParameter()) { + + /* We're OK */ + } else { if (!hasStaticMembers) { result.addUnviable(cand, MemberLookupResult::UR_TypeMemberOnInstance); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index f0f38f9ec2b0a..b91db5c06e00d 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1197,6 +1197,16 @@ ConstraintSystem::getTypeOfMemberReference( functionRefKind, locator, base); } + // Don't open existentials when accessing typealias members of + // protocols. + if (auto *alias = dyn_cast(value)) { + if (baseObjTy->isExistentialType()) { + auto memberTy = alias->getUnderlyingType(); + auto openedType = FunctionType::get(baseObjTy, memberTy); + return { openedType, memberTy }; + } + } + // Handle associated type lookup as a special case, horribly. // FIXME: This is an awful hack. if (auto assocType = dyn_cast(value)) { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index a15512b4652c6..ead480fed88b6 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1228,14 +1228,14 @@ static Type resolveNestedIdentTypeComponent( return ErrorType::get(TC.Context); } - if (auto alias = dyn_cast(member)) { - if (parentTy->isExistentialType() && memberType->hasTypeParameter()) { - if (diagnoseErrors) - TC.diagnose(comp->getIdLoc(), diag::typealias_to_assoc_type_outside_of_protocol, - comp->getIdentifier(), alias->getUnderlyingTypeLoc()); - return ErrorType::get(TC.Context); - } + if (parentTy->isExistentialType() && isa(member) && + memberType->hasTypeParameter()) { + if (diagnoseErrors) + TC.diagnose(comp->getIdLoc(), diag::typealias_outside_of_protocol, + comp->getIdentifier()); + + return ErrorType::get(TC.Context); } // If there are generic arguments, apply them now. diff --git a/test/decl/typealias/associated_types.swift b/test/decl/typealias/associated_types.swift index f835eecaedfea..40f96c1dd7c59 100644 --- a/test/decl/typealias/associated_types.swift +++ b/test/decl/typealias/associated_types.swift @@ -3,13 +3,17 @@ protocol BaseProto { associatedtype AssocTy } -var a: BaseProto.AssocTy = 4 // expected-error{{cannot use associated type 'AssocTy' outside of its protocol}} +var a: BaseProto.AssocTy = 4 +// expected-error@-1{{associated type 'AssocTy' can only be used with a concrete type or generic parameter base}} +var a = BaseProto.AssocTy.self +// expected-error@-1{{associated type 'AssocTy' can only be used with a concrete type or generic parameter base}} protocol DerivedProto : BaseProto { func associated() -> AssocTy // no-warning - func existential() -> BaseProto.AssocTy // expected-error{{cannot use associated type 'AssocTy' outside of its protocol}} + func existential() -> BaseProto.AssocTy + // expected-error@-1{{associated type 'AssocTy' can only be used with a concrete type or generic parameter base}} } diff --git a/test/decl/typealias/protocol.swift b/test/decl/typealias/protocol.swift index 76e99a79f0451..047e18312bce6 100644 --- a/test/decl/typealias/protocol.swift +++ b/test/decl/typealias/protocol.swift @@ -135,13 +135,18 @@ protocol P5 { var a: T2 { get } } +protocol P6 { + typealias A = Int + typealias B = Self +} + struct T5 : P5 { // This is OK -- the typealias is fully concrete var a: P5.T1 // OK // Invalid -- cannot represent associated type of existential - var v2: P5.T2 // expected-error {{cannot use typealias 'T2' of associated type 'A' outside of its protocol}} - var v3: P5.X // expected-error {{cannot use typealias 'X' of associated type 'Self' outside of its protocol}} + var v2: P5.T2 // expected-error {{typealias 'T2' can only be used with a concrete type or generic parameter base}} + var v3: P5.X // expected-error {{typealias 'X' can only be used with a concrete type or generic parameter base}} // FIXME: Unqualified reference to typealias from a protocol conformance var v4: T1 // expected-error {{use of undeclared type 'T1'}} @@ -150,18 +155,21 @@ struct T5 : P5 { // Qualified reference var v6: T5.T1 // OK var v7: T5.T2 // OK + + var v8 = P6.A.self + var v9 = P6.B.self // expected-error {{typealias 'B' can only be used with a concrete type or generic parameter base}} } // Unqualified lookup finds typealiases in protocol extensions, though -protocol P6 { +protocol P7 { associatedtype A } -extension P6 { +extension P7 { typealias Y = A } -struct S6 : P6 { +struct S7 : P7 { typealias A = Int // FIXME