Skip to content

Commit 772bd76

Browse files
committed
Sema: Lazily validate protocol members
1 parent 9b7f3f7 commit 772bd76

File tree

7 files changed

+63
-16
lines changed

7 files changed

+63
-16
lines changed

lib/Sema/CSApply.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,9 @@ namespace {
846846
// Class members might be virtually dispatched, so we need to know
847847
// the full layout of the class.
848848
if (auto *classDecl = dyn_cast<ClassDecl>(member->getDeclContext()))
849-
tc.requestClassLayout(classDecl);
849+
tc.requestNominalLayout(classDecl);
850+
if (auto *protocolDecl = dyn_cast<ProtocolDecl>(member->getDeclContext()))
851+
tc.requestNominalLayout(protocolDecl);
850852

851853
auto refTy = solution.simplifyType(openedFullType);
852854

lib/Sema/TypeCheckDecl.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6907,7 +6907,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
69076907
if (auto nominal = extendedTy->getAnyNominal()) {
69086908
TC.validateDecl(nominal);
69096909
if (auto *classDecl = dyn_cast<ClassDecl>(nominal))
6910-
TC.requestClassLayout(classDecl);
6910+
TC.requestNominalLayout(classDecl);
69116911

69126912
// Check the raw values of an enum, since we might synthesize
69136913
// RawRepresentable while checking conformances on this extension.
@@ -7611,7 +7611,14 @@ void TypeChecker::validateDecl(ValueDecl *D) {
76117611
markAsObjC(*this, proto, isObjC);
76127612
}
76137613

7614-
DeclsToFinalize.insert(proto);
7614+
// FIXME: IRGen likes to emit @objc protocol descriptors even if the
7615+
// protocol comes from a different module or translation unit.
7616+
//
7617+
// It would be nice if it didn't have to do that, then we could remove
7618+
// this case.
7619+
if (proto->isObjC())
7620+
requestNominalLayout(proto);
7621+
76157622
break;
76167623
}
76177624

@@ -7797,8 +7804,17 @@ void TypeChecker::validateDeclForNameLookup(ValueDecl *D) {
77977804
validateDeclForNameLookup(ATD);
77987805
}
77997806

7800-
// Make sure the protocol is fully validated by the end of Sema.
7801-
DeclsToFinalize.insert(proto);
7807+
// Compute the requirement signature later to avoid circularity.
7808+
DelayedRequirementSignatures.insert(proto);
7809+
7810+
// FIXME: IRGen likes to emit @objc protocol descriptors even if the
7811+
// protocol comes from a different module or translation unit.
7812+
//
7813+
// It would be nice if it didn't have to do that, then we could remove
7814+
// this case.
7815+
if (proto->isObjC())
7816+
requestNominalLayout(proto);
7817+
78027818
break;
78037819
}
78047820
case DeclKind::AssociatedType: {
@@ -7888,22 +7904,22 @@ static bool shouldValidateMemberDuringFinalization(NominalTypeDecl *nominal,
78887904
return false;
78897905
}
78907906

7891-
void TypeChecker::requestClassLayout(ClassDecl *classDecl) {
7892-
if (classDecl->hasValidatedLayout())
7907+
void TypeChecker::requestNominalLayout(NominalTypeDecl *nominalDecl) {
7908+
if (nominalDecl->hasValidatedLayout())
78937909
return;
78947910

7895-
classDecl->setHasValidatedLayout();
7911+
nominalDecl->setHasValidatedLayout();
78967912

7897-
if (isa<SourceFile>(classDecl->getModuleScopeContext()))
7898-
DeclsToFinalize.insert(classDecl);
7913+
if (isa<SourceFile>(nominalDecl->getModuleScopeContext()))
7914+
DeclsToFinalize.insert(nominalDecl);
78997915
}
79007916

79017917
void TypeChecker::requestSuperclassLayout(ClassDecl *classDecl) {
79027918
auto superclassTy = classDecl->getSuperclass();
79037919
if (superclassTy) {
79047920
auto *superclassDecl = superclassTy->getClassOrBoundGenericClass();
79057921
if (superclassDecl)
7906-
requestClassLayout(superclassDecl);
7922+
requestNominalLayout(superclassDecl);
79077923
}
79087924
}
79097925

lib/Sema/TypeChecker.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,16 @@ static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) {
447447
llvm_unreachable("Unhandled external definition kind");
448448
}
449449

450+
// Complete any protocol requirement signatures that were delayed
451+
// because the protocol was validated via validateDeclForNameLookup().
452+
while (!TC.DelayedRequirementSignatures.empty()) {
453+
auto decl = TC.DelayedRequirementSignatures.pop_back_val();
454+
if (decl->isInvalid() || TC.Context.hadError())
455+
continue;
456+
457+
TC.validateDecl(decl);
458+
}
459+
450460
// Validate any referenced declarations for SIL's purposes.
451461
// Note: if we ever start putting extension members in vtables, we'll need
452462
// to validate those members too.
@@ -470,6 +480,7 @@ static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) {
470480
} while (currentFunctionIdx < TC.definedFunctions.size() ||
471481
currentExternalDef < TC.Context.ExternalDefinitions.size() ||
472482
!TC.DeclsToFinalize.empty() ||
483+
!TC.DelayedRequirementSignatures.empty() ||
473484
!TC.UsedConformances.empty());
474485

