forked from OSchip/llvm-project
Recognize setjmp and friends as builtins even if jmp_buf is not declared yet.
This happens in glibc's headers. It's important that we recognize these functions so that we can mark them as returns_twice. Differential Revision: https://reviews.llvm.org/D88518
This commit is contained in:
parent
bf434a5f17
commit
1c604a9f5f
|
@ -75,6 +75,9 @@
|
|||
// U -> pure
|
||||
// c -> const
|
||||
// t -> signature is meaningless, use custom typechecking
|
||||
// T -> type is not important to semantic analysis and codegen; recognize as
|
||||
// builtin even if type doesn't match signature, and don't warn if we
|
||||
// can't be sure the type is right
|
||||
// F -> this is a libc/libm function with a '__builtin_' prefix added.
|
||||
// f -> this is a libc/libm function without the '__builtin_' prefix. It can
|
||||
// be followed by ':headername:' to state which header this function
|
||||
|
@ -896,7 +899,7 @@ LANGBUILTIN(__va_start, "vc**.", "nt", ALL_MS_LANGUAGES)
|
|||
LANGBUILTIN(__fastfail, "vUi", "nr", ALL_MS_LANGUAGES)
|
||||
|
||||
// Microsoft library builtins.
|
||||
LIBBUILTIN(_setjmpex, "iJ", "fj", "setjmpex.h", ALL_MS_LANGUAGES)
|
||||
LIBBUILTIN(_setjmpex, "iJ", "fjT", "setjmpex.h", ALL_MS_LANGUAGES)
|
||||
|
||||
// C99 library functions
|
||||
// C99 stdarg.h
|
||||
|
@ -990,8 +993,8 @@ LIBBUILTIN(wmemmove,"w*w*wC*z", "f", "wchar.h", ALL_LANGUAGES)
|
|||
// In some systems setjmp is a macro that expands to _setjmp. We undefine
|
||||
// it here to avoid having two identical LIBBUILTIN entries.
|
||||
#undef setjmp
|
||||
LIBBUILTIN(setjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(longjmp, "vJi", "fr", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(setjmp, "iJ", "fjT", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(longjmp, "vJi", "frT", "setjmp.h", ALL_LANGUAGES)
|
||||
|
||||
// Non-C library functions, active in GNU mode only.
|
||||
// Functions with (returns_twice) attribute (marked as "j") are still active in
|
||||
|
@ -1018,21 +1021,21 @@ LIBBUILTIN(strcasecmp, "icC*cC*", "f", "strings.h", ALL_GNU_LANGUAGES)
|
|||
LIBBUILTIN(strncasecmp, "icC*cC*z", "f", "strings.h", ALL_GNU_LANGUAGES)
|
||||
// POSIX unistd.h
|
||||
LIBBUILTIN(_exit, "vi", "fr", "unistd.h", ALL_GNU_LANGUAGES)
|
||||
LIBBUILTIN(vfork, "p", "fj", "unistd.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(vfork, "p", "fjT", "unistd.h", ALL_LANGUAGES)
|
||||
// POSIX pthread.h
|
||||
// FIXME: Should specify argument types.
|
||||
LIBBUILTIN(pthread_create, "", "fC<2,3>", "pthread.h", ALL_GNU_LANGUAGES)
|
||||
|
||||
// POSIX setjmp.h
|
||||
|
||||
LIBBUILTIN(_setjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(__sigsetjmp, "iSJi", "fj", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(sigsetjmp, "iSJi", "fj", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(savectx, "iJ", "fj", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(getcontext, "iK*", "fj", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(_setjmp, "iJ", "fjT", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(__sigsetjmp, "iSJi", "fjT", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(sigsetjmp, "iSJi", "fjT", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(savectx, "iJ", "fjT", "setjmp.h", ALL_LANGUAGES)
|
||||
LIBBUILTIN(getcontext, "iK*", "fjT", "setjmp.h", ALL_LANGUAGES)
|
||||
|
||||
LIBBUILTIN(_longjmp, "vJi", "fr", "setjmp.h", ALL_GNU_LANGUAGES)
|
||||
LIBBUILTIN(siglongjmp, "vSJi", "fr", "setjmp.h", ALL_GNU_LANGUAGES)
|
||||
LIBBUILTIN(_longjmp, "vJi", "frT", "setjmp.h", ALL_GNU_LANGUAGES)
|
||||
LIBBUILTIN(siglongjmp, "vSJi", "frT", "setjmp.h", ALL_GNU_LANGUAGES)
|
||||
// non-standard but very common
|
||||
LIBBUILTIN(strlcpy, "zc*cC*z", "f", "string.h", ALL_GNU_LANGUAGES)
|
||||
LIBBUILTIN(strlcat, "zc*cC*z", "f", "string.h", ALL_GNU_LANGUAGES)
|
||||
|
|
|
@ -158,6 +158,13 @@ public:
|
|||
return strchr(getRecord(ID).Attributes, 't') != nullptr;
|
||||
}
|
||||
|
||||
/// Determines whether a declaration of this builtin should be recognized
|
||||
/// even if the type doesn't match the specified signature.
|
||||
bool allowTypeMismatch(unsigned ID) const {
|
||||
return strchr(getRecord(ID).Attributes, 'T') != nullptr ||
|
||||
hasCustomTypechecking(ID);
|
||||
}
|
||||
|
||||
/// Determines whether this builtin has a result or any arguments which
|
||||
/// are pointer types.
|
||||
bool hasPtrArgsOrResult(unsigned ID) const {
|
||||
|
|
|
@ -2105,7 +2105,8 @@ NamedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID,
|
|||
|
||||
// If we have a builtin without an associated type we should not emit a
|
||||
// warning when we were not able to find a type for it.
|
||||
if (Error == ASTContext::GE_Missing_type)
|
||||
if (Error == ASTContext::GE_Missing_type ||
|
||||
Context.BuiltinInfo.allowTypeMismatch(ID))
|
||||
return nullptr;
|
||||
|
||||
// If we could not find a type for setjmp it is because the jmp_buf type was
|
||||
|
@ -2129,11 +2130,9 @@ NamedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID,
|
|||
Context.BuiltinInfo.isHeaderDependentFunction(ID))) {
|
||||
Diag(Loc, diag::ext_implicit_lib_function_decl)
|
||||
<< Context.BuiltinInfo.getName(ID) << R;
|
||||
if (Context.BuiltinInfo.getHeaderName(ID) &&
|
||||
!Diags.isIgnored(diag::ext_implicit_lib_function_decl, Loc))
|
||||
if (const char *Header = Context.BuiltinInfo.getHeaderName(ID))
|
||||
Diag(Loc, diag::note_include_header_or_declare)
|
||||
<< Context.BuiltinInfo.getHeaderName(ID)
|
||||
<< Context.BuiltinInfo.getName(ID);
|
||||
<< Header << Context.BuiltinInfo.getName(ID);
|
||||
}
|
||||
|
||||
if (R.isNull())
|
||||
|
@ -9642,17 +9641,16 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
|||
}
|
||||
}
|
||||
|
||||
// In C builtins get merged with implicitly lazily created declarations.
|
||||
// In C++ we need to check if it's a builtin and add the BuiltinAttr here.
|
||||
if (getLangOpts().CPlusPlus &&
|
||||
// If this is the first declaration of a library builtin function, add
|
||||
// attributes as appropriate.
|
||||
if (!D.isRedeclaration() &&
|
||||
NewFD->getDeclContext()->getRedeclContext()->isFileContext()) {
|
||||
if (IdentifierInfo *II = Previous.getLookupName().getAsIdentifierInfo()) {
|
||||
if (unsigned BuiltinID = II->getBuiltinID()) {
|
||||
if (NewFD->getLanguageLinkage() == CLanguageLinkage) {
|
||||
// Declarations for builtins with custom typechecking by definition
|
||||
// don't make sense. Don't attempt typechecking and simply add the
|
||||
// attribute.
|
||||
if (Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) {
|
||||
// Validate the type matches unless this builtin is specified as
|
||||
// matching regardless of its declared type.
|
||||
if (Context.BuiltinInfo.allowTypeMismatch(BuiltinID)) {
|
||||
NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID));
|
||||
} else {
|
||||
ASTContext::GetBuiltinTypeError Error;
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
// RUN: %clang_cc1 -x c %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -x c++ %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct __jmp_buf_tag { int n; };
|
||||
int setjmp(struct __jmp_buf_tag*);
|
||||
int sigsetjmp(struct __jmp_buf_tag*, int);
|
||||
int _setjmp(struct __jmp_buf_tag*);
|
||||
int __sigsetjmp(struct __jmp_buf_tag*, int);
|
||||
|
||||
typedef struct __jmp_buf_tag jmp_buf[1];
|
||||
typedef struct __jmp_buf_tag sigjmp_buf[1];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
void f() {
|
||||
jmp_buf jb;
|
||||
// CHECK: call {{.*}}@setjmp(
|
||||
setjmp(jb);
|
||||
// CHECK: call {{.*}}@sigsetjmp(
|
||||
sigsetjmp(jb, 0);
|
||||
// CHECK: call {{.*}}@_setjmp(
|
||||
_setjmp(jb);
|
||||
// CHECK: call {{.*}}@__sigsetjmp(
|
||||
__sigsetjmp(jb, 0);
|
||||
}
|
||||
|
||||
// CHECK: ; Function Attrs: returns_twice
|
||||
// CHECK-NEXT: declare {{.*}} @setjmp(
|
||||
|
||||
// CHECK: ; Function Attrs: returns_twice
|
||||
// CHECK-NEXT: declare {{.*}} @sigsetjmp(
|
||||
|
||||
// CHECK: ; Function Attrs: returns_twice
|
||||
// CHECK-NEXT: declare {{.*}} @_setjmp(
|
||||
|
||||
// CHECK: ; Function Attrs: returns_twice
|
||||
// CHECK-NEXT: declare {{.*}} @__sigsetjmp(
|
||||
|
|
@ -1,10 +1,42 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DNO_JMP_BUF %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DNO_JMP_BUF %s -ast-dump | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DWRONG_JMP_BUF %s -ast-dump | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DRIGHT_JMP_BUF %s -ast-dump | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DONLY_JMP_BUF %s -ast-dump | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -DNO_SETJMP %s -ast-dump 2>&1 | FileCheck %s
|
||||
|
||||
#ifdef NO_JMP_BUF
|
||||
extern long setjmp(long *); // expected-warning {{declaration of built-in function 'setjmp' requires the declaration of the 'jmp_buf' type, commonly provided in the header <setjmp.h>.}}
|
||||
#else
|
||||
// This happens in some versions of glibc: the declaration of __sigsetjmp
|
||||
// precedes the declaration of sigjmp_buf.
|
||||
extern long setjmp(long *); // Can't check, so we trust that this is the right type
|
||||
// FIXME: We could still diagnose the missing `jmp_buf` at the point of the call.
|
||||
// expected-no-diagnostics
|
||||
#elif WRONG_JMP_BUF
|
||||
typedef long jmp_buf;
|
||||
extern int setjmp(char); // expected-warning@8 {{incompatible redeclaration of library function 'setjmp'}}
|
||||
// expected-note@8 {{'setjmp' is a builtin with type 'int (jmp_buf)' (aka 'int (long)')}}
|
||||
extern int setjmp(char); // expected-warning {{incompatible redeclaration of library function 'setjmp'}}
|
||||
// expected-note@-1 {{'setjmp' is a builtin with type 'int (jmp_buf)' (aka 'int (long)')}}
|
||||
#elif RIGHT_JMP_BUF
|
||||
typedef long jmp_buf;
|
||||
extern int setjmp(long); // OK, right type.
|
||||
// expected-no-diagnostics
|
||||
#elif ONLY_JMP_BUF
|
||||
typedef int *jmp_buf;
|
||||
#endif
|
||||
|
||||
void use() {
|
||||
setjmp(0);
|
||||
#ifdef NO_SETJMP
|
||||
// expected-warning@-2 {{implicit declaration of function 'setjmp' is invalid in C99}}
|
||||
#elif ONLY_JMP_BUF
|
||||
// expected-warning@-4 {{implicitly declaring library function 'setjmp' with type 'int (jmp_buf)' (aka 'int (int *)')}}
|
||||
// expected-note@-5 {{include the header <setjmp.h> or explicitly provide a declaration for 'setjmp'}}
|
||||
#endif
|
||||
|
||||
#ifdef NO_SETJMP
|
||||
// In this case, the regular AST dump doesn't dump the implicit declaration of 'setjmp'.
|
||||
#pragma clang __debug dump setjmp
|
||||
#endif
|
||||
}
|
||||
|
||||
// CHECK: FunctionDecl {{.*}} used setjmp
|
||||
// CHECK: BuiltinAttr {{.*}} Implicit
|
||||
// CHECK: ReturnsTwiceAttr {{.*}} Implicit
|
||||
|
|
|
@ -54,13 +54,12 @@ main(int argc, char *argv[])
|
|||
|
||||
void snprintf() { }
|
||||
|
||||
// PR8316 & PR40692
|
||||
void longjmp(); // expected-warning{{declaration of built-in function 'longjmp' requires the declaration of the 'jmp_buf' type, commonly provided in the header <setjmp.h>.}}
|
||||
void longjmp();
|
||||
|
||||
extern float fmaxf(float, float);
|
||||
|
||||
struct __jmp_buf_tag {};
|
||||
void sigsetjmp(struct __jmp_buf_tag[1], int); // expected-warning{{declaration of built-in function 'sigsetjmp' requires the declaration of the 'jmp_buf' type, commonly provided in the header <setjmp.h>.}}
|
||||
void sigsetjmp(struct __jmp_buf_tag[1], int);
|
||||
|
||||
// PR40692
|
||||
void pthread_create(); // no warning expected
|
||||
|
|
Loading…
Reference in New Issue