re PR c++/50545 ([C++0x][DR 1172] SFINAE does not handle an explicit type conversion (functional notation) with a braced-init-list well if target type is not dependent)

PR c++/50545
	PR c++/51222
	* pt.c (instantiation_dependent_r): New.
	(instantiation_dependent_expression_p): New.
	(value_dependent_expression_p): Use it.  SCOPE_REF is always dependent.
	* semantics.c (finish_decltype_type): Use it.
	* cp-tree.h: Declare it.

From-SVN: r190830
This commit is contained in:
Jason Merrill 2012-08-30 22:50:28 -04:00 committed by Jason Merrill
parent e467c9d257
commit 2c90550220
7 changed files with 293 additions and 28 deletions

View File

@ -1,5 +1,13 @@
2012-08-30 Jason Merrill <jason@redhat.com>
PR c++/50545
PR c++/51222
* pt.c (instantiation_dependent_r): New.
(instantiation_dependent_expression_p): New.
(value_dependent_expression_p): Use it. SCOPE_REF is always dependent.
* semantics.c (finish_decltype_type): Use it.
* cp-tree.h: Declare it.
* semantics.c (finish_qualified_id_expr): Handle building up a
non-dependent SCOPE_REF here.
(finish_id_expression): Not here.

View File

@ -5391,6 +5391,7 @@ extern bool any_type_dependent_arguments_p (const VEC(tree,gc) *);
extern bool any_type_dependent_elements_p (const_tree);
extern bool type_dependent_expression_p_push (tree);
extern bool value_dependent_expression_p (tree);
extern bool instantiation_dependent_expression_p (tree);
extern bool any_value_dependent_elements_p (const_tree);
extern bool dependent_omp_for_p (tree, tree, tree, tree);
extern tree resolve_typename_type (tree, bool);

View File

@ -19100,20 +19100,7 @@ dependent_scope_p (tree scope)
/* Note that this predicate is not appropriate for general expressions;
only constant expressions (that satisfy potential_constant_expression)
can be tested for value dependence.
We should really also have a predicate for "instantiation-dependent".
fold_non_dependent_expr: fold if constant and not type-dependent and not value-dependent.
(what about instantiation-dependent constant-expressions?)
is_late_template_attribute: defer if instantiation-dependent.
compute_array_index_type: proceed if constant and not t- or v-dependent
if instantiation-dependent, need to remember full expression
uses_template_parms: FIXME - need to audit callers
tsubst_decl [function_decl]: Why is this using value_dependent_expression_p?
dependent_type_p [array_type]: dependent if index type is dependent
(or non-constant?)
static_assert - instantiation-dependent */
can be tested for value dependence. */
bool
value_dependent_expression_p (tree expression)
@ -19193,7 +19180,7 @@ value_dependent_expression_p (tree expression)
return true;
else if (TYPE_P (expression))
return dependent_type_p (expression);
return type_dependent_expression_p (expression);
return instantiation_dependent_expression_p (expression);
case AT_ENCODE_EXPR:
/* An 'encode' expression is value-dependent if the operand is
@ -19203,13 +19190,13 @@ value_dependent_expression_p (tree expression)
case NOEXCEPT_EXPR:
expression = TREE_OPERAND (expression, 0);
return type_dependent_expression_p (expression);
return instantiation_dependent_expression_p (expression);
case SCOPE_REF:
{
tree name = TREE_OPERAND (expression, 1);
return value_dependent_expression_p (name);
}
/* instantiation_dependent_r treats this as dependent so that we
check access at instantiation time, and all instantiation-dependent
expressions should also be considered value-dependent. */
return true;
case COMPONENT_REF:
return (value_dependent_expression_p (TREE_OPERAND (expression, 0))
@ -19488,6 +19475,104 @@ type_dependent_expression_p (tree expression)
return (dependent_type_p (TREE_TYPE (expression)));
}
/* walk_tree callback function for instantiation_dependent_expression_p,
below. Returns non-zero if a dependent subexpression is found. */
static tree
instantiation_dependent_r (tree *tp, int *walk_subtrees,
void *data ATTRIBUTE_UNUSED)
{
if (TYPE_P (*tp))
{
/* We don't have to worry about decltype currently because decltype
of an instantiation-dependent expr is a dependent type. This
might change depending on the resolution of DR 1172. */
*walk_subtrees = false;
return NULL_TREE;
}
enum tree_code code = TREE_CODE (*tp);
switch (code)
{
/* Don't treat an argument list as dependent just because it has no
TREE_TYPE. */
case TREE_LIST:
case TREE_VEC:
return NULL_TREE;
case TEMPLATE_PARM_INDEX:
return *tp;
/* Handle expressions with type operands. */
case SIZEOF_EXPR:
case ALIGNOF_EXPR:
case TYPEID_EXPR:
case AT_ENCODE_EXPR:
case TRAIT_EXPR:
{
tree op = TREE_OPERAND (*tp, 0);
if (TYPE_P (op))
{
if (dependent_type_p (op)
|| (code == TRAIT_EXPR
&& dependent_type_p (TREE_OPERAND (*tp, 1))))
return *tp;
else
{
*walk_subtrees = false;
return NULL_TREE;
}
}
break;
}
case COMPONENT_REF:
if (TREE_CODE (TREE_OPERAND (*tp, 1)) == IDENTIFIER_NODE)
/* In a template, finish_class_member_access_expr creates a
COMPONENT_REF with an IDENTIFIER_NODE for op1 even if it isn't
type-dependent, so that we can check access control at
instantiation time (PR 42277). See also Core issue 1273. */
return *tp;
break;
case SCOPE_REF:
/* Similarly, finish_qualified_id_expr builds up a SCOPE_REF in a
template so that we can check access at instantiation time even
though we know which member it resolves to. */
return *tp;
default:
break;
}
if (type_dependent_expression_p (*tp))
return *tp;
else
return NULL_TREE;
}
/* Returns TRUE if the EXPRESSION is instantiation-dependent, in the
sense defined by the ABI:
"An expression is instantiation-dependent if it is type-dependent
or value-dependent, or it has a subexpression that is type-dependent
or value-dependent." */
bool
instantiation_dependent_expression_p (tree expression)
{
tree result;
if (!processing_template_decl)
return false;
if (expression == error_mark_node)
return false;
result = cp_walk_tree_without_duplicates (&expression,
instantiation_dependent_r, NULL);
return result != NULL_TREE;
}
/* Like type_dependent_expression_p, but it also works while not processing
a template definition, i.e. during substitution or mangling. */

