Index: include/clang/AST/DeclBase.h =================================================================== --- include/clang/AST/DeclBase.h +++ include/clang/AST/DeclBase.h @@ -528,7 +528,7 @@ /// \brief Set whether the declaration is used, in the sense of odr-use. /// /// This should only be used immediately after creating a declaration. - void setIsUsed() { Used = true; } + void setIsUsed() { getCanonicalDecl()->Used = true; } /// \brief Mark the declaration used, in the sense of odr-use. /// Index: lib/AST/DeclBase.cpp =================================================================== --- lib/AST/DeclBase.cpp +++ lib/AST/DeclBase.cpp @@ -329,25 +329,27 @@ return Align; } -bool Decl::isUsed(bool CheckUsedAttr) const { - if (Used) +bool Decl::isUsed(bool CheckUsedAttr) const { + const Decl* canonD = getCanonicalDecl(); + if (canonD->Used) return true; - + // Check for used attribute. - if (CheckUsedAttr && hasAttr()) + if (CheckUsedAttr && canonD->hasAttr()) return true; - return false; + return false; } void Decl::markUsed(ASTContext &C) { - if (Used) + if (isUsed()) return; + // Q: Do we need to call the callback with the canonical decl? if (C.getASTMutationListener()) C.getASTMutationListener()->DeclarationMarkedUsed(this); - Used = true; + getCanonicalDecl()->Used = true; } bool Decl::isReferenced() const { Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -13011,17 +13011,9 @@ UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); } - // Normally the most current decl is marked used while processing the use and - // any subsequent decls are marked used by decl merging. This fails with - // template instantiation since marking can happen at the end of the file - // and, because of the two phase lookup, this function is called with at - // decl in the middle of a decl chain. We loop to maintain the invariant - // that once a decl is used, all decls after it are also used. - for (FunctionDecl *F = Func->getMostRecentDecl();; F = F->getPreviousDecl()) { - F->markUsed(Context); - if (F == Func) - break; - } + // It is sufficient for the canonical decl to be marked as used. + Func->markUsed(Context); + } static void Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -3112,11 +3112,6 @@ Previous->IdentifierNamespace & (Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Type); - // If the previous declaration is marked as used, then this declaration should - // be too. - if (Previous->Used) - D->Used = true; - // If the declaration declares a template, it may inherit default arguments // from the previous declaration. if (TemplateDecl *TD = dyn_cast(D)) @@ -3865,7 +3860,7 @@ // ASTMutationListeners other than an ASTWriter. // Maintain AST consistency: any later redeclarations are used too. - forAllLaterRedecls(D, [](Decl *D) { D->Used = true; }); + D->setIsUsed(); break; } Index: test/Modules/Inputs/PR27401/a.h =================================================================== --- /dev/null +++ test/Modules/Inputs/PR27401/a.h @@ -0,0 +1,17 @@ +#ifndef _LIBCPP_ALGORITHM +#define _LIBCPP_ALGORITHM +template +struct integral_constant { + static const _Tp value = _Tp(); +}; + +template +struct is_nothrow_default_constructible + : integral_constant {}; + +template +struct is_nothrow_move_constructible + : integral_constant {}; + +class allocator {}; +#endif Index: test/Modules/Inputs/PR27401/b.h =================================================================== --- /dev/null +++ test/Modules/Inputs/PR27401/b.h @@ -0,0 +1,21 @@ +#include "a.h" +#ifndef _LIBCPP_VECTOR +template +class __vector_base { +protected: + _Allocator __alloc() const; + __vector_base(_Allocator); +}; + +template +class vector : __vector_base<_Tp, _Allocator> { +public: + vector() noexcept(is_nothrow_default_constructible<_Allocator>::value); + vector(const vector &); + vector(vector &&) + noexcept(is_nothrow_move_constructible<_Allocator>::value); +}; + +#endif +void GetUniquePtrType() { vector v; } + Index: test/Modules/Inputs/PR27401/module.modulemap =================================================================== --- /dev/null +++ test/Modules/Inputs/PR27401/module.modulemap @@ -0,0 +1 @@ +module "b" { header "b.h" export * } Index: test/Modules/pr27401.cpp =================================================================== --- /dev/null +++ test/Modules/pr27401.cpp @@ -0,0 +1,38 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -std=c++11 -I%S/Inputs/PR27401 -verify %s +// RUN: %clang_cc1 -std=c++11 -fmodules -fmodule-map-file=%S/Inputs/PR27401/module.modulemap -fmodules-cache-path=%t -I%S/Inputs/PR27401 -verify %s + +#include "a.h" +#define _LIBCPP_VECTOR +template +class __vector_base { +protected: + _Allocator __alloc() const; + __vector_base(_Allocator); +}; + +template +class vector : __vector_base<_Tp, _Allocator> { +public: + vector() noexcept(is_nothrow_default_constructible<_Allocator>::value); + vector(const vector &); + vector(vector &&) + noexcept(is_nothrow_move_constructible<_Allocator>::value); +}; + +template +vector<_Tp, _Allocator>::vector(const vector &__x) : __vector_base<_Tp, _Allocator>(__x.__alloc()) {} + + struct CommentOptions { + vector ParseAllComments; + CommentOptions() {} + }; + struct PrintingPolicy { + PrintingPolicy(CommentOptions LO) : LangOpts(LO) {} + CommentOptions LangOpts; + }; + +#include "b.h" +CommentOptions fn1() { return fn1(); } + +// expected-no-diagnostics