Skip to content

[Clang] Implement CWG2369 "Ordering between constraints and substitution" #102857

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 36 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1119f0a
[Clang][NFCI] Slightly refactor getTemplateInstantiationArgs()
zyn0217 Aug 12, 2024
ca00718
Merge branch 'main' into cwg-2369-2
zyn0217 Aug 13, 2024
2dd772d
Resolve a merge conflict
zyn0217 Aug 13, 2024
96bf64c
CWG 2369
zyn0217 Aug 11, 2024
6b7072d
Rectify diagnostics
zyn0217 Aug 12, 2024
bb31d36
Instantiate function parameters as needed
zyn0217 Aug 12, 2024
631be75
Remove debugging logs
zyn0217 Aug 17, 2024
7d484e2
Revert unrelated changes
zyn0217 Aug 17, 2024
c515407
Fix tests
zyn0217 Aug 17, 2024
a9da5a3
Fix libcxx tests
zyn0217 Aug 17, 2024
cebe706
Instantiate the decls as we substituting constraints
zyn0217 Aug 18, 2024
e0b21a5
[libc++][chono] Use hidden friends for leap_second comparison.
mordante Aug 18, 2024
30dc1d1
clang-format
zyn0217 Aug 19, 2024
9d3981f
Merge branch 'main' into cwg-2369
zyn0217 Aug 21, 2024
afb8a90
Merge branch 'main' into cwg-2369
zyn0217 Aug 28, 2024
6a5085a
Quote the standard wordings, update the DR status page
zyn0217 Aug 28, 2024
974a0ad
Revert unrelated changes
zyn0217 Aug 28, 2024
d19e700
oops, I forgot it
zyn0217 Aug 28, 2024
5be9921
Merge branch 'main' into cwg-2369
zyn0217 Oct 8, 2024
8ace714
Simplify the implementation
zyn0217 Oct 8, 2024
da61fab
Add parentheses to sizeof expression
zyn0217 Oct 8, 2024
18da86a
Merge branch 'main' into cwg-2369
zyn0217 Oct 9, 2024
1b8f0f6
Revert comment changes
zyn0217 Oct 9, 2024
ef5acad
Rebase on top of the new getTemplateInstantiationArgs()
zyn0217 Oct 9, 2024
6fc2f7d
Merge branch 'main' into cwg-2369
zyn0217 Oct 9, 2024
d457f02
Merge branch 'main' into cwg-2369
zyn0217 Oct 21, 2024
f07c4a9
Address a few comments
zyn0217 Oct 21, 2024
a2818ba
Merge branch 'main' into cwg-2369
zyn0217 Nov 3, 2024
8f2dc77
Address more comments
zyn0217 Nov 3, 2024
d16f2cf
Align the comment formats in FinishTemplateArgumentDeduction()
zyn0217 Nov 3, 2024
7e7ed8e
Merge branch 'main' into cwg-2369
zyn0217 Nov 17, 2024
7bc252a
Decouple from the new version of getTemplateInstantiationArgs()
zyn0217 Nov 17, 2024
f0b1e7c
Merge branch 'main' into cwg-2369
zyn0217 Jan 3, 2025
2e9061b
Don't call TransformFunctionTypeParams; expand the parameters ourselves
zyn0217 Jan 3, 2025
9f935ab
Remove unnecessary code
zyn0217 Jan 3, 2025
e623bf5
Use MLTAL.replaceInnermostTemplateArguments to avoid the special-casing
zyn0217 Jan 3, 2025
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
17 changes: 14 additions & 3 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -13033,11 +13033,14 @@ class Sema final : public SemaBase {
/// instantiation arguments.
///
/// \param DC In the event we don't HAVE a declaration yet, we instead provide
/// the decl context where it will be created. In this case, the `Innermost`
/// should likely be provided. If ND is non-null, this is ignored.
/// the decl context where it will be created. In this case, the \p
/// Innermost should likely be provided. If \p ND is non-null and \p
/// Innermost is NULL, this is ignored.
///
/// \param Innermost if non-NULL, specifies a template argument list for the
/// template declaration passed as ND.
/// template declaration passed as \p ND. The next declaration context would
/// be switched to \p DC if present; otherwise, it would be the semantic
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what "Next Declaration Context" refers too here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It means the visitation order. In order to collect the instantiation arguments, we start with a declaration and walk up toward its template parents. This function could use a refactor and be smarter in figuring out how to walk up from ND alone, so that the DC parameter becomes unnecessary and could be removed.

/// declaration context of \p ND.
///
/// \param RelativeToPrimary true if we should get the template
/// arguments relative to the primary template, even when we're
Expand All @@ -13053,6 +13056,9 @@ class Sema final : public SemaBase {
/// ForConstraintInstantiation indicates we should continue looking when
/// encountering a lambda generic call operator, and continue looking for
/// arguments on an enclosing class template.
///
/// \param SkipForSpecialization when specified, any template specializations
/// in a traversal would be ignored.
MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false,
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
Expand Down Expand Up @@ -14443,6 +14449,11 @@ class Sema final : public SemaBase {
ArrayRef<TemplateArgument> TemplateArgs,
ConstraintSatisfaction &Satisfaction);

bool CheckFunctionConstraintsWithoutInstantiation(
SourceLocation PointOfInstantiation, FunctionTemplateDecl *Template,
ArrayRef<TemplateArgument> TemplateArgs,
ConstraintSatisfaction &Satisfaction);

/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
/// \param First whether this is the first time an unsatisfied constraint is
Expand Down
152 changes: 150 additions & 2 deletions clang/lib/Sema/SemaConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -847,7 +847,7 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
bool ForOverloadResolution) {
// Don't check constraints if the function is dependent. Also don't check if
// this is a function template specialization, as the call to
// CheckinstantiatedFunctionTemplateConstraints after this will check it
// CheckInstantiatedFunctionTemplateConstraints after this will check it
// better.
if (FD->isDependentContext() ||
FD->getTemplatedKind() ==
Expand Down Expand Up @@ -1122,6 +1122,154 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
PointOfInstantiation, Satisfaction);
}

namespace {

// We employ a TreeTransform because RAV couldn't recurse into a bunch of
// Exprs e.g. SizeOfPackExpr, CXXFoldExpr, etc.
// FIXME: Could we do the Decl instantiation as we substitute into
// the constraint expressions?
class InstantiateReferencedParameter
: public TreeTransform<InstantiateReferencedParameter> {
const MultiLevelTemplateArgumentList &TemplateArgs;

llvm::SmallPtrSet<ParmVarDecl *, 4> InstantiatedDecls;

FunctionDecl *PrimaryTemplatedFunction;

using inherited = TreeTransform<InstantiateReferencedParameter>;

bool instantiateParameterToScope(ParmVarDecl *OldParm,
LocalInstantiationScope &Scope) {
// The current context might have been changed by lambda expressions. So
// resume it before we substitute into parameters.
Sema::ContextRAII Context(SemaRef, PrimaryTemplatedFunction);
std::optional<unsigned> NumExpansions;
ParmVarDecl *NewParm = nullptr;
unsigned IndexAdjustment = 0;
if (OldParm->isParameterPack()) {
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
TypeLoc TL = OldParm->getTypeSourceInfo()->getTypeLoc();
PackExpansionTypeLoc ExpansionTL = TL.castAs<PackExpansionTypeLoc>();
TypeLoc Pattern = ExpansionTL.getPatternLoc();
SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded);

assert(!Unexpanded.empty() &&
"A pack Decl doesn't contain anything unexpanded?");

bool ShouldExpand = false;
bool RetainExpansion = false;
std::optional<unsigned> OrigNumExpansions =
ExpansionTL.getTypePtr()->getNumExpansions();
NumExpansions = OrigNumExpansions;
if (SemaRef.CheckParameterPacksForExpansion(
ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(),
Unexpanded, TemplateArgs, ShouldExpand, RetainExpansion,
NumExpansions))
return true;

assert(ShouldExpand && !RetainExpansion &&
"Shouldn't retain an expansion here!");
Scope.MakeInstantiatedLocalArgPack(OldParm);

for (unsigned I = 0; I != *NumExpansions; ++I) {
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, I);
ParmVarDecl *NewParm = SemaRef.SubstParmVarDecl(
OldParm, TemplateArgs, /*indexAdjustment=*/IndexAdjustment++,
NumExpansions, /*ExpectParameterPack=*/false,
/*EvaluateConstraints=*/false);
if (!NewParm)
return true;
}

return false;
}
NewParm = SemaRef.SubstParmVarDecl(OldParm, TemplateArgs,
/*indexAdjustment=*/IndexAdjustment,
std::nullopt,
/*ExpectParameterPack=*/false);
if (!NewParm)
return true;
Scope.InstantiatedLocal(OldParm, NewParm);
return false;
}

public:
InstantiateReferencedParameter(
Sema &SemaRef, const MultiLevelTemplateArgumentList &TemplateArgs,
FunctionDecl *PrimaryTemplatedFunction)
: inherited(SemaRef), TemplateArgs(TemplateArgs),
PrimaryTemplatedFunction(PrimaryTemplatedFunction) {}

Decl *TransformDecl(SourceLocation Loc, Decl *D) {
if (auto *PVD = dyn_cast_if_present<ParmVarDecl>(D);
PVD && PVD->getDeclContext() == PrimaryTemplatedFunction &&
!InstantiatedDecls.contains(PVD)) {
instantiateParameterToScope(PVD, *SemaRef.CurrentInstantiationScope);
InstantiatedDecls.insert(PVD);
}
return D;
}

void TraverseConstraintExprs(ArrayRef<const Expr *> Exprs) {
for (auto *E : Exprs)
TransformExpr(const_cast<Expr *>(E));
}
};

} // namespace