View File

@ -5187,14 +5187,10 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
return error_mark_node;
}
/* FIXME instantiation-dependent */
if (type_dependent_expression_p (expr)
/* In a template, a COMPONENT_REF has an IDENTIFIER_NODE for op1 even
if it isn't dependent, so that we can check access control at
instantiation time, so defer the decltype as well (PR 42277). */
|| (id_expression_or_member_access_p
&& processing_template_decl
&& TREE_CODE (expr) == COMPONENT_REF))
/* Depending on the resolution of DR 1172, we may later need to distinguish
instantiation-dependent but not type-dependent expressions so that, say,
A<decltype(sizeof(T))>::U doesn't require 'typename'. */
if (instantiation_dependent_expression_p (expr))
{
type = cxx_make_type (DECLTYPE_TYPE);
DECLTYPE_TYPE_EXPR (type) = expr;

View File

@ -0,0 +1,101 @@
// PR c++/51222
// { dg-options -std=c++11 }
template<class T>
struct add_rref {
typedef T&& type;
};
template<>
struct add_rref<void> {
typedef void type;
};
template<class T>
typename add_rref<T>::type declval();
template<class T, class U, class =
decltype(::delete ::new T(declval<U>()))
>
auto f(int) -> char;
template<class, class>
auto f(...) -> char(&)[2];
template<class T, class =
decltype(::delete ::new T())
>
auto g(int) -> char;
template<class>
auto g(...) -> char(&)[2];
template<class T, class U>
auto f2(int) -> decltype(::delete ::new T(declval<U>()), char());
template<class, class>
auto f2(...) -> char(&)[2];
template<class T>
auto g2(int) -> decltype(::delete ::new T(), char());
template<class>
auto g2(...) -> char(&)[2];
struct C { };
struct A {
virtual ~A() = 0;
};
struct D1 {
D1() = delete;
};
struct D2 {
~D2() = delete;
};
static_assert(sizeof(g<void>(0)) == 2, "Ouch");
static_assert(sizeof(g<void()>(0)) == 2, "Ouch");
static_assert(sizeof(g<void() const>(0)) == 2, "Ouch");
static_assert(sizeof(g<A>(0)) == 2, "Ouch");
static_assert(sizeof(g<D1>(0)) == 2, "Ouch");
static_assert(sizeof(g<D2>(0)) == 2, "Ouch");
static_assert(sizeof(g<int&>(0)) == 2, "Ouch");
static_assert(sizeof(g<int&&>(0)) == 2, "Ouch");
static_assert(sizeof(g<void(&)()>(0)) == 2, "Ouch");
static_assert(sizeof(g<void(&&)()>(0)) == 2, "Ouch");
static_assert(sizeof(f<void, void>(0)) == 2, "Ouch");
static_assert(sizeof(f<void(), void()>(0)) == 2, "Ouch");
static_assert(sizeof(f<void() const, void() const>(0)) == 2, "Ouch");
static_assert(sizeof(f<int, void>(0)) == 2, "Ouch");
static_assert(sizeof(f<void, int>(0)) == 2, "Ouch");
static_assert(sizeof(f<C, void>(0)) == 2, "Ouch");
static_assert(sizeof(f<C, int>(0)) == 2, "Ouch");
static_assert(sizeof(f<int&, int&>(0)) == 2, "Ouch");
static_assert(sizeof(f<int&&, int&&>(0)) == 2, "Ouch");
static_assert(sizeof(f<void(&)(), void(&)()>(0)) == 2, "Ouch");
static_assert(sizeof(f<void(&&)(), void(&&)()>(0)) == 2, "Ouch");
static_assert(sizeof(g2<void>(0)) == 2, "Ouch");
static_assert(sizeof(g2<void()>(0)) == 2, "Ouch");
static_assert(sizeof(g2<void() const>(0)) == 2, "Ouch");
static_assert(sizeof(g2<A>(0)) == 2, "Ouch");
static_assert(sizeof(g2<D1>(0)) == 2, "Ouch");
static_assert(sizeof(g2<D2>(0)) == 2, "Ouch");
static_assert(sizeof(g2<int&>(0)) == 2, "Ouch");
static_assert(sizeof(g2<int&&>(0)) == 2, "Ouch");
static_assert(sizeof(g2<void(&)()>(0)) == 2, "Ouch");
static_assert(sizeof(g2<void(&&)()>(0)) == 2, "Ouch");
static_assert(sizeof(f2<void, void>(0)) == 2, "Ouch");
static_assert(sizeof(f2<void(), void()>(0)) == 2, "Ouch");
static_assert(sizeof(f2<void() const, void() const>(0)) == 2, "Ouch");
static_assert(sizeof(f2<int, void>(0)) == 2, "Ouch");
static_assert(sizeof(f2<void, int>(0)) == 2, "Ouch");
static_assert(sizeof(f2<C, void>(0)) == 2, "Ouch");
static_assert(sizeof(f2<C, int>(0)) == 2, "Ouch");
static_assert(sizeof(f2<int&, int&>(0)) == 2, "Ouch");
static_assert(sizeof(f2<int&&, int&&>(0)) == 2, "Ouch");
static_assert(sizeof(f2<void(&)(), void(&)()>(0)) == 2, "Ouch");
static_assert(sizeof(f2<void(&&)(), void(&&)()>(0)) == 2, "Ouch");

View File

@ -0,0 +1,43 @@
// Core 1273
// { dg-do compile { target c++11 } }
template <class T> struct C;
template <class T> struct D;
class A
{
int i;
static int j;
friend struct C<int>;
friend struct D<int>;
} a;
class B
{
int i;
static int j;
friend struct C<float>;
friend struct D<float>;
} b;
template <class T>
struct C
{
template <class U> decltype (a.i) f() { } // #1
template <class U> decltype (b.i) f() { } // #2
};
template <class T>
struct D
{
template <class U> decltype (A::j) f() { } // #1
template <class U> decltype (B::j) f() { } // #2
};
int main()
{
C<int>().f<int>(); // calls #1
C<float>().f<float>(); // calls #2
D<int>().f<int>(); // calls #1
D<float>().f<float>(); // calls #2
}

View File

@ -0,0 +1,31 @@
// PR c++/50545
// { dg-do compile { target c++11 } }
template< class T >
T&& declval();
// #1
template< class T >
auto f( int )
-> decltype( int{ declval<T>() } );
// #2
template< class >
void f( ... );
#define STATIC_ASSERT( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ )
template< class T, class U >
struct is_same {
static constexpr bool value = false;
};
template< class T >
struct is_same<T, T> {
static constexpr bool value = true;
};
STATIC_ASSERT( is_same< decltype( f<int>(0) ), int >::value ); // OK; f<int>(0) calls #1.
STATIC_ASSERT( is_same< decltype( f<int*>(0) ), void >::value ); // static assertion fails; f<int*>(0) should call #2, because int{ (int*)0 } is ill-formed, but calls #1.