Skip to content

Commit d45a0ce

Browse files
committed
sema: diagnose compile-time const variables are properly initialized
1 parent 7856bba commit d45a0ce

File tree

4 files changed

+70
-5
lines changed

4 files changed

+70
-5
lines changed

include/swift/AST/DiagnosticsCommon.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ ERROR(class_subscript_not_in_class,none,
7171
"class subscripts are only allowed within classes; "
7272
"use 'static' to declare a %select{static|requirement fulfilled by either a static or class}0 subscript", (bool))
7373

74+
ERROR(require_static_for_const,none,
75+
"'static' is required for _const variable declaration", ())
76+
77+
ERROR(require_let_for_const,none,
78+
"let is required for a _const variable declaration", ())
79+
80+
ERROR(require_const_initializer_for_const,none,
81+
"_const let should be initialized with a compile-time literal", ())
82+
7483
// FIXME: Used by both the parser and the type-checker.
7584
ERROR(func_decl_without_brace,PointsToFirstBadToken,
7685
"expected '{' in body of function declaration", ())

lib/AST/Expr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ Expr *Expr::getSemanticsProvidingExpr() {
198198
return this;
199199
}
200200

201-
bool Expr:: isSemanticallyConstExpr() const {
201+
bool Expr::isSemanticallyConstExpr() const {
202202
if (auto *LE = dyn_cast<LiteralExpr>(getSemanticsProvidingExpr())) {
203203
return LE->getKind() != ExprKind::InterpolatedStringLiteral;
204204
}

lib/Sema/TypeCheckStorage.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,45 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval,
250250
return &pbe;
251251
}
252252

253+
llvm::SmallVector<VarDecl *, 2> vars;
254+
binding->getPattern(entryNumber)->collectVariables(vars);
255+
bool isReq = false;
256+
if (auto *d = binding->getDeclContext()->getAsDecl()) {
257+
isReq = isa<ProtocolDecl>(d);
258+
}
259+
for (auto *sv: vars) {
260+
bool hasConst = sv->getAttrs().getAttribute<CompileTimeConstAttr>();
261+
bool hasStatic = StaticSpelling != StaticSpellingKind::None;
262+
if (!hasConst)
263+
break;
264+
while(1) {
265+
// only static _const let/var is supported
266+
if (!hasStatic) {
267+
binding->diagnose(diag::require_static_for_const);
268+
break;
269+
}
270+
if (isReq) {
271+
break;
272+
}
273+
// var is only allowed in a protocol.
274+
if (!sv->isLet()) {
275+
binding->diagnose(diag::require_let_for_const);
276+
break;
277+
}
278+
// Diagnose when an init isn't given and it's not a compile-time constant
279+
auto *init = binding->getInit(entryNumber);
280+
if (!init) {
281+
binding->diagnose(diag::require_const_initializer_for_const);
282+
break;
283+
}
284+
if (!init->isSemanticallyConstExpr()) {
285+
binding->diagnose(diag::require_const_initializer_for_const);
286+
break;
287+
}
288+
break;
289+
}
290+
}
291+
253292
// If we have a type but no initializer, check whether the type is
254293
// default-initializable. If so, do it.
255294
if (!pbe.isInitialized() &&
@@ -297,8 +336,6 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval,
297336
// If the pattern binding appears in a type or library file context, then
298337
// it must bind at least one variable.
299338
if (!contextAllowsPatternBindingWithoutVariables(binding->getDeclContext())) {
300-
llvm::SmallVector<VarDecl *, 2> vars;
301-
binding->getPattern(entryNumber)->collectVariables(vars);
302339
if (vars.empty()) {
303340
// Selector for error message.
304341
enum : unsigned {

test/Sema/const_pass_as_arguments.swift

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,28 @@ protocol ConstFan {
3737
}
3838

3939
class ConstFanClass1: ConstFan { // expected-error {{type 'ConstFanClass1' does not conform to protocol 'ConstFan'}}
40-
static var v: String = "" // expected-note {{candidate operates as non-const, not const as required}}
40+
static let v: String = "" // expected-note {{candidate operates as non-const, not const as required}}
4141
}
4242

4343
class ConstFanClassCorrect: ConstFan {
44-
static _const var v: String = ""
44+
static _const let v: String = ""
45+
}
46+
47+
class ConstFanClassWrong1: ConstFan {
48+
static _const let v: String // expected-error {{_const let should be initialized with a compile-time literal}}
49+
// expected-error@-1 {{'static let' declaration requires an initializer expression or an explicitly stated getter}}
50+
// expected-note@-2 {{add an initializer to silence this error}}
51+
}
52+
53+
class ConstFanClassWrong2: ConstFan {
54+
static _const let v: String = "\(v)" // expected-error {{_const let should be initialized with a compile-time literal}}
55+
}
56+
57+
class ConstFanClassWrong3: ConstFan {
58+
static _const var v: String = "" // expected-error {{let is required for a _const variable declaration}}
59+
}
60+
61+
class ConstFanClassWrong4: ConstFan {
62+
static func giveMeString() -> String { return "" }
63+
static _const let v: String = giveMeString() // expected-error {{_const let should be initialized with a compile-time literal}}
4564
}

0 commit comments

Comments
 (0)