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:
Jason Merrill 2008-08-30 01:14:54 -04:00 committed by Jason Merrill
parent b3914aa38f
commit 86a09a9e99
11 changed files with 332 additions and 19 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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:

View File

@ -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"

View File

@ -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:

View 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;
}
}

View 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" }

View 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');
}

View 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>();
}