diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h --- a/clang/include/clang/Basic/IdentifierTable.h +++ b/clang/include/clang/Basic/IdentifierTable.h @@ -223,7 +223,7 @@ } void setObjCKeywordID(tok::ObjCKeywordKind ID) { ObjCOrBuiltinID = ID; } - /// True if setNotBuiltin() was called. + /// True if revertBuiltin() was called. bool hasRevertedBuiltin() const { return ObjCOrBuiltinID == tok::NUM_OBJC_KEYWORDS; } 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 @@ -3756,24 +3756,21 @@ // If the previous declaration was an implicitly-generated builtin // declaration, then at the very least we should use a specialized note. unsigned BuiltinID; - if (Old->isImplicit() && (BuiltinID = Old->getBuiltinID())) { + bool RevertedBuiltin{Old->getIdentifier()->hasRevertedBuiltin()}; + if (Old->isImplicit() && + ((BuiltinID = Old->getBuiltinID()) || RevertedBuiltin)) { // If it's actually a library-defined builtin function like 'malloc' // or 'printf', just warn about the incompatible redeclaration. - if (Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID)) { + // Also warn if it's a redeclaration of a builtin redeclaration. We know if + // it is if the previous declaration is a reverted builtin. + if (RevertedBuiltin || + Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID)) { Diag(New->getLocation(), diag::warn_redecl_library_builtin) << New; Diag(OldLocation, diag::note_previous_builtin_declaration) << Old << Old->getType(); - // If this is a global redeclaration, just forget hereafter - // about the "builtin-ness" of the function. - // - // Doing this for local extern declarations is problematic. If - // the builtin declaration remains visible, a second invalid - // local declaration will produce a hard error; if it doesn't - // remain visible, a single bogus local redeclaration (which is - // actually only a warning) could break all the downstream code. - if (!New->getLexicalDeclContext()->isFunctionOrMethod()) - New->getIdentifier()->revertBuiltin(); + // Forget hereafter about the "builtin-ness" of the function. + New->getIdentifier()->revertBuiltin(); return false; } diff --git a/clang/test/CodeGen/builtin-redeclaration.c b/clang/test/CodeGen/builtin-redeclaration.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/builtin-redeclaration.c @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -emit-llvm-only %s + +// PR45410 +// Ensure we mark local extern redeclarations with a different type as non-builtin. +void non_builtin() { + extern float exp(); + exp(); // Will crash due to wrong number of arguments if this calls the builtin. +} + +// PR45410 +// We mark exp() builtin as const with -fno-math-errno (default). +// We mustn't do that for extern redeclarations of builtins where the type differs. +float attribute() { + extern float exp(); + return exp(1); +}