Implement C++0x 'auto' semantics.
* decl.c (start_decl_1): Don't complain about auto being incomplete. (cp_finish_decl): Deduce auto. * init.c (build_new): Handle 'new auto'. * typeck2.c (cxx_incomplete_type_diagnostic): Give a different message for auto than for normal template type parms. * pt.c (type_dependent_expression_p): Handle { }. (make_auto): New function. (listify_autos): New function. (do_auto_deduction): New function. (is_auto): New function. (type_uses_auto): New function. * cp-tree.h: Declare them. * parser.c (cp_parser_decl_specifier_seq): In C++0x mode, don't treat auto as a declspec. (cp_parser_simple_type_specifier): It's a type-specifier. From-SVN: r139798
This commit is contained in:
parent
b3914aa38f
commit
86a09a9e99
@ -1,3 +1,22 @@
|
||||
2008-08-29 Jason Merrill <jason@redhat.com>
|
||||
|
||||
Implement C++0x 'auto' semantics.
|
||||
* decl.c (start_decl_1): Don't complain about auto being incomplete.
|
||||
(cp_finish_decl): Deduce auto.
|
||||
* init.c (build_new): Handle 'new auto'.
|
||||
* typeck2.c (cxx_incomplete_type_diagnostic): Give a different
|
||||
message for auto than for normal template type parms.
|
||||
* pt.c (type_dependent_expression_p): Handle { }.
|
||||
(make_auto): New function.
|
||||
(listify_autos): New function.
|
||||
(do_auto_deduction): New function.
|
||||
(is_auto): New function.
|
||||
(type_uses_auto): New function.
|
||||
* cp-tree.h: Declare them.
|
||||
* parser.c (cp_parser_decl_specifier_seq): In C++0x mode, don't
|
||||
treat auto as a declspec.
|
||||
(cp_parser_simple_type_specifier): It's a type-specifier.
|
||||
|
||||
2008-08-29 Mark Mitchell <mark@codesourcery.com>
|
||||
|
||||
* mangle.c (write_type): Add target-specific manglings for
|
||||
|
@ -4521,6 +4521,10 @@ extern void end_specialization (void);
|
||||
extern void begin_explicit_instantiation (void);
|
||||
extern void end_explicit_instantiation (void);
|
||||
extern tree check_explicit_specialization (tree, tree, int, int);
|
||||
extern tree make_auto (void);
|
||||
extern tree do_auto_deduction (tree, tree, tree);
|
||||
extern tree type_uses_auto (tree);
|
||||
extern bool is_auto (const_tree);
|
||||
extern tree process_template_parm (tree, tree, bool, bool);
|
||||
extern tree end_template_parm_list (tree);
|
||||
extern void end_template_decl (void);
|
||||
|
@ -4203,6 +4203,8 @@ start_decl_1 (tree decl, bool initialized)
|
||||
arrays which might be completed by the initialization. */
|
||||
if (complete_p)
|
||||
; /* A complete type is ok. */
|
||||
else if (type_uses_auto (type))
|
||||
; /* An auto type is ok. */
|
||||
else if (TREE_CODE (type) != ARRAY_TYPE)
|
||||
{
|
||||
error ("variable %q#D has initializer but incomplete type", decl);
|
||||
@ -4217,8 +4219,11 @@ start_decl_1 (tree decl, bool initialized)
|
||||
}
|
||||
else if (aggregate_definition_p && !complete_p)
|
||||
{
|
||||
error ("aggregate %q#D has incomplete type and cannot be defined",
|
||||
decl);
|
||||
if (type_uses_auto (type))
|
||||
error ("declaration of %q#D has no initializer", decl);
|
||||
else
|
||||
error ("aggregate %q#D has incomplete type and cannot be defined",
|
||||
decl);
|
||||
/* Change the type so that assemble_variable will give
|
||||
DECL an rtl we can live with: (mem (const_int 0)). */
|
||||
type = TREE_TYPE (decl) = error_mark_node;
|
||||
@ -5406,6 +5411,7 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
|
||||
int was_readonly = 0;
|
||||
bool var_definition_p = false;
|
||||
int saved_processing_template_decl;
|
||||
tree auto_node;
|
||||
|
||||
if (decl == error_mark_node)
|
||||
return;
|
||||
@ -5440,6 +5446,14 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
|
||||
&& (DECL_INITIAL (decl) || init))
|
||||
DECL_INITIALIZED_IN_CLASS_P (decl) = 1;
|
||||
|
||||
auto_node = type_uses_auto (type);
|
||||
if (auto_node && !type_dependent_expression_p (init))
|
||||
{
|
||||
type = TREE_TYPE (decl) = do_auto_deduction (type, init, auto_node);
|
||||
if (type == error_mark_node)
|
||||
return;
|
||||
}
|
||||
|
||||
if (processing_template_decl)
|
||||
{
|
||||
bool type_dependent_p;
|
||||
|
@ -2366,6 +2366,14 @@ build_new (tree placement, tree type, tree nelts, tree init,
|
||||
orig_nelts = nelts;
|
||||
orig_init = init;
|
||||
|
||||
if (nelts == NULL_TREE && init != void_zero_node && list_length (init) == 1
|
||||
&& !any_type_dependent_arguments_p (init))
|
||||
{
|
||||
tree auto_node = type_uses_auto (type);
|
||||
if (auto_node)
|
||||
type = do_auto_deduction (type, TREE_VALUE (init), auto_node);
|
||||
}
|
||||
|
||||
if (processing_template_decl)
|
||||
{
|
||||
if (dependent_type_p (type)
|
||||
|
@ -8324,11 +8324,11 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
|
||||
GNU Extension:
|
||||
thread */
|
||||
case RID_AUTO:
|
||||
/* Consume the token. */
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
|
||||
if (cxx_dialect == cxx98)
|
||||
{
|
||||
/* Consume the token. */
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
|
||||
/* Complain about `auto' as a storage specifier, if
|
||||
we're complaining about C++0x compatibility. */
|
||||
warning
|
||||
@ -8340,10 +8340,9 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
|
||||
cp_parser_set_storage_class (parser, decl_specs, RID_AUTO,
|
||||
token->location);
|
||||
}
|
||||
else
|
||||
/* We do not yet support the use of `auto' as a
|
||||
type-specifier. */
|
||||
error ("%HC++0x %<auto%> specifier not supported", &token->location);
|
||||
else
|
||||
/* C++0x auto type-specifier. */
|
||||
found_decl_spec = false;
|
||||
break;
|
||||
|
||||
case RID_REGISTER:
|
||||
@ -11069,14 +11068,8 @@ cp_parser_simple_type_specifier (cp_parser* parser,
|
||||
break;
|
||||
|
||||
case RID_AUTO:
|
||||
if (cxx_dialect != cxx98)
|
||||
{
|
||||
/* Consume the token. */
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
/* We do not yet support the use of `auto' as a
|
||||
type-specifier. */
|
||||
error ("%HC++0x %<auto%> specifier not supported", &token->location);
|
||||
}
|
||||
maybe_warn_cpp0x ("C++0x auto");
|
||||
type = make_auto ();
|
||||
break;
|
||||
|
||||
case RID_DECLTYPE:
|
||||
|
133
gcc/cp/pt.c
133
gcc/cp/pt.c
@ -16187,6 +16187,19 @@ type_dependent_expression_p (tree expression)
|
||||
if (TREE_CODE (expression) == STMT_EXPR)
|
||||
expression = stmt_expr_value_expr (expression);
|
||||
|
||||
if (BRACE_ENCLOSED_INITIALIZER_P (expression))
|
||||
{
|
||||
tree elt;
|
||||
unsigned i;
|
||||
|
||||
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expression), i, elt)
|
||||
{
|
||||
if (type_dependent_expression_p (elt))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TREE_TYPE (expression) == unknown_type_node)
|
||||
{
|
||||
if (TREE_CODE (expression) == ADDR_EXPR)
|
||||
@ -16673,4 +16686,124 @@ build_non_dependent_args (tree args)
|
||||
return nreverse (new_args);
|
||||
}
|
||||
|
||||
/* Returns a type which represents 'auto'. We use a TEMPLATE_TYPE_PARM
|
||||
with a level one deeper than the actual template parms. */
|
||||
|
||||
tree
|
||||
make_auto (void)
|
||||
{
|
||||
tree au;
|
||||
|
||||
/* ??? Is it worth caching this for multiple autos at the same level? */
|
||||
au = cxx_make_type (TEMPLATE_TYPE_PARM);
|
||||
TYPE_NAME (au) = build_decl (TYPE_DECL, get_identifier ("auto"), au);
|
||||
TYPE_STUB_DECL (au) = TYPE_NAME (au);
|
||||
TEMPLATE_TYPE_PARM_INDEX (au) = build_template_parm_index
|
||||
(0, processing_template_decl + 1, processing_template_decl + 1,
|
||||
TYPE_NAME (au), NULL_TREE);
|
||||
TYPE_CANONICAL (au) = canonical_type_parameter (au);
|
||||
DECL_ARTIFICIAL (TYPE_NAME (au)) = 1;
|
||||
SET_DECL_TEMPLATE_PARM_P (TYPE_NAME (au));
|
||||
|
||||
return au;
|
||||
}
|
||||
|
||||
/* Replace auto in TYPE with std::initializer_list<auto>. */
|
||||
|
||||
static tree
|
||||
listify_autos (tree type, tree auto_node)
|
||||
{
|
||||
tree std_init_list = namespace_binding
|
||||
(get_identifier ("initializer_list"), std_node);
|
||||
tree argvec;
|
||||
tree init_auto;
|
||||
if (!std_init_list || !DECL_CLASS_TEMPLATE_P (std_init_list))
|
||||
{
|
||||
error ("deducing auto from brace-enclosed initializer list requires "
|
||||
"#include <initializer_list>");
|
||||
return error_mark_node;
|
||||
}
|
||||
argvec = make_tree_vec (1);
|
||||
TREE_VEC_ELT (argvec, 0) = auto_node;
|
||||
init_auto = lookup_template_class (std_init_list, argvec, NULL_TREE,
|
||||
NULL_TREE, 0, tf_warning_or_error);
|
||||
|
||||
TREE_VEC_ELT (argvec, 0) = init_auto;
|
||||
if (processing_template_decl)
|
||||
argvec = add_to_template_args (current_template_args (), argvec);
|
||||
return tsubst (type, argvec, tf_warning_or_error, NULL_TREE);
|
||||
}
|
||||
|
||||
/* Replace occurrences of 'auto' in TYPE with the appropriate type deduced
|
||||
from INIT. AUTO_NODE is the TEMPLATE_TYPE_PARM used for 'auto' in TYPE. */
|
||||
|
||||
tree
|
||||
do_auto_deduction (tree type, tree init, tree auto_node)
|
||||
{
|
||||
tree parms, args, tparms, targs;
|
||||
int val;
|
||||
|
||||
/* [dcl.spec.auto]: Obtain P from T by replacing the occurrences of auto
|
||||
with either a new invented type template parameter U or, if the
|
||||
initializer is a braced-init-list (8.5.4), with
|
||||
std::initializer_list<U>. */
|
||||
if (BRACE_ENCLOSED_INITIALIZER_P (init))
|
||||
type = listify_autos (type, auto_node);
|
||||
|
||||
parms = build_tree_list (NULL_TREE, type);
|
||||
args = build_tree_list (NULL_TREE, init);
|
||||
tparms = make_tree_vec (1);
|
||||
targs = make_tree_vec (1);
|
||||
TREE_VEC_ELT (tparms, 0)
|
||||
= build_tree_list (NULL_TREE, TYPE_NAME (auto_node));
|
||||
val = type_unification_real (tparms, targs, parms, args, 0,
|
||||
DEDUCE_CALL, LOOKUP_NORMAL);
|
||||
if (val > 0)
|
||||
{
|
||||
error ("unable to deduce %qT from %qE", type, init);
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
if (processing_template_decl)
|
||||
targs = add_to_template_args (current_template_args (), targs);
|
||||
return tsubst (type, targs, tf_warning_or_error, NULL_TREE);
|
||||
}
|
||||
|
||||
/* Returns true iff TYPE is a TEMPLATE_TYPE_PARM representing 'auto'. */
|
||||
|
||||
bool
|
||||
is_auto (const_tree type)
|
||||
{
|
||||
if (TREE_CODE (type) == TEMPLATE_TYPE_PARM
|
||||
&& TYPE_IDENTIFIER (type) == get_identifier ("auto"))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns true iff TYPE contains a use of 'auto'. Since auto can only
|
||||
appear as a type-specifier for the declaration in question, we don't
|
||||
have to look through the whole type. */
|
||||
|
||||
tree
|
||||
type_uses_auto (tree type)
|
||||
{
|
||||
enum tree_code code;
|
||||
if (is_auto (type))
|
||||
return type;
|
||||
|
||||
code = TREE_CODE (type);
|
||||
|
||||
if (code == POINTER_TYPE || code == REFERENCE_TYPE
|
||||
|| code == OFFSET_TYPE || code == FUNCTION_TYPE
|
||||
|| code == METHOD_TYPE || code == ARRAY_TYPE)
|
||||
return type_uses_auto (TREE_TYPE (type));
|
||||
|
||||
if (TYPE_PTRMEMFUNC_P (type))
|
||||
return type_uses_auto (TREE_TYPE (TREE_TYPE
|
||||
(TYPE_PTRMEMFUNC_FN_TYPE (type))));
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
#include "gt-cp-pt.h"
|
||||
|
@ -398,8 +398,12 @@ cxx_incomplete_type_diagnostic (const_tree value, const_tree type,
|
||||
break;
|
||||
|
||||
case TEMPLATE_TYPE_PARM:
|
||||
emit_diagnostic (diag_kind, input_location, 0,
|
||||
"invalid use of template type parameter %qT", type);
|
||||
if (is_auto (type))
|
||||
emit_diagnostic (diag_kind, input_location, 0,
|
||||
"invalid use of %<auto%>");
|
||||
else
|
||||
emit_diagnostic (diag_kind, input_location, 0,
|
||||
"invalid use of template type parameter %qT", type);
|
||||
break;
|
||||
|
||||
case BOUND_TEMPLATE_TEMPLATE_PARM:
|
||||
|
72
gcc/testsuite/g++.dg/cpp0x/auto2.C
Normal file
72
gcc/testsuite/g++.dg/cpp0x/auto2.C
Normal file
@ -0,0 +1,72 @@
|
||||
// Positive test for auto
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++0x" }
|
||||
|
||||
#include <typeinfo>
|
||||
extern "C" void abort();
|
||||
|
||||
int f() {}
|
||||
|
||||
struct A
|
||||
{
|
||||
int i;
|
||||
int f() {}
|
||||
A operator+(A a) { return a; }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void g(T t)
|
||||
{
|
||||
auto x = t+t;
|
||||
if (typeid(x) != typeid(t+t))
|
||||
abort();
|
||||
|
||||
auto p = new auto(&t);
|
||||
if (typeid(p) != typeid(T**))
|
||||
abort();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
auto i = 42;
|
||||
if (typeid (i) != typeid (int))
|
||||
abort();
|
||||
|
||||
auto *p = &i;
|
||||
if (typeid (p) != typeid (int*))
|
||||
abort();
|
||||
|
||||
auto *p2 = &p;
|
||||
if (typeid (p2) != typeid (int**))
|
||||
abort();
|
||||
|
||||
auto (*fp)() = f;
|
||||
if (typeid (fp) != typeid (int (*)()))
|
||||
abort();
|
||||
|
||||
auto A::* pm = &A::i;
|
||||
if (typeid (pm) != typeid (int A::*))
|
||||
abort();
|
||||
|
||||
auto (A::*pmf)() = &A::f;
|
||||
if (typeid (pmf) != typeid (int (A::*)()))
|
||||
abort();
|
||||
|
||||
g(42);
|
||||
g(10.f);
|
||||
g(A());
|
||||
|
||||
auto *p3 = new auto (i);
|
||||
if (typeid (p3) != typeid (int*))
|
||||
abort();
|
||||
|
||||
for (auto idx = i; idx != 0; idx = 0);
|
||||
while (auto idx = 0);
|
||||
if (auto idx = 1);
|
||||
|
||||
switch (auto s = i)
|
||||
{
|
||||
case 42:
|
||||
break;
|
||||
}
|
||||
}
|
16
gcc/testsuite/g++.dg/cpp0x/auto3.C
Normal file
16
gcc/testsuite/g++.dg/cpp0x/auto3.C
Normal file
@ -0,0 +1,16 @@
|
||||
// Negative test for auto
|
||||
// { dg-options "-std=c++0x" }
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
auto x; // { dg-error "auto" }
|
||||
|
||||
// New CWG issue
|
||||
auto a[2] = { 1, 2 }; // { dg-error "auto" }
|
||||
|
||||
template<class T>
|
||||
struct A { };
|
||||
|
||||
A<int> A1;
|
||||
// CWG issue 625
|
||||
A<auto> A2 = A1; // { dg-error "auto" }
|
28
gcc/testsuite/g++.dg/cpp0x/auto4.C
Normal file
28
gcc/testsuite/g++.dg/cpp0x/auto4.C
Normal file
@ -0,0 +1,28 @@
|
||||
// Testcase for deduction of std::initializer_list for auto.
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++0x" }
|
||||
|
||||
#include <typeinfo>
|
||||
#include <initializer_list>
|
||||
extern "C" void abort();
|
||||
|
||||
template <class T>
|
||||
void f (T t)
|
||||
{
|
||||
auto ilt = { &t, &t };
|
||||
if (typeid(ilt) != typeid(std::initializer_list<T*>))
|
||||
abort();
|
||||
|
||||
auto il = { 1, 2, 3 };
|
||||
if (typeid(il) != typeid(std::initializer_list<int>))
|
||||
abort();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
auto il = { 1, 2, 3 };
|
||||
if (typeid(il) != typeid(std::initializer_list<int>))
|
||||
abort();
|
||||
|
||||
f('c');
|
||||
}
|
22
gcc/testsuite/g++.dg/cpp0x/auto5.C
Normal file
22
gcc/testsuite/g++.dg/cpp0x/auto5.C
Normal file
@ -0,0 +1,22 @@
|
||||
// Testcase for non-dependent auto in templates
|
||||
// { dg-options "-std=c++0x" }
|
||||
|
||||
struct A
|
||||
{
|
||||
template<class> void f();
|
||||
} a;
|
||||
|
||||
template <class T>
|
||||
void g()
|
||||
{
|
||||
auto aa = a;
|
||||
aa.f<int>();
|
||||
|
||||
auto p = new auto (a);
|
||||
p->f<int>();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
g<double>();
|
||||
}
|
Loading…
Reference in New Issue
Block a user