bool Sema::CheckFunctionConstraintsWithoutInstantiation(
SourceLocation PointOfInstantiation, FunctionTemplateDecl *Template,
ArrayRef<TemplateArgument> TemplateArgs,
ConstraintSatisfaction &Satisfaction) {
FunctionDecl *FD = Template->getTemplatedDecl();
SmallVector<const Expr *, 3> TemplateAC;
Template->getAssociatedConstraints(TemplateAC);
if (TemplateAC.empty()) {
Satisfaction.IsSatisfied = true;
return false;
}

// Enter the scope of this instantiation. We don't use
// PushDeclContext because we don't have a scope.
LocalInstantiationScope Scope(*this);

// Collect the list of template arguments relative to the 'primary'
// template. We need the entire list, since the constraint is completely
// uninstantiated at this point.
DeclContext *NextDC = FD->getFriendObjectKind() ? FD->getLexicalDeclContext()
: FD->getDeclContext();
MultiLevelTemplateArgumentList MLTAL =
getTemplateInstantiationArgs(FD, NextDC,
/*Final=*/false,
/*Innermost=*/TemplateArgs,
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);

FunctionDecl *PatternDecl = FD;

if (FunctionTemplateDecl *FromMemTempl =
Template->getInstantiatedFromMemberTemplate()) {
while (FromMemTempl->getInstantiatedFromMemberTemplate())
FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate();
PatternDecl = FromMemTempl->getTemplatedDecl();
}

InstantiateReferencedParameter(*this, MLTAL, PatternDecl)
.TraverseConstraintExprs(TemplateAC);

Qualifiers ThisQuals;
CXXRecordDecl *Record = nullptr;
if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) {
ThisQuals = Method->getMethodQualifiers();
Record = Method->getParent();
}
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
return CheckConstraintSatisfaction(Template, TemplateAC, MLTAL,
PointOfInstantiation, Satisfaction);
}

