[clang] e7300e7 - Diagnose extensions in 'offsetof'
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 18 05:51:29 PST 2023
Author: Aaron Ballman
Date: 2023-01-18T08:51:21-05:00
New Revision: e7300e75b51a7e7d4e81975b4be7a6c65f9a8286
URL: https://github.com/llvm/llvm-project/commit/e7300e75b51a7e7d4e81975b4be7a6c65f9a8286
DIFF: https://github.com/llvm/llvm-project/commit/e7300e75b51a7e7d4e81975b4be7a6c65f9a8286.diff
LOG: Diagnose extensions in 'offsetof'
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2350.htm made very
clear that it is an UB having type definitions with in offsetof.
Clang supports defining a type as the first argument as a conforming
extension due to how many projects use the construct in C99 and earlier
to calculate the alignment of a type. GCC also supports defining a type
as the first argument.
This adds extension warnings and documentation for the functionality
Clang explicitly supports.
Fixes #57065
Reverts the revert of 39da55e8f548a11f7dadefa73ea73d809a5f1729
Co-authored-by: Yingchi Long <i at lyc.dev>
Co-authored-by: Aaron Ballman <aaron at aaronballman.com>
Differential Revision: https://reviews.llvm.org/D133574
Added:
clang/test/C/C2x/n2350.c
Modified:
clang/docs/LanguageExtensions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticGroups.td
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Parse/Parser.h
clang/include/clang/Parse/RAIIObjectsForParser.h
clang/include/clang/Sema/Sema.h
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Parse/ParseExpr.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/test/C/drs/dr4xx.c
clang/test/CXX/drs/dr4xx.cpp
clang/test/CodeGen/offsetof.c
clang/test/Parser/declarators.c
clang/test/Sema/offsetof.c
clang/test/SemaCXX/offsetof.cpp
Removed:
################################################################################
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 9b39dc23e0a5c..dbec1cd7c1966 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -2360,6 +2360,50 @@ evaluated, so any side effects of the expression will be discarded.
Query for this feature with ``__has_builtin(__builtin_assume)``.
+``__builtin_offsetof``
+----------------------
+
+``__builtin_offsetof`` is used to implement the ``offsetof`` macro, which
+calculates the offset (in bytes) to a given member of the given type.
+
+**Syntax**:
+
+.. code-block:: c++
+
+ __builtin_offsetof(type-name, member-designator)
+
+**Example of Use**:
+
+.. code-block:: c++
+
+ struct S {
+ char c;
+ int i;
+ struct T {
+ float f[2];
+ } t;
+ };
+
+ const int offset_to_i = __builtin_offsetof(struct S, i);
+ const int ext1 = __builtin_offsetof(struct U { int i; }, i); // C extension
+ const int ext2 = __builtin_offsetof(struct S, t.f[1]); // C & C++ extension
+
+**Description**:
+
+This builtin is usable in an integer constant expression which returns a value
+of type ``size_t``. The value returned is the offset in bytes to the subobject
+designated by the member-designator from the beginning of an object of type
+``type-name``. Clang extends the required standard functionality in a few ways:
+
+* In C language modes, the first argument may be the definition of a new type.
+ Any type declared this way is scoped to the nearest scope containing the call
+ to the builtin.
+* The second argument may be a member-designator designated by a series of
+ member access expressions using the dot (``.``) operator or array subscript
+ expressions.
+
+Query for this feature with ``__has_builtin(__builtin_offsetof)``.
+
``__builtin_call_with_static_chain``
------------------------------------
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b912f77887f9a..288904b023863 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -504,7 +504,8 @@ Non-comprehensive list of changes in this release
- Clang can now generate a PCH when using ``-fdelayed-template-parsing`` for
code with templates containing loop hint pragmas, OpenMP pragmas, and
``#pragma unused``.
-
+- Now diagnoses use of a member access expression or array subscript expression
+ within ``__builtin_offsetof`` and ``offsetof`` as being a Clang extension.
New Compiler Flags
------------------
@@ -684,6 +685,12 @@ C2x Feature Support
va_end(list);
}
+- Diagnose type definitions in the ``type`` argument of ``__builtin_offsetof``
+ as a conforming C extension according to
+ `WG14 N2350 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2350.htm>`_.
+ Also documents the builtin appropriately. Note, a type definition in C++
+ continues to be rejected.
+
C++ Language Changes in Clang
-----------------------------
- Implemented `DR692 <https://wg21.link/cwg692>`_, `DR1395 <https://wg21.link/cwg1395>`_,
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index f71bb401e9a10..d754fdb594c02 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -173,6 +173,7 @@ def DeleteNonVirtualDtor : DiagGroup<"delete-non-virtual-dtor",
DeleteAbstractNonVirtualDtor]>;
def AbstractFinalClass : DiagGroup<"abstract-final-class">;
def FinalDtorNonFinalClass : DiagGroup<"final-dtor-non-final-class">;
+def GNUOffsetofExtensions : DiagGroup<"gnu-offsetof-extensions">;
def CXX11CompatDeprecatedWritableStr :
DiagGroup<"c++11-compat-deprecated-writable-strings">;
@@ -1139,9 +1140,9 @@ def GNU : DiagGroup<"gnu", [GNUAlignofExpression, GNUAnonymousStruct,
GNUFlexibleArrayUnionMember, GNUFoldingConstant,
GNUImaginaryConstant, GNUIncludeNext,
GNULabelsAsValue, GNULineMarker, GNUNullPointerArithmetic,
- GNUPointerArith, RedeclaredClassMember,
- GNURedeclaredEnum, GNUStatementExpression,
- GNUStaticFloatInit,
+ GNUOffsetofExtensions, GNUPointerArith,
+ RedeclaredClassMember, GNURedeclaredEnum,
+ GNUStatementExpression, GNUStaticFloatInit,
GNUStringLiteralOperatorTemplate, GNUUnionCast,
GNUVariableSizedTypeNotAtEnd, ZeroLengthArray,
GNUZeroLineDirective,
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 79035b4de163f..6bc35fadbf7e0 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1607,6 +1607,11 @@ def err_import_in_wrong_fragment : Error<
def err_export_empty : Error<"export declaration cannot be empty">;
}
+def ext_offsetof_member_designator : Extension<
+ "using %select{a member access expression|an array subscript expression}0 "
+ "within '%select{__builtin_offsetof|offsetof}1' is a Clang extension">,
+ InGroup<GNUOffsetofExtensions>;
+
let CategoryName = "Generics Issue" in {
def err_objc_expected_type_parameter : Error<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 318bbd7e78d36..02a6c2c4214e8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1650,6 +1650,9 @@ def err_type_defined_in_condition : Error<
"%0 cannot be defined in a condition">;
def err_type_defined_in_enum : Error<
"%0 cannot be defined in an enumeration">;
+def ext_type_defined_in_offsetof : Extension<
+ "defining a type within '%select{__builtin_offsetof|offsetof}0' is a Clang "
+ "extension">, InGroup<GNUOffsetofExtensions>;
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
index 7a33532eec14e..6f9581b9ea1fc 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -62,6 +62,7 @@ class Parser : public CodeCompletionHandler {
friend class ColonProtectionRAIIObject;
friend class ParsingOpenMPDirectiveRAII;
friend class InMessageExpressionRAIIObject;
+ friend class OffsetOfStateRAIIObject;
friend class PoisonSEHIdentifiersRAIIObject;
friend class ObjCDeclContextSwitch;
friend class ParenBraceBracketBalancer;
@@ -248,6 +249,8 @@ class Parser : public CodeCompletionHandler {
/// function call.
bool CalledSignatureHelp = false;
+ Sema::OffsetOfKind OffsetOfState = Sema::OffsetOfKind::OOK_Outside;
+
/// 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
index 5ae609e600734..cb525c9d0edd6 100644
--- a/clang/include/clang/Parse/RAIIObjectsForParser.h
+++ b/clang/include/clang/Parse/RAIIObjectsForParser.h
@@ -341,6 +341,19 @@ namespace clang {
}
};
+ 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 {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 6292da7f04822..7a95989ee6d44 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3304,6 +3304,16 @@ class Sema final {
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,
+ };
+
DeclResult ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
SourceLocation KWLoc, CXXScopeSpec &SS,
IdentifierInfo *Name, SourceLocation NameLoc,
@@ -3314,7 +3324,7 @@ class Sema final {
SourceLocation ScopedEnumKWLoc,
bool ScopedEnumUsesClassTag, TypeResult UnderlyingType,
bool IsTypeSpecifier, bool IsTemplateParamOrArg,
- SkipBodyInfo *SkipBody = nullptr);
+ OffsetOfKind OOK, SkipBodyInfo *SkipBody = nullptr);
DeclResult ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc,
unsigned TagSpec, SourceLocation TagLoc,
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 56e8038d0d849..e6812ac72c885 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4965,14 +4965,15 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
bool IsDependent = false;
const char *PrevSpec = nullptr;
unsigned DiagID;
- Decl *TagDecl = Actions.ActOnTag(
- getCurScope(), DeclSpec::TST_enum, TUK, StartLoc, SS, Name, NameLoc,
- attrs, AS, DS.getModulePrivateSpecLoc(), TParams, Owned, IsDependent,
- ScopedEnumKWLoc, IsScopedUsingClassTag, BaseType,
- DSC == DeclSpecContext::DSC_type_specifier,
- DSC == DeclSpecContext::DSC_template_param ||
- DSC == DeclSpecContext::DSC_template_type_arg,
- &SkipBody).get();
+ Decl *TagDecl =
+ Actions.ActOnTag(getCurScope(), DeclSpec::TST_enum, TUK, StartLoc, SS,
+ Name, NameLoc, attrs, AS, DS.getModulePrivateSpecLoc(),
+ TParams, Owned, IsDependent, ScopedEnumKWLoc,
+ IsScopedUsingClassTag,
+ BaseType, DSC == DeclSpecContext::DSC_type_specifier,
+ DSC == DeclSpecContext::DSC_template_param ||
+ DSC == DeclSpecContext::DSC_template_type_arg,
+ OffsetOfState, &SkipBody).get();
if (SkipBody.ShouldSkip) {
assert(TUK == Sema::TUK_Definition && "can only skip a definition");
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 5d721f48140f7..227c1df2bdddd 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -2074,7 +2074,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
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.
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index b1bf988307b12..392ed29467a97 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -2592,10 +2592,21 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
}
case tok::kw___builtin_offsetof: {
SourceLocation TypeLoc = Tok.getLocation();
- TypeResult Ty = ParseTypeName();
- if (Ty.isInvalid()) {
- SkipUntil(tok::r_paren, StopAtSemi);
- return ExprError();
+ auto OOK = Sema::OffsetOfKind::OOK_Builtin;
+ if (Tok.getLocation().isMacroID()) {
+ StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
+ Tok.getLocation(), PP.getSourceManager(), getLangOpts());
+ if (MacroName == "offsetof")
+ OOK = Sema::OffsetOfKind::OOK_Macro;
+ }
+ TypeResult Ty;
+ {
+ OffsetOfStateRAIIObject InOffsetof(*this, OOK);
+ Ty = ParseTypeName();
+ if (Ty.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
}
if (ExpectAndConsume(tok::comma)) {
@@ -2618,6 +2629,12 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
Comps.back().U.IdentInfo = Tok.getIdentifierInfo();
Comps.back().LocStart = Comps.back().LocEnd = ConsumeToken();
+ enum class Kind { MemberAccess, ArraySubscript };
+ auto DiagExt = [&](SourceLocation Loc, Kind K) {
+ Diag(Loc, diag::ext_offsetof_member_designator)
+ << (K == Kind::ArraySubscript) << (OOK == Sema::OOK_Macro);
+ };
+
// FIXME: This loop leaks the index expressions on error.
while (true) {
if (Tok.is(tok::period)) {
@@ -2631,9 +2648,9 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
SkipUntil(tok::r_paren, StopAtSemi);
return ExprError();
}
+ DiagExt(Comps.back().LocStart, Kind::MemberAccess);
Comps.back().U.IdentInfo = Tok.getIdentifierInfo();
Comps.back().LocEnd = ConsumeToken();
-
} else if (Tok.is(tok::l_square)) {
if (CheckProhibitedCXX11Attribute())
return ExprError();
@@ -2649,6 +2666,7 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
SkipUntil(tok::r_paren, StopAtSemi);
return Res;
}
+ DiagExt(Comps.back().LocStart, Kind::ArraySubscript);
Comps.back().U.E = Res.get();
ST.consumeClose();
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index af20c70718cc4..c94b7ece01297 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16583,17 +16583,16 @@ static bool isAcceptableTagRedeclContext(Sema &S, DeclContext *OldDC,
///
/// \param SkipBody If non-null, will be set to indicate if the caller should
/// skip the definition of this tag and treat it as if it were a declaration.
-DeclResult Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
- SourceLocation KWLoc, CXXScopeSpec &SS,
- IdentifierInfo *Name, SourceLocation NameLoc,
- const ParsedAttributesView &Attrs, AccessSpecifier AS,
- SourceLocation ModulePrivateLoc,
- MultiTemplateParamsArg TemplateParameterLists,
- bool &OwnedDecl, bool &IsDependent,
- SourceLocation ScopedEnumKWLoc,
- bool ScopedEnumUsesClassTag,
- TypeResult UnderlyingType, bool IsTypeSpecifier,
- bool IsTemplateParamOrArg, SkipBodyInfo *SkipBody) {
+DeclResult
+Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
+ CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
+ const ParsedAttributesView &Attrs, AccessSpecifier AS,
+ SourceLocation ModulePrivateLoc,
+ MultiTemplateParamsArg TemplateParameterLists, bool &OwnedDecl,
+ bool &IsDependent, SourceLocation ScopedEnumKWLoc,
+ bool ScopedEnumUsesClassTag, TypeResult UnderlyingType,
+ bool IsTypeSpecifier, bool IsTemplateParamOrArg,
+ OffsetOfKind OOK, SkipBodyInfo *SkipBody) {
// If this is not a definition, it must have a name.
IdentifierInfo *OrigName = Name;
assert((Name != nullptr || TUK == TUK_Definition) &&
@@ -17366,10 +17365,14 @@ DeclResult Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
cast_or_null<RecordDecl>(PrevDecl));
}
+ if (OOK != OOK_Outside && TUK == TUK_Definition && !getLangOpts().CPlusPlus)
+ Diag(New->getLocation(), diag::ext_type_defined_in_offsetof)
+ << (OOK == OOK_Macro) << New->getSourceRange();
+
// 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/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index e7dddc3da6877..c8d384c643b3d 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -16972,15 +16972,15 @@ DeclResult Sema::ActOnTemplatedFriendTag(
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);
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 6b49c3f8d87ff..b40bd0978a8ab 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -10181,13 +10181,11 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation ExternLoc,
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).get();
+ 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).get();
assert(!IsDependent && "explicit instantiation of dependent name not yet handled");
if (!TagD)
diff --git a/clang/test/C/C2x/n2350.c b/clang/test/C/C2x/n2350.c
new file mode 100644
index 0000000000000..93ab5070b6c63
--- /dev/null
+++ b/clang/test/C/C2x/n2350.c
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=silent %s
+// RUN: %clang_cc1 -fsyntax-only -verify=cpp -x c++ %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -Wno-comment -verify %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -Wno-comment -std=c89 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -Wno-comment -std=c99 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -Wno-comment -std=c11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -Wno-comment -std=c17 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -Wno-comment -std=c2x -verify %s
+
+// silent-no-diagnostics
+
+// 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 // cpp-error {{'A' cannot be defined in a type specifier}} \
+ expected-warning {{defining a type within '__builtin_offsetof' is a Clang extension}}
+ {
+ int a;
+ struct B // expected-warning {{defining a type within '__builtin_offsetof' is a Clang extension}}
+ {
+ int c;
+ int d;
+ } x;
+ }, a);
+}
+
+int anonymous_struct(void) {
+ return __builtin_offsetof(struct // cpp-error-re {{'(unnamed struct at {{.*}})' cannot be defined in a type specifier}} \
+ expected-warning {{defining a type within '__builtin_offsetof' is a Clang extension}}
+ {
+ int a;
+ int b;
+ }, a);
+}
+
+int struct_in_second_param(void) {
+ struct A {
+ int a, b;
+ int x[20];
+ };
+ return __builtin_offsetof(struct A, x[sizeof(struct B{int a;})]); // cpp-error {{'B' cannot be defined in a type specifier}} \
+ expected-warning {{using an array subscript expression within '__builtin_offsetof' is a Clang extension}}
+}
+
+
+#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER)
+
+
+int macro(void) {
+ return offsetof(struct A // cpp-error {{'A' cannot be defined in a type specifier}} \
+ expected-warning 2 {{defining a type within 'offsetof' is a Clang extension}}
+ {
+ 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 // cpp-error {{'A' cannot be defined in a type specifier}}
+ {
+ int a;
+ int b;
+ }, a);
+}
diff --git a/clang/test/C/drs/dr4xx.c b/clang/test/C/drs/dr4xx.c
index 768897cd4f2bb..3ad9b17a83df1 100644
--- a/clang/test/C/drs/dr4xx.c
+++ b/clang/test/C/drs/dr4xx.c
@@ -337,12 +337,13 @@ void dr496(void) {
* because it references an array of another struct. Clang calculates the
* correct offset to each of those fields.
*/
- _Static_assert(__builtin_offsetof(struct B, a.n) == 0, "");
+ _Static_assert(__builtin_offsetof(struct B, a.n) == 0, ""); /* expected-warning {{using a member access expression within '__builtin_offsetof' is a Clang extension}} */
/* First int below is for 'n' and the second int is for 'a[0]'; this presumes
* there is no padding involved.
*/
- _Static_assert(__builtin_offsetof(struct B, a.a[1]) == sizeof(int) + sizeof(int), "");
-
+ _Static_assert(__builtin_offsetof(struct B, a.a[1]) == sizeof(int) + sizeof(int), ""); /* expected-warning {{using a member access expression within '__builtin_offsetof' is a Clang extension}}
+ expected-warning {{using an array subscript expression within '__builtin_offsetof' is a Clang extension}}
+ */
/* However, we do not support using the -> operator to access a member, even
* if that would be a valid expression. FIXME: GCC accepts this, perhaps we
* should as well.
@@ -352,11 +353,10 @@ void dr496(void) {
*/
/* 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 and
+ * Clang both support it as an extension.
*/
- (void)__builtin_offsetof(struct S { int a; }, a);
+ (void)__builtin_offsetof(struct S { int a; }, a); /* expected-warning{{defining a type within '__builtin_offsetof' is a Clang extension}} */
}
/* WG14 DR499: yes
diff --git a/clang/test/CXX/drs/dr4xx.cpp b/clang/test/CXX/drs/dr4xx.cpp
index 3617af8b683c0..476d80ef0720d 100644
--- a/clang/test/CXX/drs/dr4xx.cpp
+++ b/clang/test/CXX/drs/dr4xx.cpp
@@ -687,9 +687,9 @@ namespace dr447 { // dr447: yes
U<__builtin_offsetof(A, n)>::type a;
U<__builtin_offsetof(T, n)>::type b; // expected-error +{{}} expected-warning 0+{{}}
// as an extension, we allow the member-designator to include array indices
- g(__builtin_offsetof(A, a[0])).h<int>();
- g(__builtin_offsetof(A, a[N])).h<int>();
- U<__builtin_offsetof(A, a[0])>::type c;
+ g(__builtin_offsetof(A, a[0])).h<int>(); // expected-error {{using an array subscript expression within '__builtin_offsetof' is a Clang extension}}
+ g(__builtin_offsetof(A, a[N])).h<int>(); // expected-error {{using an array subscript expression within '__builtin_offsetof' is a Clang extension}}
+ U<__builtin_offsetof(A, a[0])>::type c; // expected-error {{using an array subscript expression within '__builtin_offsetof' is a Clang extension}}
U<__builtin_offsetof(A, a[N])>::type d; // expected-error +{{}} expected-warning 0+{{}}
}
}
diff --git a/clang/test/CodeGen/offsetof.c b/clang/test/CodeGen/offsetof.c
index c279e2282e460..13b34ea62265a 100644
--- a/clang/test/CodeGen/offsetof.c
+++ b/clang/test/CodeGen/offsetof.c
@@ -10,3 +10,7 @@ int test(int len) {
return __builtin_offsetof(struct sockaddr_un, sun_path[len+1]);
}
+// Ensure we can form the offset to a structure defined in the first argument
+// without crashing or asserting on an invalid declaration (because the
+// declaration is actually valid).
+void c() { __builtin_offsetof(struct {int b;}, b); }
diff --git a/clang/test/Parser/declarators.c b/clang/test/Parser/declarators.c
index 464fafeaa0d27..3af09817e6b63 100644
--- a/clang/test/Parser/declarators.c
+++ b/clang/test/Parser/declarators.c
@@ -80,10 +80,6 @@ struct test9 {
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
index 3a5ddc45cf15c..8fd9ad608e949 100644
--- a/clang/test/Sema/offsetof.c
+++ b/clang/test/Sema/offsetof.c
@@ -5,35 +5,38 @@
typedef struct P { int i; float f; } PT;
struct external_sun3_core
{
- unsigned c_regs;
+ unsigned c_regs;
PT X[100];
-
+
};
+// Ensure the builtin works as a constant expression
+int i = offsetof(PT, f);
+
void swap(void)
{
int x;
x = offsetof(struct external_sun3_core, c_regs);
x = __builtin_offsetof(struct external_sun3_core, X[42].f);
-
+
x = __builtin_offsetof(struct external_sun3_core, X[42].f2); // expected-error {{no member named 'f2'}}
x = __builtin_offsetof(int, X[42].f2); // expected-error {{offsetof requires struct}}
-
+
int a[__builtin_offsetof(struct external_sun3_core, X) == 4 ? 1 : -1];
int b[__builtin_offsetof(struct external_sun3_core, X[42]) == 340 ? 1 : -1];
int c[__builtin_offsetof(struct external_sun3_core, X[42].f2) == 344 ? 1 : -1]; // expected-error {{no member named 'f2'}}
-}
+}
extern int f(void);
-struct s1 { int a; };
+struct s1 { int a; };
int v1 = offsetof (struct s1, a) == 0 ? 0 : f();
-struct s2 { int a; };
+struct s2 { int a; };
int v2 = (int)(&((struct s2 *) 0)->a) == 0 ? 0 : f();
-struct s3 { int a; };
+struct s3 { int a; };
int v3 = __builtin_offsetof(struct s3, a) == 0 ? 0 : f();
// PR3396
diff --git a/clang/test/SemaCXX/offsetof.cpp b/clang/test/SemaCXX/offsetof.cpp
index c4b288aa05d43..39ea3804dce31 100644
--- a/clang/test/SemaCXX/offsetof.cpp
+++ b/clang/test/SemaCXX/offsetof.cpp
@@ -83,3 +83,18 @@ struct Derived : virtual Base {
expected-error {{invalid application of 'offsetof' to a field of a virtual base}}
};
}
+
+int test_definition(void) {
+ return __builtin_offsetof(struct A // expected-error {{'A' cannot be defined in a type specifier}}
+ {
+ 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);
+}
More information about the cfe-commits
mailing list