Skip to content

Commit 40ea01f

Browse files
committed
[clang] Convert a default argument expression to the parameter type...
...before checking that the default argument is valid with CheckDefaultArgumentVisitor. Currently the restrictions on a default argument are checked with the visitor CheckDefaultArgumentVisitor in ActOnParamDefaultArgument before performing the conversion to the parameter type in SetParamDefaultArgument. This was fine before the previous patch but now some valid code post-CWG 2346 is rejected: void test() { const int i2 = 0; extern void h2a(int x = i2); // FIXME: ok, not odr-use extern void h2b(int x = i2 + 0); // ok, not odr-use } This is because the reference to i2 in h2a has not been marked yet with NOUR_Constant. i2 is marked NOUR_Constant when the conversion to the parameter type is done, which is done just after. The solution is to do the conversion to the parameter type before checking the restrictions on default arguments with CheckDefaultArgumentVisitor. This has the side-benefit of improving some diagnostics. Differential Revision: https://reviews.llvm.org/D81616 Reviewed By: rsmith
1 parent e966a5d commit 40ea01f

File tree

7 files changed

+38
-30
lines changed

7 files changed

+38
-30
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2381,11 +2381,13 @@ class Sema final {
23812381
void ActOnParamDefaultArgument(Decl *param,
23822382
SourceLocation EqualLoc,
23832383
Expr *defarg);
2384-
void ActOnParamUnparsedDefaultArgument(Decl *param,
2385-
SourceLocation EqualLoc,
2384+
void ActOnParamUnparsedDefaultArgument(Decl *param, SourceLocation EqualLoc,
23862385
SourceLocation ArgLoc);
23872386
void ActOnParamDefaultArgumentError(Decl *param, SourceLocation EqualLoc);
2388-
bool SetParamDefaultArgument(ParmVarDecl *Param, Expr *DefaultArg,
2387+
ExprResult ConvertParamDefaultArgument(const ParmVarDecl *Param,
2388+
Expr *DefaultArg,
2389+
SourceLocation EqualLoc);
2390+
void SetParamDefaultArgument(ParmVarDecl *Param, Expr *DefaultArg,
23892391
SourceLocation EqualLoc);
23902392

23912393
// Contexts where using non-trivial C union types can be disallowed. This is

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -254,14 +254,12 @@ void Sema::ImplicitExceptionSpecification::CalledStmt(Stmt *S) {
254254
ComputedEST = EST_None;
255255
}
256256

257-
bool
258-
Sema::SetParamDefaultArgument(ParmVarDecl *Param, Expr *Arg,
259-
SourceLocation EqualLoc) {
257+
ExprResult Sema::ConvertParamDefaultArgument(const ParmVarDecl *Param,
258+
Expr *Arg,
259+
SourceLocation EqualLoc) {
260260
if (RequireCompleteType(Param->getLocation(), Param->getType(),
261-
diag::err_typecheck_decl_incomplete_type)) {
262-
Param->setInvalidDecl();
261+
diag::err_typecheck_decl_incomplete_type))
263262
return true;
264-
}
265263

266264
// C++ [dcl.fct.default]p5
267265
// A default argument expression is implicitly converted (clause
@@ -282,7 +280,12 @@ Sema::SetParamDefaultArgument(ParmVarDecl *Param, Expr *Arg,
282280
CheckCompletedExpr(Arg, EqualLoc);
283281
Arg = MaybeCreateExprWithCleanups(Arg);
284282

285-
// Okay: add the default argument to the parameter
283+
return Arg;
284+
}
285+
286+
void Sema::SetParamDefaultArgument(ParmVarDecl *Param, Expr *Arg,
287+
SourceLocation EqualLoc) {
288+
// Add the default argument to the parameter
286289
Param->setDefaultArg(Arg);
287290

288291
// We have already instantiated this parameter; provide each of the
@@ -296,8 +299,6 @@ Sema::SetParamDefaultArgument(ParmVarDecl *Param, Expr *Arg,
296299
// We're done tracking this parameter's instantiations.
297300
UnparsedDefaultArgInstantiations.erase(InstPos);
298301
}
299-
300-
return false;
301302
}
302303

303304
/// ActOnParamDefaultArgument - Check whether the default argument
@@ -341,13 +342,18 @@ Sema::ActOnParamDefaultArgument(Decl *param, SourceLocation EqualLoc,
341342
return;
342343
}
343344

345+
ExprResult Result = ConvertParamDefaultArgument(Param, DefaultArg, EqualLoc);
346+
if (Result.isInvalid())
347+
return Fail();
348+
349+
DefaultArg = Result.getAs<Expr>();
350+
344351
// Check that the default argument is well-formed
345352
CheckDefaultArgumentVisitor DefaultArgChecker(*this, DefaultArg);
346353
if (DefaultArgChecker.Visit(DefaultArg))
347354
return Fail();
348355

349-
if (SetParamDefaultArgument(Param, DefaultArg, EqualLoc))
350-
return Fail();
356+
SetParamDefaultArgument(Param, DefaultArg, EqualLoc);
351357
}
352358

353359
/// ActOnParamUnparsedDefaultArgument - We've seen a default

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2405,7 +2405,12 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
24052405
if (NewArg.isUsable()) {
24062406
// It would be nice if we still had this.
24072407
SourceLocation EqualLoc = NewArg.get()->getBeginLoc();
2408-
SetParamDefaultArgument(NewParm, NewArg.get(), EqualLoc);
2408+
ExprResult Result =
2409+
ConvertParamDefaultArgument(NewParm, NewArg.get(), EqualLoc);
2410+
if (Result.isInvalid())
2411+
return nullptr;
2412+
2413+
SetParamDefaultArgument(NewParm, Result.getAs<Expr>(), EqualLoc);
24092414
}
24102415
} else {
24112416
// FIXME: if we non-lazily instantiated non-dependent default args for

clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ void h() {
66
// expected-error@-1 {{default argument references local variable 'i1' of enclosing function}}
77

88
const int i2 = 0;
9-
extern void h2a(int x = i2); // FIXME: ok, not odr-use
10-
// expected-error@-1 {{default argument references local variable 'i2' of enclosing function}}
9+
extern void h2a(int x = i2); // ok, not odr-use
1110
extern void h2b(int x = i2 + 0); // ok, not odr-use
1211

1312
const int i3 = 0;

clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p8.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,10 @@ class A {
66
};
77

88
void A::test() {
9-
void g(int = this); // expected-error {{default argument references 'this'}}
9+
void g(int = this);
10+
// expected-error@-1 {{cannot initialize a parameter of type 'int' with an rvalue of type 'A *'}}
11+
// expected-note@-2 {{passing argument to parameter here}}
12+
13+
void h(int = ((void)this,42));
14+
// expected-error@-1 {{default argument references 'this'}}
1015
}

clang/test/SemaCXX/vartemplate-lambda.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ template <typename> void foo0() { fn0<char>(); }
66
template<typename T> auto fn1 = [](auto a) { return a + T(1); };
77
template<typename T> auto v1 = [](int a = T()) { return a; }();
88
// expected-error@-1{{cannot initialize a parameter of type 'int' with an rvalue of type 'int *'}}
9-
// expected-error@-2{{no matching function for call}}
10-
// expected-note@-3{{passing argument to parameter 'a' here}}
11-
// expected-note@-4{{candidate function not viable}}
12-
// expected-note@-5{{conversion candidate of type 'int (*)(int)'}}
9+
// expected-note@-2{{passing argument to parameter 'a' here}}
1310

1411
struct S {
1512
template<class T>

clang/test/SemaTemplate/instantiate-local-class.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -462,18 +462,15 @@ namespace rdar23721638 {
462462
struct Inner { // expected-note {{in instantiation}}
463463
void operator()(T a = "") {} // expected-error {{conversion function from 'const char [1]' to 'rdar23721638::A' invokes a deleted function}}
464464
// expected-note@-1 {{passing argument to parameter 'a' here}}
465-
// expected-note@-2 {{candidate function not viable}}
466465
};
467-
Inner()(); // expected-error {{no matching function}}
466+
Inner()(); // expected-error {{type 'Inner' does not provide a call operator}}
468467
}
469468
template void foo<A>(); // expected-note 2 {{in instantiation}}
470469

471470
template <typename T> void bar() {
472471
auto lambda = [](T a = "") {}; // expected-error {{conversion function from 'const char [1]' to 'rdar23721638::A' invokes a deleted function}}
473472
// expected-note@-1 {{passing argument to parameter 'a' here}}
474-
// expected-note@-2 {{candidate function not viable}}
475-
// expected-note@-3 {{conversion candidate of type}}
476-
lambda(); // expected-error {{no matching function}}
473+
lambda();
477474
}
478475
template void bar<A>(); // expected-note {{in instantiation}}
479476
}
@@ -494,9 +491,6 @@ namespace PR45000 {
494491
void f(int x = [](T x = nullptr) -> int { return x; }());
495492
// expected-error@-1 {{cannot initialize a parameter of type 'int' with an rvalue of type 'nullptr_t'}}
496493
// expected-note@-2 {{passing argument to parameter 'x' here}}
497-
// expected-error@-3 {{no matching function for call}}
498-
// expected-note@-4 {{candidate function not viable: requires single argument 'x', but no arguments were provided}}
499-
// expected-note@-5 {{conversion candidate of type 'auto (*)(int) -> int'}}
500494

501495
void g() { f<int>(); }
502496
// expected-note@-1 {{in instantiation of default function argument expression for 'f<int>' required here}}

0 commit comments

Comments
 (0)