static void diagnoseUnsatisfiedRequirement(Sema &S,
concepts::ExprRequirement *Req,
bool First) {
Expand Down Expand Up @@ -1487,7 +1635,7 @@ substituteParameterMappings(Sema &S, NormalizedConstraint &N,
static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
const ConceptSpecializationExpr *CSE) {
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(),
CSE->getNamedConcept(), CSE->getNamedConcept()->getDeclContext(),
/*Final=*/false, CSE->getTemplateArguments(),
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
Expand Down
11 changes: 6 additions & 5 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5582,11 +5582,12 @@ bool Sema::CheckTemplateArgumentList(
ContextRAII Context(*this, NewContext);
CXXThisScopeRAII(*this, RD, ThisQuals, RD != nullptr);

MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
Template, NewContext, /*Final=*/false, CanonicalConverted,
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConceptInstantiation=*/true);
MultiLevelTemplateArgumentList MLTAL =
getTemplateInstantiationArgs(Template, Template->getDeclContext(),
/*Final=*/false, CanonicalConverted,
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConceptInstantiation=*/true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why we do not use NewContext anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NewContext argument didn't actually make any difference to getTemplateInstantiationArgs() previously, as it would always move to Template->getDeclContext() for the next traversal if the Innermost parameter (which is CanonicalConverted) was present. So I changed it to Template->getDeclContext() to clarify the behavior.

However, I can revert the changes on getTemplateInstantiationArgs() after @sdkrystian's refactoring patch, so this is left only for the time being.

if (EnsureTemplateArgumentListConstraints(
Template, MLTAL,
SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) {
Expand Down
50 changes: 35 additions & 15 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3834,18 +3834,6 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
Result != TemplateDeductionResult::Success)
return Result;

// C++ [temp.deduct.call]p10: [DR1391]
// If deduction succeeds for all parameters that contain
// template-parameters that participate in template argument deduction,
// and all template arguments are explicitly specified, deduced, or
// obtained from default template arguments, remaining parameters are then
// compared with the corresponding arguments. For each remaining parameter
// P with a type that was non-dependent before substitution of any
// explicitly-specified template arguments, if the corresponding argument
// A cannot be implicitly converted to P, deduction fails.
if (CheckNonDependent())
return TemplateDeductionResult::NonDependentConversionFailure;

// Form the template argument list from the deduced template arguments.
TemplateArgumentList *SugaredDeducedArgumentList =
TemplateArgumentList::CreateCopy(Context, SugaredBuilder);
Expand Down Expand Up @@ -3875,6 +3863,40 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
FD = const_cast<FunctionDecl *>(FDFriend);
Owner = FD->getLexicalDeclContext();
}

// [DR2369]
// FIXME: We have to partially instantiate lambda's captures for constraint
// evaluation.
bool NeedConstraintChecking =
!PartialOverloading ||
CanonicalBuilder.size() ==
FunctionTemplate->getTemplateParameters()->size();
bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD);
#if 1
if (!IsLambda && NeedConstraintChecking) {
if (CheckFunctionConstraintsWithoutInstantiation(
Info.getLocation(), FunctionTemplate->getCanonicalDecl(),
CanonicalBuilder, Info.AssociatedConstraintsSatisfaction))
return TemplateDeductionResult::MiscellaneousDeductionFailure;
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
Info.reset(Info.takeSugared(),
TemplateArgumentList::CreateCopy(Context, CanonicalBuilder));
Comment on lines +3981 to +3982
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Frankly these lines are also copied from the previous failure handling)

I guess the instantiated concept-related nodes that were created during the checking would use the TemplateArgumentLists created on the ASTContext, or rather they would take ownership of the TemplateArgumentList. So, in order for the pilfered argument lists to work for other clients of TemplateDeductionInfo, we have to make a copy of the list.

(I saw the patch that tried to preserve sugars in concepts turn this line into "copy sugared arguments, but do nothing for canonical arguments")

But I'm not entirely sure of such deduction things, so probably @mizvekov would correct my understanding in some way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping @mizvekov

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, what @zyn0217 said is correct, the canonical argument list will have references persist in AST nodes, so it needs to be copied here.

return TemplateDeductionResult::ConstraintsNotSatisfied;
}
}
#endif
// C++ [temp.deduct.call]p10: [DR1391]
// If deduction succeeds for all parameters that contain
// template-parameters that participate in template argument deduction,
// and all template arguments are explicitly specified, deduced, or
// obtained from default template arguments, remaining parameters are then
// compared with the corresponding arguments. For each remaining parameter
// P with a type that was non-dependent before substitution of any
// explicitly-specified template arguments, if the corresponding argument
// A cannot be implicitly converted to P, deduction fails.
if (CheckNonDependent())
return TemplateDeductionResult::NonDependentConversionFailure;

