Skip to content

Commit 78d57ea

Browse files
committed
[reference-bindings] Put reference bindings support behind -enable-experimental-feature ReferenceBindings.
1 parent fab3e3f commit 78d57ea

10 files changed

+502
-36
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,9 @@ ERROR(no_default_arg_closure,none,
964964
ERROR(no_default_arg_curried,none,
965965
"default arguments are not allowed in curried parameter lists", ())
966966
ERROR(var_pattern_in_var,none,
967+
"'%select{let||var}0' cannot appear nested inside another 'var' or "
968+
"'let' pattern", (unsigned))
969+
ERROR(var_pattern_in_var_inout,none,
967970
"'%select{let|inout|var}0' cannot appear nested inside another 'var', "
968971
"'let', or 'inout' pattern", (unsigned))
969972
ERROR(extra_var_in_multiple_pattern_list,none,

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ EXPERIMENTAL_FEATURE(ImportSymbolicCXXDecls, false)
179179
/// Generate bindings for functions that 'throw' in the C++ section of the generated Clang header.
180180
EXPERIMENTAL_FEATURE(GenerateBindingsForThrowingFunctionsInCXX, false)
181181

182+
/// Enable reference bindings.
183+
EXPERIMENTAL_FEATURE(ReferenceBindings, false)
184+
182185
#undef EXPERIMENTAL_FEATURE
183186
#undef UPCOMING_FEATURE
184187
#undef SUPPRESSIBLE_LANGUAGE_FEATURE

include/swift/Parse/Parser.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2057,7 +2057,7 @@ DeclNameRef formDeclNameRef(ASTContext &ctx,
20572057
bool isCxxClassTemplateSpec = false);
20582058

20592059
/// Whether a given token can be the start of a decl.
2060-
bool isKeywordPossibleDeclStart(const Token &Tok);
2060+
bool isKeywordPossibleDeclStart(const LangOptions &options, const Token &Tok);
20612061

20622062
} // end namespace swift
20632063

lib/AST/ASTPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3220,6 +3220,11 @@ suppressingFeatureNoAsyncAvailability(PrintOptions &options,
32203220
action();
32213221
}
32223222

3223+
static bool usesFeatureReferenceBindings(Decl *decl) {
3224+
auto *vd = dyn_cast<VarDecl>(decl);
3225+
return vd && vd->getIntroducer() == VarDecl::Introducer::InOut;
3226+
}
3227+
32233228
/// Suppress the printing of a particular feature.
32243229
static void suppressingFeature(PrintOptions &options, Feature feature,
32253230
llvm::function_ref<void()> action) {

lib/Parse/ParseDecl.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4659,8 +4659,11 @@ static unsigned skipUntilMatchingRBrace(Parser &P,
46594659
return OpenBraces;
46604660
}
46614661

4662-
bool swift::isKeywordPossibleDeclStart(const Token &Tok) {
4662+
bool swift::isKeywordPossibleDeclStart(const LangOptions &options,
4663+
const Token &Tok) {
46634664
switch (Tok.getKind()) {
4665+
case tok::kw_inout:
4666+
return options.hasFeature(Feature::ReferenceBindings);
46644667
case tok::at_sign:
46654668
case tok::kw_associatedtype:
46664669
case tok::kw_case:
@@ -4672,7 +4675,6 @@ bool swift::isKeywordPossibleDeclStart(const Token &Tok) {
46724675
case tok::kw_func:
46734676
case tok::kw_import:
46744677
case tok::kw_init:
4675-
case tok::kw_inout:
46764678
case tok::kw_internal:
46774679
case tok::kw_let:
46784680
case tok::kw_operator:
@@ -4749,7 +4751,7 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
47494751
// @rethrows does not follow the general rule of @<identifier> so
47504752
// it is needed to short circuit this else there will be an infinite
47514753
// loop on invalid attributes of just rethrows
4752-
} else if (!isKeywordPossibleDeclStart(Tok)) {
4754+
} else if (!isKeywordPossibleDeclStart(Context.LangOpts, Tok)) {
47534755
// If this is obviously not the start of a decl, then we're done.
47544756
return false;
47554757
}
@@ -4878,7 +4880,7 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
48784880
return true;
48794881

48804882
// If the next token is obviously not the start of a decl, bail early.
4881-
if (!isKeywordPossibleDeclStart(Tok2))
4883+
if (!isKeywordPossibleDeclStart(Context.LangOpts, Tok2))
48824884
return false;
48834885

48844886
// Otherwise, do a recursive parse.
@@ -5091,7 +5093,6 @@ Parser::parseDecl(ParseDeclOptions Flags,
50915093
case tok::kw_extension:
50925094
DeclResult = parseDeclExtension(Flags, Attributes);
50935095
break;
5094-
case tok::kw_inout:
50955096
case tok::kw_let:
50965097
case tok::kw_var: {
50975098
parseBindingIntroducer(/*HasLetOrVarKeyword=*/true);
@@ -5188,6 +5189,14 @@ Parser::parseDecl(ParseDeclOptions Flags,
51885189
// Obvious nonsense.
51895190
default:
51905191

5192+
// TODO: Once reference bindings is no longer experimental, move this into
5193+
// kw_let, kw_var.
5194+
if (Context.LangOpts.hasFeature(Feature::ReferenceBindings) &&
5195+
Tok.getKind() == tok::kw_inout) {
5196+
parseBindingIntroducer(/*HasLetOrVarKeyword=*/true);
5197+
break;
5198+
}
5199+
51915200
if (Tok.isContextualKeyword("actor") && peekToken().is(tok::identifier)) {
51925201
Tok.setKind(tok::contextual_keyword);
51935202
DeclResult = parseDeclClass(Flags, Attributes);

lib/Parse/ParsePattern.cpp

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,15 +1105,25 @@ ParserResult<Pattern> Parser::parsePattern() {
11051105
}
11061106
return makeParserCodeCompletionStatus();
11071107
case tok::kw_inout:
1108+
// If we don't have the reference binding feature, break if we have
1109+
// inout. Otherwise, go below.
1110+
if (!Context.LangOpts.hasFeature(Feature::ReferenceBindings))
1111+
break;
1112+
LLVM_FALLTHROUGH;
11081113
case tok::kw_var:
11091114
case tok::kw_let: {
11101115
auto newBindingState = PatternBindingState(Tok);
11111116
SourceLoc varLoc = consumeToken();
11121117

11131118
// 'var', 'let', 'inout' patterns shouldn't nest.
1114-
if (InBindingPattern.getIntroducer().hasValue())
1115-
diagnose(varLoc, diag::var_pattern_in_var,
1116-
*newBindingState.getSelectIndexForIntroducer());
1119+
if (InBindingPattern.getIntroducer().hasValue()) {
1120+
auto diag = diag::var_pattern_in_var;
1121+
unsigned index = *newBindingState.getSelectIndexForIntroducer();
1122+
if (Context.LangOpts.hasFeature(Feature::ReferenceBindings)) {
1123+
diag = diag::var_pattern_in_var_inout;
1124+
}
1125+
diagnose(varLoc, diag, index);
1126+
}
11171127

11181128
// 'let' isn't valid inside an implicitly immutable context, but var is.
11191129
if (newBindingState.isLet() &&
@@ -1142,18 +1152,21 @@ ParserResult<Pattern> Parser::parsePattern() {
11421152
}
11431153

11441154
default:
1145-
if (Tok.isKeyword() &&
1146-
(peekToken().is(tok::colon) || peekToken().is(tok::equal))) {
1147-
diagnose(Tok, diag::keyword_cant_be_identifier, Tok.getText());
1148-
diagnose(Tok, diag::backticks_to_escape)
1149-
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
1150-
SourceLoc Loc = Tok.getLoc();
1151-
consumeToken();
1152-
return makeParserErrorResult(new (Context) AnyPattern(Loc));
1153-
}
1154-
diagnose(Tok, diag::expected_pattern);
1155-
return nullptr;
1155+
break;
11561156
}
1157+
1158+
// Handle the default case.
1159+
if (Tok.isKeyword() &&
1160+
(peekToken().is(tok::colon) || peekToken().is(tok::equal))) {
1161+
diagnose(Tok, diag::keyword_cant_be_identifier, Tok.getText());
1162+
diagnose(Tok, diag::backticks_to_escape)
1163+
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
1164+
SourceLoc Loc = Tok.getLoc();
1165+
consumeToken();
1166+
return makeParserErrorResult(new (Context) AnyPattern(Loc));
1167+
}
1168+
diagnose(Tok, diag::expected_pattern);
1169+
return nullptr;
11571170
}
11581171

11591172
Pattern *Parser::createBindingFromPattern(SourceLoc loc, Identifier name,
@@ -1329,9 +1342,13 @@ ParserResult<Pattern>
13291342
Parser::parseMatchingPatternAsBinding(PatternBindingState newState,
13301343
SourceLoc varLoc, bool isExprBasic) {
13311344
// 'var', 'let', 'inout' patterns shouldn't nest.
1332-
if (InBindingPattern.getIntroducer().hasValue())
1333-
diagnose(varLoc, diag::var_pattern_in_var,
1345+
if (InBindingPattern.getIntroducer().hasValue()) {
1346+
auto diag = diag::var_pattern_in_var;
1347+
if (Context.LangOpts.hasFeature(Feature::ReferenceBindings))
1348+
diag = diag::var_pattern_in_var_inout;
1349+
diagnose(varLoc, diag,
13341350
*newState.getSelectIndexForIntroducer());
1351+
}
13351352

13361353
// 'let' isn't valid inside an implicitly immutable context, but var is.
13371354
if (newState.isLet() &&

test/Parse/matching_patterns.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,12 @@ case let a:
3232
a = 1 // expected-error {{cannot assign}}
3333
case inout a:
3434
a = 1
35-
case var var a: // expected-error {{'var' cannot appear nested inside another 'var', 'let', or 'inout' pattern}}
35+
case var var a: // expected-error {{'var' cannot appear nested inside another 'var' or 'let' pattern}}
3636
a += 1
37-
case var let a: // expected-error {{'let' cannot appear nested inside another 'var', 'let', or 'inout' pattern}}
37+
case var let a: // expected-error {{'let' cannot appear nested inside another 'var' or 'let' pattern}}
3838
print(a, terminator: "")
3939
case var (var b): // expected-error {{'var' cannot appear nested inside another 'var'}}
4040
b += 1
41-
case var inout a: // expected-error {{'inout' cannot appear nested inside another 'var', 'let', or 'inout' pattern}}
42-
break
43-
case inout (inout b): // expected-error {{'inout' cannot appear nested inside another 'var', 'let', or 'inout' pattern}}
44-
break
4541
// 'Any' pattern.
4642
case _:
4743
()

0 commit comments

Comments
 (0)