Skip to content

[Clang] Avoid transforming lambdas when rebuilding immediate expressions #108693

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 3 commits into from
Sep 18, 2024
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
3 changes: 2 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,9 @@ Bug Fixes to C++ Support
- A follow-up fix was added for (#GH61460), as the previous fix was not entirely correct. (#GH86361)
- Fixed a crash in the typo correction of an invalid CTAD guide. (#GH107887)
- Fixed a crash when clang tries to subtitute parameter pack while retaining the parameter
pack. #GH63819, #GH107560
pack. (#GH63819), (#GH107560)
- Fix a crash when a static assert declaration has an invalid close location. (#GH108687)
- Avoided a redundant friend declaration instantiation under a certain ``consteval`` context. (#GH107175)
- Fixed an assertion failure in debug mode, and potential crashes in release mode, when
diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.

Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17557,7 +17557,7 @@ static void RemoveNestedImmediateInvocation(
else
break;
}
/// ConstantExpr are the first layer of implicit node to be removed so if
/// ConstantExprs are the first layer of implicit node to be removed so if
/// Init isn't a ConstantExpr, no ConstantExpr will be skipped.
if (auto *CE = dyn_cast<ConstantExpr>(Init);
CE && CE->isImmediateInvocation())
Expand All @@ -17570,7 +17570,7 @@ static void RemoveNestedImmediateInvocation(
}
ExprResult TransformLambdaExpr(LambdaExpr *E) {
// Do not rebuild lambdas to avoid creating a new type.
// Lambdas have already been processed inside their eval context.
// Lambdas have already been processed inside their eval contexts.
return E;
}
bool AlwaysRebuild() { return false; }
Expand Down
55 changes: 18 additions & 37 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5508,50 +5508,31 @@ bool Sema::CheckTemplateArgumentList(
}

// Check whether we have a default argument.
TemplateArgumentLoc Arg;
bool HasDefaultArg;

// Retrieve the default template argument from the template
// parameter. For each kind of template parameter, we substitute the
// template arguments provided thus far and any "outer" template arguments
// (when the template parameter was part of a nested template) into
// the default argument.
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*Param)) {
if (!hasReachableDefaultArgument(TTP))
return diagnoseMissingArgument(*this, TemplateLoc, Template, TTP,
TemplateArgumentLoc Arg = SubstDefaultTemplateArgumentIfAvailable(
Template, TemplateLoc, RAngleLoc, *Param, SugaredConverted,
CanonicalConverted, HasDefaultArg);

if (Arg.getArgument().isNull()) {
if (!HasDefaultArg) {
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*Param))
return diagnoseMissingArgument(*this, TemplateLoc, Template, TTP,
NewArgs);
if (NonTypeTemplateParmDecl *NTTP =
dyn_cast<NonTypeTemplateParmDecl>(*Param))
return diagnoseMissingArgument(*this, TemplateLoc, Template, NTTP,
NewArgs);
return diagnoseMissingArgument(*this, TemplateLoc, Template,
cast<TemplateTemplateParmDecl>(*Param),
NewArgs);

if (SubstDefaultTemplateArgument(*this, Template, TemplateLoc, RAngleLoc,
TTP, SugaredConverted,
CanonicalConverted, Arg))
return true;
} else if (NonTypeTemplateParmDecl *NTTP
= dyn_cast<NonTypeTemplateParmDecl>(*Param)) {
if (!hasReachableDefaultArgument(NTTP))
return diagnoseMissingArgument(*this, TemplateLoc, Template, NTTP,
NewArgs);

if (SubstDefaultTemplateArgument(*this, Template, TemplateLoc, RAngleLoc,
NTTP, SugaredConverted,
CanonicalConverted, Arg))
return true;
} else {
TemplateTemplateParmDecl *TempParm
= cast<TemplateTemplateParmDecl>(*Param);

if (!hasReachableDefaultArgument(TempParm))
return diagnoseMissingArgument(*this, TemplateLoc, Template, TempParm,
NewArgs);

NestedNameSpecifierLoc QualifierLoc;
TemplateName Name = SubstDefaultTemplateArgument(
*this, Template, TemplateLoc, RAngleLoc, TempParm, SugaredConverted,
CanonicalConverted, QualifierLoc);
if (Name.isNull())
return true;

Arg = TemplateArgumentLoc(
Context, TemplateArgument(Name), QualifierLoc,
TempParm->getDefaultArgument().getTemplateNameLoc());
}
return true;
}

// Introduce an instantiation record that describes where we are using
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1673,6 +1673,10 @@ namespace {
}

ExprResult TransformLambdaExpr(LambdaExpr *E) {
// Do not rebuild lambdas to avoid creating a new type.
// Lambdas have already been processed inside their eval contexts.
if (SemaRef.RebuildingImmediateInvocation)
return E;
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);

Expand Down
10 changes: 7 additions & 3 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6294,9 +6294,13 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,

if (!SubstRecord) {
// T can be a dependent TemplateSpecializationType when performing a
// substitution for building a deduction guide.
assert(CodeSynthesisContexts.back().Kind ==
CodeSynthesisContext::BuildingDeductionGuides);
// substitution for building a deduction guide or for template
// argument deduction in the process of rebuilding immediate
// expressions. (Because the default argument that involves a lambda
// is untransformed and thus could be dependent at this point.)
assert(SemaRef.RebuildingImmediateInvocation ||
CodeSynthesisContexts.back().Kind ==
CodeSynthesisContext::BuildingDeductionGuides);
// Return a nullptr as a sentinel value, we handle it properly in
// the TemplateInstantiator::TransformInjectedClassNameType
// override, which we transform it to a TemplateSpecializationType.
Expand Down
24 changes: 24 additions & 0 deletions clang/test/SemaCXX/cxx2a-consteval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1248,3 +1248,27 @@ void test() {
}

}

// Test that we don't redundantly instantiate the friend declaration in
// RemoveNestedImmediateInvocation(). Otherwise, we would end up with spurious
// redefinition errors.
namespace GH107175 {

consteval void consteval_func() {}

template <auto> struct define_f {
friend void foo() {}
};

template <auto = [] {}> struct A {};

struct B {
template <auto T> consteval void func() { (void)define_f<T>{}; }
};

int main() {
B{}.func<A{}>();
consteval_func();
}

} // namespace GH107175
Loading