Bug 85662 - [8/9 Regression] "error: non-constant condition for static assertion" from __builtin_offsetof in C++
Summary: [8/9 Regression] "error: non-constant condition for static assertion" from __...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 8.0
: P2 normal
Target Milestone: 8.2
Assignee: Jakub Jelinek
URL:
Keywords: rejects-valid
Depends on:
Blocks:
 
Reported: 2018-05-05 05:20 UTC by roland
Modified: 2018-06-25 09:36 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2018-05-05 00:00:00


Attachments
gcc8-pr85662.patch (1.23 KB, patch)
2018-05-05 09:02 UTC, Jakub Jelinek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description roland 2018-05-05 05:20:03 UTC
This code was accepted by GCC 6 but is rejected by GCC 7, 8, and trunk.
The error message appears to complain that the result of the __builtin_offsetof expression has pointer type rather than size_t.


$ cat foo.cc                                                      
struct foo {                                                                    
    unsigned long x[31];                                                        
};                                                                              
                                                                                
struct bar {                                                                    
    bool b;                                                                     
    foo f;                                                                      
};                                                                              
                                                                                
static_assert(__builtin_offsetof(bar, f.x[31 - 1]) == 8 + ((31 - 1) * 8), "");  
$ ./gcc/xgcc -Bgcc/ -O2 -S foo.cc                                  
foo.cc:10:52: error: non-constant condition for static assertion                
 static_assert(__builtin_offsetof(bar, f.x[31 - 1]) == 8 + ((31 - 1) * 8), ""); 
               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~       
foo.cc:10:52: error: value ‘8’ of type ‘bar*’ is not a constant expression      
[Exit 1]                                                                        


The equivalent C code:

struct foo {                                                                    
    unsigned long x[31];                                                        
};                                                                              
                                                                                
struct bar {                                                                    
    _Bool b;                                                                    
    struct foo f;                                                               
};                                                                              
                                                                                
_Static_assert(__builtin_offsetof(struct bar, f.x[31 - 1]) == 8 + ((31 - 1) * 8), "");                                                                         
    

is accepted without complaint.


bisected to r238909 (git cb7688247fdcff08df18baed1317fce5b0e9db13)

gcc/cp/ChangeLog
                                                      
2016-07-30  Martin Sebor  <msebor@redhat.com>                                   
                                                                                
        PR c++/60760                                                            
        PR c++/71091                                                            
        * constexpr.c (cxx_eval_binary_expression): Reject invalid expressions  
        involving null pointers.                                                
        (cxx_eval_component_reference): Reject null pointer dereferences.       
        (cxx_eval_indirect_ref): Reject indirecting through null pointers.      
        (cxx_eval_constant_expression): Reject invalid expressions involving    
        null pointers.
Comment 1 Jakub Jelinek 2018-05-05 07:52:38 UTC
Weird, I can only reproduce it starting with r247495 and can't reproduce with gcc 7.
Better testcase that doesn't really depend on the actual structure layout and sizes:
struct S { unsigned long x[31]; };
struct T { bool b; S f; };
static_assert (__builtin_offsetof (T, f.x[31 - 1]) == __builtin_offsetof (T, f.x[30]), "");
Using 30 instead of 31-1 makes it go away, and the problem is that something creates or keeps around 8 as pointer constant.
Comment 2 Jakub Jelinek 2018-05-05 08:17:47 UTC
In any case, I think the problem is related to the delayed folding, C++ constexpr handling not liking pointer constants and fold_offsetof_1 done as pointer addition rather than integer addition.  With delayed folding and fold_build_pointer_plus called by fold_offsetof_1 the nested expressions aren't really folded.  We could easily fix it up by doing a cp_fold (together with recursive cp_fold_r though, because cp_fold isn't fully recursive) on the result of fold_offsetof in the C++ FE, that way we'd get a constant whenever possible.
On the other side, we wouldn't then reject __builtin_offsetof used with not valid constexpr expressions in the second argument (say out of bound array access etc. subtracted from itself).
Say:
constexpr int a[5];
struct S { int b, c[5]; };
constexpr int d = __builtin_offsetof (S, c[(&a[6] - &a[6]) + 2]);

Another possibility is in fold_offsetof_1, if we detect the TREE_CONSTANT base, if it is a pointer typed INTEGER_CST, build corresponding sizetype INTEGER_CST instead and use normal PLUS_EXPR folding (or do we actually want any folding at all?) instead of fold_build_pointer_plus.
Comment 3 Jakub Jelinek 2018-05-05 09:02:29 UTC
Created attachment 44076 [details]
gcc8-pr85662.patch

Untested fix.
Comment 4 roland 2018-05-06 00:02:33 UTC
That fix (applied to trunk) works for my test case and for the original real-world case I reduced it from.

