Skip to content

Commit 573b652

Browse files
authored
Instantiate argument default parameters (#5443)
This addresses a latent bug in Clang that was fixed _way_ after DXC was forked. We encounter it in DXC because of the way that we inject AST bits through the external sema source, but it likely wasn't exposable through C++. This change is mostly a back porting of the following upstream LLVM commits: llvm-project/73c6a2448f24 llvm-project/f721e0582b15 llvm-project/c601377b2376 llvm-project/4409a83c2935 The end result of this change is that we still lazily instantiate default arguments, but we do force them to be instantiated during sema when they are used. Fixes #5145
1 parent 051b05c commit 573b652

10 files changed

Lines changed: 397 additions & 51 deletions

File tree

tools/clang/include/clang/AST/DeclBase.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,22 @@ class Decl {
721721
return getParentFunctionOrMethod() == nullptr;
722722
}
723723

724+
/// HLSL Change Begin - back port from llvm-project/73c6a2448f24 &
725+
/// f721e0582b15. Determine whether a substitution into this declaration would
726+
/// occur as part of a substitution into a dependent local scope. Such a
727+
/// substitution transitively substitutes into all constructs nested within
728+
/// this declaration.
729+
///
730+
/// This recognizes non-defining declarations as well as members of local
731+
/// classes and lambdas:
732+
/// \code
733+
/// template<typename T> void foo() { void bar(); }
734+
/// template<typename T> void foo2() { class ABC { void bar(); }; }
735+
/// template<typename T> inline int x = [](){ return 0; }();
736+
/// \endcode
737+
bool isInLocalScopeForInstantiation() const;
738+
/// HLSL Change End - back port from llvm-project/73c6a2448f24 & f721e0582b15.
739+
724740
/// \brief If this decl is defined inside a function/method/block it returns
725741
/// the corresponding DeclContext, otherwise it returns null.
726742
const DeclContext *getParentFunctionOrMethod() const;

tools/clang/include/clang/Sema/Sema.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2736,6 +2736,13 @@ class Sema {
27362736
const ObjCObjectPointerType *OPT,
27372737
bool ErrorRecovery);
27382738

2739+
/// HLSL Change Begin - back ported from llvm-project/c601377b2376.
2740+
bool addInstantiatedParametersToScope(
2741+
FunctionDecl *Function, const FunctionDecl *PatternDecl,
2742+
LocalInstantiationScope &Scope,
2743+
const MultiLevelTemplateArgumentList &TemplateArgs);
2744+
/// HLSL Change End - back ported from llvm-project/c601377b2376.
2745+
27392746
public:
27402747
const TypoExprState &getTypoExprState(TypoExpr *TE) const;
27412748

@@ -6973,6 +6980,12 @@ class Sema {
69736980
const MultiLevelTemplateArgumentList &TemplateArgs,
69746981
SmallVectorImpl<QualType> &ParamTypes,
69756982
SmallVectorImpl<ParmVarDecl *> *OutParams = nullptr);
6983+
/// HLSL Change Begin - back ported from llvm-project/4409a83c2935.
6984+
bool SubstDefaultArgument(SourceLocation Loc, ParmVarDecl *Param,
6985+
const MultiLevelTemplateArgumentList &TemplateArgs,
6986+
bool ForCallExpr = false);
6987+
/// HLSL Change End - back ported from llvm-project/4409a83c2935.
6988+
69766989
ExprResult SubstExpr(Expr *E,
69776990
const MultiLevelTemplateArgumentList &TemplateArgs);
69786991

tools/clang/lib/AST/DeclBase.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,25 @@ void Decl::setDeclContextsImpl(DeclContext *SemaDC, DeclContext *LexicalDC,
254254
}
255255
}
256256

