Skip to content

Commit 157613a

Browse files
authored
Merge pull request #26970 from CodaFi/extension-winding
Fix a Hole in Extension Checking For Non-Nominal Types and Indirectly Nominal Types
2 parents f055791 + 5968751 commit 157613a

File tree

7 files changed

+69
-45
lines changed

7 files changed

+69
-45
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,6 +1625,11 @@ NOTE(objc_generic_extension_using_type_parameter_here,none,
16251625
"generic parameter used here", ())
16261626
NOTE(objc_generic_extension_using_type_parameter_try_objc,none,
16271627
"add '@objc' to allow uses of 'self' within the function body", ())
1628+
ERROR(invalid_nominal_extension,none,
1629+
"extension of type %0 must be declared as an extension of %1",
1630+
(Type, Type))
1631+
NOTE(invalid_nominal_extension_rewrite,none,
1632+
"did you mean to extend %0 instead?", (Type))
16281633

16291634
// Protocols
16301635
ERROR(type_does_not_conform,none,

lib/Sema/TypeCheckAccess.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,9 +1926,6 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
19261926

19271927
void visitExtensionDecl(ExtensionDecl *ED) {
19281928
auto extendedType = ED->getExtendedNominal();
1929-
// TODO: Sometimes we have an extension that is marked valid but has no
1930-
// extended type. Assert, just in case we see it while testing, but
1931-
// don't crash. rdar://50401284
19321929
assert(extendedType && "valid extension with no extended type?");
19331930
if (!extendedType || shouldSkipChecking(extendedType))
19341931
return;

lib/Sema/TypeCheckDecl.cpp

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3049,29 +3049,53 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
30493049
}
30503050

30513051
void visitExtensionDecl(ExtensionDecl *ED) {
3052+
// Produce any diagnostics for the extended type.
3053+
auto extType = ED->getExtendedType();
3054+
3055+
auto nominal = ED->getExtendedNominal();
3056+
if (nominal == nullptr) {
3057+
const bool wasAlreadyInvalid = ED->isInvalid();
3058+
ED->setInvalid();
3059+
if (extType && !extType->hasError() && extType->getAnyNominal()) {
3060+
// If we've got here, then we have some kind of extension of a prima
3061+
// fascie non-nominal type. This can come up when we're projecting
3062+
// typealiases out of bound generic types.
3063+
//
3064+
// struct Array<T> { typealias Indices = Range<Int> }
3065+
// extension Array.Indices.Bound {}
3066+
//
3067+
// Offer to rewrite it to the underlying nominal type.
3068+
auto canExtType = extType->getCanonicalType();
3069+
ED->diagnose(diag::invalid_nominal_extension, extType, canExtType)
3070+
.highlight(ED->getExtendedTypeRepr()->getSourceRange());
3071+
ED->diagnose(diag::invalid_nominal_extension_rewrite, canExtType)
3072+
.fixItReplace(ED->getExtendedTypeRepr()->getSourceRange(),
3073+
canExtType->getString());
3074+
} else if (!wasAlreadyInvalid) {
3075+
// If nothing else applies, fall back to a generic diagnostic.
3076+
ED->diagnose(diag::non_nominal_extension, extType);
3077+
}
3078+
return;
3079+
}
3080+
30523081
TC.validateExtension(ED);
30533082

30543083
checkInheritanceClause(ED);
30553084

3056-
if (auto nominal = ED->getExtendedNominal()) {
3057-
TC.validateDecl(nominal);
3058-
3059-
// Check the raw values of an enum, since we might synthesize
3060-
// RawRepresentable while checking conformances on this extension.
3061-
if (auto enumDecl = dyn_cast<EnumDecl>(nominal)) {
3062-
if (enumDecl->hasRawType())
3063-
checkEnumRawValues(TC, enumDecl);
3064-
}
3085+
// Check the raw values of an enum, since we might synthesize
3086+
// RawRepresentable while checking conformances on this extension.
3087+
if (auto enumDecl = dyn_cast<EnumDecl>(nominal)) {
3088+
if (enumDecl->hasRawType())
3089+
checkEnumRawValues(TC, enumDecl);
3090+
}
30653091

3066-
// Only generic and protocol types are permitted to have
3067-
// trailing where clauses.
3068-
if (auto trailingWhereClause = ED->getTrailingWhereClause()) {
3069-
if (!ED->getGenericParams() &&
3070-
!ED->isInvalid()) {
3071-
ED->diagnose(diag::extension_nongeneric_trailing_where,
3072-
nominal->getFullName())
3092+
// Only generic and protocol types are permitted to have
3093+
// trailing where clauses.
3094+
if (auto trailingWhereClause = ED->getTrailingWhereClause()) {
3095+
if (!ED->getGenericParams() && !ED->isInvalid()) {
3096+
ED->diagnose(diag::extension_nongeneric_trailing_where,
3097+
nominal->getFullName())
30733098
.highlight(trailingWhereClause->getSourceRange());
3074-
}
30753099
}
30763100
}
30773101

@@ -4371,13 +4395,14 @@ static Type formExtensionInterfaceType(
43714395
/// Check the generic parameters of an extension, recursively handling all of
43724396
/// the parameter lists within the extension.
43734397
static GenericEnvironment *
4374-
checkExtensionGenericParams(TypeChecker &tc, ExtensionDecl *ext, Type type,
4398+
checkExtensionGenericParams(TypeChecker &tc, ExtensionDecl *ext,
43754399
GenericParamList *genericParams) {
43764400
assert(!ext->getGenericEnvironment());
43774401

43784402
// Form the interface type of the extension.
43794403
bool mustInferRequirements = false;
43804404
SmallVector<std::pair<Type, Type>, 4> sameTypeReqs;
4405+
auto type = ext->getExtendedType();
43814406
Type extInterfaceType =
43824407
formExtensionInterfaceType(tc, ext, type, genericParams, sameTypeReqs,
43834408
mustInferRequirements);
@@ -4488,10 +4513,6 @@ void TypeChecker::validateExtension(ExtensionDecl *ext) {
44884513

44894514
DeclValidationRAII IBV(ext);
44904515

4491-
auto extendedType = evaluateOrDefault(Context.evaluator,
4492-
ExtendedTypeRequest{ext},
4493-
ErrorType::get(ext->getASTContext()));
4494-
44954516
if (auto *nominal = ext->getExtendedNominal()) {
44964517
// If this extension was not already bound, it means it is either in an
44974518
// inactive conditional compilation block, or otherwise (incorrectly)
@@ -4504,8 +4525,7 @@ void TypeChecker::validateExtension(ExtensionDecl *ext) {
45044525
validateDecl(nominal);
45054526

45064527
if (auto *genericParams = ext->getGenericParams()) {
4507-
GenericEnvironment *env =
4508-
checkExtensionGenericParams(*this, ext, extendedType, genericParams);
4528+
auto *env = checkExtensionGenericParams(*this, ext, genericParams);
45094529
ext->setGenericEnvironment(env);
45104530
}
45114531
}

test/SourceKit/Indexing/index.swift.response

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,13 +1222,6 @@
12221222
}
12231223
]
12241224
},
1225-
{
1226-
key.kind: source.lang.swift.decl.function.method.instance,
1227-
key.name: "meth()",
1228-
key.usr: <usr>,
1229-
key.line: 134,
1230-
key.column: 8
1231-
},
12321225
{
12331226
key.kind: source.lang.swift.decl.class,
12341227
key.name: "CC4",

test/attr/dynamicReplacement.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,6 @@ extension K {
5050
func replacement_finalFunction() {}
5151
}
5252

53-
extension undeclared { // expected-error{{use of undeclared type 'undeclared'}}
54-
@_dynamicReplacement(for: property) // expected-error{{replaced accessor for 'property' could not be found}}
55-
var replacement_property: Int { return 2 }
56-
57-
@_dynamicReplacement(for: func) // expected-error{{replaced function 'func' could not be found}}
58-
func func2() -> Int { return 2 }
59-
}
60-
6153
extension P {
6254
@_dynamicReplacement(for: v)
6355
var replacement_v : Int {

test/decl/ext/extensions.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,20 @@ extension DoesNotImposeClassReq_2 where Self : AnyObject {
153153
set { property = newValue } // Okay
154154
}
155155
}
156+
157+
// Reject extension of nominal type via parameterized typealias
158+
159+
struct Nest<Egg> { typealias Contents = Egg }
160+
struct Tree {
161+
typealias LimbContent = Nest<Int>
162+
typealias BoughPayload = Nest<Nest<Int>>
163+
}
164+
165+
extension Tree.LimbContent.Contents {
166+
// expected-error@-1 {{extension of type 'Tree.LimbContent.Contents' (aka 'Int') must be declared as an extension of 'Int'}}
167+
// expected-note@-2 {{did you mean to extend 'Int' instead?}} {{11-36=Int}}
168+
}
169+
170+
extension Tree.BoughPayload.Contents {
171+
// expected-error@-1 {{constrained extension must be declared on the unspecialized generic type 'Nest'}}
172+
}

test/decl/nested/type_in_extension.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ extension G {
88
}
99
}
1010

11-
extension { // expected-error {{expected type name in extension declaration}}
12-
struct S<T> {
11+
extension G {
12+
struct S<T> { // expected-note {{generic type 'S' declared here}}
1313
func foo(t: T) {}
1414
}
1515

16-
class M : S {} // expected-error {{use of undeclared type 'S'}}
16+
class M : S {} // expected-error {{reference to generic type 'G<T>.S' requires arguments in <...>}}
1717

1818
protocol P { // expected-error {{protocol 'P' cannot be nested inside another declaration}}
1919
associatedtype A

0 commit comments

Comments
 (0)