Will it be backported to 7 and 8?


Thanks for the quick work as usual, Jakub!
Comment 5 Jakub Jelinek 2018-05-10 17:41:00 UTC
Author: jakub
Date: Thu May 10 17:40:28 2018
New Revision: 260119

URL: https://gcc.gnu.org/viewcvs?rev=260119&root=gcc&view=rev
Log:
	PR c++/85662
	* c-common.h (fold_offsetof_1): Removed.
	(fold_offsetof): Add TYPE argument defaulted to size_type_node and
	CTX argument defaulted to ERROR_MARK.
	* c-common.c (fold_offsetof_1): Renamed to ...
	(fold_offsetof): ... this.  Remove wrapper function.  Add TYPE
	argument, convert the pointer constant to TYPE and use size_binop
	with PLUS_EXPR instead of fold_build_pointer_plus if type is not
	a pointer type.  Adjust recursive calls.

	* c-fold.c (c_fully_fold_internal): Use fold_offsetof rather than
	fold_offsetof_1, pass TREE_TYPE (expr) as TYPE to it and drop the
	fold_convert_loc.
	* c-typeck.c (build_unary_op): Use fold_offsetof rather than
	fold_offsetof_1, pass argtype as TYPE to it and drop the
	fold_convert_loc.

	* cp-gimplify.c (cp_fold): Use fold_offsetof rather than
	fold_offsetof_1, pass TREE_TYPE (x) as TYPE to it and drop the
	fold_convert.

	* g++.dg/ext/offsetof2.C: New test.

Added:
    trunk/gcc/testsuite/g++.dg/ext/offsetof2.C
Modified:
    trunk/gcc/c-family/ChangeLog
    trunk/gcc/c-family/c-common.c
    trunk/gcc/c-family/c-common.h
    trunk/gcc/c/ChangeLog
    trunk/gcc/c/c-fold.c
    trunk/gcc/c/c-typeck.c
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/cp-gimplify.c
    trunk/gcc/testsuite/ChangeLog
Comment 6 roland 2018-05-16 18:49:43 UTC
Thanks for the fix.  What's the status on backporting this to 8 and/or 7?
Comment 7 Jakub Jelinek 2018-05-30 07:21:30 UTC
Author: jakub
Date: Wed May 30 07:20:58 2018
New Revision: 260916

URL: https://gcc.gnu.org/viewcvs?rev=260916&root=gcc&view=rev
Log:
	Backported from mainline
	2018-05-10  Jakub Jelinek  <jakub@redhat.com>

	PR c++/85662
	* c-common.h (fold_offsetof_1): Removed.
	(fold_offsetof): Add TYPE argument defaulted to size_type_node and
	CTX argument defaulted to ERROR_MARK.
	* c-common.c (fold_offsetof_1): Renamed to ...
	(fold_offsetof): ... this.  Remove wrapper function.  Add TYPE
	argument, convert the pointer constant to TYPE and use size_binop
	with PLUS_EXPR instead of fold_build_pointer_plus if type is not
	a pointer type.  Adjust recursive calls.

	* c-fold.c (c_fully_fold_internal): Use fold_offsetof rather than
	fold_offsetof_1, pass TREE_TYPE (expr) as TYPE to it and drop the
	fold_convert_loc.
	* c-typeck.c (build_unary_op): Use fold_offsetof rather than
	fold_offsetof_1, pass argtype as TYPE to it and drop the
	fold_convert_loc.

	* cp-gimplify.c (cp_fold): Use fold_offsetof rather than
	fold_offsetof_1, pass TREE_TYPE (x) as TYPE to it and drop the
	fold_convert.

	* g++.dg/ext/offsetof2.C: New test.

Added:
    branches/gcc-8-branch/gcc/testsuite/g++.dg/ext/offsetof2.C
Modified:
    branches/gcc-8-branch/gcc/c-family/ChangeLog
    branches/gcc-8-branch/gcc/c-family/c-common.c
    branches/gcc-8-branch/gcc/c-family/c-common.h
    branches/gcc-8-branch/gcc/c/ChangeLog
    branches/gcc-8-branch/gcc/c/c-fold.c
    branches/gcc-8-branch/gcc/c/c-typeck.c
    branches/gcc-8-branch/gcc/cp/ChangeLog
    branches/gcc-8-branch/gcc/cp/cp-gimplify.c
    branches/gcc-8-branch/gcc/testsuite/ChangeLog
Comment 8 Jakub Jelinek 2018-05-30 07:59:19 UTC
Fixed for 8.2+.  As I said, can't reproduce with GCC 7.
Comment 9 roland 2018-05-31 00:09:56 UTC
Both my original test case and Jakub's smaller case do hit the bug in gcc 7.
I just tested the current gcc-7-branch: commit c66c7f7b6f41118cef03ece0c367554eb38c3d65

