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.
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);
/* 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)
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
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))
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. */
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;
--- /dev/null
+// 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");
--- /dev/null
+// 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
+}
--- /dev/null
+// 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.