Skip to content

Commit 5613006

Browse files
committed
[Sema] PreCheck: Diagnose standalone self within init accessors
'self' within init accessor could only be used to refer to properties listed in `initializes` and `accesses` attributes.
1 parent 4e2f3b1 commit 5613006

File tree

5 files changed

+90
-2
lines changed

5 files changed

+90
-2
lines changed

include/swift/AST/DeclContext.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ namespace swift {
8282
class SerializedDefaultArgumentInitializer;
8383
class SerializedTopLevelCodeDecl;
8484
class StructDecl;
85+
class AccessorDecl;
8586

8687
namespace serialization {
8788
using DeclID = llvm::PointerEmbeddedInt<unsigned, 31>;
@@ -457,6 +458,18 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
457458
return const_cast<DeclContext*>(this)->getInnermostMethodContext();
458459
}
459460

461+
/// Returns the innermost accessor context that belongs to a property.
462+
///
463+
/// This routine looks through closure, initializer, and local function
464+
/// contexts to find the innermost accessor declaration.
465+
///
466+
/// \returns the innermost accessor, or null if there is no such context.
467+
LLVM_READONLY
468+
AccessorDecl *getInnermostPropertyAccessorContext();
469+
const AccessorDecl *getInnermostPropertyAccessorContext() const {
470+
return const_cast<DeclContext*>(this)->getInnermostPropertyAccessorContext();
471+
}
472+
460473
/// Returns the innermost type context.
461474
///
462475
/// This routine looks through closure, initializer, and local function

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7342,6 +7342,10 @@ ERROR(init_accessor_accesses_attribute_on_other_declaration,none,
73427342
ERROR(init_accessor_property_both_init_and_accessed,none,
73437343
"property %0 cannot be both initialized and accessed",
73447344
(DeclName))
7345+
ERROR(invalid_use_of_self_in_init_accessor,none,
7346+
"'self' within init accessors can only be used to reference "
7347+
"properties listed in 'initializes' and 'accesses'; "
7348+
"init accessors are run before 'self' is fully available", ())
73457349

73467350

73477351
#define UNDEFINE_DIAGNOSTIC_MACROS

lib/AST/DeclContext.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,24 @@ AbstractFunctionDecl *DeclContext::getInnermostMethodContext() {
211211
return nullptr;
212212
}
213213

214+
AccessorDecl *DeclContext::getInnermostPropertyAccessorContext() {
215+
auto dc = this;
216+
do {
217+
if (auto decl = dc->getAsDecl()) {
218+
auto accessor = dyn_cast<AccessorDecl>(decl);
219+
// If we found a non-accessor decl, we're done.
220+
if (accessor == nullptr)
221+
return nullptr;
222+
223+
auto *storage = accessor->getStorage();
224+
if (isa<VarDecl>(storage) && storage->getDeclContext()->isTypeContext())
225+
return accessor;
226+
}
227+
} while ((dc = dc->getParent()));
228+
229+
return nullptr;
230+
}
231+
214232
bool DeclContext::isTypeContext() const {
215233
if (auto decl = getAsDecl())
216234
return isa<NominalTypeDecl>(decl) || isa<ExtensionDecl>(decl);

lib/Sema/PreCheckExpr.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,8 +1066,24 @@ namespace {
10661066
if (auto unresolved = dyn_cast<UnresolvedDeclRefExpr>(expr)) {
10671067
TypeChecker::checkForForbiddenPrefix(
10681068
getASTContext(), unresolved->getName().getBaseName());
1069-
return finish(true, TypeChecker::resolveDeclRefExpr(unresolved, DC,
1070-
UseErrorExprs));
1069+
auto *refExpr =
1070+
TypeChecker::resolveDeclRefExpr(unresolved, DC, UseErrorExprs);
1071+
1072+
// Check whether this is standalone `self` in init accessor, which
1073+
// is invalid.
1074+
if (auto *accessor = DC->getInnermostPropertyAccessorContext()) {
1075+
if (accessor->isInitAccessor() && isa<DeclRefExpr>(refExpr)) {
1076+
auto *DRE = cast<DeclRefExpr>(refExpr);
1077+
if (accessor->getImplicitSelfDecl() == DRE->getDecl() &&
1078+
!isa_and_nonnull<UnresolvedDotExpr>(Parent.getAsExpr())) {
1079+
Ctx.Diags.diagnose(unresolved->getLoc(),
1080+
diag::invalid_use_of_self_in_init_accessor);
1081+
refExpr = new (Ctx) ErrorExpr(unresolved->getSourceRange());
1082+
}
1083+
}
1084+
}
1085+
1086+
return finish(true, refExpr);
10711087
}
10721088

10731089
// Let's try to figure out if `InOutExpr` is out of place early

test/decl/var/init_accessors.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,40 @@ func test_duplicate_and_computed_lazy_properties() {
136136
lazy var c: Int = 42
137137
}
138138
}
139+
140+
func test_invalid_self_uses() {
141+
func capture<T>(_: T) -> Int? { nil }
142+
143+
struct Test {
144+
var a: Int {
145+
init(initialValue) {
146+
let x = self
147+
// expected-error@-1 {{'self' within init accessors can only be used to reference properties listed in 'initializes' and 'accesses'; init accessors are run before 'self' is fully available}}
148+
149+
_ = {
150+
print(self)
151+
// expected-error@-1 {{'self' within init accessors can only be used to reference properties listed in 'initializes' and 'accesses'; init accessors are run before 'self' is fully available}}
152+
}
153+
154+
_ = { [weak self] in
155+
// expected-error@-1 {{'self' within init accessors can only be used to reference properties listed in 'initializes' and 'accesses'; init accessors are run before 'self' is fully available}}
156+
}
157+
158+
_ = {
159+
guard let _ = capture(self) else {
160+
// expected-error@-1 {{'self' within init accessors can only be used to reference properties listed in 'initializes' and 'accesses'; init accessors are run before 'self' is fully available}}
161+
return
162+
}
163+
}
164+
165+
Test.test(self)
166+
// expected-error@-1 {{'self' within init accessors can only be used to reference properties listed in 'initializes' and 'accesses'; init accessors are run before 'self' is fully available}}
167+
}
168+
169+
get { 42 }
170+
set { }
171+
}
172+
173+
static func test<T>(_: T) {}
174+
}
175+
}

0 commit comments

Comments
 (0)