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:
parent
e467c9d257
commit
2c90550220
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
125
gcc/cp/pt.c
125
gcc/cp/pt.c
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
|
@ -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
|
||||
}
|
|
@ -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.
|
Loading…
Reference in New Issue