diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-02-13 23:20:09 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-02-13 23:20:09 +0000 |
commit | 3e41d60eb627dc227c770f1c1c87d06909cf05fd (patch) | |
tree | 6a67a1894695804429eb570219ae55e7e2b68555 | |
parent | 67116083e70ad44799808c7472d9cd9e7e21d4d5 (diff) |
Implicitly declare certain C library functions (malloc, strcpy, memmove,
etc.) when we perform name lookup on them. This ensures that we
produce the correct signature for these functions, which has two
practical impacts:
1) When we're supporting the "implicit function declaration" feature
of C99, these functions will be implicitly declared with the right
signature rather than as a function returning "int" with no
prototype. See PR3541 for the reason why this is important (hint:
GCC always predeclares these functions).
2) If users attempt to redeclare one of these library functions with
an incompatible signature, we produce a hard error.
This patch does a little bit of work to give reasonable error
messages. For example, when we hit case #1 we complain that we're
implicitly declaring this function with a specific signature, and then
we give a note that asks the user to include the appropriate header
(e.g., "please include <stdlib.h> or explicitly declare 'malloc'"). In
case #2, we show the type of the implicit builtin that was incorrectly
declared, so the user can see the problem. We could do better here:
for example, when displaying this latter error message we say
something like:
'strcpy' was implicitly declared here with type 'char *(char *, char
const *)'
but we should really print out a fake code line showing the
declaration, like this:
'strcpy' was implicitly declared here as:
char *strcpy(char *, char const *)
This would also be good for printing built-in candidates with C++
operator overloading.
The set of C library functions supported by this patch includes all
functions from the C99 specification's <stdlib.h> and <string.h> that
(a) are predefined by GCC and (b) have signatures that could cause
codegen issues if they are treated as functions with no prototype
returning and int. Future work could extend this set of functions to
other C library functions that we know about.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@64504 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/Builtins.def | 23 | ||||
-rw-r--r-- | include/clang/AST/Builtins.h | 21 | ||||
-rw-r--r-- | include/clang/AST/Decl.h | 2 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.def | 8 | ||||
-rw-r--r-- | lib/AST/Decl.cpp | 22 | ||||
-rw-r--r-- | lib/AST/Expr.cpp | 2 | ||||
-rw-r--r-- | lib/Analysis/GRExprEngine.cpp | 4 | ||||
-rw-r--r-- | lib/CodeGen/CGBuiltin.cpp | 3 | ||||
-rw-r--r-- | lib/CodeGen/CGExpr.cpp | 2 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenModule.cpp | 10 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 11 | ||||
-rw-r--r-- | lib/Sema/SemaChecking.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 54 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 8 | ||||
-rw-r--r-- | lib/Sema/SemaLookup.cpp | 37 | ||||
-rw-r--r-- | lib/Sema/SemaUtil.h | 5 | ||||
-rw-r--r-- | test/Analysis/exercise-ps.c | 3 | ||||
-rw-r--r-- | test/CodeGen/merge-attrs.c | 4 | ||||
-rw-r--r-- | test/Sema/implicit-builtin-decl.c | 22 |
19 files changed, 191 insertions, 52 deletions
diff --git a/include/clang/AST/Builtins.def b/include/clang/AST/Builtins.def index 7d8f27a236..e3c64bb0b8 100644 --- a/include/clang/AST/Builtins.def +++ b/include/clang/AST/Builtins.def @@ -54,6 +54,10 @@ // n -> nothrow // c -> const // 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 +// comes from, but only if 'f:headername' is the last part of the +// string. // FIXME: gcc has nonnull // Standard libc/libm functions: @@ -172,4 +176,23 @@ BUILTIN(__sync_val_compare_and_swap,"ii*ii", "n") // LLVM instruction builtin BUILTIN(__builtin_llvm_memory_barrier,"vbbbbb", "n") +// Builtin library functions +BUILTIN(alloca, "v*z", "f:stdlib.h") +BUILTIN(calloc, "v*zz", "f:stdlib.h") +BUILTIN(malloc, "v*z", "f:stdlib.h") +BUILTIN(memcpy, "v*v*vC*z", "f:string.h") +BUILTIN(memmove, "v*v*vC*z", "f:string.h") +BUILTIN(memset, "v*v*iz", "f:string.h") +BUILTIN(strcat, "c*c*cC*", "f:string.h") +BUILTIN(strchr, "c*cC*i", "f:string.h") +BUILTIN(strcpy, "c*c*cC*", "f:string.h") +BUILTIN(strcspn, "zcC*cC*", "f:string.h") +BUILTIN(strlen, "zcC*", "f:string.h") +BUILTIN(strncat, "c*c*cC*z", "f:string.h") +BUILTIN(strncpy, "c*c*cC*z", "f:string.h") +BUILTIN(strpbrk, "c*cC*cC*", "f:string.h") +BUILTIN(strrchr, "c*cC*i", "f:string.h") +BUILTIN(strspn, "zcC*cC*", "f:string.h") +BUILTIN(strstr, "c*cC*cC*", "f:string.h") + #undef BUILTIN diff --git a/include/clang/AST/Builtins.h b/include/clang/AST/Builtins.h index c1771924ba..f1b63bc843 100644 --- a/include/clang/AST/Builtins.h +++ b/include/clang/AST/Builtins.h @@ -78,6 +78,27 @@ public: return strchr(GetRecord(ID).Attributes, 'F') != 0; } + /// \brief Determines whether this builtin is a predefined libc/libm + /// function, such as "malloc", where we know the signature a + /// priori. + bool isPredefinedLibFunction(unsigned ID) const { + return strchr(GetRecord(ID).Attributes, 'f') != 0; + } + + /// \brief If this is a library function that comes from a specific + /// header, retrieve that header name. + const char *getHeaderName(unsigned ID) const { + char *Name = strchr(GetRecord(ID).Attributes, 'f'); + if (!Name) + return 0; + ++Name; + + if (*Name != ':') + return 0; + + return ++Name; + } + /// hasVAListUse - Return true of the specified builtin uses __builtin_va_list /// as an operand or return type. bool hasVAListUse(unsigned ID) const { diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index ac4b630f18..a4e30653b2 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -600,6 +600,8 @@ public: PreviousDeclaration = PrevDecl; } + unsigned getBuiltinID() const; + // Iterator access to formal parameters. unsigned param_size() const { return getNumParams(); } typedef ParmVarDecl **param_iterator; diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index bf83fd3ff9..871d03762b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -84,6 +84,14 @@ DIAG(err_declarator_need_ident, ERROR, DIAG(err_bad_language, ERROR, "unknown linkage language") +/// Built-in functions. +DIAG(ext_implicit_lib_function_decl, EXTWARN, + "implicitly declaring C library function '%0' with type %1") +DIAG(note_please_include_header, NOTE, + "please include the header <%0> or explicitly provide a declaration for '%1'") +DIAG(note_previous_builtin_declaration, NOTE, + "%0 was implicitly declared here with type %1") + /// parser diagnostics DIAG(ext_typedef_without_a_name, EXTWARN, "typedef requires a name") diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 04ee44a480..13b40e8afe 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -250,6 +250,28 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const { return 0; } +/// \brief Returns a value indicating whether this function +/// corresponds to a builtin function. +/// +/// The function corresponds to a built-in function if it is +/// declared at translation scope or within an extern "C" block and +/// its name matches with the name of a builtin. The returned value +/// will be 0 for functions that do not correspond to a builtin, a +/// value of type \c Builtin::ID if in the target-independent range +/// \c [1,Builtin::First), or a target-specific builtin value. +unsigned FunctionDecl::getBuiltinID() const { + if (getIdentifier() && + (getDeclContext()->isTranslationUnit() || + (isa<LinkageSpecDecl>(getDeclContext()) && + cast<LinkageSpecDecl>(getDeclContext())->getLanguage() + == LinkageSpecDecl::lang_c))) + return getIdentifier()->getBuiltinID(); + + // Not a builtin. + return 0; +} + + // Helper function for FunctionDecl::getNumParams and FunctionDecl::setParams() static unsigned getNumTypeParams(QualType T) { const FunctionType *FT = T->getAsFunctionType(); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 7b06a3c1ce..e294f48029 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -192,7 +192,7 @@ unsigned CallExpr::isBuiltinCall() const { if (!FDecl->getIdentifier()) return 0; - return FDecl->getIdentifier()->getBuiltinID(); + return FDecl->getBuiltinID(); } diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index c8861ac774..a52437c343 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -1269,9 +1269,7 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, NodeTy* Pred, if (isa<loc::FuncVal>(L)) { - IdentifierInfo* Info = cast<loc::FuncVal>(L).getDecl()->getIdentifier(); - - if (unsigned id = Info->getBuiltinID()) + if (unsigned id = cast<loc::FuncVal>(L).getDecl()->getBuiltinID()) switch (id) { case Builtin::BI__builtin_expect: { // For __builtin_expect, just return the value of the subexpression. diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index 9cd344fb7e..e14c9f01c6 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -327,7 +327,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(unsigned BuiltinID, const CallExpr *E) { // If this is an alias for a libm function (e.g. __builtin_sin) turn it into // that function. - if (getContext().BuiltinInfo.isLibFunction(BuiltinID)) + if (getContext().BuiltinInfo.isLibFunction(BuiltinID) || + getContext().BuiltinInfo.isPredefinedLibFunction(BuiltinID)) return EmitCallExpr(CGM.getBuiltinLibFunction(BuiltinID), E->getCallee()->getType(), E->arg_begin(), E->arg_end()); diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 83fbd72f90..ddecbebded 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -964,7 +964,7 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E) { dyn_cast<const DeclRefExpr>(IcExpr->getSubExpr())) if (const FunctionDecl *FDecl = dyn_cast<const FunctionDecl>(DRExpr->getDecl())) - if (unsigned builtinID = FDecl->getIdentifier()->getBuiltinID()) + if (unsigned builtinID = FDecl->getBuiltinID()) return EmitBuiltinExpr(builtinID, E); if (E->getCallee()->getType()->isBlockPointerType()) diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 503cc2ace5..03de730f65 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -842,10 +842,14 @@ llvm::Function *CodeGenModule::getBuiltinLibFunction(unsigned BuiltinID) { if (FunctionSlot) return FunctionSlot; - assert(Context.BuiltinInfo.isLibFunction(BuiltinID) && "isn't a lib fn"); + assert((Context.BuiltinInfo.isLibFunction(BuiltinID) || + Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID)) && + "isn't a lib fn"); - // Get the name, skip over the __builtin_ prefix. - const char *Name = Context.BuiltinInfo.GetName(BuiltinID)+10; + // Get the name, skip over the __builtin_ prefix (if necessary). + const char *Name = Context.BuiltinInfo.GetName(BuiltinID); + if (Context.BuiltinInfo.isLibFunction(BuiltinID)) + Name += 10; // Get the type for the builtin. QualType Type = Context.BuiltinInfo.GetBuiltinType(BuiltinID, Context); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index a0fa0c7f3d..fdd4c304fb 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -864,14 +864,18 @@ public: LookupResult LookupName(Scope *S, DeclarationName Name, LookupNameKind NameKind, - bool RedeclarationOnly = false); + bool RedeclarationOnly = false, + bool AllowBuiltinCreation = true, + SourceLocation Loc = SourceLocation()); LookupResult LookupQualifiedName(DeclContext *LookupCtx, DeclarationName Name, LookupNameKind NameKind, bool RedeclarationOnly = false); LookupResult LookupParsedName(Scope *S, const CXXScopeSpec *SS, DeclarationName Name, LookupNameKind NameKind, - bool RedeclarationOnly = false); + bool RedeclarationOnly = false, + bool AllowBuiltinCreation = true, + SourceLocation Loc = SourceLocation()); typedef llvm::SmallPtrSet<NamespaceDecl *, 16> AssociatedNamespaceSet; typedef llvm::SmallPtrSet<CXXRecordDecl *, 16> AssociatedClassSet; @@ -887,7 +891,8 @@ public: ObjCInterfaceDecl *getObjCInterfaceDecl(IdentifierInfo *Id); NamedDecl *LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID, - Scope *S); + Scope *S, bool ForRedeclaration, + SourceLocation Loc); NamedDecl *ImplicitlyDefineFunction(SourceLocation Loc, IdentifierInfo &II, Scope *S); diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index e52c730442..e058861283 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -34,7 +34,7 @@ Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) { if (!FnInfo) return move(TheCallResult); - switch (FnInfo->getBuiltinID()) { + switch (FDecl->getBuiltinID()) { case Builtin::BI__builtin___CFStringMakeConstantString: assert(TheCall->getNumArgs() == 1 && "Wrong # arguments to builtin CFStringMakeConstantString"); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 0dbd6d3804..d0997b03eb 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -45,7 +45,8 @@ using namespace clang; Sema::TypeTy *Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc, Scope *S, const CXXScopeSpec *SS) { Decl *IIDecl = 0; - LookupResult Result = LookupParsedName(S, SS, &II, LookupOrdinaryName, false); + LookupResult Result = LookupParsedName(S, SS, &II, LookupOrdinaryName, + false, false); switch (Result.getKind()) { case LookupResult::NotFound: case LookupResult::FoundOverloaded: @@ -285,21 +286,38 @@ void Sema::InitBuiltinVaListType() { Context.setBuiltinVaListType(Context.getTypedefType(VaTypedef)); } -/// LazilyCreateBuiltin - The specified Builtin-ID was first used at file scope. -/// lazily create a decl for it. +/// LazilyCreateBuiltin - The specified Builtin-ID was first used at +/// file scope. lazily create a decl for it. ForRedeclaration is true +/// if we're creating this built-in in anticipation of redeclaring the +/// built-in. NamedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned bid, - Scope *S) { + Scope *S, bool ForRedeclaration, + SourceLocation Loc) { Builtin::ID BID = (Builtin::ID)bid; if (Context.BuiltinInfo.hasVAListUse(BID)) InitBuiltinVaListType(); - + QualType R = Context.BuiltinInfo.GetBuiltinType(BID, Context); + + if (!ForRedeclaration && Context.BuiltinInfo.isPredefinedLibFunction(BID)) { + Diag(Loc, diag::ext_implicit_lib_function_decl) + << Context.BuiltinInfo.GetName(BID) + << R; + if (Context.BuiltinInfo.getHeaderName(BID) && + Diags.getDiagnosticMapping(diag::ext_implicit_lib_function_decl) + != diag::MAP_IGNORE) + Diag(Loc, diag::note_please_include_header) + << Context.BuiltinInfo.getHeaderName(BID) + << Context.BuiltinInfo.GetName(BID); + } + FunctionDecl *New = FunctionDecl::Create(Context, Context.getTranslationUnitDecl(), - SourceLocation(), II, R, + Loc, II, R, FunctionDecl::Extern, false); - + New->setImplicit(); + // Create Decl objects for each parameter, adding them to the // FunctionDecl. if (FunctionTypeProto *FT = dyn_cast<FunctionTypeProto>(R)) { @@ -491,9 +509,12 @@ Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, bool &Redeclaration) { diag::kind PrevDiag; if (Old->isThisDeclarationADefinition()) PrevDiag = diag::note_previous_definition; - else if (Old->isImplicit()) - PrevDiag = diag::note_previous_implicit_declaration; - else + else if (Old->isImplicit()) { + if (Old->getBuiltinID()) + PrevDiag = diag::note_previous_builtin_declaration; + else + PrevDiag = diag::note_previous_implicit_declaration; + } else PrevDiag = diag::note_previous_declaration; QualType OldQType = Context.getCanonicalType(Old->getType()); @@ -510,7 +531,7 @@ Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, bool &Redeclaration) { = cast<FunctionType>(NewQType.getTypePtr())->getResultType(); if (OldReturnType != NewReturnType) { Diag(New->getLocation(), diag::err_ovl_diff_return_type); - Diag(Old->getLocation(), PrevDiag); + Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); Redeclaration = true; return New; } @@ -523,7 +544,7 @@ Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, bool &Redeclaration) { // is a static member function declaration. if (OldMethod->isStatic() || NewMethod->isStatic()) { Diag(New->getLocation(), diag::err_ovl_static_nonstatic_member); - Diag(Old->getLocation(), PrevDiag); + Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); return New; } @@ -544,7 +565,7 @@ Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, bool &Redeclaration) { NewDiag = diag::err_member_redeclared; Diag(New->getLocation(), NewDiag); - Diag(Old->getLocation(), PrevDiag); + Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); } } @@ -577,7 +598,7 @@ Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, bool &Redeclaration) { // TODO: This is totally simplistic. It should handle merging functions // together etc, merging extern int X; int X; ... Diag(New->getLocation(), diag::err_conflicting_types) << New->getDeclName(); - Diag(Old->getLocation(), PrevDiag); + Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); return New; } @@ -1217,10 +1238,11 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl, // See if this is a redefinition of a variable in the same scope. if (!D.getCXXScopeSpec().isSet() && !D.getCXXScopeSpec().isInvalid()) { DC = CurContext; - PrevDecl = LookupName(S, Name, LookupOrdinaryName); + PrevDecl = LookupName(S, Name, LookupOrdinaryName, true, true, + D.getIdentifierLoc()); } else { // Something like "int foo::x;" DC = static_cast<DeclContext*>(D.getCXXScopeSpec().getScopeRep()); - PrevDecl = LookupQualifiedName(DC, Name, LookupOrdinaryName); + PrevDecl = LookupQualifiedName(DC, Name, LookupOrdinaryName, true); // C++ 7.3.1.2p2: // Members (including explicit specializations of templates) of a named diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 6b82eadaea..cec771d4e5 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -547,7 +547,8 @@ Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc, // Could be enum-constant, value decl, instance variable, etc. if (SS && SS->isInvalid()) return ExprError(); - LookupResult Lookup = LookupParsedName(S, SS, Name, LookupOrdinaryName); + LookupResult Lookup = LookupParsedName(S, SS, Name, LookupOrdinaryName, + false, true, Loc); if (getLangOptions().CPlusPlus && (!SS || !SS->isSet()) && HasTrailingLParen && Lookup.getKind() == LookupResult::NotFound) { @@ -1922,9 +1923,8 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc, } if (Ovl || (getLangOptions().CPlusPlus && (FDecl || UnqualifiedName))) { - // We don't perform ADL for builtins. - if (FDecl && FDecl->getIdentifier() && - FDecl->getIdentifier()->getBuiltinID()) + // We don't perform ADL for implicit declarations of builtins. + if (FDecl && FDecl->getBuiltinID() && FDecl->isImplicit()) ADL = false; // We don't perform ADL in C. diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index f4bfe5ca3c..d1bb99acc5 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -728,21 +728,17 @@ Sema::CppLookupName(Scope *S, DeclarationName Name, /// /// @param Name The name of the entity that we are searching for. /// -/// @param Criteria The criteria that this routine will use to -/// determine which names are visible and which names will be -/// found. Note that name lookup will find a name that is visible by -/// the given criteria, but the entity itself may not be semantically -/// correct or even the kind of entity expected based on the -/// lookup. For example, searching for a nested-name-specifier name -/// might result in an EnumDecl, which is visible but is not permitted -/// as a nested-name-specifier in C++03. +/// @param Loc If provided, the source location where we're performing +/// name lookup. At present, this is only used to produce diagnostics when +/// C library functions (like "malloc") are implicitly declared. /// /// @returns The result of name lookup, which includes zero or more /// declarations and possibly additional information used to diagnose /// ambiguities. Sema::LookupResult Sema::LookupName(Scope *S, DeclarationName Name, LookupNameKind NameKind, - bool RedeclarationOnly) { + bool RedeclarationOnly, bool AllowBuiltinCreation, + SourceLocation Loc) { if (!Name) return LookupResult::CreateLookupResult(Context, 0); if (!getLangOptions().CPlusPlus) { @@ -812,12 +808,19 @@ Sema::LookupName(Scope *S, DeclarationName Name, LookupNameKind NameKind, // now, injecting it into translation unit scope, and return it. if (NameKind == LookupOrdinaryName) { IdentifierInfo *II = Name.getAsIdentifierInfo(); - if (II) { + if (II && AllowBuiltinCreation) { // If this is a builtin on this (or all) targets, create the decl. - if (unsigned BuiltinID = II->getBuiltinID()) + if (unsigned BuiltinID = II->getBuiltinID()) { + // In C++, we don't have any predefined library functions like + // 'malloc'. Instead, we'll just error. + if (getLangOptions().CPlusPlus && + Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID)) + return LookupResult::CreateLookupResult(Context, 0); + return LookupResult::CreateLookupResult(Context, LazilyCreateBuiltin((IdentifierInfo *)II, BuiltinID, - S)); + S, RedeclarationOnly, Loc)); + } } if (getLangOptions().ObjC1 && II) { // @interface and @compatibility_alias introduce typedef-like names. @@ -995,11 +998,16 @@ Sema::LookupQualifiedName(DeclContext *LookupCtx, DeclarationName Name, /// @param Name The name of the entity that name lookup will /// search for. /// +/// @param Loc If provided, the source location where we're performing +/// name lookup. At present, this is only used to produce diagnostics when +/// C library functions (like "malloc") are implicitly declared. +/// /// @returns The result of qualified or unqualified name lookup. Sema::LookupResult Sema::LookupParsedName(Scope *S, const CXXScopeSpec *SS, DeclarationName Name, LookupNameKind NameKind, - bool RedeclarationOnly) { + bool RedeclarationOnly, bool AllowBuiltinCreation, + SourceLocation Loc) { if (SS) { if (SS->isInvalid()) return LookupResult::CreateLookupResult(Context, 0); @@ -1009,7 +1017,8 @@ Sema::LookupParsedName(Scope *S, const CXXScopeSpec *SS, Name, NameKind, RedeclarationOnly); } - return LookupName(S, Name, NameKind, RedeclarationOnly); + return LookupName(S, Name, NameKind, RedeclarationOnly, + AllowBuiltinCreation, Loc); } diff --git a/lib/Sema/SemaUtil.h b/lib/Sema/SemaUtil.h index 35452b18e6..5c64c76e29 100644 --- a/lib/Sema/SemaUtil.h +++ b/lib/Sema/SemaUtil.h @@ -24,8 +24,9 @@ static inline bool isCallBuiltin(CallExpr* cexp) { Expr* sub = cexp->getCallee()->IgnoreParenCasts(); if (DeclRefExpr* E = dyn_cast<DeclRefExpr>(sub)) - if (E->getDecl()->getIdentifier()->getBuiltinID() > 0) - return true; + if (FunctionDecl *Fn = dyn_cast<FunctionDecl>(E->getDecl())) + if (Fn->getBuiltinID() > 0) + return true; return false; } diff --git a/test/Analysis/exercise-ps.c b/test/Analysis/exercise-ps.c index 4aaba8e8dd..217135ec06 100644 --- a/test/Analysis/exercise-ps.c +++ b/test/Analysis/exercise-ps.c @@ -20,5 +20,6 @@ void_typedef f2_helper(); static void f2(void *buf) { F12_typedef* x; x = f2_helper(); - memcpy((&x[1]), (buf), 1); + memcpy((&x[1]), (buf), 1); // expected-warning{{implicitly declaring C library function 'memcpy' with type 'void *(void *, void const *}} \ + // expected-note{{please include the header <string.h> or explicitly provide a declaration for 'memcpy'}} } diff --git a/test/CodeGen/merge-attrs.c b/test/CodeGen/merge-attrs.c index c3349d8e97..3c1d62aeca 100644 --- a/test/CodeGen/merge-attrs.c +++ b/test/CodeGen/merge-attrs.c @@ -1,12 +1,12 @@ // RUN: clang %s -emit-llvm -o %t -void *malloc(int size) __attribute__ ((__nothrow__)); +void *malloc(__SIZE_TYPE__ size) __attribute__ ((__nothrow__)); inline static void __zend_malloc() { malloc(1); } -void *malloc(int size) __attribute__ ((__nothrow__)); +void *malloc(__SIZE_TYPE__ size) __attribute__ ((__nothrow__)); void fontFetch() { __zend_malloc(1); diff --git a/test/Sema/implicit-builtin-decl.c b/test/Sema/implicit-builtin-decl.c new file mode 100644 index 0000000000..3faef0b719 --- /dev/null +++ b/test/Sema/implicit-builtin-decl.c @@ -0,0 +1,22 @@ +// RUN: clang -fsyntax-only -verify %s +void f() { + int *ptr = malloc(sizeof(int) * 10); // expected-warning{{implicitly declaring C library function 'malloc' with type}} \ + // expected-note{{please include the header <stdlib.h> or explicitly provide a declaration for 'malloc'}} \ + // expected-note{{'malloc' was implicitly declared here with type 'void *}} +} + +void *alloca(__SIZE_TYPE__); // redeclaration okay + +int *calloc(__SIZE_TYPE__, __SIZE_TYPE__); // expected-error{{conflicting types for 'calloc'}} \ + // expected-note{{'calloc' was implicitly declared here with type 'void *}} + + +void g(int malloc) { // okay: these aren't functions + int calloc = 1; +} + +void h() { + int malloc(int); // expected-error{{conflicting types for 'malloc'}} + int strcpy(int); // expected-error{{conflicting types for 'strcpy'}} \ + // expected-note{{'strcpy' was implicitly declared here with type 'char *(char *, char const *)'}} +} |