Skip to content

Commit 2ff9994

Browse files
committed
Sema: Improve circularity checks
The previous patches regressed a test where we used to diagnose (poorly) a circular associated type, like so: associatedtype e: e With the error "inheritance from non-protocol, non-class type 'e'". This error went away, because we end up not setting the interface type of the associated type early enough. Instead, we return an ErrorType from resolveTypeInContext() and diagnose nothing. With this patch, emit a diagnostic at the point where the ErrorType first appears. Also, remove the isRecursive() bit from AssociatedTypeDecl, and remove isBeingTypeChecked() which duplicates a bit with the same name in Decl.
1 parent c4dbf91 commit 2ff9994

File tree

12 files changed

+30
-50
lines changed

12 files changed

+30
-50
lines changed

include/swift/AST/Decl.h

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -515,13 +515,8 @@ class alignas(1 << DeclAlignInBits) Decl {
515515
class AssociatedTypeDeclBitfields {
516516
friend class AssociatedTypeDecl;
517517
unsigned : NumTypeDeclBits;
518-
519-
unsigned Recursive : 1;
520-
521-
/// Whether or not this declaration is currently being type-checked.
522-
unsigned BeingTypeChecked : 1;
523518
};
524-
enum { NumAssociatedTypeDeclBits = NumTypeDeclBits + 2 };
519+
enum { NumAssociatedTypeDeclBits = NumTypeDeclBits };
525520
static_assert(NumAssociatedTypeDeclBits <= 32, "fits in an unsigned");
526521

527522
class ImportDeclBitfields {
@@ -2521,17 +2516,6 @@ class AssociatedTypeDecl : public AbstractTypeParamDecl {
25212516
SourceLoc getStartLoc() const { return KeywordLoc; }
25222517
SourceRange getSourceRange() const;
25232518

2524-
void setIsRecursive();
2525-
bool isRecursive() { return AssociatedTypeDeclBits.Recursive; }
2526-
2527-
/// Whether the declaration is currently being validated.
2528-
bool isBeingTypeChecked() { return AssociatedTypeDeclBits.BeingTypeChecked; }
2529-
2530-
/// Toggle whether or not the declaration is being validated.
2531-
void setIsBeingTypeChecked(bool ibt = true) {
2532-
AssociatedTypeDeclBits.BeingTypeChecked = ibt;
2533-
}
2534-
25352519
static bool classof(const Decl *D) {
25362520
return D->getKind() == DeclKind::AssociatedType;
25372521
}

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,6 +1543,8 @@ ERROR(requires_generic_param_made_equal_to_concrete,none,
15431543
ERROR(requires_superclass_conflict,none,
15441544
"generic parameter %0 cannot be a subclass of both %1 and %2",
15451545
(Identifier, Type, Type))
1546+
ERROR(recursive_type_reference,none,
1547+
"type %0 references itself", (Identifier))
15461548
ERROR(recursive_requirement_reference,none,
15471549
"type may not reference itself as a requirement",())
15481550
ERROR(recursive_same_type_constraint,none,

lib/AST/ArchetypeBuilder.cpp

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,18 +1429,9 @@ bool ArchetypeBuilder::addAbstractTypeParamRequirements(
14291429
auto markRecursive = [&](AssociatedTypeDecl *assocType,
14301430
ProtocolDecl *proto,
14311431
SourceLoc loc) {
1432-
if (!pa->isRecursive() && !assocType->isRecursive()) {
1432+
if (!pa->isRecursive() && !assocType->isInvalid()) {
14331433
Diags.diagnose(assocType->getLoc(),
14341434
diag::recursive_requirement_reference);
1435-
1436-
// Mark all associatedtypes in this protocol as recursive (and error-type)
1437-
// to avoid later crashes dealing with this invalid protocol in other
1438-
// contexts.
1439-
auto containingProto =
1440-
assocType->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
1441-
for (auto member : containingProto->getMembers())
1442-
if (auto assocType = dyn_cast<AssociatedTypeDecl>(member))
1443-
assocType->setIsRecursive();
14441435
}
14451436
pa->setIsRecursive();
14461437

lib/AST/Decl.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2270,9 +2270,7 @@ AssociatedTypeDecl::AssociatedTypeDecl(DeclContext *dc, SourceLoc keywordLoc,
22702270
TypeLoc defaultDefinition)
22712271
: AbstractTypeParamDecl(DeclKind::AssociatedType, dc, name, nameLoc),
22722272
KeywordLoc(keywordLoc), DefaultDefinition(defaultDefinition)
2273-
{
2274-
AssociatedTypeDeclBits.Recursive = 0;
2275-
}
2273+
{ }
22762274

22772275
AssociatedTypeDecl::AssociatedTypeDecl(DeclContext *dc, SourceLoc keywordLoc,
22782276
Identifier name, SourceLoc nameLoc,
@@ -2283,7 +2281,6 @@ AssociatedTypeDecl::AssociatedTypeDecl(DeclContext *dc, SourceLoc keywordLoc,
22832281
ResolverContextData(resolverData)
22842282
{
22852283
assert(Resolver && "missing resolver");
2286-
AssociatedTypeDeclBits.Recursive = 0;
22872284
}
22882285

22892286
void AssociatedTypeDecl::computeType() {
@@ -2314,10 +2311,6 @@ SourceRange AssociatedTypeDecl::getSourceRange() const {
23142311
return SourceRange(KeywordLoc, endLoc);
23152312
}
23162313

2317-
void AssociatedTypeDecl::setIsRecursive() {
2318-
AssociatedTypeDeclBits.Recursive = true;
2319-
}
2320-
23212314
EnumDecl::EnumDecl(SourceLoc EnumLoc,
23222315
Identifier Name, SourceLoc NameLoc,
23232316
MutableArrayRef<TypeLoc> Inherited,

lib/Sema/TypeCheckDecl.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6877,11 +6877,6 @@ void TypeChecker::validateDecl(ValueDecl *D, bool resolveTypeParams) {
68776877
case DeclKind::AssociatedType: {
68786878
auto typeParam = cast<AbstractTypeParamDecl>(D);
68796879
auto assocType = dyn_cast<AssociatedTypeDecl>(typeParam);
6880-
if (assocType && assocType->isRecursive()) {
6881-
D->setInvalid();
6882-
break;
6883-
}
6884-
68856880
DeclContext *DC = typeParam->getDeclContext();
68866881

68876882
if (assocType && !assocType->hasInterfaceType())

lib/Sema/TypeCheckType.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,9 @@ Type TypeChecker::applyUnboundGenericArguments(
595595
auto genericSig = decl->getGenericSignature();
596596
if (!decl->hasInterfaceType() ||
597597
decl->isValidatingGenericSignature()) {
598-
diagnose(loc, diag::recursive_requirement_reference);
598+
diagnose(loc, diag::recursive_type_reference,
599+
decl->getName());
600+
diagnose(noteLoc, diag::type_declared_here);
599601
return nullptr;
600602
}
601603

@@ -662,8 +664,12 @@ static Type resolveTypeDecl(TypeChecker &TC, TypeDecl *typeDecl, SourceLoc loc,
662664
TC.validateDecl(typeDecl);
663665

664666
// FIXME: More principled handling of circularity.
665-
if (!typeDecl->hasInterfaceType())
667+
if (!typeDecl->hasInterfaceType()) {
668+
TC.diagnose(loc, diag::recursive_type_reference,
669+
typeDecl->getName());
670+
TC.diagnose(typeDecl->getLoc(), diag::type_declared_here);
666671
return ErrorType::get(TC.Context);
672+
}
667673
}
668674

669675
// Resolve the type declaration to a specific type. How this occurs

test/Generics/generic_types.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,9 @@ var y: X5<X4, Int> // expected-error{{'X5' requires the types 'X4.AssocP' (aka '
226226

227227
// Recursive generic signature validation.
228228
class Top {}
229-
class Bottom<T : Bottom<Top>> {} // expected-error {{type may not reference itself as a requirement}}
229+
class Bottom<T : Bottom<Top>> {}
230+
// expected-error@-1 {{type 'Bottom' references itself}}
231+
// expected-note@-2 {{type declared here}}
230232

231233
// Invalid inheritance clause
232234

test/NameBinding/scope_map_lookup.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ protocol P1 {
2424

2525
// Protocols involving associated types.
2626
protocol AProtocol {
27-
associatedtype e : e // expected-error {{inheritance from non-protocol, non-class type 'Self.e'}}
27+
associatedtype e : e
28+
// expected-error@-1 {{type 'e' references itself}}
29+
// expected-note@-2 {{type declared here}}
2830
}
2931

3032
// Extensions.

test/Sema/circular_decl_checking.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ var TopLevelVar: TopLevelVar? { return nil } // expected-error 2 {{use of undecl
4444

4545

4646
protocol AProtocol {
47-
associatedtype e : e // expected-error {{inheritance from non-protocol, non-class type 'Self.e'}}
47+
associatedtype e : e
48+
// expected-error@-1 {{type 'e' references itself}}
49+
// expected-note@-2 {{type declared here}}
4850
}
4951

5052

test/decl/protocol/req/recursion.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ protocol Y {
2323
public protocol P {
2424
associatedtype T
2525
}
26-
public struct S<A: P> where A.T == S<A> {} // expected-error{{type may not reference itself as a requirement}}
26+
public struct S<A: P> where A.T == S<A> {}
27+
// expected-error@-1{{type 'S' references itself}}
28+
// expected-note@-2{{type declared here}}
2729

2830
protocol I {
2931
init() // expected-note 2{{protocol requires initializer 'init()' with type '()'}}
@@ -33,8 +35,9 @@ protocol PI {
3335
associatedtype T : I
3436
}
3537

36-
struct SI<A: PI> : I where A : I, A.T == SI<A> { // expected-error{{type may not reference itself as a requirement}}
37-
}
38+
struct SI<A: PI> : I where A : I, A.T == SI<A> {}
39+
// expected-error@-1{{type 'SI' references itself}}
40+
// expected-note@-2{{type declared here}}
3841

3942
// Used to hit infinite recursion
4043
struct S4<A: PI> : I where A : I {

validation-test/compiler_crashers/28393-swift-type-transform.swift renamed to validation-test/compiler_crashers_fixed/28393-swift-type-transform.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
// See https://swift.org/LICENSE.txt for license information
66
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

8-
// RUN: not --crash %target-swift-frontend %s -typecheck
8+
// RUN: not %target-swift-frontend %s -typecheck
99
// REQUIRES: asserts
1010
enum S:A{let d=b}protocol A{associatedtype b:A=S

validation-test/compiler_crashers/28438-swift-typebase-getcanonicaltype.swift renamed to validation-test/compiler_crashers_fixed/28438-swift-typebase-getcanonicaltype.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
// See https://swift.org/LICENSE.txt for license information
66
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

8-
// RUN: not --crash %target-swift-frontend %s -typecheck
8+
// RUN: not %target-swift-frontend %s -typecheck
99
// REQUIRES: asserts
1010
protocol A{typealias e}struct B:A{var f=e

0 commit comments

Comments
 (0)