Skip to content

Commit 8c73b7d

Browse files
committed
Define NamingPatternRequest
Use it to provide an idealized API for the VarDecl case in validateDecl. In reality, a lot of work is needed to rationalize the dependency structure of this request. To start, the callers of typeCheckPatternBinding must be eliminated piecemeal. Once that is done, the AST should introduce pattern binding decls along all the places where getParentStmt() used to apply.
1 parent 71a5255 commit 8c73b7d

25 files changed

+204
-118
lines changed

include/swift/AST/ASTTypeIDZone.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ SWIFT_TYPEID_NAMED(GenericTypeParamType *, GenericTypeParamType)
3333
SWIFT_TYPEID_NAMED(InfixOperatorDecl *, InfixOperatorDecl)
3434
SWIFT_TYPEID_NAMED(IterableDeclContext *, IterableDeclContext)
3535
SWIFT_TYPEID_NAMED(ModuleDecl *, ModuleDecl)
36+
SWIFT_TYPEID_NAMED(NamedPattern *, NamedPattern)
3637
SWIFT_TYPEID_NAMED(NominalTypeDecl *, NominalTypeDecl)
3738
SWIFT_TYPEID_NAMED(OpaqueTypeDecl *, OpaqueTypeDecl)
3839
SWIFT_TYPEID_NAMED(OperatorDecl *, OperatorDecl)

include/swift/AST/ASTTypeIDs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class GenericTypeParamType;
3232
class InfixOperatorDecl;
3333
class IterableDeclContext;
3434
class ModuleDecl;
35+
class NamedPattern;
3536
class NominalTypeDecl;
3637
class OperatorDecl;
3738
class OpaqueTypeDecl;

