Skip to content

Commit

Permalink
P0859R0: List-initialization is potentially-constant-evaluated and
Browse files Browse the repository at this point in the history
triggers instantiation of constexpr functions.

We mostly implemented this since Clang 6, but missed the template
instantiation case.

We do not implement the '&cast-expression' special case. It appears to
be a mistake / oversight. I've mailed CWG to see if we can remove it.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@343064 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
zygoloid committed Sep 26, 2018
1 parent ac26bee commit 471124b
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 10 deletions.
41 changes: 32 additions & 9 deletions lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -3386,6 +3386,11 @@ ExprResult TreeTransform<Derived>::TransformInitializer(Expr *Init,
if (Construct && Construct->isStdInitListInitialization())
return TransformInitializer(Construct->getArg(0), NotCopyInit);

// Enter a list-init context if this was list initialization.
EnterExpressionEvaluationContext Context(
getSema(), EnterExpressionEvaluationContext::InitList,
Construct->isListInitialization());

SmallVector<Expr*, 8> NewArgs;
bool ArgChanged = false;
if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(),
Expand Down Expand Up @@ -9549,6 +9554,9 @@ TreeTransform<Derived>::TransformInitListExpr(InitListExpr *E) {

bool InitChanged = false;

EnterExpressionEvaluationContext Context(
getSema(), EnterExpressionEvaluationContext::InitList);

SmallVector<Expr*, 4> Inits;
if (getDerived().TransformExprs(E->getInits(), E->getNumInits(), false,
Inits, &InitChanged))
Expand Down Expand Up @@ -10780,9 +10788,14 @@ TreeTransform<Derived>::TransformCXXConstructExpr(CXXConstructExpr *E) {

bool ArgumentChanged = false;
SmallVector<Expr*, 8> Args;
if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
&ArgumentChanged))
return ExprError();
{
EnterExpressionEvaluationContext Context(
getSema(), EnterExpressionEvaluationContext::InitList,
E->isListInitialization());
if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
&ArgumentChanged))
return ExprError();
}

if (!getDerived().AlwaysRebuild() &&
T == E->getType() &&
Expand Down Expand Up @@ -10865,9 +10878,14 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
bool ArgumentChanged = false;
SmallVector<Expr*, 8> Args;
Args.reserve(E->getNumArgs());
if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
&ArgumentChanged))
return ExprError();
{
EnterExpressionEvaluationContext Context(
getSema(), EnterExpressionEvaluationContext::InitList,
E->isListInitialization());
if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
&ArgumentChanged))
return ExprError();
}

if (!getDerived().AlwaysRebuild() &&
T == E->getTypeSourceInfo() &&
Expand Down Expand Up @@ -11159,9 +11177,14 @@ TreeTransform<Derived>::TransformCXXUnresolvedConstructExpr(
bool ArgumentChanged = false;
SmallVector<Expr*, 8> Args;
Args.reserve(E->arg_size());
if (getDerived().TransformExprs(E->arg_begin(), E->arg_size(), true, Args,
&ArgumentChanged))
return ExprError();
{
EnterExpressionEvaluationContext Context(
getSema(), EnterExpressionEvaluationContext::InitList,
E->isListInitialization());
if (getDerived().TransformExprs(E->arg_begin(), E->arg_size(), true, Args,
&ArgumentChanged))
return ExprError();
}

if (!getDerived().AlwaysRebuild() &&
T == E->getTypeSourceInfo() &&
Expand Down
39 changes: 39 additions & 0 deletions test/CXX/expr/expr.const/p6.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// RUN: %clang_cc1 -std=c++17 -verify %s

template<typename T> int not_constexpr() { return T::error; }
template<typename T> constexpr int is_constexpr() { return T::error; } // expected-error {{'::'}}

template<typename T> int not_constexpr_var = T::error;
template<typename T> constexpr int is_constexpr_var = T::error; // expected-error {{'::'}}
template<typename T> const int is_const_var = T::error; // expected-error {{'::'}}
template<typename T> const volatile int is_const_volatile_var = T::error;
template<typename T> T is_dependent_var = T::error; // expected-error {{'::'}}
template<typename T> int &is_reference_var = T::error; // expected-error {{'::'}}
template<typename T> float is_float_var = T::error;

void test() {
// Do not instantiate functions referenced in unevaluated operands...
(void)sizeof(not_constexpr<long>());
(void)sizeof(is_constexpr<long>());
(void)sizeof(not_constexpr_var<long>);
(void)sizeof(is_constexpr_var<long>);
(void)sizeof(is_const_var<long>);
(void)sizeof(is_const_volatile_var<long>);
(void)sizeof(is_dependent_var<long>);
(void)sizeof(is_dependent_var<const long>);
(void)sizeof(is_reference_var<long>);
(void)sizeof(is_float_var<long>);

// ... but do if they are potentially constant evaluated, and refer to
// constexpr functions or to variables usable in constant expressions.
(void)sizeof(int{not_constexpr<int>()});
(void)sizeof(int{is_constexpr<int>()}); // expected-note {{instantiation of}}
(void)sizeof(int{not_constexpr_var<int>});
(void)sizeof(int{is_constexpr_var<int>}); // expected-note {{instantiation of}}
(void)sizeof(int{is_const_var<int>}); // expected-note {{instantiation of}}
(void)sizeof(int{is_const_volatile_var<int>});
(void)sizeof(int{is_dependent_var<int>});
(void)sizeof(int{is_dependent_var<const int>}); // expected-note {{instantiation of}}
(void)sizeof(int{is_reference_var<int>}); // expected-note {{instantiation of}}
(void)sizeof(int{is_float_var<int>}); // expected-error {{cannot be narrowed}} expected-note {{cast}}
}
12 changes: 12 additions & 0 deletions test/CXX/temp/temp.spec/temp.inst/p7.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %clang_cc1 -verify -std=c++17 %s

template<typename T> constexpr int f() { return T::value; } // expected-error {{'::'}}
template<bool B, typename T> void g(decltype(B ? f<T>() : 0));
template<bool B, typename T> void g(...);
template<bool B, typename T> void h(decltype(int{B ? f<T>() : 0})); // expected-note {{instantiation of}}
template<bool B, typename T> void h(...);
void x() {
g<false, int>(0); // ok
g<true, int>(0); // ok
h<false, int>(0); // expected-note {{while substituting}}
}
2 changes: 1 addition & 1 deletion www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ <h2 id="cxx11">C++11 implementation status</h2>
<tr>
<!-- from Albuquerque 2017 -->
<td><a href="http://wg21.link/p0859r0">P0859R0</a> (<a href="#dr">DR</a>)</td>
<td class="none" align="center">No</td>
<td class="svn" align="center">SVN</td>
</tr>
<tr>
<td>Alignment support</td>
Expand Down

0 comments on commit 471124b

Please sign in to comment.