diff options
Diffstat (limited to 'clang/lib/Sema/SemaDecl.cpp')
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 236 |
1 files changed, 55 insertions, 181 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 9f9bf1e39c7..a87cf61ccfe 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4359,14 +4359,10 @@ TryToFixInvalidVariablyModifiedTypeSourceInfo(TypeSourceInfo *TInfo, /// function-scope declarations. void Sema::RegisterLocallyScopedExternCDecl(NamedDecl *ND, Scope *S) { - if (!getLangOpts().CPlusPlus && - ND->getLexicalDeclContext()->getRedeclContext()->isTranslationUnit()) - // Don't need to track declarations in the TU in C. - return; - + assert( + !ND->getLexicalDeclContext()->getRedeclContext()->isTranslationUnit() && + "Decl is not a locally-scoped decl!"); // Note that we have a locally-scoped external with this name. - // FIXME: There can be multiple such declarations if they are functions marked - // __attribute__((overloadable)) declared in function scope in C. LocallyScopedExternCDecls[ND->getDeclName()] = ND; } @@ -4685,32 +4681,6 @@ static bool isFunctionDefinitionDiscarded(Sema &S, FunctionDecl *FD) { return isC99Inline; } -/// Determine whether a variable is extern "C" prior to attaching -/// an initializer. We can't just call isExternC() here, because that -/// will also compute and cache whether the declaration is externally -/// visible, which might change when we attach the initializer. -/// -/// This can only be used if the declaration is known to not be a -/// redeclaration of an internal linkage declaration. -/// -/// For instance: -/// -/// auto x = []{}; -/// -/// Attaching the initializer here makes this declaration not externally -/// visible, because its type has internal linkage. -/// -/// FIXME: This is a hack. -template<typename T> -static bool isIncompleteDeclExternC(Sema &S, const T *D) { - if (S.getLangOpts().CPlusPlus) { - // In C++, the overloadable attribute negates the effects of extern "C". - if (!D->isInExternCContext() || D->template hasAttr<OverloadableAttr>()) - return false; - } - return D->isExternC(); -} - static bool shouldConsiderLinkage(const VarDecl *VD) { const DeclContext *DC = VD->getDeclContext()->getRedeclContext(); if (DC->isFunctionOrMethod()) @@ -5100,10 +5070,16 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, ProcessPragmaWeak(S, NewVD); checkAttributesAfterMerging(*this, *NewVD); - // If this is the first declaration of an extern C variable, update - // the map of such variables. - if (!NewVD->getPreviousDecl() && !NewVD->isInvalidDecl() && - isIncompleteDeclExternC(*this, NewVD)) + // If this is the first declaration of an extern C variable that is not + // declared directly in the translation unit, update the map of such + // variables. + if (!CurContext->getRedeclContext()->isTranslationUnit() && + !NewVD->getPreviousDecl() && !NewVD->isInvalidDecl() && + // FIXME: We only check isExternC if we're in an extern C context, + // to avoid computing and caching an 'externally visible' flag which + // could change if the variable's type is not visible. + (!getLangOpts().CPlusPlus || NewVD->isInExternCContext()) && + NewVD->isExternC()) RegisterLocallyScopedExternCDecl(NewVD, S); return NewVD; @@ -5204,120 +5180,30 @@ void Sema::CheckShadow(Scope *S, VarDecl *D) { CheckShadow(S, D, R); } -/// Check for conflict between this global or extern "C" declaration and -/// previous global or extern "C" declarations. This is only used in C++. template<typename T> -static bool checkGlobalOrExternCConflict( - Sema &S, const T *ND, bool IsGlobal, LookupResult &Previous) { - assert(S.getLangOpts().CPlusPlus && "only C++ has extern \"C\""); - NamedDecl *Prev = S.findLocallyScopedExternCDecl(ND->getDeclName()); - - if (!Prev && IsGlobal && !isIncompleteDeclExternC(S, ND)) { - // The common case: this global doesn't conflict with any extern "C" - // declaration. - return false; - } - - if (Prev) { - if (!IsGlobal || isIncompleteDeclExternC(S, ND)) { - // Both the old and new declarations have C language linkage. This is a - // redeclaration. - Previous.clear(); - Previous.addDecl(Prev); - return true; - } - - // This is a global, non-extern "C" declaration, and there is a previous - // non-global extern "C" declaration. Diagnose. - } else { - // The declaration is extern "C". Check for any declaration in the - // translation unit which might conflict. - if (IsGlobal) { - // We have already performed the lookup into the translation unit. - IsGlobal = false; - for (LookupResult::iterator I = Previous.begin(), E = Previous.end(); - I != E; ++I) { - if (isa<VarDecl>(*I) || isa<FunctionDecl>(*I)) { - Prev = *I; - break; - } - } - } else { - DeclContext::lookup_result R = - S.Context.getTranslationUnitDecl()->lookup(ND->getDeclName()); - for (DeclContext::lookup_result::iterator I = R.begin(), E = R.end(); - I != E; ++I) { - if (isa<VarDecl>(*I) || isa<FunctionDecl>(*I)) { - Prev = *I; - break; - } - // FIXME: If we have any other entity with this name in global scope, - // the declaration is ill-formed, but that is a defect: it breaks the - // 'stat' hack, for instance. - } - } +static bool mayConflictWithNonVisibleExternC(const T *ND) { + const DeclContext *DC = ND->getDeclContext(); + if (DC->getRedeclContext()->isTranslationUnit()) + return true; - if (!Prev) + // We know that is the first decl we see, other than function local + // extern C ones. If this is C++ and the decl is not in a extern C context + // it cannot have C language linkage. Avoid calling isExternC in that case. + // We need to this because of code like + // + // namespace { struct bar {}; } + // auto foo = bar(); + // + // This code runs before the init of foo is set, and therefore before + // the type of foo is known. Not knowing the type we cannot know its linkage + // unless it is in an extern C block. + if (!ND->isInExternCContext()) { + const ASTContext &Context = ND->getASTContext(); + if (Context.getLangOpts().CPlusPlus) return false; } - // Use the first declaration's location to ensure we point at something which - // is lexically inside an extern "C" linkage-spec. - assert(Prev && "should have found a previous declaration to diagnose"); - if (FunctionDecl *FD = dyn_cast<FunctionDecl>(Prev)) - Prev = FD->getFirstDeclaration(); - else - Prev = cast<VarDecl>(Prev)->getFirstDeclaration(); - - S.Diag(ND->getLocation(), diag::err_extern_c_global_conflict) - << IsGlobal << ND; - S.Diag(Prev->getLocation(), diag::note_extern_c_global_conflict) - << IsGlobal; - return false; -} - -/// Apply special rules for handling extern "C" declarations. Returns \c true -/// if we have found that this is a redeclaration of some prior entity. -/// -/// Per C++ [dcl.link]p6: -/// Two declarations [for a function or variable] with C language linkage -/// with the same name that appear in different scopes refer to the same -/// [entity]. An entity with C language linkage shall not be declared with -/// the same name as an entity in global scope. -template<typename T> -static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND, - LookupResult &Previous) { - if (!S.getLangOpts().CPlusPlus) { - // In C, when declaring a global variable, look for a corresponding 'extern' - // variable declared in function scope. - // - // FIXME: The corresponding case in C++ does not work. We should instead - // set the semantic DC for an extern local variable to be the innermost - // enclosing namespace, and ensure they are only found by redeclaration - // lookup. - if (ND->getDeclContext()->getRedeclContext()->isTranslationUnit()) { - if (NamedDecl *Prev = S.findLocallyScopedExternCDecl(ND->getDeclName())) { - Previous.clear(); - Previous.addDecl(Prev); - return true; - } - } - return false; - } - - // A declaration in the translation unit can conflict with an extern "C" - // declaration. - if (ND->getDeclContext()->getRedeclContext()->isTranslationUnit()) - return checkGlobalOrExternCConflict(S, ND, /*IsGlobal*/true, Previous); - - // An extern "C" declaration can conflict with a declaration in the - // translation unit or can be a redeclaration of an extern "C" declaration - // in another scope. - if (isIncompleteDeclExternC(S,ND)) - return checkGlobalOrExternCConflict(S, ND, /*IsGlobal*/false, Previous); - - // Neither global nor extern "C": nothing to do. - return false; + return ND->isExternC(); } void Sema::CheckVariableDeclarationType(VarDecl *NewVD) { @@ -5500,9 +5386,14 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD, // The most important point here is that we're not allowed to // update our understanding of the type according to declarations // not in scope. - bool PreviousWasHidden = - Previous.empty() && - checkForConflictWithNonVisibleExternC(*this, NewVD, Previous); + bool PreviousWasHidden = false; + if (Previous.empty() && mayConflictWithNonVisibleExternC(NewVD)) { + if (NamedDecl *ExternCPrev = + findLocallyScopedExternCDecl(NewVD->getDeclName())) { + Previous.addDecl(ExternCPrev); + PreviousWasHidden = true; + } + } // Filter out any non-conflicting previous declarations. filterNonConflictingPreviousDecls(Context, NewVD, Previous); @@ -6734,10 +6625,12 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // marking the function. AddCFAuditedAttribute(NewFD); - // If this is the first declaration of an extern C variable, update - // the map of such variables. - if (!NewFD->getPreviousDecl() && !NewFD->isInvalidDecl() && - isIncompleteDeclExternC(*this, NewFD)) + // If this is the first declaration of an extern C variable that is not + // declared directly in the translation unit, update the map of such + // variables. + if (!CurContext->getRedeclContext()->isTranslationUnit() && + !NewFD->getPreviousDecl() && NewFD->isExternC() && + !NewFD->isInvalidDecl()) RegisterLocallyScopedExternCDecl(NewFD, S); // Set this FunctionDecl's range up to the right paren. @@ -6841,6 +6734,15 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, assert(!NewFD->getResultType()->isVariablyModifiedType() && "Variably modified return types are not handled here"); + // Check for a previous declaration of this name. + if (Previous.empty() && mayConflictWithNonVisibleExternC(NewFD)) { + // Since we did not find anything by this name, look for a non-visible + // extern "C" declaration with the same name. + if (NamedDecl *ExternCPrev = + findLocallyScopedExternCDecl(NewFD->getDeclName())) + Previous.addDecl(ExternCPrev); + } + // Filter out any non-conflicting previous declarations. filterNonConflictingPreviousDecls(Context, NewFD, Previous); @@ -6895,34 +6797,6 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, } } - // Check for a previous extern "C" declaration with this name. - if (!Redeclaration && - checkForConflictWithNonVisibleExternC(*this, NewFD, Previous)) { - filterNonConflictingPreviousDecls(Context, NewFD, Previous); - if (!Previous.empty()) { - // This is an extern "C" declaration with the same name as a previous - // declaration, and thus redeclares that entity... - Redeclaration = true; - OldDecl = Previous.getFoundDecl(); - - // ... except in the presence of __attribute__((overloadable)). - if (OldDecl->hasAttr<OverloadableAttr>()) { - if (!getLangOpts().CPlusPlus && !NewFD->hasAttr<OverloadableAttr>()) { - Diag(NewFD->getLocation(), diag::err_attribute_overloadable_missing) - << Redeclaration << NewFD; - Diag(Previous.getFoundDecl()->getLocation(), - diag::note_attribute_overloadable_prev_overload); - NewFD->addAttr(::new (Context) OverloadableAttr(SourceLocation(), - Context)); - } - if (IsOverload(NewFD, cast<FunctionDecl>(OldDecl), false)) { - Redeclaration = false; - OldDecl = 0; - } - } - } - } - // C++11 [dcl.constexpr]p8: // A constexpr specifier for a non-static member function that is not // a constructor declares that member function to be const. |