475486
// FIXME: Horrible hack. Store this somewhere more appropriate.

lib/Sema/TypeChecker.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,11 @@ class TypeChecker final : public LazyResolver {
715715
/// we can hand them off to SILGen etc.
716716
llvm::SetVector<ValueDecl *> DeclsToFinalize;
717717

718+
/// The list of protocols that need their requirement signatures computed,
719+
/// because they were first validated by validateDeclForNameLookup(),
720+
/// which skips this step.
721+
llvm::SetVector<ProtocolDecl *> DelayedRequirementSignatures;
722+
718723
/// The list of types whose circularity checks were delayed.
719724
SmallVector<NominalTypeDecl*, 8> DelayedCircularityChecks;
720725

@@ -1068,7 +1073,7 @@ class TypeChecker final : public LazyResolver {
10681073

10691074
/// Request that the given class needs to have all members validated
10701075
/// after everything in the translation unit has been processed.
1071-
void requestClassLayout(ClassDecl *classDecl);
1076+
void requestNominalLayout(NominalTypeDecl *nominalDecl);
10721077

10731078
/// Request that the superclass of the given class, if any, needs to have
10741079
/// all members validated after everything in the translation unit has

validation-test/compiler_scale/class_members.gyb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,4 @@ class Class${N} {
1414
static var prop = 0
1515
subscript(n: Int) -> Int { return 0 }
1616
% end
17-
18-
struct Nested {}
1917
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %scale-test --sum-multi --typecheck --begin 5 --end 16 --step 5 --select validateDecl %s
2+
// REQUIRES: OS=macosx
3+
// REQUIRES: asserts
4+
5+
protocol Protocol${N} {
6+
% if int(N) > 1:
7+
func method(_: Protocol${int(N)-1})
8+
var prop: Protocol${int(N)-1} { get set }
9+
static var prop: Protocol${int(N)-1} { get set }
10+
subscript(n: Int) -> Protocol${int(N)-1} { get set }
11+
% else:
12+
func method()
13+
var prop: Int { get set }
14+
static var prop: Int { get set }
15+
subscript(n: Int) -> Int { get set }
16+
% end
17+
}

validation-test/compiler_scale/struct_members.gyb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,4 @@ struct Struct${N} {
1414
static var prop = 0
1515
subscript(n: Int) -> Int { return 0 }
1616
% end
17-
18-
struct Nested {}
1917
}

0 commit comments

Comments
 (0)