257+
/// HLSL Change Begin - back port from llvm-project/73c6a2448f24 & f721e0582b15.
258+
bool Decl::isInLocalScopeForInstantiation() const {
259+
const DeclContext *LDC = getLexicalDeclContext();
260+
if (!LDC->isDependentContext())
261+
return false;
262+
while (true) {
263+
if (LDC->isFunctionOrMethod())
264+
return true;
265+
if (!isa<TagDecl>(LDC))
266+
return false;
267+
if (const auto *CRD = dyn_cast<CXXRecordDecl>(LDC))
268+
if (CRD->isLambda())
269+
return true;
270+
LDC = LDC->getLexicalParent();
271+
}
272+
return false;
273+
}
274+
/// HLSL Change End - back port from llvm-project/73c6a2448f24 & f721e0582b15.
275+
257276
bool Decl::isInAnonymousNamespace() const {
258277
const DeclContext *DC = getDeclContext();
259278
do {

tools/clang/lib/Sema/SemaExpr.cpp

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4380,50 +4380,15 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
43804380
}
43814381

43824382
if (Param->hasUninstantiatedDefaultArg()) {
4383-
Expr *UninstExpr = Param->getUninstantiatedDefaultArg();
4384-
4385-
EnterExpressionEvaluationContext EvalContext(*this, PotentiallyEvaluated,
4386-
Param);
4387-
4383+
/// HLSL Change Begin - back ported from llvm-project/4409a83c2935.
43884384
// Instantiate the expression.
43894385
MultiLevelTemplateArgumentList MutiLevelArgList
43904386
= getTemplateInstantiationArgs(FD, nullptr, /*RelativeToPrimary=*/true);
43914387

4392-
InstantiatingTemplate Inst(*this, CallLoc, Param,
4393-
MutiLevelArgList.getInnermost());
4394-
if (Inst.isInvalid())
4395-
return ExprError();
4396-
4397-
ExprResult Result;
4398-
{
4399-
// C++ [dcl.fct.default]p5:
4400-
// The names in the [default argument] expression are bound, and
4401-
// the semantic constraints are checked, at the point where the
4402-
// default argument expression appears.
4403-
ContextRAII SavedContext(*this, FD);
4404-
LocalInstantiationScope Local(*this);
4405-
Result = SubstExpr(UninstExpr, MutiLevelArgList);
4406-
}
4407-
if (Result.isInvalid())
4388+
if (SubstDefaultArgument(CallLoc, Param, MutiLevelArgList,
4389+
/*ForCallExpr*/ true))
44084390
return ExprError();
4409-
4410-
// Check the expression as an initializer for the parameter.
4411-
InitializedEntity Entity
4412-
= InitializedEntity::InitializeParameter(Context, Param);
4413-
InitializationKind Kind
4414-
= InitializationKind::CreateCopy(Param->getLocation(),
4415-
/*FIXME:EqualLoc*/UninstExpr->getLocStart());
4416-
Expr *ResultE = Result.getAs<Expr>();
4417-
4418-
InitializationSequence InitSeq(*this, Entity, Kind, ResultE);
4419-
Result = InitSeq.Perform(*this, Entity, Kind, ResultE);
4420-
if (Result.isInvalid())
4421-
return ExprError();
4422-
4423-
Expr *Arg = Result.getAs<Expr>();
4424-
CheckCompletedExpr(Arg, Param->getOuterLocStart());
4425-
// Build the default argument expression.
4426-
return CXXDefaultArgExpr::Create(Context, CallLoc, Param, Arg);
4391+
/// HLSL Change End - back ported from llvm-project/4409a83c2935.
44274392
}
44284393

44294394
// If the default expression creates temporaries, we need to

tools/clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,86 @@ bool Sema::SubstParmTypes(SourceLocation Loc,
17421742
OutParams);
17431743
}
17441744