On x86_64-linux-gnu:

$ ../../gcc/configure --enable-languages=c,c++,lto
$ make all-gcc
$ ./gcc/xgcc -Bgcc/ -O2 -S bar.cc
bar.cc:3:1: error: non-constant condition for static assertion
 static_assert (__builtin_offsetof (T, f.x[31 - 1]) == __builtin_offsetof (T, f.x[30]), "");
 ^~~~~~~~~~~~~
bar.cc:3:1: error: value ‘8’ of type ‘T*’ is not a constant expression
[Exit 1]
$ cat bar.cc
struct S { unsigned long x[31]; };
struct T { bool b; S f; };
static_assert (__builtin_offsetof (T, f.x[31 - 1]) == __builtin_offsetof (T, f.x[30]), "");
$
Comment 10 Jakub Jelinek 2018-06-22 17:23:15 UTC
Author: jakub
Date: Fri Jun 22 17:22:43 2018
New Revision: 261909

URL: https://gcc.gnu.org/viewcvs?rev=261909&root=gcc&view=rev
Log:
	PR c++/85662
	* g++.dg/ext/offsetof3.C: New test.

Added:
    trunk/gcc/testsuite/g++.dg/ext/offsetof3.C
Modified:
    trunk/gcc/testsuite/ChangeLog
Comment 11 Jakub Jelinek 2018-06-22 17:27:16 UTC
Author: jakub
Date: Fri Jun 22 17:26:44 2018
New Revision: 261910

URL: https://gcc.gnu.org/viewcvs?rev=261910&root=gcc&view=rev
Log:
	PR c++/85662
	* g++.dg/ext/offsetof3.C: New test.

Added:
    branches/gcc-8-branch/gcc/testsuite/g++.dg/ext/offsetof3.C
Modified:
    branches/gcc-8-branch/gcc/testsuite/ChangeLog
Comment 12 Jakub Jelinek 2018-06-22 21:23:35 UTC
Author: jakub
Date: Fri Jun 22 21:23:03 2018
New Revision: 261962

URL: https://gcc.gnu.org/viewcvs?rev=261962&root=gcc&view=rev
Log:
	Backported from mainline
	2018-06-22  Jakub Jelinek  <jakub@redhat.com>

	PR c++/85662
	* g++.dg/ext/offsetof3.C: New test.

	2018-05-10  Jakub Jelinek  <jakub@redhat.com>

	PR c++/85662
	* c-common.h (fold_offsetof_1): Removed.
	(fold_offsetof): Add TYPE argument defaulted to size_type_node and
	CTX argument defaulted to ERROR_MARK.
	* c-common.c (fold_offsetof_1): Renamed to ...
	(fold_offsetof): ... this.  Remove wrapper function.  Add TYPE
	argument, convert the pointer constant to TYPE and use size_binop
	with PLUS_EXPR instead of fold_build_pointer_plus if type is not
	a pointer type.  Adjust recursive calls.

	* c-fold.c (c_fully_fold_internal): Use fold_offsetof rather than
	fold_offsetof_1, pass TREE_TYPE (expr) as TYPE to it and drop the
	fold_convert_loc.
	* c-typeck.c (build_unary_op): Use fold_offsetof rather than
	fold_offsetof_1, pass argtype as TYPE to it and drop the
	fold_convert_loc.

	* cp-gimplify.c (cp_fold): Use fold_offsetof rather than
	fold_offsetof_1, pass TREE_TYPE (x) as TYPE to it and drop the
	fold_convert.

	* g++.dg/ext/offsetof2.C: New test.

Added:
    branches/gcc-7-branch/gcc/testsuite/g++.dg/ext/offsetof2.C
    branches/gcc-7-branch/gcc/testsuite/g++.dg/ext/offsetof3.C
Modified:
    branches/gcc-7-branch/gcc/c-family/ChangeLog
    branches/gcc-7-branch/gcc/c-family/c-common.c
    branches/gcc-7-branch/gcc/c-family/c-common.h
    branches/gcc-7-branch/gcc/c/ChangeLog
    branches/gcc-7-branch/gcc/c/c-fold.c
    branches/gcc-7-branch/gcc/c/c-typeck.c
    branches/gcc-7-branch/gcc/cp/ChangeLog
    branches/gcc-7-branch/gcc/cp/cp-gimplify.c
    branches/gcc-7-branch/gcc/testsuite/ChangeLog
Comment 13 Jakub Jelinek 2018-06-25 09:36:51 UTC
Fixed for 7.4+.