Implement N3271

Implement N3271
	* parser.c (cp_convert_range_for): Split into
	cp_parser_perform_range_for_lookup.
	(cp_parser_perform_range_for_lookup): New.
	(cp_parser_range_for_member_function): New.
	(cp_parser_for_init_statement): Correct error message.
	* semantics.c (finish_call_expr): Accept COMPONENT_REF.

From-SVN: r172531
This commit is contained in:
Jason Merrill 2011-04-15 20:28:17 -04:00
parent 5a0f6eba53
commit deb9642d05
14 changed files with 639 additions and 58 deletions

View file

@ -1,3 +1,13 @@
2011-04-15 Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>
Implement N3271
* parser.c (cp_convert_range_for): Split into
cp_parser_perform_range_for_lookup.
(cp_parser_perform_range_for_lookup): New.
(cp_parser_range_for_member_function): New.
(cp_parser_for_init_statement): Correct error message.
* semantics.c (finish_call_expr): Accept COMPONENT_REF.
2011-04-14 Nicola Pero <nicola.pero@meta-innovation.com>
* parser.c (cp_parser_objc_protocol_declaration): Updated for

View file

@ -1607,6 +1607,10 @@ static tree cp_parser_c_for
(cp_parser *, tree, tree);
static tree cp_parser_range_for
(cp_parser *, tree, tree, tree);
static tree cp_parser_perform_range_for_lookup
(tree, tree *, tree *);
static tree cp_parser_range_for_member_function
(tree, tree);
static tree cp_parser_jump_statement
(cp_parser *);
static void cp_parser_declaration_statement
@ -8557,14 +8561,20 @@ cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl)
}
If RANGE_EXPR is an array:
BEGIN_EXPR = __range
END_EXPR = __range + ARRAY_SIZE(__range)
BEGIN_EXPR = __range
END_EXPR = __range + ARRAY_SIZE(__range)
Else if RANGE_EXPR has a member 'begin' or 'end':
BEGIN_EXPR = __range.begin()
END_EXPR = __range.end()
Else:
BEGIN_EXPR = begin(__range)
END_EXPR = end(__range);
When calling begin()/end() we must use argument dependent
lookup, but always considering 'std' as an associated namespace. */
If __range has a member 'begin' but not 'end', or vice versa, we must
still use the second alternative (it will surely fail, however).
When calling begin()/end() in the third alternative we must use
argument dependent lookup, but always considering 'std' as an associated
namespace. */
tree
cp_convert_range_for (tree statement, tree range_decl, tree range_expr)
@ -8581,12 +8591,12 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr)
else
{
/* Find out the type deduced by the declaration
* `auto &&__range = range_expr' */
`auto &&__range = range_expr'. */
range_type = cp_build_reference_type (make_auto (), true);
range_type = do_auto_deduction (range_type, range_expr,
type_uses_auto (range_type));
/* Create the __range variable */
/* Create the __range variable. */
range_temp = build_decl (input_location, VAR_DECL,
get_identifier ("__for_range"), range_type);
TREE_USED (range_temp) = 1;
@ -8597,51 +8607,11 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr)
LOOKUP_ONLYCONVERTING);
range_temp = convert_from_reference (range_temp);
if (TREE_CODE (TREE_TYPE (range_temp)) == ARRAY_TYPE)
{
/* If RANGE_TEMP is an array we will use pointer arithmetic */
iter_type = build_pointer_type (TREE_TYPE (TREE_TYPE (range_temp)));
begin_expr = range_temp;
end_expr
= build_binary_op (input_location, PLUS_EXPR,
range_temp,
array_type_nelts_top (TREE_TYPE (range_temp)),
0);
}
else
{
/* If it is not an array, we must call begin(__range)/end__range() */
VEC(tree,gc) *vec;
begin_expr = get_identifier ("begin");
vec = make_tree_vector ();
VEC_safe_push (tree, gc, vec, range_temp);
begin_expr = perform_koenig_lookup (begin_expr, vec,
/*include_std=*/true);
begin_expr = finish_call_expr (begin_expr, &vec, false, true,
tf_warning_or_error);
release_tree_vector (vec);
end_expr = get_identifier ("end");
vec = make_tree_vector ();
VEC_safe_push (tree, gc, vec, range_temp);
end_expr = perform_koenig_lookup (end_expr, vec,
/*include_std=*/true);
end_expr = finish_call_expr (end_expr, &vec, false, true,
tf_warning_or_error);
release_tree_vector (vec);
/* The unqualified type of the __begin and __end temporaries should
* be the same as required by the multiple auto declaration */
iter_type = cv_unqualified (TREE_TYPE (begin_expr));
if (!same_type_p (iter_type, cv_unqualified (TREE_TYPE (end_expr))))
error ("inconsistent begin/end types in range-based for: %qT and %qT",
TREE_TYPE (begin_expr), TREE_TYPE (end_expr));
}
iter_type = cp_parser_perform_range_for_lookup (range_temp,
&begin_expr, &end_expr);
}
/* The new for initialization statement */
/* The new for initialization statement. */
begin = build_decl (input_location, VAR_DECL,
get_identifier ("__for_begin"), iter_type);
TREE_USED (begin) = 1;
@ -8662,18 +8632,18 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr)
finish_for_init_stmt (statement);
/* The new for condition */
/* The new for condition. */
condition = build_x_binary_op (NE_EXPR,
begin, ERROR_MARK,
end, ERROR_MARK,
NULL, tf_warning_or_error);
finish_for_cond (condition, statement);
/* The new increment expression */
/* The new increment expression. */
expression = finish_unary_op_expr (PREINCREMENT_EXPR, begin);
finish_for_expr (expression, statement);
/* The declaration is initialized with *__begin inside the loop body */
/* The declaration is initialized with *__begin inside the loop body. */
cp_finish_decl (range_decl,
build_x_indirect_ref (begin, RO_NULL, tf_warning_or_error),
/*is_constant_init*/false, NULL_TREE,
@ -8682,6 +8652,124 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr)
return statement;
}
/* Solves BEGIN_EXPR and END_EXPR as described in cp_convert_range_for.
We need to solve both at the same time because the method used
depends on the existence of members begin or end.
Returns the type deduced for the iterator expression. */
static tree
cp_parser_perform_range_for_lookup (tree range, tree *begin, tree *end)
{
if (!COMPLETE_TYPE_P (TREE_TYPE (range)))
{
error ("range-based %<for%> expression of type %qT "
"has incomplete type", TREE_TYPE (range));
*begin = *end = error_mark_node;
return error_mark_node;
}
if (TREE_CODE (TREE_TYPE (range)) == ARRAY_TYPE)
{
/* If RANGE is an array, we will use pointer arithmetic. */
*begin = range;
*end = build_binary_op (input_location, PLUS_EXPR,
range,
array_type_nelts_top (TREE_TYPE (range)),
0);
return build_pointer_type (TREE_TYPE (TREE_TYPE (range)));
}
else
{
/* If it is not an array, we must do a bit of magic. */
tree id_begin, id_end;
tree member_begin, member_end;
*begin = *end = error_mark_node;
id_begin = get_identifier ("begin");
id_end = get_identifier ("end");
member_begin = lookup_member (TREE_TYPE (range), id_begin,
/*protect=*/2, /*want_type=*/false);
member_end = lookup_member (TREE_TYPE (range), id_end,
/*protect=*/2, /*want_type=*/false);
if (member_begin != NULL_TREE || member_end != NULL_TREE)
{
/* Use the member functions. */
if (member_begin != NULL_TREE)
*begin = cp_parser_range_for_member_function (range, id_begin);
else
error ("range-based %<for%> expression of type %qT has an "
"%<end%> member but not a %<begin%>", TREE_TYPE (range));
if (member_end != NULL_TREE)
*end = cp_parser_range_for_member_function (range, id_end);
else
error ("range-based %<for%> expression of type %qT has a "
"%<begin%> member but not an %<end%>", TREE_TYPE (range));
}
else
{
/* Use global functions with ADL. */
VEC(tree,gc) *vec;
vec = make_tree_vector ();
VEC_safe_push (tree, gc, vec, range);
member_begin = perform_koenig_lookup (id_begin, vec,
/*include_std=*/true);
*begin = finish_call_expr (member_begin, &vec, false, true,
tf_warning_or_error);
member_end = perform_koenig_lookup (id_end, vec,
/*include_std=*/true);
*end = finish_call_expr (member_end, &vec, false, true,
tf_warning_or_error);
release_tree_vector (vec);
}
/* Last common checks. */
if (*begin == error_mark_node || *end == error_mark_node)
{
/* If one of the expressions is an error do no more checks. */
*begin = *end = error_mark_node;
return error_mark_node;
}
else
{
tree iter_type = cv_unqualified (TREE_TYPE (*begin));
/* The unqualified type of the __begin and __end temporaries should
be the same, as required by the multiple auto declaration. */
if (!same_type_p (iter_type, cv_unqualified (TREE_TYPE (*end))))
error ("inconsistent begin/end types in range-based %<for%> "
"statement: %qT and %qT",
TREE_TYPE (*begin), TREE_TYPE (*end));
return iter_type;
}
}
}
/* Helper function for cp_parser_perform_range_for_lookup.
Builds a tree for RANGE.IDENTIFIER(). */
static tree
cp_parser_range_for_member_function (tree range, tree identifier)
{
tree member, res;
VEC(tree,gc) *vec;
member = finish_class_member_access_expr (range, identifier,
false, tf_warning_or_error);
if (member == error_mark_node)
return error_mark_node;
vec = make_tree_vector ();
res = finish_call_expr (member, &vec,
/*disallow_virtual=*/false,
/*koenig_p=*/false,
tf_warning_or_error);
release_tree_vector (vec);
return res;
}
/* Parse an iteration-statement.
@ -8830,7 +8918,7 @@ cp_parser_for_init_statement (cp_parser* parser, tree *decl)
if (cxx_dialect < cxx0x)
{
error_at (cp_lexer_peek_token (parser->lexer)->location,
"range-based-for loops are not allowed "
"range-based %<for%> loops are not allowed "
"in C++98 mode");
*decl = error_mark_node;
}

View file

@ -2074,6 +2074,22 @@ finish_call_expr (tree fn, VEC(tree,gc) **args, bool disallow_virtual,
make_args_non_dependent (*args);
}
if (TREE_CODE (fn) == COMPONENT_REF)
{
tree member = TREE_OPERAND (fn, 1);
if (BASELINK_P (member))
{
tree object = TREE_OPERAND (fn, 0);
return build_new_method_call (object, member,
args, NULL_TREE,
(disallow_virtual
? LOOKUP_NORMAL | LOOKUP_NONVIRTUAL
: LOOKUP_NORMAL),
/*fn_p=*/NULL,
complain);
}
}
if (is_overloaded_fn (fn))
fn = baselink_for_fns (fn);

View file

@ -1,3 +1,19 @@
2011-04-15 Jason Merrill <jason@redhat.com>
* g++.dg/cpp0x/range-for17.C: New.
2011-04-15 Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>
* g++.dg/cpp0x/range-for2.C: Correct for declaration.
* g++.dg/cpp0x/range-for3.C: Likewise.
* g++.dg/cpp0x/range-for9.C: Correct error message.
* g++.dg/cpp0x/range-for11.C: New.
* g++.dg/cpp0x/range-for12.C: New.
* g++.dg/cpp0x/range-for13.C: New.
* g++.dg/cpp0x/range-for14.C: New.
* g++.dg/cpp0x/range-for15.C: New.
* g++.dg/cpp0x/range-for16.C: New.
2011-04-15 Tobias Burnus <burnus@net-b.de>
PR fortran/18918

View file

@ -0,0 +1,40 @@
// Test for range-based for loop
// Test the loop with a custom iterator
// with begin/end as member functions
// { dg-do compile }
// { dg-options "-std=c++0x" }
struct iterator
{
int x;
explicit iterator(int v) :x(v) {}
iterator &operator ++() { ++x; return *this; }
int operator *() { return x; }
bool operator != (const iterator &o) { return x != o.x; }
};
namespace foo
{
struct container
{
int min, max;
container(int a, int b) :min(a), max(b) {}
iterator begin()
{
return iterator(min);
}
iterator end()
{
return iterator(max + 1);
}
};
}
int main()
{
foo::container c(1,4);
for (int it : c)
;
}

View file

@ -0,0 +1,116 @@
// Test for range-based for loop with templates
// and begin/end as member functions
// { dg-do run }
// { dg-options "-std=c++0x" }
/* Preliminary declarations */
namespace pre
{
struct iterator
{
int x;
explicit iterator (int v) :x(v) {}
iterator &operator ++() { ++x; return *this; }
int operator *() { return x; }
bool operator != (const iterator &o) { return x != o.x; }
};
struct container
{
int min, max;
container(int a, int b) :min(a), max(b) {}
iterator begin() const
{
return iterator(min);
}
iterator end() const
{
return iterator(max);
}
};
} //namespace pre
using pre::container;
extern "C" void abort(void);
container run_me_just_once()
{
static bool run = false;
if (run)
abort();
run = true;
return container(1,2);
}
/* Template with dependent expression. */
template<typename T> int test1(const T &r)
{
int t = 0;
for (int i : r)
t += i;
return t;
}
/* Template with non-dependent expression and dependent declaration. */
template<typename T> int test2(const container &r)
{
int t = 0;
for (T i : r)
t += i;
return t;
}
/* Template with non-dependent expression (array) and dependent declaration. */
template<typename T> int test2(const int (&r)[4])
{
int t = 0;
for (T i : r)
t += i;
return t;
}
/* Template with non-dependent expression and auto declaration. */
template<typename T> int test3(const container &r)
{
int t = 0;
for (auto i : r)
t += i;
return t;
}
/* Template with non-dependent expression (array) and auto declaration. */
template<typename T> int test3(const int (&r)[4])
{
int t = 0;
for (auto i : r)
t += i;
return t;
}
int main ()
{
container c(1,5);
int a[4] = {5,6,7,8};
for (auto x : run_me_just_once())
;
if (test1 (c) != 10)
abort();
if (test1 (a) != 26)
abort();
if (test2<int> (c) != 10)
abort();
if (test2<int> (a) != 26)
abort();
if (test3<int> (c) != 10)
abort();
if (test3<int> (a) != 26)
abort();
return 0;
}

View file

@ -0,0 +1,103 @@
// Test for errors in range-based for loops
// with member begin/end
// { dg-do compile }
// { dg-options "-std=c++0x" }
//These should not be used
template<typename T> int *begin(T &t)
{
T::fail;
}
template<typename T> int *end(T &t)
{
T::fail;
}
struct container1
{
int *begin();
//no end
};
struct container2
{
int *end();
//no begin
};
struct container3
{
private:
int *begin(); // { dg-error "is private" }
int *end(); // { dg-error "is private" }
};
struct container4
{
int *begin;
int *end;
};
struct container5
{
typedef int *begin;
typedef int *end;
};
struct callable
{
int *operator()();
};
struct container6
{
callable begin;
callable end;
};
struct container7
{
static callable begin;
static callable end;
};
struct container8
{
static int *begin();
int *end();
};
struct private_callable
{
private:
int *operator()(); // { dg-error "is private" }
};
struct container9
{
private_callable begin;
private_callable end;
};
struct container10
{
typedef int *(*function)();
function begin;
static function end;
};
void test1()
{
for (int x : container1()); // { dg-error "member but not" }
for (int x : container2()); // { dg-error "member but not" }
for (int x : container3()); // { dg-error "within this context" }
for (int x : container4()); // { dg-error "cannot be used as a function" }
for (int x : container5()); // { dg-error "invalid use of" }
for (int x : container6());
for (int x : container7());
for (int x : container8());
for (int x : container9()); // { dg-error "within this context" }
for (int x : container10());
}

View file

@ -0,0 +1,95 @@
// Test for other range-based for loops with
// begin/end member functions
// { dg-do compile }
// { dg-options "-std=c++0x" }
//These should not be used
template<typename T> int *begin(T &t)
{
T::fail;
}
template<typename T> int *end(T &t)
{
T::fail;
}
//Test for defaults
struct default1
{
int *begin(int x); // { dg-message "note" }
int *end();
};
struct default2
{
int *begin(int x=0);
int *end();
};
struct default3
{
template <typename T> T *begin(); // { dg-message "note" }
int *end();
};
struct default4
{
template <typename T=int> T *begin();
int *end();
};
struct default5
{
template <typename T=int> T *begin(int x=0);
int *end();
};
void test1()
{
for (int x : default1()); // { dg-error "no matching function|note" }
for (int x : default2());
for (int x : default3()); // { dg-error "no matching function|note" }
for (int x : default4());
for (int x : default5());
}
//Inheritance tests
struct base_begin
{
int *begin(); // { dg-error "" }
};
struct base_end
{
int *end();
};
struct derived1 : base_begin, base_end
{
};
struct base_begin2 : base_begin
{
};
struct derived2 : base_begin, base_end, base_begin2 // { dg-warning "" }
{
};
struct base_begin3 : virtual base_begin
{
};
struct derived3 : virtual base_begin, base_end, base_begin3
{
};
void test2()
{
for (int x : derived1());
for (int x : derived2()); // { dg-error "is ambiguous" }
for (int x : derived3());
}

View file

@ -0,0 +1,59 @@
// Test for range-based for loop with templates
// and begin/end as member (non-)virtual functions
// { dg-do run }
// { dg-options "-std=c++0x" }
unsigned int g;
struct A
{
virtual int *begin()
{
g |= 1;
return 0;
}
int *end()
{
g |= 2;
return 0;
}
};
struct B : A
{
virtual int *begin()
{
g |= 4;
return 0;
}
int *end()
{
g |= 8;
return 0;
}
};
extern "C" void abort(void);
int main ()
{
A a;
B b;
A &aa = b;
g = 0;
for (int x : a);
if (g != (1 | 2))
abort();
g = 0;
for (int x : b);
if (g != (4 | 8))
abort();
g = 0;
for (int x : aa);
if (g != (4 | 2))
abort();
}

View file

@ -0,0 +1,21 @@
// Test for range-based for loop with arrays of
// incomplete type or unknown size
// { dg-do compile }
// { dg-options "-std=c++0x" }
extern int a[10];
extern int b[];
struct S;
extern S c[10];
extern S d[];
void test()
{
for (int n : a);
for (int n : b); // { dg-error "incomplete type" }
for (S &n : c); // { dg-error "incomplete type" }
for (S &n : d); // { dg-error "incomplete type" }
for (int n : *c); // { dg-error "incomplete type" }
}

View file

@ -0,0 +1,17 @@
// { dg-options -std=c++0x }
extern "C" int printf (const char *, ...);
enum E { e1, e2, e3, X };
E operator*(E e) { return e; }
E begin(E e) { return e; }
E end(E e) { return X; };
E operator++(E& e) { return e = E(e+1); }
int main()
{
for (auto e: e1)
{
printf ("%d ", e);
}
}

View file

@ -8,7 +8,7 @@
struct iterator
{
int x;
iterator(int v) :x(v) {}
explicit iterator(int v) :x(v) {}
iterator &operator ++() { ++x; return *this; }
int operator *() { return x; }
bool operator != (const iterator &o) { return x != o.x; }
@ -36,6 +36,6 @@ namespace foo
int main()
{
foo::container c(1,4);
for (iterator it : c)
for (int it : c)
;
}

View file

@ -8,7 +8,7 @@
struct iterator
{
int x;
iterator(int v) :x(v) {}
explicit iterator(int v) :x(v) {}
iterator &operator ++() { ++x; return *this; }
int operator *() { return x; }
bool operator != (const iterator &o) { return x != o.x; }
@ -36,7 +36,7 @@ namespace std
int main()
{
container c(1,4);
for (iterator it : c)
for (int it : c)
{
}
}

View file

@ -6,6 +6,6 @@
void test()
{
int a[] = {0,1,2};
for (int x : a) // { dg-error "range-based-for" }
for (int x : a) // { dg-error "range-based 'for'" }
;
}