https://github.com/nickdesaulniers updated https://github.com/llvm/llvm-project/pull/231696
>From cf873fa4f74fc4a4f0f8516bdd72b9a115301f98 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers <[email protected]> Date: Mon, 2 Nov 2026 15:31:13 -0800 Subject: [PATCH] [Clang][Builtins] Support typedefs and struct tags in LibBuiltin prototypes Adding more checks for -Wfortify-source when signatures have platform specific types requires having: 1. unspecified Prototype fields for LibBuiltin defs. 2. boilerplate type checks on the parameters in `Sema::checkFortifiedBuiltinMemoryFunction`. This is not ideal. Instead, teach ClangBuiltinsEmitter, ASTContext::DecodeTypeFromStr, and SemaDecl to support struct tags (`u<len><name>`), `*_t` typedef names (`t<len><name>`), `ssize_t` (`Sz`), and `WindowsPrototype` (`|`). Adds an optional WindowsPrototype field to LibBuiltin which is the preferred signature when the OS target is Windows. This allows us to specify Prototypes for umask, recv, recvfrom, poll, and ppoll in Builtins.td and drop their manual signature checks in SemaChecking. Link: #142230 Assisted-by: Gemini --- clang/include/clang/Basic/Builtins.def | 4 ++ clang/include/clang/Basic/Builtins.td | 33 ++++++------- clang/include/clang/Basic/BuiltinsBase.td | 1 + clang/lib/AST/ASTContext.cpp | 47 +++++++++++++++++-- clang/lib/Sema/SemaChecking.cpp | 38 +-------------- clang/lib/Sema/SemaDecl.cpp | 22 +++++++-- clang/test/CodeGen/umask-inline-builtin.c | 2 + .../warn-fortify-source-recv-not-builtin.c | 22 +++++++-- .../warn-fortify-source-umask-not-builtin.c | 2 + .../Sema/warn-fortify-source-umask-redecl.c | 14 +++--- clang/test/Sema/warn-fortify-source.c | 26 ++++++---- .../target-builtins-prototype-parser.td | 22 +++++++++ clang/utils/TableGen/ClangBuiltinsEmitter.cpp | 41 ++++++++++++---- 13 files changed, 180 insertions(+), 94 deletions(-) diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index 77fca0a7ad8c1d..65ef856b627b1a 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -22,6 +22,7 @@ // f -> float // d -> double // z -> size_t +// Sz -> ssize_t // w -> wchar_t // F -> constant CFString // G -> id @@ -44,6 +45,9 @@ // SJ -> sigjmp_buf // K -> ucontext_t // p -> pid_t +// u -> struct, followed by the byte length of the tag name and the tag name. +// t -> typedef, followed by the byte length of the name and the name. +// | -> separates the default prototype from the Windows prototype. // . -> "...". This may only occur at the end of the function list. // // Types may be prefixed with the following modifiers: diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index d04fd899eb63a6..c90d95d08c47be 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -3914,44 +3914,39 @@ def VFork : LibBuiltin<"unistd.h"> { def Umask : LibBuiltin<"sys/stat.h"> { let Spellings = ["umask"]; - let Attributes = [IgnoreSignature, NoThrow]; - let Prototype = ""; // mode_t(mode_t); mode_t is target-specific + let Attributes = [NoThrow]; + let Prototype = "mode_t(mode_t)"; } // POSIX sys/socket.h def Recv : LibBuiltin<"sys/socket.h"> { let Spellings = ["recv"]; - let Attributes = [IgnoreSignature]; - // ssize_t(int, void*, size_t, int); ssize_t is target-specific - let Prototype = ""; + let Prototype = "ssize_t(int, void*, size_t, int)"; + let WindowsPrototype = "int(size_t, char*, int, int)"; } def RecvFrom : LibBuiltin<"sys/socket.h"> { let Spellings = ["recvfrom"]; - let Attributes = [IgnoreSignature]; - // ssize_t(int, void*, size_t, int, struct sockaddr*, socklen_t*); - // types are target-specific - let Prototype = ""; + let Prototype = "ssize_t(int, void*, size_t, int, struct sockaddr*, socklen_t*)"; + let WindowsPrototype = "int(size_t, char*, int, int, struct sockaddr*, int*)"; } // POSIX poll.h def Poll : LibBuiltin<"poll.h"> { let Spellings = ["poll"]; - let Attributes = [IgnoreSignature]; - // int(struct pollfd*, nfds_t, int); struct pollfd and nfds_t are - // target-specific - let Prototype = ""; + let Prototype = "int(struct pollfd*, nfds_t, int)"; } def PPoll : LibBuiltin<"poll.h"> { - let Spellings = ["ppoll", "ppoll64"]; - let Attributes = [IgnoreSignature]; - // int(struct pollfd*, nfds_t, const struct timespec*, const sigset_t*); - // struct pollfd, nfds_t, struct timespec, and sigset_t/sigset64_t are - // target-specific - let Prototype = ""; + let Spellings = ["ppoll"]; + let Prototype = "int(struct pollfd*, nfds_t, struct timespec const*, sigset_t const*)"; +} + +def PPoll64 : LibBuiltin<"poll.h"> { + let Spellings = ["ppoll64"]; + let Prototype = "int(struct pollfd*, nfds_t, struct timespec const*, sigset64_t const*)"; } // POSIX pthread.h diff --git a/clang/include/clang/Basic/BuiltinsBase.td b/clang/include/clang/Basic/BuiltinsBase.td index 1f34374148363e..21e6e3de3b5df9 100644 --- a/clang/include/clang/Basic/BuiltinsBase.td +++ b/clang/include/clang/Basic/BuiltinsBase.td @@ -180,6 +180,7 @@ class AtomicBuiltin : Builtin; class LibBuiltin<string header, string languages = "ALL_LANGUAGES"> : Builtin { string Header = header; string Languages = languages; + string WindowsPrototype; bit AddBuiltinPrefixedAlias = 0; bit OnlyBuiltinPrefixedAliasIsConstexpr = 0; } diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 01d71388cbc0e3..ec14f3ec6d94b6 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12861,9 +12861,9 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context, assert(HowLong == 0 && !Signed && !Unsigned && "Bad modifiers for 'b'!"); Type = Context.BoolTy; break; - case 'z': // size_t. - assert(HowLong == 0 && !Signed && !Unsigned && "Bad modifiers for 'z'!"); - Type = Context.getSizeType(); + case 'z': // size_t or ssize_t. + assert(HowLong == 0 && !Unsigned && "Bad modifiers for 'z'!"); + Type = Signed ? Context.getSignedSizeType() : Context.getSizeType(); break; case 'w': // wchar_t. assert(HowLong == 0 && !Signed && !Unsigned && "Bad modifiers for 'w'!"); @@ -13014,6 +13014,39 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context, case 'm': Type = Context.MFloat8Ty; break; + case 'u': + case 't': { + assert(HowLong == 0 && !Signed && !Unsigned && + "Bad modifiers for 'u' or 't'!"); + bool IsStruct = Str[-1] == 'u'; + char *End; + unsigned Len = strtoul(Str, &End, 10); + assert(End != Str && "Missing identifier length"); + StringRef Name(End, Len); + Str = End + Len; + for (NamedDecl *ND : + Context.getTranslationUnitDecl()->lookup(&Context.Idents.get(Name))) { + if (ND->isInvalidDecl()) + continue; + if (IsStruct) { + if (const auto *RD = dyn_cast<RecordDecl>(ND); RD && RD->isStruct()) { + Type = Context.getCanonicalTagType(RD); + break; + } + } else if (const auto *TD = dyn_cast<TypedefNameDecl>(ND); + TD && TD->getUnderlyingType()->isObjectType() && + !TD->getUnderlyingType()->isArrayType()) { + Type = Context.getTypedefType(ElaboratedTypeKeyword::None, + /*Qualifier=*/std::nullopt, TD); + break; + } + } + if (Type.isNull()) { + Error = ASTContext::GE_Missing_type; + return {}; + } + break; + } } // If there are modifiers and if we're allowed to parse them, go for it. @@ -13080,6 +13113,10 @@ QualType ASTContext::GetBuiltinType(unsigned Id, Error = GE_Missing_type; return {}; } + if (Target->getTriple().isOSWindows()) { + if (const char *WinTypeStr = strchr(TypeStr, '|')) + TypeStr = WinTypeStr + 1; + } SmallVector<QualType, 8> ArgTypes; @@ -13092,7 +13129,7 @@ QualType ASTContext::GetBuiltinType(unsigned Id, assert(!RequiresICE && "Result of intrinsic cannot be required to be an ICE"); - while (TypeStr[0] && TypeStr[0] != '.') { + while (TypeStr[0] && TypeStr[0] != '.' && TypeStr[0] != '|') { QualType Ty = DecodeTypeFromStr(TypeStr, *this, Error, RequiresICE, true); if (Error != GE_None) return {}; @@ -13112,7 +13149,7 @@ QualType ASTContext::GetBuiltinType(unsigned Id, if (Id == Builtin::BI__GetExceptionInfo) return {}; - assert((TypeStr[0] != '.' || TypeStr[1] == 0) && + assert((TypeStr[0] != '.' || TypeStr[1] == 0 || TypeStr[1] == '|') && "'.' should only occur at end of builtin type list!"); bool Variadic = (TypeStr[0] == '.'); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 406f03a17f7ebe..68c75a018642db 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1498,11 +1498,6 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, case Builtin::BIrecv: case Builtin::BIrecvfrom: { - unsigned ExpectedArgs = BuiltinID == Builtin::BIrecv ? 4 : 6; - if (TheCall->getNumArgs() != ExpectedArgs || - !TheCall->getArg(1)->getType()->isPointerType() || - !TheCall->getArg(2)->getType()->isIntegerType()) - return; DiagID = diag::warn_fortify_source_size_mismatch; AccessSize = Checker.ComputeExplicitObjectSizeArgument(2); BufferSize = Checker.ComputeSizeArgument(1); @@ -1512,28 +1507,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, case Builtin::BIpoll: case Builtin::BIppoll: case Builtin::BIppoll64: { - unsigned ExpectedArgs = BuiltinID == Builtin::BIpoll ? 3 : 4; - if (TheCall->getNumArgs() != ExpectedArgs || - !TheCall->getArg(1)->getType()->isIntegerType()) - return; QualType PointeeTy = TheCall->getArg(0)->getType()->getPointeeType(); - if (PointeeTy.isNull()) - return; - const RecordDecl *RD = PointeeTy->getAsRecordDecl(); - if (!RD || !RD->getIdentifier() || RD->getName() != "pollfd") - return; - if (BuiltinID == Builtin::BIpoll) { - if (!TheCall->getArg(2)->getType()->isIntegerType()) - return; - } else { - QualType TmoPointeeTy = TheCall->getArg(2)->getType()->getPointeeType(); - if (TmoPointeeTy.isNull()) - return; - const RecordDecl *TmoRD = TmoPointeeTy->getAsRecordDecl(); - if (!TmoRD || !TmoRD->getIdentifier() || TmoRD->getName() != "timespec" || - !TheCall->getArg(3)->getType()->isPointerType()) - return; - } std::optional<CharUnits> ElemSize = Context.getTypeSizeInCharsIfKnown(PointeeTy); if (!ElemSize) @@ -1689,21 +1663,13 @@ void Sema::checkFortifiedLibcArgument(FunctionDecl *FD, CallExpr *TheCall) { return; // Recognize the libc function by builtin identity rather than by name and - // system-header origin. umask is a LibBuiltin marked IgnoreSignature, so the - // builtin id is attached to any file-scope, C-linkage declaration of umask - // regardless of the libc's mode_t spelling -- including a hand-written - // forward declaration without <sys/stat.h>. A static/local lookalike or a - // C++ (non-extern-"C") declaration keeps a zero builtin id and is ignored. - if (FD->getBuiltinID() != Builtin::BIumask) + // system-header origin. + if (FD->getBuiltinID() != Builtin::BIumask || TheCall->getNumArgs() != 1) return; // umask(mode_t): warn when the constant-evaluated argument has bits set // outside the file-permission mask (0777). Those bits are ignored. - if (TheCall->getNumArgs() != 1) - return; Expr *Arg = TheCall->getArg(0); - if (!Arg->getType()->isIntegerType()) - return; Expr::EvalResult R; if (!Arg->EvaluateAsInt(R, getASTContext())) return; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index f3d38a555a524a..9500385b543c0f 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2473,6 +2473,10 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type, NamedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID, Scope *S, bool ForRedeclaration, SourceLocation Loc) { + if (ForRedeclaration && Context.BuiltinInfo.isPredefinedLibFunction(ID) && + strpbrk(Context.BuiltinInfo.getTypeString(ID), "|ut")) + return nullptr; + LookupNecessaryTypesForBuiltin(S, ID); ASTContext::GetBuiltinTypeError Error; @@ -11275,10 +11279,20 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, LookupNecessaryTypesForBuiltin(S, BuiltinID); QualType BuiltinType = Context.GetBuiltinType(BuiltinID, Error); - if (!Error && !BuiltinType.isNull() && - Context.hasSameFunctionTypeIgnoringExceptionSpec( - NewFD->getType(), BuiltinType)) - NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID)); + if (!Error && !BuiltinType.isNull() && NewFD->hasPrototype()) { + BuiltinType = QualType( + Context.adjustFunctionType( + BuiltinType->castAs<FunctionType>(), + NewFD->getType()->castAs<FunctionType>()->getExtInfo()), + 0); + if (getLangOpts().CPlusPlus + ? Context.hasSameFunctionTypeIgnoringExceptionSpec( + NewFD->getType(), BuiltinType) + : Context.typesAreCompatible(NewFD->getType(), + BuiltinType)) + NewFD->addAttr( + BuiltinAttr::CreateImplicit(Context, BuiltinID)); + } } } } else if (InStdNamespace && NewFD->isInStdNamespace() && diff --git a/clang/test/CodeGen/umask-inline-builtin.c b/clang/test/CodeGen/umask-inline-builtin.c index e0ab7b35cbcfb4..119726d15fc62d 100644 --- a/clang/test/CodeGen/umask-inline-builtin.c +++ b/clang/test/CodeGen/umask-inline-builtin.c @@ -1,6 +1,8 @@ // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --filter "call " --include-generated-funcs --version 6 // RUN: %clang_cc1 -triple x86_64 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s +typedef int mode_t; + extern inline __attribute__((always_inline)) __attribute__((gnu_inline)) __attribute__((preserve_most)) int umask(int m) { return m; diff --git a/clang/test/Sema/warn-fortify-source-recv-not-builtin.c b/clang/test/Sema/warn-fortify-source-recv-not-builtin.c index 0af79e0cea86f4..ee88f16b42aed3 100644 --- a/clang/test/Sema/warn-fortify-source-recv-not-builtin.c +++ b/clang/test/Sema/warn-fortify-source-recv-not-builtin.c @@ -19,19 +19,31 @@ struct sockaddr; #ifdef MISMATCHED_SIG #ifdef __cplusplus extern "C" { -#endif -int recv(int fd, void *buf, size_t len); +int recv(size_t fd, char *buf, int len, int flags); int recvfrom(int fd, void *buf, size_t len, int flags); -#ifdef __cplusplus } -#endif void call_mismatched_recv(int fd) { char buf[10]; - (void)recv(fd, buf, 20); + (void)recv(fd, buf, 20, 0); (void)recvfrom(fd, buf, 20, 0); } #else +int recv(int fd, void *buf, size_t len); +typedef union { + int *ip; + long *lp; +} bad_sockaddr_tu __attribute__((__transparent_union__)); +ssize_t recvfrom(int fd, void *buf, size_t len, int flags, + bad_sockaddr_tu addr, socklen_t *addrlen); + +void call_mismatched_recv(int fd, int *p, socklen_t *len) { + char buf[10]; + (void)recv(fd, buf, 20); + (void)recvfrom(fd, buf, 20, 0, p, len); +} +#endif +#else static ssize_t recv(int fd, void *buf, size_t len, int flags) { (void)fd; (void)buf; diff --git a/clang/test/Sema/warn-fortify-source-umask-not-builtin.c b/clang/test/Sema/warn-fortify-source-umask-not-builtin.c index 57e3706a5f1b1e..1a41a58a46573f 100644 --- a/clang/test/Sema/warn-fortify-source-umask-not-builtin.c +++ b/clang/test/Sema/warn-fortify-source-umask-not-builtin.c @@ -7,6 +7,8 @@ // * a file-local umask with internal linkage; // * in C++, a umask without C language linkage (e.g. a user's own overload). +typedef int mode_t; + static int umask(int m) { return m; } void call_static_umask(void) { diff --git a/clang/test/Sema/warn-fortify-source-umask-redecl.c b/clang/test/Sema/warn-fortify-source-umask-redecl.c index c125a6a17936e4..d4f9d0cd28fbae 100644 --- a/clang/test/Sema/warn-fortify-source-umask-redecl.c +++ b/clang/test/Sema/warn-fortify-source-umask-redecl.c @@ -1,16 +1,14 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -verify +// expected-no-diagnostics -// A user declaration of umask whose integer type differs from the historical -// unsigned-int spelling -- here a plain int, as in a K&R-era or sloppy -// prototype -- is still recognized as the libc umask and checked. Because -// umask is a LibBuiltin with no prototype to match, the declaration is not -// flagged as an incompatible library redeclaration (it would have been when -// the builtin carried a fixed unsigned-int prototype), and the fortify check -// still fires on an out-of-range constant. +// A user declaration of umask whose signature does not match mode_t(mode_t) is +// neither flagged as an incompatible library redeclaration nor recognized as +// the libc umask builtin. +typedef unsigned int mode_t; extern int umask(int); void call_user_umask(void) { umask(0644); - umask(0xFFFF); // expected-warning {{'umask' argument sets non-file-permission bits (0177000); those bits are ignored}} + umask(0xFFFF); } diff --git a/clang/test/Sema/warn-fortify-source.c b/clang/test/Sema/warn-fortify-source.c index 3e61e732682064..d9d930f1de5f5b 100644 --- a/clang/test/Sema/warn-fortify-source.c +++ b/clang/test/Sema/warn-fortify-source.c @@ -1,14 +1,14 @@ // RUN: %clang_cc1 -triple x86_64-apple-macosx10.14.0 %s -verify -// RUN: %clang_cc1 -triple x86_64-apple-macosx10.14.0 %s -verify -DUSE_BUILTINS +// RUN: %clang_cc1 -triple x86_64-w64-windows-gnu %s -verify -DUSE_BUILTINS // RUN: %clang_cc1 -xc++ -triple x86_64-apple-macosx10.14.0 %s -verify -// RUN: %clang_cc1 -xc++ -triple x86_64-apple-macosx10.14.0 %s -verify -DUSE_BUILTINS +// RUN: %clang_cc1 -xc++ -triple x86_64-w64-windows-gnu %s -verify -DUSE_BUILTINS // RUN: %clang_cc1 -triple x86_64-apple-macosx10.14.0 %s -verify -fexperimental-new-constant-interpreter -// RUN: %clang_cc1 -triple x86_64-apple-macosx10.14.0 %s -verify -DUSE_BUILTINS -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -triple x86_64-w64-windows-gnu %s -verify -DUSE_BUILTINS -fexperimental-new-constant-interpreter // RUN: %clang_cc1 -xc++ -triple x86_64-apple-macosx10.14.0 %s -verify -fexperimental-new-constant-interpreter -// RUN: %clang_cc1 -xc++ -triple x86_64-apple-macosx10.14.0 %s -verify -DUSE_BUILTINS -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -xc++ -triple x86_64-w64-windows-gnu %s -verify -DUSE_BUILTINS -fexperimental-new-constant-interpreter -typedef unsigned long size_t; +typedef __SIZE_TYPE__ size_t; typedef long ssize_t; typedef unsigned int socklen_t; struct sockaddr; @@ -33,14 +33,24 @@ extern int vsprintf(char *str, const char *format, __builtin_va_list ap); #define memcpy(x,y,z) __builtin_memcpy(x,y,z) #define sprintf(x, ...) __builtin_sprintf(x, __VA_ARGS__) #define vsprintf(x, y, z) __builtin_vsprintf(x, y, z) -// Also test the Windows winsock2.h signature where len is a signed int. -int recv(int, char *, int, int); -int recvfrom(int, char *, int, int, struct sockaddr *, int *); +// Also test the Windows winsock2.h signature where SOCKET is UINT_PTR (size_t) +// and len/fromlen use int. +int recv(size_t, char *, int, int); +int recvfrom(size_t, char *, int, int, struct sockaddr *, int *); typedef unsigned int nfds_t; #else void *memcpy(void *dst, const void *src, size_t c); ssize_t recv(int, void *, size_t, int); +#ifndef __cplusplus +struct sockaddr_in; +typedef union { + struct sockaddr_in *__sockaddr_in__; + struct sockaddr *__sockaddr__; +} __SOCKADDR_ARG __attribute__((__transparent_union__)); +ssize_t recvfrom(int, void *, size_t, int, __SOCKADDR_ARG, socklen_t *); +#else ssize_t recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *); +#endif typedef unsigned long nfds_t; #endif int poll(struct pollfd *, nfds_t, int); diff --git a/clang/test/TableGen/target-builtins-prototype-parser.td b/clang/test/TableGen/target-builtins-prototype-parser.td index 09189333f584d7..4a16548cfa5e67 100644 --- a/clang/test/TableGen/target-builtins-prototype-parser.td +++ b/clang/test/TableGen/target-builtins-prototype-parser.td @@ -6,6 +6,7 @@ // RUN: not clang-tblgen -I %p/../../include/ %s --gen-clang-builtins -DERROR_EXPECTED_B 2>&1 | FileCheck %s --check-prefix=ERROR_EXPECTED_B // RUN: not clang-tblgen -I %p/../../include/ %s --gen-clang-builtins -DERROR_EXPECTED_C 2>&1 | FileCheck %s --check-prefix=ERROR_EXPECTED_C // RUN: not clang-tblgen -I %p/../../include/ %s --gen-clang-builtins -DERROR_EXPECTED_D 2>&1 | FileCheck %s --check-prefix=ERROR_EXPECTED_D +// RUN: not clang-tblgen -I %p/../../include/ %s --gen-clang-builtins -DERROR_EXPECTED_E 2>&1 | FileCheck %s --check-prefix=ERROR_EXPECTED_E include "clang/Basic/BuiltinsBase.td" @@ -111,6 +112,19 @@ def : Builtin { let Spellings = ["__builtin_17"]; } +def : LibBuiltin<"poll.h"> { +// CHECK: Builtin::Info{{.*}} __builtin_18 {{.*}} /* iu6pollfd*t6nfds_tu8timespecC*t8sigset_tC* */ + let Prototype = "int(struct pollfd *, nfds_t, struct timespec const *, sigset_t const *)"; + let Spellings = ["__builtin_18"]; +} + +def : LibBuiltin<"sys/socket.h"> { +// CHECK: Builtin::Info{{.*}} __builtin_19 {{.*}} /* Sziv*ziu8sockaddr*t9socklen_t*|izc*iiu8sockaddr*i* */ + let Prototype = "ssize_t(int, void*, size_t, int, struct sockaddr*, socklen_t*)"; + let WindowsPrototype = "int(size_t, char*, int, int, struct sockaddr*, int*)"; + let Spellings = ["__builtin_19"]; +} + #ifdef ERROR_EXPECTED_LANES def : Builtin { // ERROR_EXPECTED_LANES: :[[# @LINE + 1]]:7: error: Expected number of lanes after '_ExtVector<' @@ -166,3 +180,11 @@ def : Builtin { let Spellings = ["__builtin_test_use_clang_extended_vectors"]; } #endif + +#ifdef ERROR_EXPECTED_E +def : Builtin { +// ERROR_EXPECTED_E: :[[# @LINE + 1]]:7: error: Unknown Type: uint128_t + let Prototype = "uint128_t(void)"; + let Spellings = ["__builtin_test_unknown_typedef_in_builtin"]; +} +#endif diff --git a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp index e5300ff7ffbdc6..af1db606596d92 100644 --- a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp +++ b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp @@ -156,27 +156,36 @@ class PrototypeParser { public: PrototypeParser(StringRef Substitution, const Record *Builtin) : Loc(Builtin->getFieldLoc("Prototype")), Substitution(Substitution), - EnableOpenCLLong(Builtin->getValueAsBit("EnableOpenCLLong")) { + EnableOpenCLLong(Builtin->getValueAsBit("EnableOpenCLLong")), + IsLibBuiltin(Builtin->isSubClassOf("LibBuiltin")) { ParsePrototype(Builtin->getValueAsString("Prototype")); + if (std::optional<StringRef> WinProto = + Builtin->getValueAsOptionalString("WindowsPrototype")) { + Loc = Builtin->getFieldLoc("WindowsPrototype"); + if (Type.empty()) + PrintFatalError(Loc, "WindowsPrototype requires a non-empty Prototype"); + Type += '|'; + ParsePrototype(*WinProto); + } } std::string takeTypeString() && { return std::move(Type); } private: + static bool isValidIdentifier(StringRef S) { + return !S.empty() && !isDigit(S[0]) && llvm::all_of(S, [](char C) { + return isLower(C) || isDigit(C) || C == '_'; + }); + } + void ParsePrototype(StringRef Prototype) { Prototype = Prototype.trim(); // Some builtins don't have an expressible prototype, simply emit an empty // string for them. - if (Prototype.empty()) { - Type = ""; + if (Prototype.empty()) return; - } - ParseTypes(Prototype); - } - - void ParseTypes(StringRef &Prototype) { auto ReturnType = Prototype.take_until([](char c) { return c == '('; }); ParseType(ReturnType); Prototype = Prototype.drop_front(ReturnType.size() + 1); @@ -295,6 +304,11 @@ class PrototypeParser { } else if (T.consume_back(")")) { ParseType(T); Type += "&"; + } else if (IsLibBuiltin && T.consume_front("struct ")) { + T = T.trim(); + if (!isValidIdentifier(T)) + PrintFatalError(Loc, "Expected struct name after 'struct'"); + Type += "u" + std::to_string(T.size()) + T.str(); } else if (EnableOpenCLLong && T.consume_front("long long")) { Type += "O"; ParseType(T); @@ -376,6 +390,7 @@ class PrototypeParser { .Case("short", "s") .Case("sigjmp_buf", "SJ") .Case("size_t", "z") + .Case("ssize_t", "Sz") .Case("std::meta::info", "r") .Case("ucontext_t", "K") .Case("uint8_t", "UBi") @@ -394,8 +409,15 @@ class PrototypeParser { .Case("wchar_t", "w") .Case("...", ".") .Default("error"); - if (ReturnTypeVal == "error") + if (ReturnTypeVal == "error") { + // For LibBuiltins, allow unrecognized `*_t` typedef names (e.g. + // nfds_t, socklen_t) to be resolved by translation-unit lookup. + if (IsLibBuiltin && T.ends_with("_t") && isValidIdentifier(T)) { + Type += "t" + std::to_string(T.size()) + T.str(); + return; + } PrintFatalError(Loc, "Unknown Type: " + T); + } Type += ReturnTypeVal; } } @@ -403,6 +425,7 @@ class PrototypeParser { SMLoc Loc; StringRef Substitution; bool EnableOpenCLLong; + bool IsLibBuiltin; std::string Type; }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
