Skip to content

Commit 111eea7

Browse files
committed
AST/SILGen: Requestify var initializer expression typechecking.
Allow initializer expressions to be emitted during SILGen when `-experimental-lazy-typecheck` is specified by introducing a new request that fully typechecks the init expressions of pattern binding declarations on-demand. There are still a few rough edges, like missing support for wrapped properties and incomplete handling of subsumed initializers. Fixing these issues is not an immediate priority because in the short term `-experimental-lazy-typecheck` will always be accompanied by `-enable-library-evolution` and `-experimental-skip-non-exportable-decls`. This means that only the initializers of properties on `@frozen` types will need to be emitted and property wrappers are not yet fully supported on properties belonging to `@frozen` types. Resolves rdar://117448868
1 parent 89bda71 commit 111eea7

13 files changed

+189
-22
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2276,6 +2276,10 @@ class PatternBindingDecl final : public Decl,
22762276
getMutablePatternList()[i].setOriginalInit(E);
22772277
}
22782278

2279+
/// Returns a fully typechecked executable init expression for the pattern at
2280+
/// the given index.
2281+
Expr *getCheckedExecutableInit(unsigned i) const;
2282+
22792283
Pattern *getPattern(unsigned i) const {
22802284
return getPatternList()[i].getPattern();
22812285
}

