Skip to content

Commit 823ed14

Browse files
authored
Merge pull request #16987 from AnthonyLatsis/warn-unconstructible-enum
[Diagnostics][Sema] warn inconstructible enum
2 parents e1fef4d + abb864d commit 823ed14

File tree

3 files changed

+76
-1
lines changed

3 files changed

+76
-1
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3207,6 +3207,8 @@ ERROR(unsupported_recursive_struct,none,
32073207
"contains it",
32083208
(Type))
32093209

3210+
WARNING(enum_non_well_founded,none,
3211+
"enum containing only recursive cases is impossible to instantiate", ())
32103212
ERROR(recursive_enum_not_indirect,none,
32113213
"recursive enum %0 is not marked 'indirect'", (Type))
32123214
ERROR(unsupported_infinitely_sized_type,none,

lib/Sema/TypeCheckCircularity.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ class CircularityChecker {
169169
bool diagnoseInfiniteRecursion(CanType parentType, ValueDecl *member,
170170
CanType memberType);
171171

172+
void diagnoseNonWellFoundedEnum(EnumDecl *E);
173+
172174
void addPathElementsTo(Path &path, CanType type);
173175
void addPathElement(Path &path, ValueDecl *member, CanType memberType);
174176

@@ -272,7 +274,11 @@ bool CircularityChecker::expandStruct(CanType type, StructDecl *S,
272274
bool CircularityChecker::expandEnum(CanType type, EnumDecl *E,
273275
unsigned depth) {
274276
// Indirect enums are representational leaves.
275-
if (E->isIndirect()) return false;
277+
if (E->isIndirect()) {
278+
// Diagnose whether the enum is non-well-founded before bailing
279+
diagnoseNonWellFoundedEnum(E);
280+
return false;
281+
}
276282

277283
startExpandingType(type);
278284

@@ -298,6 +304,7 @@ bool CircularityChecker::expandEnum(CanType type, EnumDecl *E,
298304
if (addMember(type, elt, eltType, depth))
299305
return true;
300306
}
307+
diagnoseNonWellFoundedEnum(E);
301308

302309
return false;
303310
}
@@ -601,3 +608,44 @@ bool CircularityChecker::diagnoseInfiniteRecursion(CanType parentType,
601608

602609
return true;
603610
}
611+
612+
/// Show a warning if all cases of the given enum are recursive,
613+
/// making it impossible to be instantiated. Such an enum is 'non-well-founded'.
614+
/// The outcome of this method is irrelevant.
615+
void CircularityChecker::diagnoseNonWellFoundedEnum(EnumDecl *E) {
616+
617+
auto containsType = [](TupleType *tuple, Type E) -> bool {
618+
for (auto type: tuple->getElementTypes()) {
619+
if (type->isEqual(E))
620+
return true;
621+
}
622+
return false;
623+
};
624+
auto isNonWellFounded = [containsType, E]() -> bool {
625+
auto elts = E->getAllElements();
626+
if (elts.empty())
627+
return false;
628+
629+
for (auto elt: elts) {
630+
if (!elt->hasInterfaceType() ||
631+
!(elt->isIndirect() || E->isIndirect()))
632+
return false;
633+
634+
auto argTy = elt->getArgumentInterfaceType();
635+
if (!argTy)
636+
return false;
637+
638+
if (auto tuple = argTy->getAs<TupleType>()) {
639+
if (!containsType(tuple, E->getSelfInterfaceType()))
640+
return false;
641+
} else if (auto paren = dyn_cast<ParenType>(argTy.getPointer())) {
642+
if (!E->getSelfInterfaceType()->isEqual(paren->getUnderlyingType()))
643+
return false;
644+
}
645+
}
646+
return true;
647+
};
648+
649+
if (isNonWellFounded())
650+
TC.diagnose(E, diag::enum_non_well_founded);
651+
}

test/Sema/unsupported_recursive_value_type.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,31 @@ enum RecursiveByGenericSubstitutionEnum<T> {
4242
case A(T)
4343
}
4444

45+
enum InconstructibleEnum1 { // expected-warning {{enum containing only recursive cases is impossible to instantiate}}
46+
indirect case A(InconstructibleEnum1)
47+
}
48+
enum InconstructibleEnum2 { // OK
49+
indirect case A(InconstructibleEnum2)
50+
case B(Bool)
51+
indirect case C(Int, InconstructibleEnum2)
52+
}
53+
enum InconstructibleEnum3 { // expected-warning {{enum containing only recursive cases is impossible to instantiate}}
54+
indirect case B(Int, InconstructibleEnum3)
55+
}
56+
indirect enum InconstructibleEnum4 {
57+
// expected-warning@-1 {{enum containing only recursive cases is impossible to instantiate}}
58+
case A(InconstructibleEnum4)
59+
}
60+
indirect enum InconstructibleEnum5 {
61+
// expected-warning@-1 {{enum containing only recursive cases is impossible to instantiate}}
62+
case B(Int, InconstructibleEnum5)
63+
}
64+
indirect enum InconstructibleEnum6 { // OK
65+
case A(InconstructibleEnum6)
66+
case B(Bool)
67+
case C(Int, InconstructibleEnum6)
68+
}
69+
4570
struct RecursiveByBeingInTupleStruct {
4671
let a: (Int, RecursiveByBeingInTupleStruct) // expected-error{{value type 'RecursiveByBeingInTupleStruct' cannot have a stored property that recursively contains it}}
4772
// expected-note@-1 {{cycle beginning here: (Int, RecursiveByBeingInTupleStruct) -> (.1: RecursiveByBeingInTupleStruct)}}

0 commit comments

Comments
 (0)