MultiLevelTemplateArgumentList SubstArgs(
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
/*Final=*/false);
Expand Down Expand Up @@ -3909,9 +3931,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
// ([temp.constr.decl]), those constraints are checked for satisfaction
// ([temp.constr.constr]). If the constraints are not satisfied, type
// deduction fails.
if (!PartialOverloading ||
(CanonicalBuilder.size() ==
FunctionTemplate->getTemplateParameters()->size())) {
if (IsLambda && NeedConstraintChecking) {
if (CheckInstantiatedFunctionTemplateConstraints(
Info.getLocation(), Specialization, CanonicalBuilder,
Info.AssociatedConstraintsSatisfaction))
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/Sema/SemaTemplateDeductionGuide.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -904,10 +904,12 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef,
Context.getTrivialTypeSourceInfo(
Context.getDeducedTemplateSpecializationType(
TemplateName(AliasTemplate), /*DeducedType=*/QualType(),
/*IsDependent=*/true)), // template specialization type whose
// arguments will be deduced.
/*IsDependent=*/true),
AliasTemplate->getLocation()), // template specialization type whose
// arguments will be deduced.
Context.getTrivialTypeSourceInfo(
ReturnType), // type from which template arguments are deduced.
ReturnType, AliasTemplate->getLocation()), // type from which template
// arguments are deduced.
};
return TypeTraitExpr::Create(
Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(),
Expand Down
15 changes: 7 additions & 8 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
// has a depth of 0.
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl))
HandleDefaultTempArgIntoTempTempParam(TTP, Result);
CurDecl = Response::UseNextDecl(CurDecl).NextDecl;
CurDecl = DC ? Decl::castFromDeclContext(DC)
: Response::UseNextDecl(CurDecl).NextDecl;
}