include/swift/AST/Pattern.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,11 @@ class alignas(8) Pattern : public ASTAllocated<Pattern> {
6868
// clang-format off
6969
union { uint64_t OpaqueBits;
7070

71-
SWIFT_INLINE_BITFIELD_BASE(Pattern, bitmax(NumPatternKindBits,8)+1+1,
71+
SWIFT_INLINE_BITFIELD_BASE(Pattern, bitmax(NumPatternKindBits,8)+1+1+1,
7272
Kind : bitmax(NumPatternKindBits,8),
7373
isImplicit : 1,
74-
hasInterfaceType : 1
74+
hasInterfaceType : 1,
75+
executableInitChecked : 1
7576
);
7677

7778
SWIFT_INLINE_BITFIELD_FULL(TuplePattern, Pattern, 32,
@@ -104,6 +105,7 @@ class alignas(8) Pattern : public ASTAllocated<Pattern> {
104105
Bits.Pattern.Kind = unsigned(kind);
105106
Bits.Pattern.isImplicit = false;
106107
Bits.Pattern.hasInterfaceType = false;
108+
Bits.Pattern.executableInitChecked = false;
107109
}
108110

109111
private:
@@ -136,6 +138,11 @@ class alignas(8) Pattern : public ASTAllocated<Pattern> {
136138
bool isImplicit() const { return Bits.Pattern.isImplicit; }
137139
void setImplicit() { Bits.Pattern.isImplicit = true; }
138140

141+
bool executableInitChecked() const {
142+
return Bits.Pattern.executableInitChecked;
143+
}
144+
void setExecutableInitChecked() { Bits.Pattern.executableInitChecked = true; }
145+
139146
/// Find the smallest subpattern which obeys the property that matching it is
140147
/// equivalent to matching this pattern.
141148
///

include/swift/AST/TypeCheckRequests.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,6 +2347,27 @@ class PatternBindingEntryRequest
23472347
void cacheResult(const PatternBindingEntry *value) const;
23482348
};
23492349

2350+
class PatternBindingCheckedExecutableInitRequest
2351+
: public SimpleRequest<PatternBindingCheckedExecutableInitRequest,
2352+
Expr *(PatternBindingDecl *, unsigned),
2353+
RequestFlags::SeparatelyCached> {
2354+
public:
2355+
using SimpleRequest::SimpleRequest;
2356+
2357+
private:
2358+
friend SimpleRequest;
2359+
2360+
// Evaluation.
2361+
Expr *evaluate(Evaluator &evaluator, PatternBindingDecl *PBD,
2362+
unsigned i) const;
2363+
2364+
public:
2365+
// Separate caching.
2366+
bool isCached() const { return true; }
2367+
llvm::Optional<Expr *> getCachedResult() const;
2368+
void cacheResult(Expr *expr) const;
2369+
};
2370+
23502371
class NamingPatternRequest
23512372
: public SimpleRequest<NamingPatternRequest, NamedPattern *(VarDecl *),
23522373
RequestFlags::SeparatelyCached> {

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ SWIFT_REQUEST(TypeChecker, OverriddenDeclsRequest,
258258
SWIFT_REQUEST(TypeChecker, PatternBindingEntryRequest,
259259
const PatternBindingEntry *(PatternBindingDecl *, unsigned, bool),
260260
SeparatelyCached, NoLocationInfo)
261+
SWIFT_REQUEST(TypeChecker, PatternBindingCheckedExecutableInitRequest,
262+
Expr *(PatternBindingDecl *, unsigned),
263+
SeparatelyCached, NoLocationInfo)
261264
SWIFT_REQUEST(TypeChecker, PrimarySourceFilesRequest,
262265
ArrayRef<SourceFile *>(ModuleDecl *), Cached, NoLocationInfo)
263266
SWIFT_REQUEST(TypeChecker, PropertyWrapperAuxiliaryVariablesRequest,

lib/AST/Decl.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,6 +2198,13 @@ PatternBindingDecl::getInitializerIsolation(unsigned i) const {
21982198
return var->getInitializerIsolation();
21992199
}
22002200

2201+
Expr *PatternBindingDecl::getCheckedExecutableInit(unsigned i) const {
2202+
return evaluateOrDefault(getASTContext().evaluator,
2203+
PatternBindingCheckedExecutableInitRequest{
2204+
const_cast<PatternBindingDecl *>(this), i},
2205+
nullptr);
2206+
}
2207+
22012208
bool PatternBindingDecl::hasStorage() const {
22022209
// Walk the pattern, to check to see if any of the VarDecls included in it
22032210
// have storage.

lib/AST/TypeCheckRequests.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,26 @@ void PatternBindingEntryRequest::cacheResult(
10221022
PBD->getMutablePatternList()[idx].setFullyValidated();
10231023
}
10241024

1025+
//----------------------------------------------------------------------------//
1026+
// PatternCheckedExecutableInitRequest computation.
1027+
//----------------------------------------------------------------------------//
1028+
1029+
llvm::Optional<Expr *>
1030+
PatternBindingCheckedExecutableInitRequest::getCachedResult() const {
1031+
auto *PBD = std::get<0>(getStorage());
1032+
auto idx = std::get<1>(getStorage());
1033+
if (!PBD->getPattern(idx)->executableInitChecked())
1034+
return llvm::None;
1035+
return PBD->getExecutableInit(idx);
1036+
}
1037+
1038+
void PatternBindingCheckedExecutableInitRequest::cacheResult(Expr *expr) const {
1039+
auto *PBD = std::get<0>(getStorage());
1040+
auto idx = std::get<1>(getStorage());
1041+
assert(expr == PBD->getExecutableInit(idx));
1042+
PBD->getPattern(idx)->setExecutableInitChecked();
1043+
}
1044+
10251045
//----------------------------------------------------------------------------//
10261046
// NamingPatternRequest computation.
10271047
//----------------------------------------------------------------------------//

lib/SILGen/SILGen.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,10 @@ emitStoredPropertyInitialization(PatternBindingDecl *pbd, unsigned i) {
16561656
!pbd->getAnchoringVarDecl(i)->isInitExposedToClients())
16571657
return;
16581658

1659+
// Force the executable init to be type checked before emission.
1660+
if (!pbd->getCheckedExecutableInit(i))
1661+
return;
1662+
16591663
auto *var = pbd->getAnchoringVarDecl(i);
16601664
SILDeclRef constant(var, SILDeclRef::Kind::StoredPropertyInitializer);
16611665
emitOrDelayFunction(constant);
@@ -1666,6 +1670,9 @@ emitPropertyWrapperBackingInitializer(VarDecl *var) {
16661670
auto initInfo = var->getPropertyWrapperInitializerInfo();
16671671

16681672
if (initInfo.hasInitFromWrappedValue()) {
1673+
// FIXME: Fully typecheck the original property's init expression on-demand
1674+
// for lazy typechecking mode.
1675+
16691676
SILDeclRef constant(var, SILDeclRef::Kind::PropertyWrapperBackingInitializer);
16701677
emitOrDelayFunction(constant);
16711678
}

lib/SILGen/SILGenGlobalVariable.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ void SILGenModule::emitGlobalInitialization(PatternBindingDecl *pd,
219219
->areAllParamsConcrete());
220220
}
221221

222+
// Force the executable init to be type checked before emission.
223+
if (!pd->getCheckedExecutableInit(pbdEntry))
224+
return;
225+
222226
Mangle::ASTMangler TokenMangler;
223227
std::string onceTokenBuffer = TokenMangler.mangleGlobalInit(pd, pbdEntry,
224228
false);

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2662,22 +2662,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26622662
// as a replacement.
26632663
diagnoseWrittenPlaceholderTypes(Ctx, PBD->getPattern(i), init);
26642664

2665-
// If we entered an initializer context, contextualize any
2666-
// auto-closures we might have created.
2667-
// Note that we don't contextualize the initializer for a property
2668-
// with a wrapper, because the initializer will have been subsumed
2669-
// by the backing storage property.
2670-
if (!DC->isLocalContext() &&
2671-
!(PBD->getSingleVar() &&
2672-
PBD->getSingleVar()->hasAttachedPropertyWrapper())) {
2673-
auto *initContext = cast_or_null<PatternBindingInitializer>(
2674-
PBD->getInitContext(i));
2675-
if (initContext) {
2676-
TypeChecker::contextualizeInitializer(initContext, init);
2677-
(void)PBD->getInitializerIsolation(i);
2678-
TypeChecker::checkInitializerEffects(initContext, init);
2679-
}
2680-
}
2665+
// Trigger a request that will complete typechecking for the
2666+
// initializer.
2667+
(void)PBD->getCheckedExecutableInit(i);
26812668
}
26822669
}
26832670

@@ -2686,6 +2673,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26862673
// If this is an init accessor property with a default initializer,
26872674
// make sure that it subsumes initializers of all of its "initializes"
26882675
// stored properties.
2676+
// FIXME: This should be requestified.
26892677
auto *initAccessor = var->getAccessor(AccessorKind::Init);
26902678
if (initAccessor && PBD->isInitialized(0)) {
26912679
for (auto *property : initAccessor->getInitializedProperties()) {

lib/Sema/TypeCheckStorage.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,43 @@ const PatternBindingEntry *PatternBindingEntryRequest::evaluate(
585585
return &pbe;
586586
}
587587

588+
Expr *PatternBindingCheckedExecutableInitRequest::evaluate(
589+
Evaluator &eval, PatternBindingDecl *binding, unsigned i) const {
590+
// Force the entry to be checked.
591+
(void)binding->getCheckedPatternBindingEntry(i);
592+
if (binding->isInvalid())
593+
return nullptr;
594+
595+
if (!binding->isInitialized(i))
596+
return nullptr;
597+
598+
if (!binding->isInitializerChecked(i))
599+
TypeChecker::typeCheckPatternBinding(binding, i);
600+
601+
if (binding->isInvalid())
602+
return nullptr;
603+
604+
// If we entered an initializer context, contextualize any auto-closures we
605+
// might have created. Note that we don't contextualize the initializer for a
606+
// property with a wrapper, because the initializer will have been subsumed by
607+
// the backing storage property.
608+
auto *init = binding->getInit(i);
609+
610+
if (!binding->getDeclContext()->isLocalContext() &&
611+
!(binding->getSingleVar() &&
612+
binding->getSingleVar()->hasAttachedPropertyWrapper())) {
613+
auto *initContext =
614+
cast_or_null<PatternBindingInitializer>(binding->getInitContext(i));
615+
if (initContext) {
616+
TypeChecker::contextualizeInitializer(initContext, init);
617+
(void)binding->getInitializerIsolation(i);
618+
TypeChecker::checkInitializerEffects(initContext, init);
619+
}
620+
}
621+
622+
return binding->getExecutableInit(i);
623+
}
624+
588625
bool
589626
IsGetterMutatingRequest::evaluate(Evaluator &evaluator,
590627
AbstractStorageDecl *storage) const {

test/Inputs/lazy_typecheck.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ struct InternalWrapper {
8787

8888
// MARK: - Global vars
8989

90-
public var publicGlobalVar: Int = 0
90+
public var publicGlobalVar: Int = NoTypecheck.int
9191
public var publicGlobalVarInferredType = ""
9292
public var (publicGlobalVarInferredTuplePatX, publicGlobalVarInferredTuplePatY) = (0, 1)
9393

@@ -131,16 +131,17 @@ protocol InternalProtoConformingToPublicProto: PublicProto {
131131
}
132132

133133
public struct PublicStruct {
134-
public var publicProperty: Int
134+
public var publicProperty: Int = NoTypecheck.int
135135
public var publicPropertyInferredType = ""
136136
@PublicWrapper public var publicWrappedProperty = 3.14
137137
@_transparent public var publicTransparentProperty: Int {
138138
get { return 1 }
139139
}
140140
public dynamic var publicDynamicProperty: Int = 5
141+
public static let publicStaticProperty: Int = NoTypecheck.int
142+
public static let publicStaticPropertyInferred = 2
141143

142144
public init(x: Int) {
143-
self.publicProperty = 1
144145
_ = NoTypecheck()
145146
}
146147

@@ -189,9 +190,11 @@ struct InternalStruct: NoTypecheckProto {
189190
}
190191

191192
public class PublicClass {
192-
public var publicProperty: Int
193+
public var publicProperty: Int = NoTypecheck.int
193194
public var publicPropertyInferredType = ""
194195
@PublicWrapper public final var publicFinalWrappedProperty: Bool = false
196+
public static let publicStaticProperty: Int = NoTypecheck.int
197+
public static let publicStaticPropertyInferred = 2
195198

196199
public init(x: Int) {
197200
self.publicProperty = x

test/Inputs/lazy_typecheck_client.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ func testPublicStructs() {
4343
let _: Int = s.publicDynamicProperty
4444
PublicStruct.publicStaticMethod()
4545
PublicStruct.activeMethod()
46+
let _: Int = PublicStruct.publicStaticProperty
47+
let _: Int = PublicStruct.publicStaticPropertyInferred
4648

4749
let _ = FrozenPublicStruct(1)
4850
}
@@ -59,12 +61,16 @@ func testPublicClasses() {
5961
let _: String = c.publicPropertyInferredType
6062
c.publicFinalWrappedProperty = true
6163
PublicClass.publicClassMethod()
64+
let _: Int = PublicClass.publicStaticProperty
65+
let _: Int = PublicClass.publicStaticPropertyInferred
6266

6367
let d = PublicDerivedClass(x: 3)
6468
let _: Int = d.publicMethod()
6569
let _: Int = d.publicProperty
6670
let _: String = d.publicPropertyInferredType
6771
PublicDerivedClass.publicClassMethod()
72+
let _: Int = PublicDerivedClass.publicStaticProperty
73+
let _: Int = PublicDerivedClass.publicStaticPropertyInferred
6874

6975
class DerivedFromPublicClassSynthesizedDesignatedInit: PublicClassSynthesizedDesignatedInit {
7076
init() {}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s -parse-as-library -module-name Test | %FileCheck %s --check-prefixes=CHECK,CHECK-NON-LAZY
2+
// RUN: %target-swift-frontend -emit-silgen %s -parse-as-library -module-name Test -experimental-lazy-typecheck | %FileCheck %s --check-prefixes=CHECK,CHECK-LAZY
3+
4+
enum E {
5+
case a, b
6+
}
7+
8+
func internalFunc(_ e: E = .a) -> Int {
9+
switch e {
10+
case .a: return 1
11+
case .b: return 2
12+
}
13+
}
14+
15+
// CHECK-LABEL: sil private [global_init_once_fn]{{.*}} @$s4Test9globalVar_WZ : $@convention(c) (Builtin.RawPointer) -> () {
16+
public var globalVar = internalFunc()
17+
18+
public struct S {
19+
// CHECK-LABEL: sil [transparent]{{.*}} @$s4Test1SV11instanceVarSivpfi : $@convention(thin) () -> Int {
20+
public var instanceVar = internalFunc()
21+
22+
// CHECK-LABEL: sil [transparent]{{.*}} @$s4Test1SV12instanceVar2Sivpfi : $@convention(thin) () -> Int {
23+
public var instanceVar2 = internalFunc(.b)
24+
25+
// FIXME: This initializer should be subsumed.
26+
// CHECK-LAZY: sil [transparent] [ossa] @$s4Test1SV15lazyInstanceVarSivpfi : $@convention(thin) () -> Int {
27+
// CHECK-NON-LAZY-NOT: sil [transparent] [ossa] @$s4Test1SV15lazyInstanceVarSivpfi : $@convention(thin) () -> Int {
28+
// CHECK-LABEL: sil [transparent]{{.*}} @$s4Test1SV018$__lazy_storage_$_B11InstanceVar33_0E4F053AA3AB7D4CDE3A37DBA8EF0430LLSiSgvpfi : $@convention(thin) () -> Optional<Int> {
29+
public lazy var lazyInstanceVar = internalFunc()
30+
31+
// CHECK-LABEL: sil private [global_init_once_fn]{{.*}} @$s4Test1SV9staticVar_WZ : $@convention(c) (Builtin.RawPointer) -> () {
32+
public static var staticVar = internalFunc()
33+
34+
// FIXME: This initializer should be subsumed.
35+
// CHECK-LAZY: sil [transparent] [ossa] @$s4Test1SV15subsumedInitVarSivpfi : $@convention(thin) () -> Int {
36+
// CHECK-NON-LAZY-NOT: sil [transparent] [ossa] @$s4Test1SV15subsumedInitVarSivpfi : $@convention(thin) () -> Int {
37+
public var subsumedInitVar = internalFunc()
38+
39+
// CHECK-LABEL: sil [transparent] [ossa] @$s4Test1SV19varWithInitAccessorSivpfi : $@convention(thin) () -> Int {
40+
public var varWithInitAccessor: Int = internalFunc() {
41+
@storageRestrictions(initializes: subsumedInitVar)
42+
init {
43+
subsumedInitVar = newValue
44+
}
45+
get { subsumedInitVar }
46+
}
47+
48+
// FIXME: Test vars with property wrappers.
49+
}
50+
51+
extension S {
52+
// CHECK-LABEL: sil private [global_init_once_fn]{{.*}} @$s4Test1SV20staticVarInExtension_WZ : $@convention(c) (Builtin.RawPointer) -> () {
53+
public static var staticVarInExtension = internalFunc()
54+
}
55+
56+
public func returnsS() -> S {
57+
// Force the synthesized initializer for S to be emitted.
58+
// CHECK: function_ref @$s4Test1SVACycfC : $@convention(method) (@thin S.Type) -> S
59+
return S()
60+
}

0 commit comments

Comments
 (0)