PR42108 Consistently diagnose binding a reference template parameter to

a temporary.

We previously failed to materialize a temporary when performing an
implicit conversion to a reference type, resulting in our thinking the
argument was a value rather than a reference in some cases.
This commit is contained in:
Richard Smith 2020-01-19 18:14:18 -08:00
parent 819421745c
commit 13fa4e2e5a
3 changed files with 26 additions and 2 deletions

View File

@ -3866,7 +3866,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
ICS.DiagnoseAmbiguousConversion(*this, From->getExprLoc(),
PDiag(diag::err_typecheck_ambiguous_condition)
<< From->getSourceRange());
return ExprError();
return ExprError();
case ImplicitConversionSequence::EllipsisConversion:
llvm_unreachable("Cannot perform an ellipsis conversion");
@ -4349,6 +4349,16 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
VK_RValue, nullptr, CCK).get();
}
// Materialize a temporary if we're implicitly converting to a reference
// type. This is not required by the C++ rules but is necessary to maintain
// AST invariants.
if (ToType->isReferenceType() && From->isRValue()) {
ExprResult Res = TemporaryMaterializationConversion(From);
if (Res.isInvalid())
return ExprError();
From = Res.get();
}
// If this conversion sequence succeeded and involved implicitly converting a
// _Nullable type to a _Nonnull one, complain.
if (!isCast(CCK))

View File

@ -6670,7 +6670,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
// -- a predefined __func__ variable
APValue::LValueBase Base = Value.getLValueBase();
auto *VD = const_cast<ValueDecl *>(Base.dyn_cast<const ValueDecl *>());
if (Base && !VD) {
if (Base && (!VD || isa<LifetimeExtendedTemporaryDecl>(VD))) {
auto *E = Base.dyn_cast<const Expr *>();
if (E && isa<CXXUuidofExpr>(E)) {
Converted = TemplateArgument(ArgResult.get()->IgnoreImpCasts());

View File

@ -434,3 +434,17 @@ namespace VoidPtr {
int n;
template void f<(void*)&n>();
}
namespace PR42108 {
struct R {};
struct S { constexpr S() {} constexpr S(R) {} };
struct T { constexpr operator S() { return {}; } };
template <const S &> struct A {};
void f() {
A<R{}>(); // expected-error {{would bind reference to a temporary}}
A<S{}>(); // expected-error {{non-type template argument is not a constant expression}} expected-note 2{{temporary}}
// FIXME: We could diagnose this better if we treated this as not binding
// directly. It's unclear whether that's the intent.
A<T{}>(); // expected-error {{non-type template argument is not a constant expression}} expected-note 2{{temporary}}
}
}