From: Jason Merrill Date: Fri, 31 Aug 2012 02:50:28 +0000 (-0400) Subject: re PR c++/50545 ([C++0x][DR 1172] SFINAE does not handle an explicit type conversion... X-Git-Tag: upstream/12.2.0~74509 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2c905502202e55fc91c2a2c9a94c3ef286f52f73;p=platform%2Fupstream%2Fgcc.git 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 --- diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index c202dc4..0bfcde7 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,13 @@ 2012-08-30 Jason Merrill + 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. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 7ffc929..1b085bd 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 6506a67..5ce6e8a 100644 --- a/gcc/cp/pt.c +++ b/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. */ diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 6d7004b..183a78a 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -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::U doesn't require 'typename'. */ + if (instantiation_dependent_expression_p (expr)) { type = cxx_make_type (DECLTYPE_TYPE); DECLTYPE_TYPE_EXPR (type) = expr; diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype40.C b/gcc/testsuite/g++.dg/cpp0x/decltype40.C new file mode 100644 index 0000000..7933c95 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/decltype40.C @@ -0,0 +1,101 @@ +// PR c++/51222 +// { dg-options -std=c++11 } + +template +struct add_rref { + typedef T&& type; +}; + +template<> +struct add_rref { + typedef void type; +}; + +template +typename add_rref::type declval(); + +template())) +> +auto f(int) -> char; + +template +auto f(...) -> char(&)[2]; + +template +auto g(int) -> char; + +template +auto g(...) -> char(&)[2]; + +template +auto f2(int) -> decltype(::delete ::new T(declval()), char()); + +template +auto f2(...) -> char(&)[2]; + +template +auto g2(int) -> decltype(::delete ::new T(), char()); + +template +auto g2(...) -> char(&)[2]; + +struct C { }; + +struct A { + virtual ~A() = 0; +}; + +struct D1 { + D1() = delete; +}; + +struct D2 { + ~D2() = delete; +}; + +static_assert(sizeof(g(0)) == 2, "Ouch"); +static_assert(sizeof(g(0)) == 2, "Ouch"); +static_assert(sizeof(g(0)) == 2, "Ouch"); +static_assert(sizeof(g(0)) == 2, "Ouch"); +static_assert(sizeof(g(0)) == 2, "Ouch"); +static_assert(sizeof(g(0)) == 2, "Ouch"); +static_assert(sizeof(g(0)) == 2, "Ouch"); +static_assert(sizeof(g(0)) == 2, "Ouch"); +static_assert(sizeof(g(0)) == 2, "Ouch"); +static_assert(sizeof(g(0)) == 2, "Ouch"); +static_assert(sizeof(f(0)) == 2, "Ouch"); +static_assert(sizeof(f(0)) == 2, "Ouch"); +static_assert(sizeof(f(0)) == 2, "Ouch"); +static_assert(sizeof(f(0)) == 2, "Ouch"); +static_assert(sizeof(f(0)) == 2, "Ouch"); +static_assert(sizeof(f(0)) == 2, "Ouch"); +static_assert(sizeof(f(0)) == 2, "Ouch"); +static_assert(sizeof(f(0)) == 2, "Ouch"); +static_assert(sizeof(f(0)) == 2, "Ouch"); +static_assert(sizeof(f(0)) == 2, "Ouch"); +static_assert(sizeof(f(0)) == 2, "Ouch"); + +static_assert(sizeof(g2(0)) == 2, "Ouch"); +static_assert(sizeof(g2(0)) == 2, "Ouch"); +static_assert(sizeof(g2(0)) == 2, "Ouch"); +static_assert(sizeof(g2(0)) == 2, "Ouch"); +static_assert(sizeof(g2(0)) == 2, "Ouch"); +static_assert(sizeof(g2(0)) == 2, "Ouch"); +static_assert(sizeof(g2(0)) == 2, "Ouch"); +static_assert(sizeof(g2(0)) == 2, "Ouch"); +static_assert(sizeof(g2(0)) == 2, "Ouch"); +static_assert(sizeof(g2(0)) == 2, "Ouch"); +static_assert(sizeof(f2(0)) == 2, "Ouch"); +static_assert(sizeof(f2(0)) == 2, "Ouch"); +static_assert(sizeof(f2(0)) == 2, "Ouch"); +static_assert(sizeof(f2(0)) == 2, "Ouch"); +static_assert(sizeof(f2(0)) == 2, "Ouch"); +static_assert(sizeof(f2(0)) == 2, "Ouch"); +static_assert(sizeof(f2(0)) == 2, "Ouch"); +static_assert(sizeof(f2(0)) == 2, "Ouch"); +static_assert(sizeof(f2(0)) == 2, "Ouch"); +static_assert(sizeof(f2(0)) == 2, "Ouch"); +static_assert(sizeof(f2(0)) == 2, "Ouch"); diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype41.C b/gcc/testsuite/g++.dg/cpp0x/decltype41.C new file mode 100644 index 0000000..1439e15 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/decltype41.C @@ -0,0 +1,43 @@ +// Core 1273 +// { dg-do compile { target c++11 } } + +template struct C; +template struct D; + +class A +{ + int i; + static int j; + friend struct C; + friend struct D; +} a; + +class B +{ + int i; + static int j; + friend struct C; + friend struct D; +} b; + +template +struct C +{ + template decltype (a.i) f() { } // #1 + template decltype (b.i) f() { } // #2 +}; + +template +struct D +{ + template decltype (A::j) f() { } // #1 + template decltype (B::j) f() { } // #2 +}; + +int main() +{ + C().f(); // calls #1 + C().f(); // calls #2 + D().f(); // calls #1 + D().f(); // calls #2 +} diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype42.C b/gcc/testsuite/g++.dg/cpp0x/decltype42.C new file mode 100644 index 0000000..6c1aa43 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/decltype42.C @@ -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() } ); + +// #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 { + static constexpr bool value = true; +}; + + +STATIC_ASSERT( is_same< decltype( f(0) ), int >::value ); // OK; f(0) calls #1. +STATIC_ASSERT( is_same< decltype( f(0) ), void >::value ); // static assertion fails; f(0) should call #2, because int{ (int*)0 } is ill-formed, but calls #1.