1745+
/// HLSL Change Begin - back ported from llvm-project/4409a83c2935.
1746+
/// Substitute the given template arguments into the default argument.
1747+
bool Sema::SubstDefaultArgument(
1748+
SourceLocation Loc,
1749+
ParmVarDecl *Param,
1750+
const MultiLevelTemplateArgumentList &TemplateArgs,
1751+
bool ForCallExpr) {
1752+
FunctionDecl *FD = cast<FunctionDecl>(Param->getDeclContext());
1753+
Expr *PatternExpr = Param->getUninstantiatedDefaultArg();
1754+
1755+
EnterExpressionEvaluationContext EvalContext(
1756+
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Param);
1757+
1758+
InstantiatingTemplate Inst(*this, Loc, Param, TemplateArgs.getInnermost());
1759+
if (Inst.isInvalid())
1760+
return true;
1761+
1762+
ExprResult Result;
1763+
{
1764+
// C++ [dcl.fct.default]p5:
1765+
// The names in the [default argument] expression are bound, and
1766+
// the semantic constraints are checked, at the point where the
1767+
// default argument expression appears.
1768+
ContextRAII SavedContext(*this, FD);
1769+
std::unique_ptr<LocalInstantiationScope> LIS;
1770+
1771+
if (ForCallExpr) {
1772+
// When instantiating a default argument due to use in a call expression,
1773+
// an instantiation scope that includes the parameters of the callee is
1774+
// required to satisfy references from the default argument. For example:
1775+
// template<typename T> void f(T a, int = decltype(a)());
1776+
// void g() { f(0); }
1777+
LIS = std::make_unique<LocalInstantiationScope>(*this);
1778+
FunctionDecl *PatternFD = FD->getTemplateInstantiationPattern();
1779+
if (addInstantiatedParametersToScope(FD, PatternFD, *LIS, TemplateArgs))
1780+
return true;
1781+
}
1782+
1783+
Result = SubstInitializer(PatternExpr, TemplateArgs,
1784+
/*DirectInit*/false);
1785+
}
1786+
if (Result.isInvalid())
1787+
return true;
1788+
1789+
if (ForCallExpr) {
1790+
// Check the expression as an initializer for the parameter.
1791+
if (RequireCompleteType(Param->getLocation(), Param->getType(),
1792+
diag::err_typecheck_decl_incomplete_type))
1793+
return true;
1794+
InitializedEntity Entity
1795+
= InitializedEntity::InitializeParameter(Context, Param);
1796+
InitializationKind Kind = InitializationKind::CreateCopy(
1797+
Param->getLocation(),
1798+
/*FIXME:EqualLoc*/ PatternExpr->getLocStart());
1799+
Expr *ResultE = Result.getAs<Expr>();
1800+
1801+
InitializationSequence InitSeq(*this, Entity, Kind, ResultE);
1802+
Result = InitSeq.Perform(*this, Entity, Kind, ResultE);
1803+
if (Result.isInvalid())
1804+
return true;
1805+
1806+
Result =
1807+
ActOnFinishFullExpr(Result.getAs<Expr>(), Param->getOuterLocStart(),
1808+
/*DiscardedValue*/ false);
1809+
} else {
1810+
// FIXME: Obtain the source location for the '=' token.
1811+
SourceLocation EqualLoc = PatternExpr->getLocStart();
1812+
Result = SetParamDefaultArgument(Param, Result.getAs<Expr>(), EqualLoc);
1813+
}
1814+
if (Result.isInvalid())
1815+
return true;
1816+
1817+
// Remember the instantiated default argument.
1818+
Param->setDefaultArg(Result.getAs<Expr>());
1819+
1820+
return false;
1821+
}
1822+
1823+
/// HLSL Change End - back ported from llvm-project/4409a83c2935.
1824+
17451825
/// \brief Perform substitution on the base class specifiers of the
17461826
/// given class template specialization.
17471827
///

tools/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,33 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
15651565
Previous.clear();
15661566
}
15671567

