Author: Aaron Ballman Date: 2022-06-06T07:17:35-04:00 New Revision: 881125ad9178120acef186f579e36ced0888dfdb
URL: https://github.com/llvm/llvm-project/commit/881125ad9178120acef186f579e36ced0888dfdb DIFF: https://github.com/llvm/llvm-project/commit/881125ad9178120acef186f579e36ced0888dfdb.diff LOG: Allow use of an elaborated type specifier in a _Generic association in C++ Currently, Clang accepts this code in C mode (where the tag is required to be used) but rejects it in C++ mode thinking that the association is defining a new type. void foo(void) { struct S { int a; }; _Generic(something, struct S : 1); } Clang thinks this in C++ because it sees struct S : when parsing the class specifier and decides that must be a type definition (because the colon signifies the presence of a base class type). This patch adds a new declarator context to represent a _Generic association so that we can distinguish these situations properly. Fixes #55562 Differential Revision: https://reviews.llvm.org/D126969 Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Parse/Parser.h clang/include/clang/Sema/DeclSpec.h clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseDeclCXX.cpp clang/lib/Parse/ParseExpr.cpp clang/lib/Sema/SemaType.cpp clang/test/Sema/generic-selection.c clang/test/SemaCXX/generic-selection.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c377738401474..57519787ecc47 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -163,6 +163,9 @@ Bug Fixes - Unscoped and scoped enumeration types can no longer be initialized from a brace-init-list containing a single element of a diff erent scoped enumeration type. +- Allow use of an elaborated type specifier as a ``_Generic`` selection + association in C++ mode. This fixes + `Issue 55562 <https://github.com/llvm/llvm-project/issues/55562>`_. Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index a20866e410aa4..5bf7b276c328d 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2195,7 +2195,8 @@ class Parser : public CodeCompletionHandler { DSC_template_param, // template parameter context DSC_template_type_arg, // template type argument context DSC_objc_method_result, // ObjC method result context, enables 'instancetype' - DSC_condition // condition declaration context + DSC_condition, // condition declaration context + DSC_association // A _Generic selection expression's type association }; /// Is this a context in which we are parsing just a type-specifier (or @@ -2214,6 +2215,7 @@ class Parser : public CodeCompletionHandler { case DeclSpecContext::DSC_type_specifier: case DeclSpecContext::DSC_trailing: case DeclSpecContext::DSC_alias_declaration: + case DeclSpecContext::DSC_association: return true; } llvm_unreachable("Missing DeclSpecContext case"); @@ -2238,7 +2240,7 @@ class Parser : public CodeCompletionHandler { /// so permit class and enum definitions in addition to non-defining class and /// enum elaborated-type-specifiers)? static AllowDefiningTypeSpec - isDefiningTypeSpecifierContext(DeclSpecContext DSC) { + isDefiningTypeSpecifierContext(DeclSpecContext DSC, bool IsCPlusPlus) { switch (DSC) { case DeclSpecContext::DSC_normal: case DeclSpecContext::DSC_class: @@ -2255,6 +2257,10 @@ class Parser : public CodeCompletionHandler { case DeclSpecContext::DSC_type_specifier: return AllowDefiningTypeSpec::NoButErrorRecovery; + case DeclSpecContext::DSC_association: + return IsCPlusPlus ? AllowDefiningTypeSpec::NoButErrorRecovery + : AllowDefiningTypeSpec::Yes; + case DeclSpecContext::DSC_trailing: return AllowDefiningTypeSpec::No; } @@ -2276,6 +2282,7 @@ class Parser : public CodeCompletionHandler { case DeclSpecContext::DSC_template_type_arg: case DeclSpecContext::DSC_type_specifier: case DeclSpecContext::DSC_trailing: + case DeclSpecContext::DSC_association: return false; } llvm_unreachable("Missing DeclSpecContext case"); @@ -2291,6 +2298,7 @@ class Parser : public CodeCompletionHandler { case DeclSpecContext::DSC_top_level: case DeclSpecContext::DSC_condition: case DeclSpecContext::DSC_type_specifier: + case DeclSpecContext::DSC_association: return true; case DeclSpecContext::DSC_objc_method_result: diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index c03ead9c79b34..dc1fbf098a8ac 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1786,7 +1786,8 @@ enum class DeclaratorContext { TemplateTypeArg, // Template type argument (in default argument). AliasDecl, // C++11 alias-declaration. AliasTemplate, // C++11 alias-declaration template. - RequiresExpr // C++2a requires-expression. + RequiresExpr, // C++2a requires-expression. + Association // C11 _Generic selection expression association. }; /// Information about one declarator, including the parsed type @@ -2024,6 +2025,7 @@ class Declarator { case DeclaratorContext::TrailingReturn: case DeclaratorContext::TrailingReturnVar: case DeclaratorContext::RequiresExpr: + case DeclaratorContext::Association: return true; } llvm_unreachable("unknown context kind!"); @@ -2063,6 +2065,7 @@ class Declarator { case DeclaratorContext::TemplateTypeArg: case DeclaratorContext::TrailingReturn: case DeclaratorContext::TrailingReturnVar: + case DeclaratorContext::Association: return false; } llvm_unreachable("unknown context kind!"); @@ -2106,6 +2109,7 @@ class Declarator { case DeclaratorContext::TemplateTypeArg: case DeclaratorContext::TrailingReturn: case DeclaratorContext::TrailingReturnVar: + case DeclaratorContext::Association: return false; } llvm_unreachable("unknown context kind!"); @@ -2162,6 +2166,7 @@ class Declarator { case DeclaratorContext::TemplateTypeArg: case DeclaratorContext::TrailingReturn: case DeclaratorContext::RequiresExpr: + case DeclaratorContext::Association: return false; } llvm_unreachable("unknown context kind!"); @@ -2384,6 +2389,7 @@ class Declarator { case DeclaratorContext::TrailingReturn: case DeclaratorContext::TrailingReturnVar: case DeclaratorContext::RequiresExpr: + case DeclaratorContext::Association: return false; } llvm_unreachable("unknown context kind!"); @@ -2418,6 +2424,7 @@ class Declarator { case DeclaratorContext::TrailingReturnVar: case DeclaratorContext::TemplateTypeArg: case DeclaratorContext::RequiresExpr: + case DeclaratorContext::Association: return false; case DeclaratorContext::Block: diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 18f4f12bf5a4d..0cdf9158cb3b4 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2870,6 +2870,8 @@ Parser::getDeclSpecContextFromDeclaratorContext(DeclaratorContext Context) { if (Context == DeclaratorContext::AliasDecl || Context == DeclaratorContext::AliasTemplate) return DeclSpecContext::DSC_alias_declaration; + if (Context == DeclaratorContext::Association) + return DeclSpecContext::DSC_association; return DeclSpecContext::DSC_normal; } @@ -4573,7 +4575,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, // Determine whether this declaration is permitted to have an enum-base. AllowDefiningTypeSpec AllowEnumSpecifier = - isDefiningTypeSpecifierContext(DSC); + isDefiningTypeSpecifierContext(DSC, getLangOpts().CPlusPlus); bool CanBeOpaqueEnumDeclaration = DS.isEmpty() && isOpaqueEnumDeclarationContext(DSC); bool CanHaveEnumBase = (getLangOpts().CPlusPlus11 || getLangOpts().ObjC || diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index c135dc2860972..b5874e28786a5 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1752,13 +1752,15 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy(); Sema::TagUseKind TUK; - if (isDefiningTypeSpecifierContext(DSC) == AllowDefiningTypeSpec::No || + if (isDefiningTypeSpecifierContext(DSC, getLangOpts().CPlusPlus) == + AllowDefiningTypeSpec::No || (getLangOpts().OpenMP && OpenMPDirectiveParsing)) TUK = Sema::TUK_Reference; else if (Tok.is(tok::l_brace) || - (getLangOpts().CPlusPlus && Tok.is(tok::colon)) || - (isClassCompatibleKeyword() && - (NextToken().is(tok::l_brace) || NextToken().is(tok::colon)))) { + (DSC != DeclSpecContext::DSC_association && + (getLangOpts().CPlusPlus && Tok.is(tok::colon)) || + (isClassCompatibleKeyword() && + (NextToken().is(tok::l_brace) || NextToken().is(tok::colon))))) { if (DS.isFriendSpecified()) { // C++ [class.friend]p2: // A class shall not be defined in a friend declaration. diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 1002a0e11c0f9..6d0fd7e2f6014 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -3276,7 +3276,7 @@ ExprResult Parser::ParseGenericSelectionExpression() { Ty = nullptr; } else { ColonProtectionRAIIObject X(*this); - TypeResult TR = ParseTypeName(); + TypeResult TR = ParseTypeName(nullptr, DeclaratorContext::Association); if (TR.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return ExprError(); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 3c1b2931efc7f..c4b86ee2c5ee8 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -3538,6 +3538,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, break; // auto(x) LLVM_FALLTHROUGH; case DeclaratorContext::TypeName: + case DeclaratorContext::Association: Error = 15; // Generic break; case DeclaratorContext::File: @@ -3648,6 +3649,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::ObjCCatch: case DeclaratorContext::TemplateArg: case DeclaratorContext::TemplateTypeArg: + case DeclaratorContext::Association: DiagID = diag::err_type_defined_in_type_specifier; break; case DeclaratorContext::Prototype: @@ -4735,6 +4737,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::TypeName: case DeclaratorContext::FunctionalCast: case DeclaratorContext::RequiresExpr: + case DeclaratorContext::Association: // Don't infer in these contexts. break; } @@ -5777,6 +5780,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::TrailingReturnVar: case DeclaratorContext::TemplateArg: case DeclaratorContext::TemplateTypeArg: + case DeclaratorContext::Association: // FIXME: We may want to allow parameter packs in block-literal contexts // in the future. S.Diag(D.getEllipsisLoc(), diff --git a/clang/test/Sema/generic-selection.c b/clang/test/Sema/generic-selection.c index 0bd537c79d316..1f17896ca4cda 100644 --- a/clang/test/Sema/generic-selection.c +++ b/clang/test/Sema/generic-selection.c @@ -78,3 +78,11 @@ void unreachable_associations(const int i, const struct Test t) { default : 3 ) == 1, "we had better pick struct Test, not const struct Test!"); // C-specific result } + +void GH55562(void) { + // Ensure that you can still define a type within a generic selection + // association (despite it not being particularly useful). + (void)_Generic(1, struct S { int a; } : 0, default : 0); // ext-warning {{'_Generic' is a C11 extension}} + struct S s = { 0 }; + int i = s.a; +} diff --git a/clang/test/SemaCXX/generic-selection.cpp b/clang/test/SemaCXX/generic-selection.cpp index 79e0776cfa395..7b4fad65a1811 100644 --- a/clang/test/SemaCXX/generic-selection.cpp +++ b/clang/test/SemaCXX/generic-selection.cpp @@ -69,3 +69,24 @@ void unreachable_associations(const int i, const Test t) { default : 3 ) == 2, "we had better pick const Test, not Test!"); // C++-specific result } + +namespace GH55562 { +struct S { // expected-note {{declared here}} + int i; +}; + +void func(struct S s) { + // We would previously reject this because the parser thought 'struct S :' + // was the start of a definition (with a base class specifier); it's not, it + // is an elaborated type specifier followed by the association's value and + // it should work the same as in C. + (void)_Generic(s, struct S : 1); + + // The rest of these cases test that we still produce a reasonable diagnostic + // when referencing an unknown type or trying to define a type in other ways. + (void)_Generic(s, struct T : 1); // expected-error {{type 'struct T' in generic association incomplete}} + (void)_Generic(s, struct U { int a; } : 1); // expected-error {{'U' cannot be defined in a type specifier}} + (void)_Generic(s, struct V : S); // expected-error {{'S' does not refer to a value}} + (void)_Generic(s, struct W : S { int b; } : 1); // expected-error {{expected '(' for function-style cast or type construction}} +} +} // namespace GH55562 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits