va_start(list); // Invalid in C17 and earlier, valid in C2x and later.
va_end(list);
}
+
+- Reject type definitions in the ``type`` argument of ``__builtin_offsetof``
+ according to `WG14 N2350 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2350.htm>`_.
C++ Language Changes in Clang
-----------------------------
"%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 '%select{__builtin_offsetof|offsetof}1'">;
def note_pure_virtual_function : Note<
"unimplemented pure virtual method %0 in %1">;
friend class ColonProtectionRAIIObject;
friend class ParsingOpenMPDirectiveRAII;
friend class InMessageExpressionRAIIObject;
+ friend class OffsetOfStateRAIIObject;
friend class PoisonSEHIdentifiersRAIIObject;
friend class ObjCDeclContextSwitch;
friend class ParenBraceBracketBalancer;
/// function call.
bool CalledSignatureHelp = false;
+ Sema::OffsetOfKind OffsetOfState = Sema::OffsetOfKind::OOK_Outside;
+
/// The "depth" of the template parameters currently being parsed.
unsigned TemplateParameterDepth;
}
};
+ class OffsetOfStateRAIIObject {
+ Sema::OffsetOfKind &OffsetOfState;
+ Sema::OffsetOfKind OldValue;
+
+ public:
+ OffsetOfStateRAIIObject(Parser &P, Sema::OffsetOfKind Value)
+ : OffsetOfState(P.OffsetOfState), OldValue(P.OffsetOfState) {
+ OffsetOfState = Value;
+ }
+
+ ~OffsetOfStateRAIIObject() { OffsetOfState = 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 {
TUK_Friend // Friend declaration: 'friend struct foo;'
};
+ enum OffsetOfKind {
+ // Not parsing a type within __builtin_offsetof.
+ OOK_Outside,
+ // Parsing a type within __builtin_offsetof.
+ OOK_Builtin,
+ // Parsing a type within macro "offsetof", defined in __buitin_offsetof
+ // To improve our diagnostic message.
+ OOK_Macro,
+ };
+
Decl *ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
SourceLocation KWLoc, CXXScopeSpec &SS, IdentifierInfo *Name,
SourceLocation NameLoc, const ParsedAttributesView &Attr,
bool &IsDependent, SourceLocation ScopedEnumKWLoc,
bool ScopedEnumUsesClassTag, TypeResult UnderlyingType,
bool IsTypeSpecifier, bool IsTemplateParamOrArg,
- SkipBodyInfo *SkipBody = nullptr);
+ OffsetOfKind OOK, SkipBodyInfo *SkipBody = nullptr);
Decl *ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc,
unsigned TagSpec, SourceLocation TagLoc,
DSC == DeclSpecContext::DSC_type_specifier,
DSC == DeclSpecContext::DSC_template_param ||
DSC == DeclSpecContext::DSC_template_type_arg,
- &SkipBody);
+ OffsetOfState, &SkipBody);
if (SkipBody.ShouldSkip) {
assert(TUK == Sema::TUK_Definition && "can only skip a definition");
DSC == DeclSpecContext::DSC_type_specifier,
DSC == DeclSpecContext::DSC_template_param ||
DSC == DeclSpecContext::DSC_template_type_arg,
- &SkipBody);
+ OffsetOfState, &SkipBody);
// If ActOnTag said the type was dependent, try again with the
// less common call.
}
case tok::kw___builtin_offsetof: {
SourceLocation TypeLoc = Tok.getLocation();
- TypeResult Ty = ParseTypeName();
- if (Ty.isInvalid()) {
- SkipUntil(tok::r_paren, StopAtSemi);
- return ExprError();
+ auto K = Sema::OffsetOfKind::OOK_Builtin;
+ if (Tok.getLocation().isMacroID()) {
+ StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
+ Tok.getLocation(), PP.getSourceManager(), getLangOpts());
+ if (MacroName == "offsetof")
+ K = Sema::OffsetOfKind::OOK_Macro;
+ }
+ TypeResult Ty;
+ {
+ OffsetOfStateRAIIObject InOffsetof(*this, K);
+ Ty = ParseTypeName();
+ if (Ty.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
}
if (ExpectAndConsume(tok::comma)) {
SourceLocation ScopedEnumKWLoc,
bool ScopedEnumUsesClassTag, TypeResult UnderlyingType,
bool IsTypeSpecifier, bool IsTemplateParamOrArg,
- SkipBodyInfo *SkipBody) {
+ OffsetOfKind OOK, SkipBodyInfo *SkipBody) {
// If this is not a definition, it must have a name.
IdentifierInfo *OrigName = Name;
assert((Name != nullptr || TUK == TUK_Definition) &&
cast_or_null<RecordDecl>(PrevDecl));
}
+ if (OOK != OOK_Outside && TUK == TUK_Definition) {
+ Diag(New->getLocation(), diag::err_type_defined_in_offsetof)
+ << Context.getTagDeclType(New) << static_cast<int>(OOK == OOK_Macro);
+ 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;
if (SS.isEmpty()) {
bool Owned = false;
bool IsDependent = false;
- return ActOnTag(S, TagSpec, TUK_Friend, TagLoc, SS, Name, NameLoc,
- Attr, AS_public,
+ return ActOnTag(S, TagSpec, TUK_Friend, TagLoc, SS, Name, NameLoc, Attr,
+ AS_public,
/*ModulePrivateLoc=*/SourceLocation(),
MultiTemplateParamsArg(), Owned, IsDependent,
/*ScopedEnumKWLoc=*/SourceLocation(),
/*ScopedEnumUsesClassTag=*/false,
/*UnderlyingType=*/TypeResult(),
/*IsTypeSpecifier=*/false,
- /*IsTemplateParamOrArg=*/false);
+ /*IsTemplateParamOrArg=*/false, /*OOK=*/OOK_Outside);
}
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
bool Owned = false;
bool IsDependent = false;
- Decl *TagD = ActOnTag(S, TagSpec, Sema::TUK_Reference,
- KWLoc, SS, Name, NameLoc, Attr, AS_none,
- /*ModulePrivateLoc=*/SourceLocation(),
- MultiTemplateParamsArg(), Owned, IsDependent,
- SourceLocation(), false, TypeResult(),
- /*IsTypeSpecifier*/false,
- /*IsTemplateParamOrArg*/false);
+ Decl *TagD = ActOnTag(
+ S, TagSpec, Sema::TUK_Reference, KWLoc, SS, Name, NameLoc, Attr, AS_none,
+ /*ModulePrivateLoc=*/SourceLocation(), MultiTemplateParamsArg(), Owned,
+ IsDependent, SourceLocation(), false, TypeResult(),
+ /*IsTypeSpecifier*/ false,
+ /*IsTemplateParamOrArg=*/false, /*OOK=*/OOK_Outside);
assert(!IsDependent && "explicit instantiation of dependent name not yet handled");
if (!TagD)
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c89 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c17 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c2x -verify %s
+
+// Reject definitions in __builtin_offsetof
+// https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2350.htm
+int simple(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);
+}
+
+int anonymous_struct() {
+ return __builtin_offsetof(struct // expected-error-re{{'struct (unnamed at {{.*}})' cannot be defined in '__builtin_offsetof'}}
+ {
+ int a;
+ int b;
+ }, a);
+}
+
+int struct_in_second_param() {
+ struct A {
+ int a, b;
+ int x[20];
+ };
+ return __builtin_offsetof(struct A, x[sizeof(struct B{int a;})]); // no-error
+}
+
+
+#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER)
+
+
+int macro(void) {
+ return offsetof(struct A // expected-error{{'struct A' cannot be defined in 'offsetof'}}
+ // expected-error@-1{{'struct B' cannot be defined in 'offsetof'}}
+ {
+ int a;
+ struct B // verifier seems to think the error is emitted by the macro
+ // In fact the location of the error is "B" on the line above
+ {
+ int c;
+ int d;
+ } x;
+ }, a);
+}
+
+#undef offsetof
+
+#define offsetof(TYPE, MEMBER) (&((TYPE *)0)->MEMBER)
+
+// no warning for traditional offsetof as a function-like macro
+int * macro_func(void) {
+ return offsetof(struct A // no-warning
+ {
+ int a;
+ int b;
+ }, a);
+}
*/
/* The DR asked a question about whether defining a new type within offsetof
- * is allowed. C2x N2350 made this explicitly undefined behavior, but Clang
- * has always supported defining a type in this location, and GCC also
- * supports it.
+ * is allowed. C2x N2350 made this explicitly undefined behavior, but GCC
+ * supports it, Clang diagnoses this a UB and rejects it.
*/
- (void)__builtin_offsetof(struct S { int a; }, a);
+ (void)__builtin_offsetof(struct S { int a; }, a); /* expected-error{{'struct S' cannot be defined in '__builtin_offsetof'}} */
}
/* WG14 DR499: yes
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);
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 // FIXME: error diagnostic message for nested definitions
+ // https://reviews.llvm.org/D133574
+ // fixme-error{{'A' cannot be defined in '__builtin_offsetof'}}
+ {
+ int c;
+ int d;
+ };
+ B x;
+ }, a);
+}