1568+
// HLSL Change Begin - back ported from llvm-project/4409a83c2935.
1569+
// Per [temp.inst], default arguments in function declarations at local scope
1570+
// are instantiated along with the enclosing declaration. For example:
1571+
//
1572+
// template<typename T>
1573+
// void ft() {
1574+
// void f(int = []{ return T::value; }());
1575+
// }
1576+
// template void ft<int>(); // error: type 'int' cannot be used prior
1577+
// to '::' because it has no members
1578+
//
1579+
// The error is issued during instantiation of ft<int>() because substitution
1580+
// into the default argument fails; the default argument is instantiated even
1581+
// though it is never used.
1582+
if (Function->isLocalExternDecl()) {
1583+
for (ParmVarDecl *PVD : Function->parameters()) {
1584+
if (!PVD->hasDefaultArg())
1585+
continue;
1586+
if (SemaRef.SubstDefaultArgument(D->getInnerLocStart(), PVD,
1587+
TemplateArgs)) {
1588+
Function->setInvalidDecl();
1589+
return nullptr;
1590+
}
1591+
}
1592+
}
1593+
// HLSL Change End - back ported from llvm-project/4409a83c2935.
1594+
15681595
SemaRef.CheckFunctionDeclaration(/*Scope*/ nullptr, Function, Previous,
15691596
isExplicitSpecialization);
15701597

@@ -1862,6 +1889,33 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
18621889
Previous.clear();
18631890
}
18641891

1892+
// HLSL Change Begin - back ported from llvm-project/4409a83c2935.
1893+
// Per [temp.inst], default arguments in member functions of local classes
1894+
// are instantiated along with the member function declaration. For example:
1895+
//
1896+
// template<typename T>
1897+
// void ft() {
1898+
// struct lc {
1899+
// int operator()(int p = []{ return T::value; }());
1900+
// };
1901+
// }
1902+
// template void ft<int>(); // error: type 'int' cannot be used prior
1903+
// to '::'because it has no members
1904+
//
1905+
// The error is issued during instantiation of ft<int>()::lc::operator()
1906+
// because substitution into the default argument fails; the default argument
1907+
// is instantiated even though it is never used.
1908+
if (D->isInLocalScopeForInstantiation()) {
1909+
for (unsigned P = 0; P < Params.size(); ++P) {
1910+
if (!Params[P]->hasDefaultArg())
1911+
continue;
1912+
if (SemaRef.SubstDefaultArgument(StartLoc, Params[P], TemplateArgs)) {
1913+
return nullptr;
1914+
}
1915+
}
1916+
}
1917+
// HLSL Change End - back ported from llvm-project/4409a83c2935.
1918+
18651919
if (!IsClassScopeSpecialization)
18661920
SemaRef.CheckFunctionDeclaration(nullptr, Method, Previous, false);
18671921