while (!CurDecl->isFileContextDecl()) {
Expand Down Expand Up @@ -3240,15 +3241,13 @@ bool Sema::SubstDefaultArgument(
/*ForDefinition*/ false);
if (addInstantiatedParametersToScope(FD, PatternFD, *LIS, TemplateArgs))
return true;
const FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
if (PrimaryTemplate && PrimaryTemplate->isOutOfLine()) {
TemplateArgumentList *CurrentTemplateArgumentList =
TemplateArgumentList::CreateCopy(getASTContext(),
TemplateArgs.getInnermost());
// FIXME: Investigate if we shall validate every FunctionTemplateDecl
// along the getInstantiatedFromMemberTemplate() chain.
if (auto *PrimaryTemplate = FD->getPrimaryTemplate();
PrimaryTemplate && PrimaryTemplate->isOutOfLine())
NewTemplateArgs = getTemplateInstantiationArgs(
FD, FD->getDeclContext(), /*Final=*/false,
CurrentTemplateArgumentList->asArray(), /*RelativeToPrimary=*/true);
}
TemplateArgs.getInnermost(), /*RelativeToPrimary=*/true);
}

runWithSufficientStackSpace(Loc, [&] {
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "clang/Sema/SemaSwift.h"
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateInstCallback.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/TimeProfiler.h"
#include <optional>

Expand Down Expand Up @@ -2136,7 +2137,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
return nullptr;
QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo);

if (TemplateParams && TemplateParams->size()) {
if (false && TemplateParams && TemplateParams->size()) {
auto *LastParam =
dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back());
if (LastParam && LastParam->isImplicit() &&
Expand Down Expand Up @@ -2548,7 +2549,7 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
return nullptr;
QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo);

if (TemplateParams && TemplateParams->size()) {
if (false && TemplateParams && TemplateParams->size()) {
auto *LastParam =
dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back());
if (LastParam && LastParam->isImplicit() &&
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ class TreeTransform {
/// variables vector are acceptable.
///
/// LastParamTransformed, if non-null, will be set to the index of the last
/// parameter on which transfromation was started. In the event of an error,
/// parameter on which transformation was started. In the event of an error,
/// this will contain the parameter which failed to instantiate.
///
/// Return true on error.
Expand Down
Loading
Loading