Message ID | 87mx2l9emz.fsf@fleche.redhat.com |
---|---|
State | New |
Headers | show |
Could you explain the choices you have made for the issues raised on comp.std.c last month (regarding the handling of qualifiers on controlling expressions) and the rationale for those choices (and make sure there are appropriate testcases)?
>>>>> "Joseph" == Joseph S Myers <joseph@codesourcery.com> writes:
Joseph> Could you explain the choices you have made for the issues
Joseph> raised on comp.std.c last month (regarding the handling of
Joseph> qualifiers on controlling expressions) and the rationale for
Joseph> those choices (and make sure there are appropriate testcases)?
I found this:
https://groups.google.com/forum/?fromgroups#!topic/comp.std.c/InNlRotSWTc
I wasn't really aware of 6.3.2.1, but after reading it and re-reading
6.5.1.1, I think I agree with his "model 0" interpretation: no promotion
or conversion.
I don't have a standards-based reason for this, though; just my belief
that _Generic was omitted from 6.3.2.1 by mistake.
That's an amateur opinion though. I think it would be better for you to
say what you think.
I thought I was implementing "model 0", but the test program he posts
shows differently.
I'm happy to try to fix that up, but I'd like your guidance as to the
proper direction.
Tom
On Fri, 27 Jul 2012, Tom Tromey wrote: > I found this: > > https://groups.google.com/forum/?fromgroups#!topic/comp.std.c/InNlRotSWTc > > I wasn't really aware of 6.3.2.1, but after reading it and re-reading > 6.5.1.1, I think I agree with his "model 0" interpretation: no promotion > or conversion. > > I don't have a standards-based reason for this, though; just my belief > that _Generic was omitted from 6.3.2.1 by mistake. > > That's an amateur opinion though. I think it would be better for you to > say what you think. I think there are three standards issues and two GCC issues: * Integer promotions. I think it's clear that those do not occur; a "short" value remains as "short" rather than "int". * Conversion of qualified to unqualified types (which normally would occur as part of lvalue-to-rvalue conversion, but might also be an issue with casts to qualified types). The given example of a cbrt type-generic macro would only work as users expect (given that an argument might be a variable of type "const long double", or a cast to const long double) if the expression (whether lvalue or rvalue) is converted from qualified to unqualified type. That suggests qualifiers should be discarded on the controlling expression (and if one of the type names specifies a qualified type, that case can never match). * Conversion of arrays and function designators to pointers. Given the removal of qualifiers it would seem natural for this to apply as well (to the controlling expression - not of course to the selected expression, given that this may explicitly be a function designator). But unlike the case of qualifiers, there's no real pointer in the standard to what's intended. And the GCC issues: * GCC doesn't have very well-defined semantics for whether an rvalue has a qualified type or not. This only appears as an issue with typeof at present, but does require more care about eliminating qualifiers for _Generic. * build_unary_op has code /* If the lvalue is const or volatile, merge that into the type to which the address will point. This is only needed for function types. */ that will give pointer-to-qualified type to expressions such as &abort - the address of a noreturn function. Again, at language level this is only visible with typeof at present. But in C11 terms, _Noreturn is not part of a function's type. This means that if the controlling expression is something such as &abort, with pointer-to-qualified-function type, the qualifiers on the pointer target type need to be removed for the comparisons.
>>>>> "Joseph" == Joseph S Myers <joseph@codesourcery.com> writes:
Tom> I wasn't really aware of 6.3.2.1, but after reading it and re-reading
Tom> 6.5.1.1, I think I agree with his "model 0" interpretation: no promotion
Tom> or conversion.
Tom> I don't have a standards-based reason for this, though; just my belief
Tom> that _Generic was omitted from 6.3.2.1 by mistake.
I re-re-read the sections and came up with a standards-based reason:
6.3 is about conversions, and the first paragraph starts "several
operators convert ...". Based on this, and other such phrases in the
text, I think the entire section applies to operators.
However, _Generic is not called an operator in the text. It is a
primary expression.
Therefore, 6.3.2 does not apply.
This seems plausible to me, if a bit legalistic.
Joseph> * Conversion of qualified to unqualified types (which normally
Joseph> would occur as part of lvalue-to-rvalue conversion, but might
Joseph> also be an issue with casts to qualified types). The given
Joseph> example of a cbrt type-generic macro would only work as users
Joseph> expect (given that an argument might be a variable of type
Joseph> "const long double", or a cast to const long double) if the
Joseph> expression (whether lvalue or rvalue) is converted from
Joseph> qualified to unqualified type.
It seems to me that keeping the qualifiers is obviously more useful.
Qualifiers can be stripped in various ways, but once stripped, can't be
regained.
The cbrt example can be salvaged by adding a few extra generic
associations. This can even be easily done via a macro.
#define CLAUSE(TYPE, EXPR) \
const TYPE: EXPR,
TYPE: EXPR
#define cbrt(X) _Generic ((X), CLAUSE (double, ...), ...)
Joseph> * Conversion of arrays and function designators to pointers.
If the above interpretation is correct then this conversion is not done
either. IIUC.
Joseph> * GCC doesn't have very well-defined semantics for whether an
Joseph> rvalue has a qualified type or not. This only appears as an
Joseph> issue with typeof at present, but does require more care about
Joseph> eliminating qualifiers for _Generic.
Joseph> * build_unary_op has code
[...]
Thanks. I'll take a look, but these sound a bit scary at first glance.
Tom
On Mon, 30 Jul 2012, Tom Tromey wrote: > 6.3 is about conversions, and the first paragraph starts "several > operators convert ...". Based on this, and other such phrases in the > text, I think the entire section applies to operators. 6.3.2.1 paragraphs 2 and 3 are phrased in terms of operators *preventing* conversion and certain conversions happening unless there is an operator to prevent them. It seems entirely clear that you can use a function designator inside a braced initializer for an array of function pointers, for example, although an element of such an initializer is not an operand of an operator. (I cannot find anything in a quick look through the ISO/IEC Directives Part 2 to indicate whether subclause titles such as "Other operands" are meant to be normative or informative.) > However, _Generic is not called an operator in the text. It is a > primary expression. A generic-selection is a primary expression. _Generic is a keyword that is part of the syntax for a generic-selection. C99 and C11 do not have a complete syntactic definition for "operator" anywhere (C90 did have such a syntax production). > The cbrt example can be salvaged by adding a few extra generic > associations. This can even be easily done via a macro. > > #define CLAUSE(TYPE, EXPR) \ > const TYPE: EXPR, > TYPE: EXPR > > #define cbrt(X) _Generic ((X), CLAUSE (double, ...), ...) That doesn't handle volatile, or _Atomic. If dealing with pointers you have restrict as well. And in the presence of TR 18037 you have address space qualifiers, meaning such a macro cannot be written in a way agnostic to the set of address spaces on the system where the code is used. (Using +(X) or (X)+0 may work in some cases - if the promotions those cause aren't problems and it isn't a case where those operations are invalid.) And to keep qualifiers here you'd need various other parts of the standard to be much clearer about whether qualifiers are present in the types of rvalues - an issue that previously hasn't been relevant.
>>>>> "Joseph" == Joseph S Myers <joseph@codesourcery.com> writes: Joseph> On Mon, 30 Jul 2012, Tom Tromey wrote: >> 6.3 is about conversions, and the first paragraph starts "several >> operators convert ...". Based on this, and other such phrases in the >> text, I think the entire section applies to operators. Joseph> 6.3.2.1 paragraphs 2 and 3 are phrased in terms of operators Joseph> *preventing* conversion and certain conversions happening unless Joseph> there is an operator to prevent them. Wow, I really don't read it that way at all. Looking at it yet again, now, I can't even really make it come out that way. I think it is now clear that I ought to drop this. Tom
On Mon, 30 Jul 2012, Tom Tromey wrote: > >>>>> "Joseph" == Joseph S Myers <joseph@codesourcery.com> writes: > > Joseph> On Mon, 30 Jul 2012, Tom Tromey wrote: > >> 6.3 is about conversions, and the first paragraph starts "several > >> operators convert ...". Based on this, and other such phrases in the > >> text, I think the entire section applies to operators. > > Joseph> 6.3.2.1 paragraphs 2 and 3 are phrased in terms of operators > Joseph> *preventing* conversion and certain conversions happening unless > Joseph> there is an operator to prevent them. > > Wow, I really don't read it that way at all. Looking at it yet again, > now, I can't even really make it come out that way. "Except when it is the operand of [...] an lvalue [...] is converted [...]" seems straightforward enough to me. Thus, as another example volatile int *p; void f(void) { *p; } dereferences the pointer when f is called (the lvalue *p is converted to an rvalue in the expression statement, without *p being an operand of an operator; if it were an operand of unary '&', that would stop the conversion).
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index b72506b..cc880de 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -418,6 +418,7 @@ const struct c_common_resword c_common_reswords[] = { "_Sat", RID_SAT, D_CONLY | D_EXT }, { "_Static_assert", RID_STATIC_ASSERT, D_CONLY }, { "_Noreturn", RID_NORETURN, D_CONLY }, + { "_Generic", RID_GENERIC, D_CONLY }, { "__FUNCTION__", RID_FUNCTION_NAME, 0 }, { "__PRETTY_FUNCTION__", RID_PRETTY_FUNCTION_NAME, 0 }, { "__alignof", RID_ALIGNOF, 0 }, diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 050112e..415b6e0 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1,6 +1,6 @@ /* Definitions for c-common.c. Copyright (C) 1987, 1993, 1994, 1995, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011 + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. This file is part of GCC. @@ -108,7 +108,7 @@ enum rid RID_FRACT, RID_ACCUM, /* C11 */ - RID_ALIGNAS, + RID_ALIGNAS, RID_GENERIC, /* This means to warn that this is a C++ keyword, and then treat it as a normal identifier. */ diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 2237749..360cc58 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -6158,6 +6158,195 @@ c_parser_get_builtin_args (c_parser *parser, const char *bname, return true; } +/* This represents a single generic-association. */ + +struct c_generic_association +{ + /* The location of the starting token of the type. */ + location_t type_location; + /* The association's type, or NULL_TREE for 'default'.. */ + tree type; + /* The association's expression. */ + struct c_expr expression; +}; + +typedef struct c_generic_association c_generic_association_d; + +DEF_VEC_O (c_generic_association_d); +DEF_VEC_ALLOC_O (c_generic_association_d, heap); + +/* Parse a generic-selection. (C11 6.5.1.1). + + generic-selection: + _Generic ( assignment-expression , generic-assoc-list ) + + generic-assoc-list: + generic-association + generic-assoc-list , generic-association + + generic-association: + type-name : assignment-expression + default : assignment-expression +*/ + +static struct c_expr +c_parser_generic_selection (c_parser *parser) +{ + VEC (c_generic_association_d, heap) *associations = NULL; + struct c_expr selector, error_expr; + tree selector_type; + struct c_generic_association matched_assoc; + int match_found = 0; + location_t generic_loc, selector_loc; + + error_expr.original_code = ERROR_MARK; + error_expr.original_type = NULL; + error_expr.value = error_mark_node; + + gcc_assert (c_parser_next_token_is_keyword (parser, RID_GENERIC)); + generic_loc = c_parser_peek_token (parser)->location; + c_parser_consume_token (parser); + if (!flag_isoc11) + { + if (flag_isoc99) + pedwarn (generic_loc, OPT_Wpedantic, + "ISO C99 does not support %<_Generic%>"); + else + pedwarn (generic_loc, OPT_Wpedantic, + "ISO C90 does not support %<_Generic%>"); + } + + if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<.%>")) + return error_expr; + + c_inhibit_evaluation_warnings++; + selector_loc = c_parser_peek_token (parser)->location; + selector = c_parser_expr_no_commas (parser, NULL); + c_inhibit_evaluation_warnings--; + + if (selector.value == error_mark_node) + return selector; + mark_exp_read (selector.value); + selector_type = TREE_TYPE (selector.value); + + if (!c_parser_require (parser, CPP_COMMA, "expected %<.%>")) + return error_expr; + + while (1) + { + struct c_generic_association assoc, *iter; + int ix; + c_token *token = c_parser_peek_token (parser); + + assoc.type_location = token->location; + if (token->type == CPP_KEYWORD && token->keyword == RID_DEFAULT) + { + c_parser_consume_token (parser); + assoc.type = NULL_TREE; + } + else + { + struct c_type_name *type_name; + + type_name = c_parser_type_name (parser); + if (type_name == NULL) + goto error_exit; + assoc.type = groktypename (type_name, NULL, NULL); + if (assoc.type == error_mark_node) + goto error_exit; + + if (! COMPLETE_TYPE_P (assoc.type)) + error_at (assoc.type_location, + "%<_Generic%> association has incomplete type"); + + if (variably_modified_type_p (assoc.type, NULL_TREE)) + error_at (assoc.type_location, + "%<_Generic%> association has " + "variable length type"); + } + + if (!c_parser_require (parser, CPP_COLON, "expected %<.%>")) + goto error_exit; + + assoc.expression = c_parser_expr_no_commas (parser, NULL); + if (assoc.expression.value == error_mark_node) + goto error_exit; + + for (ix = 0; + VEC_iterate (c_generic_association_d, associations, ix, iter); + ++ix) + { + if (assoc.type == NULL_TREE) + { + if (iter->type == NULL_TREE) + { + error_at (assoc.type_location, + "duplicate %<default%> case in %<_Generic%>"); + inform (iter->type_location, "original %<default%> is here"); + } + } + else if (iter->type != NULL_TREE) + { + if (comptypes (assoc.type, iter->type)) + { + error_at (assoc.type_location, + "%<_Generic%> specifies two compatible types"); + inform (iter->type_location, "compatible type is here"); + } + } + } + + if (assoc.type == NULL_TREE) + { + if (!match_found) + { + matched_assoc = assoc; + match_found = 1; + } + } + else if (comptypes (assoc.type, selector_type)) + { + if (!match_found || matched_assoc.type == NULL_TREE) + { + matched_assoc = assoc; + match_found = 1; + } + else + { + error_at (assoc.type_location, + "%<_Generic> selector matches multiple associations"); + inform (matched_assoc.type_location, + "other match is here"); + } + } + + VEC_safe_push (c_generic_association_d, heap, associations, &assoc); + + if (c_parser_peek_token (parser)->type != CPP_COMMA) + break; + c_parser_consume_token (parser); + } + + VEC_free (c_generic_association_d, heap, associations); + + if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<.%>")) + return error_expr; + + if (!match_found) + { + error_at (selector_loc, "%<_Generic%> selector of type %qT is not " + "compatible with any association", + selector_type); + return error_expr; + } + + mark_exp_read (matched_assoc.expression.value); + return matched_assoc.expression; + + error_exit: + VEC_free (c_generic_association_d, heap, associations); + return error_expr; +} /* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2). @@ -6181,6 +6370,7 @@ c_parser_get_builtin_args (c_parser *parser, const char *bname, constant string-literal ( expression ) + generic-selection GNU extensions: @@ -6749,6 +6939,9 @@ c_parser_postfix_expression (c_parser *parser) expr.value = objc_build_encode_expr (type); } break; + case RID_GENERIC: + expr = c_parser_generic_selection (parser); + break; default: c_parser_error (parser, "expected expression"); expr.value = error_mark_node; diff --git a/gcc/testsuite/gcc.dg/c11-generic-1.c b/gcc/testsuite/gcc.dg/c11-generic-1.c new file mode 100644 index 0000000..156d3a6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-generic-1.c @@ -0,0 +1,28 @@ +/* Test C11 _Generic. Valid uses. */ +/* { dg-do run } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +extern void exit (int); +extern void abort (void); + +void +check (int n) +{ + if (n) + abort (); +} + +int +main (void) +{ + int n = 0; + + check (_Generic (n++, int: 0)); + /* _Generic should not evaluate its argument. */ + check (n); + + check (_Generic (n, double: n++, default: 0)); + check (n); + + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/c11-generic-2.c b/gcc/testsuite/gcc.dg/c11-generic-2.c new file mode 100644 index 0000000..db06a8e --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-generic-2.c @@ -0,0 +1,24 @@ +/* Test C11 _Generic. Error cases. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +struct incomplete; + +void +f (int n) +{ + /* Multiple 'default's. */ + _Generic (n, default: 1, default: 2); /* { dg-error "duplicate .*default.* case" } */ + + /* Variably-modified type not ok. */ + _Generic (n, int[n]: 0, default: 1); /* { dg-error "variable length type" } */ + /* Type must be complete. */ + _Generic (n, struct incomplete: 0, default: 1); /* { dg-error "incomplete type" } */ + _Generic (n, void: 0, default: 1); /* { dg-error "incomplete type" } */ + + /* Two compatible types in association list. */ + _Generic (&n, int: 5, signed int: 7, default: 23); /* { dg-error "two compatible types" } */ + + /* No matching association. */ + _Generic (n, void *: 5); /* { dg-error "not compatible with any association" } */ +}