@@ -3127,10 +3181,11 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
31273181
/// Introduce the instantiated function parameters into the local
31283182
/// instantiation scope, and set the parameter names to those used
31293183
/// in the template.
3130-
static bool addInstantiatedParametersToScope(Sema &S, FunctionDecl *Function,
3131-
const FunctionDecl *PatternDecl,
3132-
LocalInstantiationScope &Scope,
3133-
const MultiLevelTemplateArgumentList &TemplateArgs) {
3184+
/// HLSL Change Begin - back ported from llvm-project/601377b23767.
3185+
bool Sema::addInstantiatedParametersToScope(
3186+
FunctionDecl *Function, const FunctionDecl *PatternDecl,
3187+
LocalInstantiationScope &Scope,
3188+
const MultiLevelTemplateArgumentList &TemplateArgs) {
31343189
unsigned FParamIdx = 0;
31353190
for (unsigned I = 0, N = PatternDecl->getNumParams(); I != N; ++I) {
31363191
const ParmVarDecl *PatternParam = PatternDecl->getParamDecl(I);
@@ -3146,7 +3201,7 @@ static bool addInstantiatedParametersToScope(Sema &S, FunctionDecl *Function,
31463201
// it's instantiation-dependent.
31473202
// FIXME: Updating the type to work around this is at best fragile.
31483203
if (!PatternDecl->getType()->isDependentType()) {
3149-
QualType T = S.SubstType(PatternParam->getType(), TemplateArgs,
3204+
QualType T = SubstType(PatternParam->getType(), TemplateArgs,
31503205
FunctionParam->getLocation(),
31513206
FunctionParam->getDeclName());
31523207
if (T.isNull())
@@ -3162,7 +3217,7 @@ static bool addInstantiatedParametersToScope(Sema &S, FunctionDecl *Function,
31623217
// Expand the parameter pack.
31633218
Scope.MakeInstantiatedLocalArgPack(PatternParam);
31643219
Optional<unsigned> NumArgumentsInExpansion
3165-
= S.getNumArgumentsInExpansion(PatternParam->getType(), TemplateArgs);
3220+
= getNumArgumentsInExpansion(PatternParam->getType(), TemplateArgs);
31663221
assert(NumArgumentsInExpansion &&
31673222
"should only be called when all template arguments are known");
31683223
QualType PatternType =
@@ -3171,8 +3226,8 @@ static bool addInstantiatedParametersToScope(Sema &S, FunctionDecl *Function,
31713226
ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx);
31723227
FunctionParam->setDeclName(PatternParam->getDeclName());
31733228
if (!PatternDecl->getType()->isDependentType()) {
3174-
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(S, Arg);
3175-
QualType T = S.SubstType(PatternType, TemplateArgs,
3229+
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(*this, Arg);
3230+
QualType T = SubstType(PatternType, TemplateArgs,
31763231
FunctionParam->getLocation(),
31773232
FunctionParam->getDeclName());
31783233
if (T.isNull())
@@ -3187,6 +3242,7 @@ static bool addInstantiatedParametersToScope(Sema &S, FunctionDecl *Function,
31873242

31883243
return false;
31893244
}
3245+
/// HLSL Change End - back ported from llvm-project/601377b23767.
31903246

31913247
void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
31923248
FunctionDecl *Decl) {
@@ -3212,7 +3268,8 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
32123268
getTemplateInstantiationArgs(Decl, nullptr, /*RelativeToPrimary*/true);
32133269

32143270
FunctionDecl *Template = Proto->getExceptionSpecTemplate();
3215-
if (addInstantiatedParametersToScope(*this, Decl, Template, Scope,
3271+
// HLSL Change - back ported from llvm-project/601377b23767.
3272+
if (addInstantiatedParametersToScope(Decl, Template, Scope,
32163273
TemplateArgs)) {
32173274
UpdateExceptionSpec(Decl, EST_None);
32183275
return;
@@ -3494,7 +3551,8 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
34943551
// PushDeclContext because we don't have a scope.
34953552
Sema::ContextRAII savedContext(*this, Function);
34963553

3497-
if (addInstantiatedParametersToScope(*this, Function, PatternDecl, Scope,
3554+
// HLSL Change - back ported from llvm-project/601377b23767.
3555+
if (addInstantiatedParametersToScope(Function, PatternDecl, Scope,
34983556
TemplateArgs))
34993557
return;
35003558

tools/clang/test/CodeGenSPIRV/fn.param.default.hlsl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ float4 main(uint vertex_id : SV_VertexID) : SV_Position
1212
// CHECK: OpStore %param_var_b %float_2
1313
// CHECK: [[first:%\d+]] = OpFunctionCall %float %test %param_var_a %param_var_b
1414
// CHECK: OpStore %param_var_a_0 %float_4
15-
// CHECK: [[default:%\d+]] = OpConvertSToF %float %int_0
16-
// CHECK: OpStore %param_var_b_0 [[default]]
15+
// CHECK: OpStore %param_var_b_0 %float_0
1716
// CHECK: [[second:%\d+]] = OpFunctionCall %float %test %param_var_a_0 %param_var_b_0
1817
// CHECK: {{%\d+}} = OpCompositeConstruct %v4float [[first]] [[second]] %float_0 %float_0
1918
return float4(test<float>(1,2), test<float>(4), 0, 0);

0 commit comments

Comments
 (0)