diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -276,6 +276,8 @@ Clang will now consider default argument promotions in ``printf``, and remove unnecessary warnings. Especially ``int`` argument with specifier ``%hhd`` and ``%hd``. +- Reject type definitions in the ``type`` argument of ``__builtin_offsetof`` + according to `WG14 N2350 `_. C2x Feature Support ------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1646,6 +1646,8 @@ "%0 cannot be defined in a condition">; def err_type_defined_in_enum : Error< "%0 cannot be defined in an enumeration">; +def err_type_defined_in_offsetof : Error< + "%0 cannot be defined in '__builtin_offsetof'">; def note_pure_virtual_function : Note< "unimplemented pure virtual method %0 in %1">; 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 @@ -61,6 +61,7 @@ friend class ColonProtectionRAIIObject; friend class ParsingOpenMPDirectiveRAII; friend class InMessageExpressionRAIIObject; + friend class InBuiltInOffsetOfBaseRAIIObject; friend class PoisonSEHIdentifiersRAIIObject; friend class ObjCDeclContextSwitch; friend class ParenBraceBracketBalancer; @@ -247,6 +248,9 @@ /// function call. bool CalledSignatureHelp = false; + /// Parsing a type within __builtin_offsetof. + bool InBuiltInOffsetOfBase = false; + /// The "depth" of the template parameters currently being parsed. unsigned TemplateParameterDepth; diff --git a/clang/include/clang/Parse/RAIIObjectsForParser.h b/clang/include/clang/Parse/RAIIObjectsForParser.h --- a/clang/include/clang/Parse/RAIIObjectsForParser.h +++ b/clang/include/clang/Parse/RAIIObjectsForParser.h @@ -341,6 +341,20 @@ } }; + class InBuiltInOffsetOfBaseRAIIObject { + bool &InBuiltInOffsetOfBase; + bool OldValue; + + public: + InBuiltInOffsetOfBaseRAIIObject(Parser &P, bool Value) + : InBuiltInOffsetOfBase(P.InBuiltInOffsetOfBase), + OldValue(P.InBuiltInOffsetOfBase) { + InBuiltInOffsetOfBase = Value; + } + + ~InBuiltInOffsetOfBaseRAIIObject() { InBuiltInOffsetOfBase = OldValue; } + }; + /// RAII object that makes sure paren/bracket/brace count is correct /// after declaration/statement parsing, even when there's a parsing error. class ParenBraceBracketBalancer { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3276,7 +3276,8 @@ bool &IsDependent, SourceLocation ScopedEnumKWLoc, bool ScopedEnumUsesClassTag, TypeResult UnderlyingType, bool IsTypeSpecifier, bool IsTemplateParamOrArg, - SkipBodyInfo *SkipBody = nullptr); + SkipBodyInfo *SkipBody = nullptr, + bool IsWithinOffsetOf = false); Decl *ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc, 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 @@ -2030,7 +2030,7 @@ DSC == DeclSpecContext::DSC_type_specifier, DSC == DeclSpecContext::DSC_template_param || DSC == DeclSpecContext::DSC_template_type_arg, - &SkipBody); + &SkipBody, InBuiltInOffsetOfBase); // If ActOnTag said the type was dependent, try again with the // less common call. diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -2579,6 +2579,7 @@ } case tok::kw___builtin_offsetof: { SourceLocation TypeLoc = Tok.getLocation(); + InBuiltInOffsetOfBaseRAIIObject InOffsetof(*this, true); TypeResult Ty = ParseTypeName(); if (Ty.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16256,7 +16256,7 @@ SourceLocation ScopedEnumKWLoc, bool ScopedEnumUsesClassTag, TypeResult UnderlyingType, bool IsTypeSpecifier, bool IsTemplateParamOrArg, - SkipBodyInfo *SkipBody) { + SkipBodyInfo *SkipBody, bool IsWithinOffsetOf) { // If this is not a definition, it must have a name. IdentifierInfo *OrigName = Name; assert((Name != nullptr || TUK == TUK_Definition) && @@ -17029,10 +17029,16 @@ cast_or_null(PrevDecl)); } + if (IsWithinOffsetOf && TUK == TUK_Definition) { + Diag(New->getLocation(), diag::err_type_defined_in_offsetof) + << Context.getTagDeclType(New); + Invalid = true; + } + // C++11 [dcl.type]p3: // A type-specifier-seq shall not define a class or enumeration [...]. - if (getLangOpts().CPlusPlus && (IsTypeSpecifier || IsTemplateParamOrArg) && - TUK == TUK_Definition) { + if (!Invalid && getLangOpts().CPlusPlus && + (IsTypeSpecifier || IsTemplateParamOrArg) && TUK == TUK_Definition) { Diag(New->getLocation(), diag::err_type_defined_in_type_specifier) << Context.getTagDeclType(New); Invalid = true; diff --git a/clang/test/Parser/declarators.c b/clang/test/Parser/declarators.c --- a/clang/test/Parser/declarators.c +++ b/clang/test/Parser/declarators.c @@ -80,10 +80,6 @@ struct test10 { int a; } static test10x; struct test11 { int a; } const test11x; -// PR6216 -void test12(void) { - (void)__builtin_offsetof(struct { char c; int i; }, i); -} // rdar://7608537 struct test13 { int a; } (test13x); diff --git a/clang/test/Sema/offsetof.c b/clang/test/Sema/offsetof.c --- a/clang/test/Sema/offsetof.c +++ b/clang/test/Sema/offsetof.c @@ -70,3 +70,16 @@ return __builtin_offsetof(Array, array[*(int*)0]); // expected-warning{{indirection of non-volatile null pointer}} expected-note{{__builtin_trap}} } +// Reject definitions in __builtin_offsetof +// https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2350.htm +int test_definition(void) { + return __builtin_offsetof(struct A // expected-error{{'struct A' cannot be defined in '__builtin_offsetof'}} + { + int a; + struct B // expected-error{{'struct B' cannot be defined in '__builtin_offsetof'}} + { + int c; + int d; + } x; + }, a); +} diff --git a/clang/test/SemaCXX/offsetof.cpp b/clang/test/SemaCXX/offsetof.cpp --- a/clang/test/SemaCXX/offsetof.cpp +++ b/clang/test/SemaCXX/offsetof.cpp @@ -83,3 +83,18 @@ expected-error {{invalid application of 'offsetof' to a field of a virtual base}} }; } + +// Reject definitions in __builtin_offsetof +// https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2350.htm +int test_definition(void) { + return __builtin_offsetof(struct A // expected-error{{'A' cannot be defined in '__builtin_offsetof'}} + { + int a; + struct B + { + int c; + int d; + }; + B x; + }, a); +}