From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id CD83D385661A for ; Sat, 10 Jun 2023 10:37:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CD83D385661A Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1686393469; h=from:from:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type; bh=hkpjjTBnC381D1NgiGI0jMHHCupGhc+fa9vMdMtwc18=; b=JtlqiLmMp6hyK6D1qAR6k7wGX9klBoPtc7sj7mU5GnA3mdVTC1+ntMOf0ckEOxKAag1Jhe kvUI5O0Ga/l9NPd/SgV9Ouoov8otd3F1O9C0uflDgum6JcGQMzkpNJarpUK+lQpjW7rZeE N7Yx3vbFC3vThPpAU5t7Hn/iwlhV6Y0= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-5-9rLZSKrfNHKagcvYmo1KOA-1; Sat, 10 Jun 2023 06:37:46 -0400 X-MC-Unique: 9rLZSKrfNHKagcvYmo1KOA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id A735E85A5AA; Sat, 10 Jun 2023 10:37:45 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.39.194.30]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 4589A2166B25; Sat, 10 Jun 2023 10:37:45 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 35AAbgFa178737 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Sat, 10 Jun 2023 12:37:42 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 35AAbfCI178736; Sat, 10 Jun 2023 12:37:41 +0200 Date: Sat, 10 Jun 2023 12:37:40 +0200 From: Jakub Jelinek To: "Joseph S. Myers" , Marek Polacek Cc: gcc-patches@gcc.gnu.org Subject: [RFC] Add stdckdint.h header for C23 Message-ID: Reply-To: Jakub Jelinek MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.6 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=us-ascii Content-Disposition: inline X-Spam-Status: No, score=-3.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,KAM_NUMSUBJECT,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Hi! The following patch is an attempt to implement the C23 stdckdint.h header on top of our GNU extension - __builtin_{add,sub,mul}_overflow builtins. I have looked at gnulib stdckdint.h and they are full of workarounds for various compilers, EDG doesn't do this, clang <= 14 can't multiply __int128, ..., so I think the header belongs into the compiler rather than C library, because it would be a nightmare to maintain it there. What I'm struggling with is enforcing the weird restrictions C23 imposes on these. The builtins error on the result pointer not being writable, or having boolean or enumeral type (the reason for disallowing bool was that it would be questionable whether it should act as if storing to an unsigned 1-bit precision type which would overflow if result is not in [0,1] or whether it would never overflow for bool * result and simply store false if the infinite precision result is 0 and true otherwise, and for enums because of the uncertainities on just the enumerators vs. range from smallest to largest enumerator vs. strict enum precision with underlying type). They do allow storing result in plain char. And the source operands can have any integral types, including plain char, including booleans and including enumeral types. The plain is to allow even _BitInt(N) as both source and result later on. Now, C23 says that suitable types for both type2/type3 and type1 are integral types other than plain char, bool, a bit-precise integer type, or an enumerated type. And it also says: It is recommended to produce a diagnostic message if type2 or type3 are not suitable integer types, or if *result is not a modifiable lvalue of a suitable integer type. I've tried to first check it with: static_assert (_Generic ((a), char: 0, const char: 0, volatile char: 0, const volatile char: 0, default: __builtin_classify_type (a) - 1 <= 1U), "...") but that only catches plain char and doesn't catch _Bool/bool and doesn't catch enumerated types (note, for the *result we diagnose it for the builtins, but not for the other args), because __builtin_classify_type sadly promotes its argument. The _Generic in the patch below is slightly better, it catches also _Bool/bool, but doesn't catch enumerated types, comptypes used by _Generic says enumeral type is compatible with the underlying integer type. But catching just plain char and bool would be also doable with just _Generic listing the non-allowed types. I think changing __builtin_classify_type behavior after 35 years would be dangerous, shall we introduce a new similar builtin which would just never promote the argument/perform array/function/enum conversions on it, so that __builtin_type_classify (true) == boolean_type_class enum E { E1, E2 } e; __builtin_type_classify (e) == enumeral_type_class int a[2]; __builtin_type_classify (a) == array_type_class etc.? Seems clang changed __builtin_type_classify at some point so that it e.g. returns enumeral_type_class for enum arguments and array_type_class for arrays, but doesn't return boolean_type_class for _Bool argument. Also, shall we introduce __typeof_unqual__ keyword which could be used in < C23 modes and perhaps C++? 2023-06-10 Jakub Jelinek * Makefile.in (USER_H): Add stdckdint.h. * ginclude/stdckdint.h: New file. * gcc.dg/stdckdint-1.c: New test. * gcc.dg/stdckdint-2.c: New test. --- gcc/Makefile.in.jj 2023-06-06 20:02:35.581211930 +0200 +++ gcc/Makefile.in 2023-06-10 10:17:05.062270115 +0200 @@ -466,6 +466,7 @@ USER_H = $(srcdir)/ginclude/float.h \ $(srcdir)/ginclude/stdnoreturn.h \ $(srcdir)/ginclude/stdalign.h \ $(srcdir)/ginclude/stdatomic.h \ + $(srcdir)/ginclude/stdckdint.h \ $(EXTRA_HEADERS) USER_H_INC_NEXT_PRE = @user_headers_inc_next_pre@ --- gcc/ginclude/stdckdint.h.jj 2023-06-10 09:20:39.154053338 +0200 +++ gcc/ginclude/stdckdint.h 2023-06-10 12:02:33.454947780 +0200 @@ -0,0 +1,78 @@ +/* Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* ISO C23: 7.20 Checked Integer Arithmetic . */ + +#ifndef _STDCKDINT_H +#define _STDCKDINT_H + +#define __STDC_VERSION_STDCKDINT_H__ 202311L + +#if __STDC_VERSION__ > 201710L +# ifdef __SIZEOF_INT20__ +# define __ckd_type_int20 __int20: 1, unsigned __int20: 1, +# else +# define __ckd_type_int20 +# endif +# ifdef __SIZEOF_INT128__ +# define __ckd_type_int128 __int128: 1, unsigned __int128: 1, +# else +# define __ckd_type_int128 +# endif +# define __ckd_type_check_1(a, n) \ + static_assert (_Generic (((typeof_unqual (a)) 0), \ + signed char: 1, \ + unsigned char: 1, \ + short: 1, \ + unsigned short: 1, \ + int: 1, \ + unsigned int: 1, \ + long: 1, \ + unsigned long: 1, \ + long long: 1, \ + unsigned long long: 1, \ + __ckd_type_int20 \ + __ckd_type_int128 \ + default: 0), \ + "types used in " n " should be integral other than plain " \ + "char, bool, bit-precise integer or enumerated type") +# define __ckd_type_check(r, a, b, n) \ + (__extension__ ({ __ckd_type_check_1 ((r)[0], n); \ + __ckd_type_check_1 (a, n); \ + __ckd_type_check_1 (b, n); \ + (r); })) +#else +# define __ckd_type_check(r, a, b, n) r +#endif + +#define ckd_add(r, a, b) \ + ((_Bool) __builtin_add_overflow (a, b, \ + __ckd_type_check (r, a, b, "ckd_add"))) +#define ckd_sub(r, a, b) \ + ((_Bool) __builtin_sub_overflow (a, b, \ + __ckd_type_check (r, a, b, "ckd_sub"))) +#define ckd_mul(r, a, b) \ + ((_Bool) __builtin_mul_overflow (a, b, \ + __ckd_type_check (r, a, b, "ckd_mul"))) + +#endif /* stdckdint.h */ --- gcc/testsuite/gcc.dg/stdckdint-1.c.jj 2023-06-10 10:04:23.547792318 +0200 +++ gcc/testsuite/gcc.dg/stdckdint-1.c 2023-06-10 10:50:29.748579415 +0200 @@ -0,0 +1,61 @@ +/* Test C23 Checked Integer Arithmetic macros in . */ +/* { dg-do run } */ +/* { dg-options "-std=c2x" } */ + +#include + +#if __STDC_VERSION_STDCKDINT_H__ != 202311L +# error __STDC_VERSION_STDCKDINT_H__ not defined to 202311L +#endif + +extern void abort (void); + +int +main () +{ + unsigned int a; + if (ckd_add (&a, 1, 2) || a != 3) + abort (); + if (ckd_add (&a, ~2U, 2) || a != ~0U) + abort (); + if (!ckd_add (&a, ~2U, 4) || a != 1) + abort (); + if (ckd_sub (&a, 42, 2) || a != 40) + abort (); + if (!ckd_sub (&a, 11, ~0ULL) || a != 12) + abort (); + if (ckd_mul (&a, 42, 16U) || a != 672) + abort (); + if (ckd_mul (&a, ~0UL, 0) || a != 0) + abort (); + if (ckd_mul (&a, 1, ~0U) || a != ~0U) + abort (); + if (ckd_mul (&a, ~0UL, 1) != (~0UL > ~0U) || a != ~0U) + abort (); + static_assert (_Generic (ckd_add (&a, 1, 1), bool: 1, default: 0)); + static_assert (_Generic (ckd_sub (&a, 1, 1), bool: 1, default: 0)); + static_assert (_Generic (ckd_mul (&a, 1, 1), bool: 1, default: 0)); + signed char b; + if (ckd_add (&b, 8, 12) || b != 20) + abort (); + if (ckd_sub (&b, 8UL, 12ULL) || b != -4) + abort (); + if (ckd_mul (&b, 2, 3) || b != 6) + abort (); + unsigned char c; + if (ckd_add (&c, 8, 12) || c != 20) + abort (); + if (ckd_sub (&c, 8UL, 12ULL) != (-4ULL > (unsigned char) -4U) + || c != (unsigned char) -4U) + abort (); + if (ckd_mul (&c, 2, 3) || c != 6) + abort (); + long long d; + if (ckd_add (&d, ~0U, ~0U) != (~0U + 1ULL < ~0U) + || d != (long long) (2 * (unsigned long long) ~0U)) + abort (); + if (ckd_sub (&d, 0, 0) || d != 0) + abort (); + if (ckd_mul (&d, 16, 1) || d != 16) + abort (); +} --- gcc/testsuite/gcc.dg/stdckdint-2.c.jj 2023-06-10 10:04:31.600681050 +0200 +++ gcc/testsuite/gcc.dg/stdckdint-2.c 2023-06-10 12:00:25.560711350 +0200 @@ -0,0 +1,53 @@ +/* Test C23 Checked Integer Arithmetic macros in . */ +/* { dg-do run } */ +/* { dg-options "-std=c2x" } */ + +#include + +int +main () +{ + char a; + bool b; + enum E { E1, E2 } c = E1; + int d; + ckd_add (&a, 1, 1); /* { dg-error "types used in ckd_add should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_sub (&a, 1, 1); /* { dg-error "types used in ckd_sub should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_mul (&a, 1, 1); /* { dg-error "types used in ckd_mul should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_add (&b, 1, 1); /* { dg-error "types used in ckd_add should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + /* { dg-error "has pointer to boolean type" "" { target *-*-* } .-1 } */ + ckd_sub (&b, 1, 1); /* { dg-error "types used in ckd_sub should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + /* { dg-error "has pointer to boolean type" "" { target *-*-* } .-1 } */ + ckd_mul (&b, 1, 1); /* { dg-error "types used in ckd_mul should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + /* { dg-error "has pointer to boolean type" "" { target *-*-* } .-1 } */ + ckd_add (&c, 1, 1); /* { dg-error "types used in ckd_add should be integral other than plain char, bool, bit-precise integer or enumerated type" "" { xfail *-*-* } } */ + /* { dg-error "has pointer to enumerated type" "" { target *-*-* } .-1 } */ + ckd_sub (&c, 1, 1); /* { dg-error "types used in ckd_sub should be integral other than plain char, bool, bit-precise integer or enumerated type" "" { xfail *-*-* } } */ + /* { dg-error "has pointer to enumerated type" "" { target *-*-* } .-1 } */ + ckd_mul (&c, 1, 1); /* { dg-error "types used in ckd_mul should be integral other than plain char, bool, bit-precise integer or enumerated type" "" { xfail *-*-* } } */ + /* { dg-error "has pointer to enumerated type" "" { target *-*-* } .-1 } */ + ckd_add (&d, (char) 1, 1); /* { dg-error "types used in ckd_add should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_sub (&d, (char) 1, 1); /* { dg-error "types used in ckd_sub should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_mul (&d, (char) 1, 1); /* { dg-error "types used in ckd_mul should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_add (&d, false, 1); /* { dg-error "types used in ckd_add should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_sub (&d, false, 1); /* { dg-error "types used in ckd_sub should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_mul (&d, false, 1); /* { dg-error "types used in ckd_mul should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_add (&d, true, 1); /* { dg-error "types used in ckd_add should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_sub (&d, true, 1); /* { dg-error "types used in ckd_sub should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_mul (&d, true, 1); /* { dg-error "types used in ckd_mul should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_add (&d, c, 1); /* { dg-error "types used in ckd_add should be integral other than plain char, bool, bit-precise integer or enumerated type" "" { xfail *-*-* } } */ + ckd_sub (&d, c, 1); /* { dg-error "types used in ckd_sub should be integral other than plain char, bool, bit-precise integer or enumerated type" "" { xfail *-*-* } } */ + ckd_mul (&d, c, 1); /* { dg-error "types used in ckd_mul should be integral other than plain char, bool, bit-precise integer or enumerated type" "" { xfail *-*-* } } */ + ckd_add (&d, 1, (char) 1); /* { dg-error "types used in ckd_add should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_sub (&d, 1, (char) 1); /* { dg-error "types used in ckd_sub should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_mul (&d, 1, (char) 1); /* { dg-error "types used in ckd_mul should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_add (&d, 1, false); /* { dg-error "types used in ckd_add should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_sub (&d, 1, false); /* { dg-error "types used in ckd_sub should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_mul (&d, 1, false); /* { dg-error "types used in ckd_mul should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_add (&d, 1, true); /* { dg-error "types used in ckd_add should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_sub (&d, 1, true); /* { dg-error "types used in ckd_sub should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_mul (&d, 1, true); /* { dg-error "types used in ckd_mul should be integral other than plain char, bool, bit-precise integer or enumerated type" } */ + ckd_add (&d, 1, c); /* { dg-error "types used in ckd_add should be integral other than plain char, bool, bit-precise integer or enumerated type" "" { xfail *-*-* } } */ + ckd_sub (&d, 1, c); /* { dg-error "types used in ckd_sub should be integral other than plain char, bool, bit-precise integer or enumerated type" "" { xfail *-*-* } } */ + ckd_mul (&d, 1, c); /* { dg-error "types used in ckd_mul should be integral other than plain char, bool, bit-precise integer or enumerated type" "" { xfail *-*-* } } */ +} Jakub