Skip to content

Commit 6f77014

Browse files
committed
Merge pull request #1075 from dduan/sema_circularity_pr
[Sema] implement better type circularity check
2 parents 0356ec8 + 6374a8e commit 6f77014

File tree

10 files changed

+292
-74
lines changed

10 files changed

+292
-74
lines changed

Diff for: include/swift/AST/DiagnosticsSIL.def

-6
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,6 @@ ERROR(inout_argument_alias,none,
7878
NOTE(previous_inout_alias,none,
7979
"previous aliasing argument", ())
8080

81-
ERROR(unsupported_recursive_type,none,
82-
"recursive value type %0 is not allowed", (Type))
83-
84-
ERROR(recursive_enum_not_indirect,none,
85-
"recursive enum %0 is not marked 'indirect'", (Type))
86-
8781
ERROR(unsupported_c_function_pointer_conversion,none,
8882
"C function pointer signature %0 is not compatible with expected type %1",
8983
(Type, Type))

Diff for: include/swift/AST/DiagnosticsSema.def

+7
Original file line numberDiff line numberDiff line change
@@ -2247,6 +2247,13 @@ WARNING(no_throw_in_do_with_catch,none,
22472247
// Type Check Types
22482248
//------------------------------------------------------------------------------
22492249

2250+
ERROR(unsupported_recursive_type,none,
2251+
"value type %0 cannot have a stored property that references itself",
2252+
(Type))
2253+
2254+
ERROR(recursive_enum_not_indirect,none,
2255+
"recursive enum %0 is not marked 'indirect'", (Type))
2256+
22502257
ERROR(sugar_type_not_found,none,
22512258
"broken standard library: cannot find "
22522259
"%select{Array|Optional|ImplicitlyUnwrappedOptional|Dictionary|"

Diff for: include/swift/SIL/TypeLowering.h

-3
Original file line numberDiff line numberDiff line change
@@ -530,9 +530,6 @@ class TypeConverter {
530530

531531
llvm::DenseMap<AnyFunctionRef, CaptureInfo> LoweredCaptures;
532532

533-
/// The set of recursive types we've already diagnosed.
534-
llvm::DenseSet<NominalTypeDecl *> RecursiveNominalTypes;
535-
536533
/// The current generic context signature.
537534
CanGenericSignature CurGenericContext;
538535

Diff for: lib/SIL/TypeLowering.cpp

+1-14
Original file line numberDiff line numberDiff line change
@@ -1339,20 +1339,7 @@ const TypeLowering *TypeConverter::find(TypeKey k) {
13391339
// Try to complain about a nominal type.
13401340
auto nomTy = k.SubstType.getAnyNominal();
13411341
assert(nomTy && "non-nominal types should not be recursive");
1342-
1343-
if (RecursiveNominalTypes.insert(nomTy).second) {
1344-
auto &diags = M.getASTContext().Diags;
1345-
if (auto *ED = dyn_cast<EnumDecl>(nomTy)) {
1346-
diags.diagnose(ED->getStartLoc(),
1347-
diag::recursive_enum_not_indirect,
1348-
nomTy->getDeclaredTypeInContext())
1349-
.fixItInsert(ED->getStartLoc(), "indirect ");
1350-
} else {
1351-
diags.diagnose(nomTy->getLoc(),
1352-
diag::unsupported_recursive_type,
1353-
nomTy->getDeclaredTypeInContext());
1354-
}
1355-
}
1342+
13561343
auto result = new (*this, k.isDependent()) RecursiveErrorTypeLowering(
13571344
SILType::getPrimitiveAddressType(k.SubstType));
13581345
found->second = result;

Diff for: lib/Sema/TypeCheckDecl.cpp

+149
Original file line numberDiff line numberDiff line change
@@ -2695,6 +2695,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26952695
if ((IsSecondPass && !IsFirstPass) ||
26962696
decl->getDeclContext()->isProtocolOrProtocolExtensionContext()) {
26972697
TC.checkUnsupportedProtocolType(decl);
2698+
if (auto nominal = dyn_cast<NominalTypeDecl>(decl)) {
2699+
TC.checkDeclCircularity(nominal);
2700+
}
26982701
}
26992702
}
27002703

@@ -5603,6 +5606,151 @@ static Optional<ObjCReason> shouldMarkClassAsObjC(TypeChecker &TC,
56035606
return None;
56045607
}
56055608

5609+
using NominalDeclSet = llvm::SmallPtrSetImpl<NominalTypeDecl *>;
5610+
static bool checkEnumDeclCircularity(EnumDecl *E, NominalDeclSet &known,
5611+
Type baseType, bool isGenericArg = false);
5612+
5613+
static bool checkStructDeclCircularity(StructDecl *S, NominalDeclSet &known,
5614+
Type baseType,
5615+
bool isGenericArg = false);
5616+
5617+
// dispatch arbitrary declaration to relavent circularity checks.
5618+
static bool checkNominalDeclCircularity(Decl *decl, NominalDeclSet &known,
5619+
Type baseType,
5620+
bool isGenericArg = false) {
5621+
if (auto s = dyn_cast<StructDecl>(decl)) {
5622+
if (checkStructDeclCircularity(s, known, baseType, isGenericArg)) {
5623+
return true;
5624+
}
5625+
} else if (auto e = dyn_cast<EnumDecl>(decl)) {
5626+
if (checkEnumDeclCircularity(e, known, baseType, isGenericArg)) {
5627+
return true;
5628+
}
5629+
}
5630+
return false;
5631+
}
5632+
5633+
5634+
// break down type that "contains" other types and check them each.
5635+
static bool deconstructTypeForDeclCircularity(Type type,
5636+
NominalDeclSet &known) {
5637+
5638+
if (auto optional = dyn_cast<OptionalType>(type.getPointer())) {
5639+
// unwrap optionals, check again.
5640+
if (deconstructTypeForDeclCircularity(optional->getBaseType(), known)) {
5641+
return true;
5642+
}
5643+
} else if (auto tuple = type->getAs<TupleType>()) {
5644+
// check each element in tuple
5645+
for (auto Elt: tuple->getElements()) {
5646+
if (deconstructTypeForDeclCircularity(Elt.getType(), known)) {
5647+
return true;
5648+
}
5649+
}
5650+
} else {
5651+
if (auto decl = type->getAnyNominal()) {
5652+
// Found a circularity! Stop checking from here on out.
5653+
if (known.count(decl)) {
5654+
return true;
5655+
}
5656+
5657+
bool isGenericArg = type->getAs<BoundGenericType>() != nullptr;
5658+
if (checkNominalDeclCircularity(decl, known, type, isGenericArg)) {
5659+
return true;
5660+
}
5661+
}
5662+
5663+
}
5664+
5665+
return false;
5666+
}
5667+
5668+
5669+
static bool checkStructDeclCircularity(StructDecl *S, NominalDeclSet &known,
5670+
Type baseType, bool isGenericArg) {
5671+
5672+
// if we are checking a generic argument, don't make it a starting point
5673+
// of a circularity.
5674+
if (!isGenericArg) {
5675+
known.insert(S);
5676+
}
5677+
5678+
for (auto field: S->getStoredProperties()) {
5679+
if (auto vd = dyn_cast<VarDecl>(field)) {
5680+
// skip uninteresting fields.
5681+
if (vd->isStatic() || !vd->hasStorage() || !vd->hasType()) {
5682+
continue;
5683+
}
5684+
5685+
auto vdt = baseType->getTypeOfMember(S->getModuleContext(), vd, nullptr);
5686+
5687+
if (deconstructTypeForDeclCircularity(vdt, known)) {
5688+
return true;
5689+
}
5690+
}
5691+
}
5692+
5693+
// we didn't find any circularity, clean up.
5694+
if (!isGenericArg) {
5695+
known.erase(S);
5696+
}
5697+
return false;
5698+
}
5699+
5700+
static bool checkEnumDeclCircularity(EnumDecl *E, NominalDeclSet &known,
5701+
Type baseType, bool isGenericArg) {
5702+
// enums marked as 'indirect' are safe
5703+
if (E->isIndirect()) { return false; }
5704+
5705+
5706+
// if we are checking a generic argument, don't make it a starting point
5707+
// of a circularity.
5708+
if (!isGenericArg) {
5709+
known.insert(E);
5710+
}
5711+
5712+
for (auto elt: E->getAllElements()) {
5713+
// skip uninteresting fields.
5714+
if (!elt->hasArgumentType() || elt->isIndirect()) {
5715+
continue;
5716+
}
5717+
auto eltType = baseType->getTypeOfMember(E->getModuleContext(), elt,
5718+
nullptr, elt->getArgumentInterfaceType());
5719+
5720+
if (!eltType) { continue; }
5721+
5722+
if (deconstructTypeForDeclCircularity(eltType, known)) {
5723+
return true;
5724+
}
5725+
}
5726+
5727+
// we didn't find any circularity, clean up.
5728+
if (!isGenericArg) {
5729+
known.erase(E);
5730+
}
5731+
5732+
return false;
5733+
}
5734+
5735+
void TypeChecker::checkDeclCircularity(NominalTypeDecl *decl) {
5736+
llvm::SmallPtrSet<NominalTypeDecl *, 16> knownTypes;
5737+
auto ty = decl->getDeclaredInterfaceType();
5738+
if (auto structDecl = dyn_cast<StructDecl>(decl)) {
5739+
if (checkStructDeclCircularity(structDecl, knownTypes, ty)) {
5740+
diagnose(decl->getLoc(),
5741+
diag::unsupported_recursive_type,
5742+
decl->getDeclaredTypeInContext());
5743+
}
5744+
} else if (auto enumDecl = dyn_cast<EnumDecl>(decl)) {
5745+
if (checkEnumDeclCircularity(enumDecl, knownTypes, ty)) {
5746+
diagnose(decl->getLoc(),
5747+
diag::recursive_enum_not_indirect,
5748+
decl->getDeclaredTypeInContext())
5749+
.fixItInsert(decl->getStartLoc(), "indirect ");
5750+
}
5751+
}
5752+
}
5753+
56065754
void TypeChecker::validateDecl(ValueDecl *D, bool resolveTypeParams) {
56075755
if (hasEnabledForbiddenTypecheckPrefix())
56085756
checkForForbiddenPrefix(D);
@@ -6036,6 +6184,7 @@ void TypeChecker::validateDecl(ValueDecl *D, bool resolveTypeParams) {
60366184
assert(D->hasType());
60376185
}
60386186

6187+
60396188
void TypeChecker::validateAccessibility(ValueDecl *D) {
60406189
if (D->hasAccessibility())
60416190
return;

Diff for: lib/Sema/TypeChecker.h

+6
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,12 @@ class TypeChecker final : public LazyResolver {
11241124
bool typeCheckExpressionShallow(Expr *&expr, DeclContext *dc,
11251125
Type convertType = Type());
11261126

1127+
/// \brief Type check whether the given type declaration includes members of
1128+
/// unsupported recursive value types.
1129+
///
1130+
/// \param decl The declaration to be type-checked. This process will not
1131+
/// modify the declaration.
1132+
void checkDeclCircularity(NominalTypeDecl *decl);
11271133

11281134
/// \brief Type check the given expression as a condition, which converts
11291135
/// it to a logic value.

Diff for: test/SILGen/unsupported_recursive_value_type.swift

-50
This file was deleted.

0 commit comments

Comments
 (0)