Skip to content

[Clang] Don't give up on an unsuccessful function instantiation #126723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ Bug Fixes to C++ Support
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
- Clang now prints the correct instantiation context for diagnostics suppressed
by template argument deduction.
- Clang is now better at instantiating the function definition after its use inside
of a constexpr lambda. (#GH125747)
- The initialization kind of elements of structured bindings
direct-list-initialized from an array is corrected to direct-initialization.
- Clang no longer crashes when a coroutine is declared ``[[noreturn]]``. (#GH127327)
Expand Down
23 changes: 16 additions & 7 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -13657,12 +13657,16 @@ class Sema final : public SemaBase {

class LocalEagerInstantiationScope {
public:
LocalEagerInstantiationScope(Sema &S) : S(S) {
LocalEagerInstantiationScope(Sema &S, bool AtEndOfTU)
: S(S), AtEndOfTU(AtEndOfTU) {
SavedPendingLocalImplicitInstantiations.swap(
S.PendingLocalImplicitInstantiations);
}

void perform() { S.PerformPendingInstantiations(/*LocalOnly=*/true); }
void perform() {
S.PerformPendingInstantiations(/*LocalOnly=*/true,
/*AtEndOfTU=*/AtEndOfTU);
}

~LocalEagerInstantiationScope() {
assert(S.PendingLocalImplicitInstantiations.empty() &&
Expand All @@ -13673,6 +13677,7 @@ class Sema final : public SemaBase {

private:
Sema &S;
bool AtEndOfTU;
std::deque<PendingImplicitInstantiation>
SavedPendingLocalImplicitInstantiations;
};
Expand All @@ -13695,8 +13700,8 @@ class Sema final : public SemaBase {

class GlobalEagerInstantiationScope {
public:
GlobalEagerInstantiationScope(Sema &S, bool Enabled)
: S(S), Enabled(Enabled) {
GlobalEagerInstantiationScope(Sema &S, bool Enabled, bool AtEndOfTU)
: S(S), Enabled(Enabled), AtEndOfTU(AtEndOfTU) {
if (!Enabled)
return;

Expand All @@ -13710,7 +13715,8 @@ class Sema final : public SemaBase {
void perform() {
if (Enabled) {
S.DefineUsedVTables();
S.PerformPendingInstantiations();
S.PerformPendingInstantiations(/*LocalOnly=*/false,
/*AtEndOfTU=*/AtEndOfTU);
}
}

Expand All @@ -13725,7 +13731,8 @@ class Sema final : public SemaBase {
S.SavedVTableUses.pop_back();

// Restore the set of pending implicit instantiations.
if (S.TUKind != TU_Prefix || !S.LangOpts.PCHInstantiateTemplates) {
if ((S.TUKind != TU_Prefix || !S.LangOpts.PCHInstantiateTemplates) &&
AtEndOfTU) {
assert(S.PendingInstantiations.empty() &&
"PendingInstantiations should be empty before it is discarded.");
S.PendingInstantiations.swap(S.SavedPendingInstantiations.back());
Expand All @@ -13744,6 +13751,7 @@ class Sema final : public SemaBase {
private:
Sema &S;
bool Enabled;
bool AtEndOfTU;
};

ExplicitSpecifier instantiateExplicitSpecifier(
Expand Down Expand Up @@ -13929,7 +13937,8 @@ class Sema final : public SemaBase {

/// Performs template instantiation for all implicit template
/// instantiations we have seen until this point.
void PerformPendingInstantiations(bool LocalOnly = false);
void PerformPendingInstantiations(bool LocalOnly = false,
bool AtEndOfTU = true);

TemplateParameterList *
SubstTemplateParams(TemplateParameterList *Params, DeclContext *Owner,
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Interpreter/IncrementalParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ llvm::Expected<TranslationUnitDecl *>
IncrementalParser::ParseOrWrapTopLevelDecl() {
// Recover resources if we crash before exiting this method.
llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);
Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true);
Sema::LocalEagerInstantiationScope LocalInstantiations(S);
Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true,
/*AtEndOfTU=*/true);
Sema::LocalEagerInstantiationScope LocalInstantiations(S, /*AtEndOfTU=*/true);

// Add a new PTU.
ASTContext &C = S.getASTContext();
Expand Down
47 changes: 30 additions & 17 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2362,7 +2362,8 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) {
// DR1484 clarifies that the members of a local class are instantiated as part
// of the instantiation of their enclosing entity.
if (D->isCompleteDefinition() && D->isLocalClass()) {
Sema::LocalEagerInstantiationScope LocalInstantiations(SemaRef);
Sema::LocalEagerInstantiationScope LocalInstantiations(SemaRef,
/*AtEndOfTU=*/false);

SemaRef.InstantiateClass(D->getLocation(), Record, D, TemplateArgs,
TSK_ImplicitInstantiation,
Expand Down Expand Up @@ -5344,8 +5345,10 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
// This has to happen before LateTemplateParser below is called, so that
// it marks vtables used in late parsed templates as used.
GlobalEagerInstantiationScope GlobalInstantiations(*this,
/*Enabled=*/Recursive);
LocalEagerInstantiationScope LocalInstantiations(*this);
/*Enabled=*/Recursive,
/*AtEndOfTU=*/AtEndOfTU);
LocalEagerInstantiationScope LocalInstantiations(*this,
/*AtEndOfTU=*/AtEndOfTU);

// Call the LateTemplateParser callback if there is a need to late parse
// a templated function definition.
Expand Down Expand Up @@ -5919,10 +5922,12 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
// If we're performing recursive template instantiation, create our own
// queue of pending implicit instantiations that we will instantiate
// later, while we're still within our own instantiation context.
GlobalEagerInstantiationScope GlobalInstantiations(*this,
/*Enabled=*/Recursive);
GlobalEagerInstantiationScope GlobalInstantiations(
*this,
/*Enabled=*/Recursive, /*AtEndOfTU=*/AtEndOfTU);
LocalInstantiationScope Local(*this);
LocalEagerInstantiationScope LocalInstantiations(*this);
LocalEagerInstantiationScope LocalInstantiations(*this,
/*AtEndOfTU=*/AtEndOfTU);

// Enter the scope of this instantiation. We don't use
// PushDeclContext because we don't have a scope.
Expand Down Expand Up @@ -6019,14 +6024,16 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
// queue of pending implicit instantiations that we will instantiate later,
// while we're still within our own instantiation context.
GlobalEagerInstantiationScope GlobalInstantiations(*this,
/*Enabled=*/Recursive);
/*Enabled=*/Recursive,
/*AtEndOfTU=*/AtEndOfTU);

// Enter the scope of this instantiation. We don't use
// PushDeclContext because we don't have a scope.
ContextRAII PreviousContext(*this, Var->getDeclContext());
LocalInstantiationScope Local(*this);

LocalEagerInstantiationScope LocalInstantiations(*this);
LocalEagerInstantiationScope LocalInstantiations(*this,
/*AtEndOfTU=*/AtEndOfTU);

VarDecl *OldVar = Var;
if (Def->isStaticDataMember() && !Def->isOutOfLine()) {
Expand Down Expand Up @@ -6774,18 +6781,20 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
return D;
}

void Sema::PerformPendingInstantiations(bool LocalOnly) {
std::deque<PendingImplicitInstantiation> delayedPCHInstantiations;
void Sema::PerformPendingInstantiations(bool LocalOnly, bool AtEndOfTU) {
std::deque<PendingImplicitInstantiation> DelayedImplicitInstantiations;
while (!PendingLocalImplicitInstantiations.empty() ||
(!LocalOnly && !PendingInstantiations.empty())) {
PendingImplicitInstantiation Inst;

bool LocalInstantiation = false;
if (PendingLocalImplicitInstantiations.empty()) {
Inst = PendingInstantiations.front();
PendingInstantiations.pop_front();
} else {
Inst = PendingLocalImplicitInstantiations.front();
PendingLocalImplicitInstantiations.pop_front();
LocalInstantiation = true;
}

// Instantiate function definitions
Expand All @@ -6794,22 +6803,26 @@ void Sema::PerformPendingInstantiations(bool LocalOnly) {
TSK_ExplicitInstantiationDefinition;
if (Function->isMultiVersion()) {
getASTContext().forEachMultiversionedFunctionVersion(
Function, [this, Inst, DefinitionRequired](FunctionDecl *CurFD) {
Function,
[this, Inst, DefinitionRequired, AtEndOfTU](FunctionDecl *CurFD) {
InstantiateFunctionDefinition(/*FIXME:*/ Inst.second, CurFD, true,
DefinitionRequired, true);
DefinitionRequired, AtEndOfTU);
if (CurFD->isDefined())
CurFD->setInstantiationIsPending(false);
});
} else {
InstantiateFunctionDefinition(/*FIXME:*/ Inst.second, Function, true,
DefinitionRequired, true);
DefinitionRequired, AtEndOfTU);
if (Function->isDefined())
Function->setInstantiationIsPending(false);
}
// Definition of a PCH-ed template declaration may be available only in the TU.
if (!LocalOnly && LangOpts.PCHInstantiateTemplates &&
TUKind == TU_Prefix && Function->instantiationIsPending())
delayedPCHInstantiations.push_back(Inst);
DelayedImplicitInstantiations.push_back(Inst);
else if (!AtEndOfTU && Function->instantiationIsPending() &&
!LocalInstantiation)
DelayedImplicitInstantiations.push_back(Inst);
continue;
}

Expand Down Expand Up @@ -6853,11 +6866,11 @@ void Sema::PerformPendingInstantiations(bool LocalOnly) {
// Instantiate static data member definitions or variable template
// specializations.
InstantiateVariableDefinition(/*FIXME:*/ Inst.second, Var, true,
DefinitionRequired, true);
DefinitionRequired, AtEndOfTU);
}

if (!LocalOnly && LangOpts.PCHInstantiateTemplates)
PendingInstantiations.swap(delayedPCHInstantiations);
if (!DelayedImplicitInstantiations.empty())
PendingInstantiations.swap(DelayedImplicitInstantiations);
}

void Sema::PerformDependentDiagnostics(const DeclContext *Pattern,
Expand Down
19 changes: 18 additions & 1 deletion clang/test/CodeGenCXX/function-template-specialization.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple %s -o - | FileCheck %s
// RUN: %clang_cc1 -emit-llvm -Wundefined-func-template -verify -triple %itanium_abi_triple %s -o - | FileCheck %s
// expected-no-diagnostics

// CHECK-DAG: _ZZN7PR219047GetDataIiEERKibE1i = internal global i32 4
// CHECK-DAG: _ZZN7PR219047GetDataIiEERKibE1i_0 = internal global i32 2
Expand Down Expand Up @@ -43,3 +44,19 @@ const int &GetData<int>(bool b) {
return i;
}
}

namespace GH125747 {

template<typename F> constexpr int visit(F f) { return f(0); }

template <class T> int G(T t);

int main() { return visit([](auto s) -> int { return G(s); }); }

template <class T> int G(T t) {
return 0;
}

// CHECK: define {{.*}} @_ZN8GH1257471GIiEEiT_

}
Loading