diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -134,6 +134,8 @@ C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ - Implemented `P1938R3: if consteval `_. +- Implemented `P2360R0: Extend init-statement to allow alias-declaration `_. + CUDA Language Changes in Clang ------------------------------ 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,12 @@ "variable declaration in condition cannot have a parenthesized initializer">; def err_extraneous_rparen_in_condition : Error< "extraneous ')' after condition, expected a statement">; +def ext_alias_in_init_statement : ExtWarn< + "alias declaration in this context is a C++2b extension">, + InGroup; +def warn_cxx20_alias_in_init_statement : Warning< + "alias declaration in this context is incompatible with C++ standards before C++2b">, + DefaultIgnore, 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,9 @@ Sema::ConditionKind CK, ForRangeInfo *FRI = nullptr, bool EnterForConditionScope = false); + DeclGroupPtrTy + ParseAliasDeclarationInInitStatement(DeclaratorContext Context, + ParsedAttributesWithRange &Attrs); //===--------------------------------------------------------------------===// // C++ Coroutines @@ -2396,7 +2399,8 @@ if (getLangOpts().OpenMP) Actions.startOpenMPLoop(); if (getLangOpts().CPlusPlus) - return isCXXSimpleDeclaration(/*AllowForRangeDecl=*/true); + return Tok.is(tok::kw_using) || + isCXXSimpleDeclaration(/*AllowForRangeDecl=*/true); return isDeclarationSpecifier(true); } diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -678,7 +678,10 @@ SourceLocation UsingLoc, SourceLocation &DeclEnd, ParsedAttributesWithRange &PrefixAttrs, AccessSpecifier AS) { SourceLocation UELoc; - if (TryConsumeToken(tok::kw_enum, UELoc)) { + bool InInitStatement = Context == DeclaratorContext::SelectionInit || + Context == DeclaratorContext::ForInit; + + if (TryConsumeToken(tok::kw_enum, UELoc) && !InInitStatement) { // C++20 using-enum Diag(UELoc, getLangOpts().CPlusPlus20 ? diag::warn_cxx17_compat_using_enum_declaration @@ -714,6 +717,9 @@ ParsedAttributesWithRange MisplacedAttrs(AttrFactory); MaybeParseCXX11Attributes(MisplacedAttrs); + if (InInitStatement && Tok.isNot(tok::identifier)) + return nullptr; + UsingDeclarator D; bool InvalidDeclarator = ParseUsingDeclarator(Context, D); @@ -732,7 +738,7 @@ } // Maybe this is an alias-declaration. - if (Tok.is(tok::equal)) { + if (Tok.is(tok::equal) || InInitStatement) { if (InvalidDeclarator) { SkipUntil(tok::semi); return nullptr; 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,28 @@ } } +Parser::DeclGroupPtrTy +Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context, + ParsedAttributesWithRange &Attrs) { + assert(Tok.is(tok::kw_using) && "Expected using"); + assert((Context == DeclaratorContext::ForInit || + Context == DeclaratorContext::SelectionInit) && + "Unexpected Declarator Context"); + DeclGroupPtrTy DG; + SourceLocation DeclStart = ConsumeToken(), DeclEnd; + + DG = ParseUsingDeclaration(Context, {}, DeclStart, DeclEnd, Attrs, AS_none); + if (!DG) + return DG; + + Diag(DeclStart, !getLangOpts().CPlusPlus2b + ? diag::ext_alias_in_init_statement + : diag::warn_cxx20_alias_in_init_statement) + << SourceRange(DeclStart, DeclEnd); + + return DG; +} + /// ParseCXXCondition - if/switch/while condition expression. /// /// condition: @@ -2017,9 +2039,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( + DeclaratorContext::SelectionInit, 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,42 @@ 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(DeclaratorContext::ForInit, + 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,6 +483,8 @@ ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement, CanBeForRangeDecl); + if (CanBeInitStatement && Tok.is(tok::kw_using)) + return ConditionOrInitStatement::InitStmtDecl; if (State.update(isCXXDeclarationSpecifier())) return State.result(); 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,29 @@ +// 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 this context is a C++2b extension}} + + switch(using foo = int; 0) { //expected-cxx20-warning {{alias declaration in this context is a C++2b extension}} + case 0: break; + } + + if(using foo = int; false) {} //expected-cxx20-warning {{alias declaration in this context is a C++2b extension}} + + + if (using enum ns::e; false){} // expected-error {{expected '='}} + + for (using ns::i; true;); // expected-error {{expected '='}} + + if (using ns::i; false){} // expected-error {{expected '='}} + + switch(using ns::i; 0) { // expected-error {{expected '='}} + 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