Module Name:    src
Committed By:   rillig
Date:           Wed May  1 17:42:58 UTC 2024

Modified Files:
        src/tests/usr.bin/xlint/lint1: decl.c
        src/usr.bin/xlint/lint1: tree.c

Log Message:
lint: make 'offsetof(t, array-member)' a constant expression

The macro 'offsetof(t, m)' already expanded to a constant expression for
scalar members but not for arrays.  This was because the macro expanded
to '(size_t)(((t *)0)->m)', which lint internally represents as
'addr(indir(ptr(0) + offset(m)))', and build_address simplifies
'addr(indir(x))' to 'x' if the types match.  The types only match for
scalar types though, but not for arrays.

When build_address happens, the type information is incomplete,
therefore 'offsetof(t, array)' has to be simplified at a later point.


To generate a diff of this commit:
cvs rdiff -u -r1.29 -r1.30 src/tests/usr.bin/xlint/lint1/decl.c
cvs rdiff -u -r1.638 -r1.639 src/usr.bin/xlint/lint1/tree.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/tests/usr.bin/xlint/lint1/decl.c
diff -u src/tests/usr.bin/xlint/lint1/decl.c:1.29 src/tests/usr.bin/xlint/lint1/decl.c:1.30
--- src/tests/usr.bin/xlint/lint1/decl.c:1.29	Wed May  1 12:36:56 2024
+++ src/tests/usr.bin/xlint/lint1/decl.c	Wed May  1 17:42:57 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: decl.c,v 1.29 2024/05/01 12:36:56 rillig Exp $	*/
+/*	$NetBSD: decl.c,v 1.30 2024/05/01 17:42:57 rillig Exp $	*/
 # 3 "decl.c"
 
 /*
@@ -243,22 +243,29 @@ get_x(struct point3d { struct point3d_nu
 }
 
 // Expressions of the form '(size_t)&null_ptr->member' are used by several
-// C implementations to implement the offsetof macro.  Dereferencing a null
-// pointer invokes undefined behavior, though, even in this form.
+// C implementations to implement the offsetof macro.
 void
 offsetof_on_array_member(void)
 {
-	struct s1 {
+	typedef struct {
 		int padding, plain, arr[2];
-	};
+	} s1;
 
+	// Bit-fields must have a constant number of bits.
 	struct s2 {
-		unsigned int off_plain:(unsigned long)&((struct s1 *)0)->plain;
-		// FIXME: offsetof must work for array members as well.
-		/* expect+1: error: integral constant expression expected [55] */
-		unsigned int off_arr:(unsigned long)&((struct s1 *)0)->arr;
-		// FIXME: offsetof must work for array members as well.
-		/* expect+1: error: integral constant expression expected [55] */
-		unsigned int off_arr_element:(unsigned long)&((struct s1 *)0)->arr[0];
+		unsigned int off_plain:(unsigned long)&((s1 *)0)->plain;
+		unsigned int off_arr:(unsigned long)&((s1 *)0)->arr;
+		unsigned int off_arr_0:(unsigned long)&((s1 *)0)->arr[0];
+		unsigned int off_arr_3:(unsigned long)&((s1 *)0)->arr[3];
 	};
+
+	// Arrays may be variable-width, but the diagnostic reveals the size.
+	/* expect+1: error: negative array dimension (-4) [20] */
+	typedef int off_plain[-(int)(unsigned long)&((s1 *)0)->plain];
+	/* expect+1: error: negative array dimension (-8) [20] */
+	typedef int off_arr[-(int)(unsigned long)&((s1 *)0)->arr];
+	/* expect+1: error: negative array dimension (-8) [20] */
+	typedef int off_arr_0[-(int)(unsigned long)&((s1 *)0)->arr[0]];
+	/* expect+1: error: negative array dimension (-20) [20] */
+	typedef int off_arr_3[-(int)(unsigned long)&((s1 *)0)->arr[3]];
 }

Index: src/usr.bin/xlint/lint1/tree.c
diff -u src/usr.bin/xlint/lint1/tree.c:1.638 src/usr.bin/xlint/lint1/tree.c:1.639
--- src/usr.bin/xlint/lint1/tree.c:1.638	Wed May  1 05:49:33 2024
+++ src/usr.bin/xlint/lint1/tree.c	Wed May  1 17:42:57 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: tree.c,v 1.638 2024/05/01 05:49:33 rillig Exp $	*/
+/*	$NetBSD: tree.c,v 1.639 2024/05/01 17:42:57 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: tree.c,v 1.638 2024/05/01 05:49:33 rillig Exp $");
+__RCSID("$NetBSD: tree.c,v 1.639 2024/05/01 17:42:57 rillig Exp $");
 #endif
 
 #include <float.h>
@@ -4112,6 +4112,28 @@ cast_to_union(tnode_t *otn, bool sys, ty
 	return NULL;
 }
 
+// In GCC mode, allow 'nullptr + offset' as a constant expression.
+static tnode_t *
+null_pointer_offset(tnode_t *tn)
+{
+	uint64_t off = 0;
+	const tnode_t *n = tn;
+	while ((n->tn_op == PLUS || n->tn_op == MINUS)
+	    && is_integer(n->u.ops.right->tn_type->t_tspec)) {
+		off += (uint64_t)n->u.ops.right->u.value.u.integer;
+		n = n->u.ops.left;
+	}
+	if (n->tn_type->t_tspec == PTR
+	    && n->tn_op == ADDR
+	    && n->u.ops.left->tn_op == INDIR
+	    && n->u.ops.left->u.ops.left->tn_op == CON
+	    && n->u.ops.left->u.ops.left->tn_type->t_tspec == PTR) {
+		off += (uint64_t)n->u.ops.left->u.ops.left->u.value.u.integer;
+		return build_integer_constant(SIZEOF_TSPEC, (int64_t)off);
+	}
+	return tn;
+}
+
 tnode_t *
 cast(tnode_t *tn, bool sys, type_t *tp)
 {
@@ -4144,7 +4166,7 @@ cast(tnode_t *tn, bool sys, type_t *tp)
 		error(148);
 		return NULL;
 	} else if (is_integer(nt) && is_scalar(ot)) {
-		/* ok */
+		tn = null_pointer_offset(tn);
 	} else if (is_floating(nt) && is_arithmetic(ot)) {
 		/* ok */
 	} else if (nt == PTR && is_integer(ot)) {

Reply via email to