[clang] 881125a - Allow use of an elaborated type specifier in a _Generic association in C++
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 6 04:18:32 PDT 2022
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
More information about the cfe-commits
mailing list