diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -549,6 +549,11 @@ "variable declaration in condition cannot have a parenthesized initializer">; def err_extraneous_rparen_in_condition : Error< "extraneous ')' after condition, expected a statement">; +def err_expected_alias_after_using_in_init_statement : Error< + "expected alias declaration after using in init statement">; +def ext_alias_in_init_statement : ExtWarn< + "alias declaration in init statements is a C++2b extension">, + InGroup; def warn_dangling_else : Warning< "add explicit braces to avoid dangling else">, InGroup; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1977,6 +1977,8 @@ Sema::ConditionKind CK, ForRangeInfo *FRI = nullptr, bool EnterForConditionScope = false); + DeclGroupPtrTy + ParseAliasDeclarationInInitStatement(ParsedAttributesWithRange &attrs); //===--------------------------------------------------------------------===// // C++ Coroutines @@ -2396,7 +2398,8 @@ if (getLangOpts().OpenMP) Actions.startOpenMPLoop(); if (getLangOpts().CPlusPlus) - return isCXXSimpleDeclaration(/*AllowForRangeDecl=*/true); + return isCXXSimpleDeclaration(/*AllowForRangeDecl=*/true) || + Tok.is(tok::kw_using); return isDeclarationSpecifier(true); } diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1910,6 +1910,31 @@ } } +Parser::DeclGroupPtrTy +Parser::ParseAliasDeclarationInInitStatement(ParsedAttributesWithRange &attrs) { + assert(Tok.is(tok::kw_using) && "Expected using"); + DeclGroupPtrTy DG; + SourceLocation DeclStart = ConsumeToken(), DeclEnd; + + DG = ParseUsingDeclaration(DeclaratorContext::SelectionInit, {}, DeclStart, + DeclEnd, attrs, AS_none); + if (!DG) + return DG; + + const Decl *InitDecl = DG.get().getSingleDecl(); + if (!isa(InitDecl)) { + Diag(DeclStart, diag::err_expected_alias_after_using_in_init_statement) + << SourceRange(DeclStart, DeclEnd); + } + + if (!getLangOpts().CPlusPlus2b) { + Diag(DeclStart, diag::ext_alias_in_init_statement) + << SourceRange(DeclStart, DeclEnd); + } + + return DG; +} + /// ParseCXXCondition - if/switch/while condition expression. /// /// condition: @@ -2017,9 +2042,14 @@ case ConditionOrInitStatement::InitStmtDecl: { WarnOnInit(); + DeclGroupPtrTy DG; SourceLocation DeclStart = Tok.getLocation(), DeclEnd; - DeclGroupPtrTy DG = ParseSimpleDeclaration( - DeclaratorContext::SelectionInit, DeclEnd, attrs, /*RequireSemi=*/true); + if (Tok.is(tok::kw_using)) { + DG = ParseAliasDeclarationInInitStatement(attrs); + } else { + DG = ParseSimpleDeclaration(DeclaratorContext::SelectionInit, DeclEnd, + attrs, /*RequireSemi=*/true); + } *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd); return ParseCXXCondition(nullptr, Loc, CK); } diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -1823,6 +1823,7 @@ /// [C++] for-init-statement: /// [C++] expression-statement /// [C++] simple-declaration +/// [C++2b] alias-declaration /// /// [C++0x] for-range-declaration: /// [C++0x] attribute-specifier-seq[opt] type-specifier-seq declarator @@ -1928,36 +1929,41 @@ Diag(Tok, diag::ext_c99_variable_decl_in_for_loop); Diag(Tok, diag::warn_gcc_variable_decl_in_for_loop); } + DeclGroupPtrTy DG; + if (Tok.is(tok::kw_using)) { + DG = ParseAliasDeclarationInInitStatement(attrs); + } else { + // In C++0x, "for (T NS:a" might not be a typo for :: + bool MightBeForRangeStmt = getLangOpts().CPlusPlus; + ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt); - // In C++0x, "for (T NS:a" might not be a typo for :: - bool MightBeForRangeStmt = getLangOpts().CPlusPlus; - ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt); - - SourceLocation DeclStart = Tok.getLocation(), DeclEnd; - DeclGroupPtrTy DG = ParseSimpleDeclaration( - DeclaratorContext::ForInit, DeclEnd, attrs, false, - MightBeForRangeStmt ? &ForRangeInfo : nullptr); - FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation()); - if (ForRangeInfo.ParsedForRangeDecl()) { - Diag(ForRangeInfo.ColonLoc, getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_for_range : diag::ext_for_range); - ForRangeInfo.LoopVar = FirstPart; - FirstPart = StmtResult(); - } else if (Tok.is(tok::semi)) { // for (int x = 4; - ConsumeToken(); - } else if ((ForEach = isTokIdentifier_in())) { - Actions.ActOnForEachDeclStmt(DG); - // ObjC: for (id x in expr) - ConsumeToken(); // consume 'in' - - if (Tok.is(tok::code_completion)) { - cutOffParsing(); - Actions.CodeCompleteObjCForCollection(getCurScope(), DG); - return StmtError(); + SourceLocation DeclStart = Tok.getLocation(), DeclEnd; + DG = ParseSimpleDeclaration( + DeclaratorContext::ForInit, DeclEnd, attrs, false, + MightBeForRangeStmt ? &ForRangeInfo : nullptr); + FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation()); + if (ForRangeInfo.ParsedForRangeDecl()) { + Diag(ForRangeInfo.ColonLoc, getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_for_range + : diag::ext_for_range); + ForRangeInfo.LoopVar = FirstPart; + FirstPart = StmtResult(); + } else if (Tok.is(tok::semi)) { // for (int x = 4; + ConsumeToken(); + } else if ((ForEach = isTokIdentifier_in())) { + Actions.ActOnForEachDeclStmt(DG); + // ObjC: for (id x in expr) + ConsumeToken(); // consume 'in' + + if (Tok.is(tok::code_completion)) { + cutOffParsing(); + Actions.CodeCompleteObjCForCollection(getCurScope(), DG); + return StmtError(); + } + Collection = ParseExpression(); + } else { + Diag(Tok, diag::err_expected_semi_for); } - Collection = ParseExpression(); - } else { - Diag(Tok, diag::err_expected_semi_for); } } else { ProhibitAttributes(attrs); diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -483,7 +483,9 @@ ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement, CanBeForRangeDecl); - if (State.update(isCXXDeclarationSpecifier())) + if (CanBeInitStatement && Tok.is(tok::kw_using)) { + return ConditionOrInitStatement::InitStmtDecl; + } else if (State.update(isCXXDeclarationSpecifier())) return State.result(); // It might be a declaration; we need tentative parsing. diff --git a/clang/test/Parser/cxx2b-init-statement.cpp b/clang/test/Parser/cxx2b-init-statement.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Parser/cxx2b-init-statement.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -fsyntax-only -verify=expected -std=c++2b -Wall %s +// RUN: %clang_cc1 -fsyntax-only -verify=expected,expected-cxx20 -std=c++20 -Wall %s + +namespace ns { + int i; + enum class e {}; +} +void f() { + + for (using foo = int;true;); //expected-cxx20-warning {{alias declaration in init statements is a C++2b extension}} + + switch(using foo = int; 0) { //expected-cxx20-warning {{alias declaration in init statements is a C++2b extension}} + case 0: break; + } + + if(using foo = int; false) {} //expected-cxx20-warning {{alias declaration in init statements is a C++2b extension}} + + + if (using enum ns::e; false){} // expected-error {{expected alias declaration after using in init statement}} \ + // expected-cxx20-warning {{alias declaration in init statements is a C++2b extension}} + + + for (using ns::i; true;); // expected-error {{expected alias declaration after using in init statement}} \ + // expected-cxx20-warning {{alias declaration in init statements is a C++2b extension}} + + if (using ns::i; false){} // expected-error {{expected alias declaration after using in init statement}} \ + // expected-cxx20-warning {{alias declaration in init statements is a C++2b extension}} + + switch(using ns::i; 0) { // expected-error {{expected alias declaration after using in init statement}} \ + // expected-cxx20-warning {{alias declaration in init statements is a C++2b extension}} + case 0: break; + } + +} diff --git a/clang/test/SemaCXX/cxx2b-init-statement.cpp b/clang/test/SemaCXX/cxx2b-init-statement.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/cxx2b-init-statement.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -verify -std=c++2b -Wall -Wshadow %s + +void f() { + + for (using foo = int;true;) {} //expected-warning {{unused type alias 'foo'}} + + switch(using foo = int; 0) { //expected-warning {{unused type alias 'foo'}} + case 0: break; + } + + if(using foo = int; false) {} // expected-warning {{unused type alias 'foo'}} + + int x; // expected-warning {{unused variable 'x'}} + if(using x = int; true) {} // expected-warning {{unused type alias 'x'}} + + using y = int; // expected-warning {{unused type alias 'y'}} \ + // expected-note 2{{previous declaration is here}} + + if(using y = double; true) {} // expected-warning {{unused type alias 'y'}} \ + // expected-warning {{declaration shadows a type alias in function 'f'}} + + for(using y = double; true;) { // expected-warning {{declaration shadows a type alias in function 'f'}} + y foo = 0; + (void)foo; + constexpr y var = 0; + static_assert(var == 0); + } +} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1365,7 +1365,7 @@ Extend init-statement to allow alias-declaration P2360R0 - No + Clang 14