include/swift/AST/Decl.h

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,8 +1926,20 @@ class PatternBindingEntry {
19261926

19271927
friend class PatternBindingInitializer;
19281928

1929+
// FIXME: This API is transitional. Once the callers of
1930+
// typeCheckPatternBinding are requestified, merge this bit with
1931+
// Flags::Checked.
1932+
friend class PatternBindingEntryRequest;
1933+
19291934
bool IsFullyValidated = false;
19301935

1936+
bool isFullyValidated() const {
1937+
return IsFullyValidated;
1938+
}
1939+
void setFullyValidated() {
1940+
IsFullyValidated = true;
1941+
}
1942+
19311943
public:
19321944
/// \p E is the initializer as parsed.
19331945
PatternBindingEntry(Pattern *P, SourceLoc EqualLoc, Expr *E,
@@ -1997,13 +2009,6 @@ class PatternBindingEntry {
19972009
PatternAndFlags.setInt(PatternAndFlags.getInt() | Flags::Checked);
19982010
}
19992011

2000-
bool isFullyValidated() const {
2001-
return IsFullyValidated;
2002-
}
2003-
void setFullyValidated() {
2004-
IsFullyValidated = true;
2005-
}
2006-
20072012
bool isInitializerSubsumed() const {
20082013
return PatternAndFlags.getInt().contains(Flags::Subsumed);
20092014
}
@@ -4800,6 +4805,7 @@ enum class PropertyWrapperSynthesizedPropertyKind {
48004805

48014806
/// VarDecl - 'var' and 'let' declarations.
48024807
class VarDecl : public AbstractStorageDecl {
4808+
friend class NamingPatternRequest;
48034809
NamedPattern *NamingPattern = nullptr;
48044810

48054811
public:
@@ -4921,8 +4927,8 @@ class VarDecl : public AbstractStorageDecl {
49214927
Parent = v;
49224928
}
49234929

4924-
NamedPattern *getNamingPattern() const { return NamingPattern; }
4925-
void setNamingPattern(NamedPattern *Pat) { NamingPattern = Pat; }
4930+
NamedPattern *getNamingPattern() const;
4931+
void setNamingPattern(NamedPattern *Pat);
49264932

49274933
/// If this is a VarDecl that does not belong to a CaseLabelItem's pattern,
49284934
/// return this. Otherwise, this VarDecl must belong to a CaseStmt's

include/swift/AST/TypeCheckRequests.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,26 @@ class PatternBindingEntryRequest
14071407
void cacheResult(const PatternBindingEntry *value) const;
14081408
};
14091409

1410+
class NamingPatternRequest
1411+
: public SimpleRequest<NamingPatternRequest, NamedPattern *(VarDecl *),
1412+
CacheKind::SeparatelyCached> {
1413+
public:
1414+
using SimpleRequest::SimpleRequest;
1415+
1416+
private:
1417+
friend SimpleRequest;
1418+
1419+
// Evaluation.
1420+
llvm::Expected<NamedPattern *> evaluate(Evaluator &evaluator,
1421+
VarDecl *VD) const;
1422+
1423+
public:
1424+
// Separate caching.
1425+
bool isCached() const { return true; }
1426+
Optional<NamedPattern *> getCachedResult() const;
1427+
void cacheResult(NamedPattern *P) const;
1428+
};
1429+
14101430
// Allow AnyValue to compare two Type values, even though Type doesn't
14111431
// support ==.
14121432
template<>

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ SWIFT_REQUEST(TypeChecker, LazyStoragePropertyRequest, VarDecl *(VarDecl *),
8585
Cached, NoLocationInfo)
8686
SWIFT_REQUEST(TypeChecker, MangleLocalTypeDeclRequest,
8787
std::string(const TypeDecl *), Cached, NoLocationInfo)
88+
SWIFT_REQUEST(TypeChecker, NamingPatternRequest,
89+
NamedPattern *(VarDecl *), SeparatelyCached, NoLocationInfo)
8890
SWIFT_REQUEST(TypeChecker, OpaqueReadOwnershipRequest,
8991
OpaqueReadOwnership(AbstractStorageDecl *), SeparatelyCached,
9092
NoLocationInfo)

lib/AST/Decl.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5309,6 +5309,17 @@ Pattern *VarDecl::getParentPattern() const {
53095309
return nullptr;
53105310
}
53115311

5312+
NamedPattern *VarDecl::getNamingPattern() const {
5313+
return evaluateOrDefault(getASTContext().evaluator,
5314+
NamingPatternRequest{const_cast<VarDecl *>(this)},
5315+
nullptr);
5316+
}
5317+
5318+
void VarDecl::setNamingPattern(NamedPattern *Pat) {
5319+
getASTContext().evaluator.cacheOutput(NamingPatternRequest{this},
5320+
std::move(Pat));
5321+
}
5322+
53125323
TypeRepr *VarDecl::getTypeReprOrParentPatternTypeRepr() const {
53135324
if (auto *param = dyn_cast<ParamDecl>(this))
53145325
return param->getTypeRepr();

lib/AST/TypeCheckRequests.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,3 +984,20 @@ void PatternBindingEntryRequest::cacheResult(
984984
auto idx = std::get<1>(getStorage());
985985
PBD->getMutablePatternList()[idx].setFullyValidated();
986986
}
987+
988+
//----------------------------------------------------------------------------//
989+
// NamingPatternRequest computation.
990+
//----------------------------------------------------------------------------//
991+
992+
Optional<NamedPattern *> NamingPatternRequest::getCachedResult() const {
993+
auto *VD = std::get<0>(getStorage());
994+
if (auto *Pat = VD->NamingPattern) {
995+
return Pat;
996+
}
997+
return None;
998+
}
999+
1000+
void NamingPatternRequest::cacheResult(NamedPattern *value) const {
1001+
auto *VD = std::get<0>(getStorage());
1002+
VD->NamingPattern = value;
1003+
}

lib/Sema/ConstraintSystem.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,16 @@ Type ConstraintSystem::getEffectiveOverloadType(const OverloadChoice &overload,
14611461
if (decl->isImplicitlyUnwrappedOptional())
14621462
return Type();
14631463

1464+
// In a pattern binding initializer, all of its bound variables have no
1465+
// effective overload type.
1466+
if (auto *PBI = dyn_cast<PatternBindingInitializer>(useDC)) {
1467+
if (auto *VD = dyn_cast<VarDecl>(decl)) {
1468+
if (PBI->getBinding() == VD->getParentPatternBinding()) {
1469+
return Type();
1470+
}
1471+
}
1472+
}
1473+
14641474
// Retrieve the interface type.
14651475
auto type = decl->getInterfaceType();
14661476
if (!type || type->hasError()) {

lib/Sema/TypeCheckDecl.cpp

Lines changed: 71 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,9 +2410,20 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
24102410
entry->getPattern()->forEachVariable([&](VarDecl *var) {
24112411
this->visitBoundVariable(var);
24122412

2413+
if (entry->isInitialized()) {
2414+
// Add the attribute that preserves the "has an initializer" value
2415+
// across module generation, as required for TBDGen.
2416+
if (var->hasStorage() &&
2417+
!var->getAttrs().hasAttribute<HasInitialValueAttr>()) {
2418+
var->getAttrs().add(new (TC.Context)
2419+
HasInitialValueAttr(/*IsImplicit=*/true));
2420+
}
2421+
return;
2422+
}
2423+
24132424
// If this is a declaration without an initializer, reject code if
24142425
// uninitialized vars are not allowed.
2415-
if (entry->isInitialized() || isInSILMode) return;
2426+
if (isInSILMode) return;
24162427

24172428
// If the variable has no storage, it never needs an initializer.
24182429
if (!var->hasStorage())
@@ -4187,52 +4198,10 @@ void TypeChecker::validateDecl(ValueDecl *D) {
41874198

41884199
case DeclKind::Var: {
41894200
auto *VD = cast<VarDecl>(D);
4190-
auto *PBD = VD->getParentPatternBinding();
4191-
if (PBD) {
4192-
// FIXME: Sink this into a request for the naming pattern.
4193-
unsigned i = PBD->getPatternEntryIndexForVarDecl(VD);
4194-
(void)evaluateOrDefault(Context.evaluator,
4195-
PatternBindingEntryRequest{PBD, i},
4196-
nullptr);
4197-
if (PBD->isInvalid()) {
4198-
VD->getParentPattern()->setType(ErrorType::get(Context));
4199-
setBoundVarsTypeError(VD->getParentPattern(), Context);
4200-
break;
4201-
}
4202-
} else if (!VD->getParentPatternStmt() && !VD->getParentVarDecl()) {
4203-
// No parent? That's an error.
4204-
VD->setInterfaceType(ErrorType::get(Context));
4205-
break;
4206-
}
4207-
4208-
// Go digging for the named pattern that declares this variable.
42094201
auto *namingPattern = VD->getNamingPattern();
42104202
if (!namingPattern) {
4211-
auto *canVD = VD->getCanonicalVarDecl();
4212-
namingPattern = canVD->getNamingPattern();
4213-
4214-
// HACK: If no other diagnostic applies, emit a generic diagnostic about
4215-
// a variable being unbound. We can't do better than this at the
4216-
// moment because TypeCheckPattern does not reliably invalidate parts of
4217-
// the pattern AST on failure.
4218-
//
4219-
// Once that's through, this will only fire during circular validation.
4220-
if (!namingPattern) {
4221-
if (!VD->isInvalid() && !VD->getParentPattern()->isImplicit()) {
4222-
VD->diagnose(diag::variable_bound_by_no_pattern, VD->getName());
4223-
}
4224-
4225-
VD->getParentPattern()->setType(ErrorType::get(Context));
4226-
setBoundVarsTypeError(VD->getParentPattern(), Context);
4227-
VD->setInterfaceType(ErrorType::get(Context));
4228-
break;
4229-
}
4230-
}
4231-
assert(namingPattern && "Bound variable with no naming pattern!");
4232-
4233-
if (!namingPattern->hasType()) {
4234-
namingPattern->setType(ErrorType::get(Context));
4235-
setBoundVarsTypeError(namingPattern, Context);
4203+
VD->setInterfaceType(ErrorType::get(Context));
4204+
break;
42364205
}
42374206

42384207
Type interfaceType = namingPattern->getType();
@@ -4312,6 +4281,63 @@ void TypeChecker::validateDecl(ValueDecl *D) {
43124281
assert(D->hasInterfaceType());
43134282
}
43144283

4284+
llvm::Expected<NamedPattern *>
4285+
NamingPatternRequest::evaluate(Evaluator &evaluator, VarDecl *VD) const {
4286+
auto &Context = VD->getASTContext();
4287+
auto *PBD = VD->getParentPatternBinding();
4288+
// FIXME: In order for this request to properly express its dependencies,
4289+
// all of the places that allow variable bindings need to also use pattern
4290+
// binding decls. Otherwise, we'll have to go digging around in case
4291+
// statements and patterns to find named patterns.
4292+
if (PBD) {
4293+
// FIXME: For now, this works because PatternBindingEntryRequest fills in
4294+
// the naming pattern as a side effect in this case, and TypeCheckStmt
4295+
// and TypeCheckPattern handle the others. But that's all really gross.
4296+
unsigned i = PBD->getPatternEntryIndexForVarDecl(VD);
4297+
(void)evaluateOrDefault(evaluator,
4298+
PatternBindingEntryRequest{PBD, i},
4299+
nullptr);
4300+
if (PBD->isInvalid()) {
4301+
VD->getParentPattern()->setType(ErrorType::get(Context));
4302+
setBoundVarsTypeError(VD->getParentPattern(), Context);
4303+
return nullptr;
4304+
}
4305+
} else if (!VD->getParentPatternStmt() && !VD->getParentVarDecl()) {
4306+
// No parent? That's an error.
4307+
return nullptr;
4308+
}
4309+
4310+
// Go digging for the named pattern that declares this variable.
4311+
auto *namingPattern = VD->NamingPattern;
4312+
if (!namingPattern) {
4313+
auto *canVD = VD->getCanonicalVarDecl();
4314+
namingPattern = canVD->NamingPattern;
4315+
4316+
// HACK: If no other diagnostic applies, emit a generic diagnostic about
4317+
// a variable being unbound. We can't do better than this at the
4318+
// moment because TypeCheckPattern does not reliably invalidate parts of
4319+
// the pattern AST on failure.
4320+
//
4321+
// Once that's through, this will only fire during circular validation.
4322+
if (!namingPattern) {
4323+
if (!VD->isInvalid() && !VD->getParentPattern()->isImplicit()) {
4324+
VD->diagnose(diag::variable_bound_by_no_pattern, VD->getName());
4325+
}
4326+
4327+
VD->getParentPattern()->setType(ErrorType::get(Context));
4328+
setBoundVarsTypeError(VD->getParentPattern(), Context);
4329+
return nullptr;
4330+
}
4331+
}
4332+
4333+
if (!namingPattern->hasType()) {
4334+
namingPattern->setType(ErrorType::get(Context));
4335+
setBoundVarsTypeError(namingPattern, Context);
4336+
}
4337+
4338+
return namingPattern;
4339+
}
4340+
43154341
llvm::Expected<DeclRange>
43164342
EmittedMembersRequest::evaluate(Evaluator &evaluator,
43174343
ClassDecl *CD) const {

lib/Sema/TypeCheckStorage.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -237,18 +237,6 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval,
237237
}
238238
}
239239

240-
if (pbe.isInitialized()) {
241-
// Add the attribute that preserves the "has an initializer" value across
242-
// module generation, as required for TBDGen.
243-
pattern->forEachVariable([&](VarDecl *VD) {
244-
if (VD->hasStorage() &&
245-
!VD->getAttrs().hasAttribute<HasInitialValueAttr>()) {
246-
auto *attr = new (Context) HasInitialValueAttr(/*IsImplicit=*/true);
247-
VD->getAttrs().add(attr);
248-
}
249-
});
250-
}
251-
252240
// If the pattern didn't get a type or if it contains an unbound generic type,
253241
// we'll need to check the initializer.
254242
if (!pattern->hasType() || pattern->getType()->hasUnboundGenericType()) {

test/ModuleInterface/access-filter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ extension UFIProto {
109109

110110
// CHECK: extension PublicStruct {{[{]$}}
111111
extension PublicStruct {
112-
// CHECK: @_hasInitialValue @_hasStorage public static var secretlySettable: Swift.Int {
112+
// CHECK: @_hasInitialValue public static var secretlySettable: Swift.Int {
113113
// CHECK-NEXT: get
114114
// CHECK-NEXT: }
115115
public private(set) static var secretlySettable: Int = 0

test/ModuleInterface/stored-properties.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public struct HasStoredProperties {
5353
// RESILIENT-NOT: private var privateVar: Swift.Bool
5454
private var privateVar: Bool
5555

56-
// CHECK: {{@_hasInitialValue @_hasStorage|@_hasStorage @_hasInitialValue}} public var storedWithObserversInitialValue: Swift.Int {
56+
// CHECK: @_hasStorage @_hasInitialValue public var storedWithObserversInitialValue: Swift.Int {
5757
// RESILIENT: {{^}} public var storedWithObserversInitialValue: Swift.Int {
5858
// COMMON-NEXT: get
5959
// COMMON-NEXT: set

test/NameBinding/name_lookup.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,6 @@ struct PatternBindingWithTwoVars3 { var x = y, y = x }
628628

629629
// https://bugs.swift.org/browse/SR-9015
630630
func sr9015() {
631-
let closure1 = { closure2() } // expected-error {{circular reference}} expected-error{{variable 'closure1' is not bound by any pattern}}
632-
let closure2 = { closure1() } // expected-note {{through reference here}}
631+
let closure1 = { closure2() } // expected-error {{circular reference}} expected-error{{variable 'closure1' is not bound by any pattern}} expected-note {{through reference here}}
632+
let closure2 = { closure1() } // expected-note {{through reference here}} expected-note {{through reference here}}
633633
}

test/SIL/Parser/final.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// RUN: %target-swift-frontend %s -emit-silgen | %FileCheck %s
22

33
// CHECK: final class Rect
4-
// CHECK: @_hasInitialValue @_hasStorage final var orgx: Double
4+
// CHECK: @_hasStorage @_hasInitialValue final var orgx: Double
55
final class Rect {
66
var orgx = 0.0
77
}
88

99
protocol P { }
1010
// CHECK: struct Rect2 : P {
11-
// CHECK: @_hasInitialValue @_hasStorage var orgx: Double
11+
// CHECK: @_hasStorage @_hasInitialValue var orgx: Double
1212
struct Rect2 : P {
1313
var orgx = 0.0
1414
}

test/SILGen/implicit_property_initializers.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// RUN: %target-swift-emit-silgen -module-name implicit_property_initializers -Xllvm -sil-full-demangle -enable-testing %s | %FileCheck %s
22

33
// CHECK: struct HasDefaultTupleOfNils {
4-
// CHECK: @_hasInitialValue @_hasStorage var x: (Int?, Int?)
5-
// CHECK: @_hasInitialValue @_hasStorage var y: Int?
4+
// CHECK: @_hasStorage @_hasInitialValue var x: (Int?, Int?)
5+
// CHECK: @_hasStorage @_hasInitialValue var y: Int?
66
// CHECK: @_hasStorage var z: Int
7-
// CHECK: @_hasInitialValue @_hasStorage var w: ((Int?, (), Int?), (Int?, Int?))
7+
// CHECK: @_hasStorage @_hasInitialValue var w: ((Int?, (), Int?), (Int?, Int?))
88
// CHECK: init(x: (Int?, Int?) = (nil, nil),
99
// CHECK-SAME: y: Int? = nil,
1010
// CHECK-SAME: z: Int,

test/SILOptimizer/access_dom_call.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ bb0:
9898
// Accessors
9999

100100
public class C {
101-
@_hasInitialValue @_hasStorage public var field: Int64 { get set }
101+
@_hasStorage @_hasInitialValue public var field: Int64 { get set }
102102
}
103103

104104
sil_property #C.field ()

test/SILOptimizer/access_enforcement_opts.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1596,7 +1596,7 @@ bb3(%1 : $*X):
15961596
// cannot be merged without introducing a false conflict.
15971597

15981598
public class TestClass {
1599-
@_hasInitialValue @_hasStorage var flags: Int64 { get set }
1599+
@_hasStorage @_hasInitialValue var flags: Int64 { get set }
16001600
}
16011601

16021602
// CHECK-LABEL: sil hidden [noinline] @readFlags : $@convention(method) (Int64, @guaranteed TestClass) -> Bool {

0 commit comments

Comments
 (0)