From patchwork Fri Jul 27 19:40:20 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Tromey X-Patchwork-Id: 173769 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id BE7592C0092 for ; Sat, 28 Jul 2012 05:40:56 +1000 (EST) Comment: DKIM? See http://www.dkim.org DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gcc.gnu.org; s=default; x=1344022857; h=Comment: DomainKey-Signature:Received:Received:Received:Received:Received: From:To:Subject:Date:Message-ID:User-Agent:MIME-Version: Content-Type:Mailing-List:Precedence:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:Sender:Delivered-To; bh=5RAIZbV kN0a5CJlHN8WoLjd2TLY=; b=Aguwl4xeHc9xXcrMQDXrX9oc7wAGhxDEvFqN7NM tXY5ir7mB0nVgmVXBFZsCtA0EKH8htoq+5WKniDtLsP0yJbpOjAH0/jshT0Tjiz3 Vy1kW43tlFhBoY7bY/e17Uq+iLllZFY8HOC/2XdBGI2NBfVKR/rbLeJ+5MO8413r 94Dk= Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=gcc.gnu.org; h=Received:Received:X-SWARE-Spam-Status:X-Spam-Check-By:Received:Received:Received:From:To:Subject:Date:Message-ID:User-Agent:MIME-Version:Content-Type:Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:Sender:Delivered-To; b=PiM4SkSGjZ9KCmQY77vbMcxXlgtVeyohpglnJZKK5asN9LlGALTYL2wTjxoonF 6lOisYHngQHLT2mlHVmA0bRbeJLuhqpHtZVbUz4O1Y8OJg9SAAhVg2cyKH0J+Ga3 tMTo5VGkvLwU5XVb5a/1FHyDVZsE+uGQOeScpAf6P6jvY=; Received: (qmail 26514 invoked by alias); 27 Jul 2012 19:40:51 -0000 Received: (qmail 26502 invoked by uid 22791); 27 Jul 2012 19:40:48 -0000 X-SWARE-Spam-Status: No, hits=-6.5 required=5.0 tests=AWL, BAYES_00, KHOP_RCVD_UNTRUST, RCVD_IN_DNSWL_HI, RCVD_IN_HOSTKARMA_W, SPF_HELO_PASS, T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 27 Jul 2012 19:40:24 +0000 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q6RJeNZ5017703 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Fri, 27 Jul 2012 15:40:23 -0400 Received: from barimba (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id q6RJeKHQ008417 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO); Fri, 27 Jul 2012 15:40:21 -0400 From: Tom Tromey To: gcc-patches@gcc.gnu.org Subject: RFA: implement C11 _Generic Date: Fri, 27 Jul 2012 13:40:20 -0600 Message-ID: <87mx2l9emz.fsf@fleche.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.1 (gnu/linux) MIME-Version: 1.0 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org This patch attempts to implement the C11 _Generic feature. Based on the last comment in http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46073 I am not at all sure I've done it correctly. There are a couple of other things that aren't clear to me. First, should c_parser_generic_selection call mark_exp_read on each expression in the generic association list? I chose not to, on the basis that those expressions are parsed but not evaluated or otherwise used; but I am not sure that this is correct. (I did choose to call it for the controlling expression, by analogy with typeof). Second, it isn't clear to me whether setting c_inhibit_evaluation_warnings is sufficient here. I welcome your advice. Finally, I'd appreciate advice on the content of the various error messages. I wrote some new tests. I tried to test every constraint in the spec, but I have never really been all that good at language lawyering. Comments? 2012-07-27 Tom Tromey * c-common.h (enum rid) : New constant. * c-common.c (c_common_reswords): Add _Generic. 2012-07-27 Tom Tromey * c-parser.c (struct c_generic_association): New. (c_generic_association_d): New typedef. (c_parser_generic_selection): New function. (c_parser_postfix_expression): Handle RID_GENERIC. 2012-07-27 Tom Tromey * gcc.dg/c11-generic-2.c: New file. * gcc.dg/c11-generic-1.c: New file. --- gcc/c-family/ChangeLog | 5 + gcc/c-family/c-common.c | 1 + gcc/c-family/c-common.h | 4 +- gcc/c/ChangeLog | 7 ++ gcc/c/c-parser.c | 193 ++++++++++++++++++++++++++++++++++ gcc/testsuite/ChangeLog | 5 + gcc/testsuite/gcc.dg/c11-generic-1.c | 28 +++++ gcc/testsuite/gcc.dg/c11-generic-2.c | 24 ++++ 8 files changed, 265 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/c11-generic-1.c create mode 100644 gcc/testsuite/gcc.dg/c11-generic-2.c 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 % case in %<_Generic%>"); + inform (iter->type_location, "original % 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" } */ +}