Module Name:    src
Committed By:   rillig
Date:           Fri Jun 30 21:06:19 UTC 2023

Modified Files:
        src/tests/usr.bin/xlint/lint1: expr_sizeof.c init_braces.c msg_102.c
        src/usr.bin/xlint/lint1: cgram.y debug.c decl.c externs1.h init.c
            tree.c

Log Message:
lint: fix handling of unnamed struct/union members

The support for unnamed struct/union members that was added in decl.c
1.60 from 2015-10-13 was simple but wrong. It didn't cover initializers
of these structures and computed wrong sizes for structures containing
anonymous unions. At that time, the handling of initializers was broken
as well, it was fixed 6 years later in init.c 1.229 from 2021-12-22.

Real-life examples for code that lint couldn't handle are:

        * external/bsd/jemalloc/dist/src/jemalloc.c
        * external/mit/xorg/lib/dri.old/Makefile


To generate a diff of this commit:
cvs rdiff -u -r1.11 -r1.12 src/tests/usr.bin/xlint/lint1/expr_sizeof.c
cvs rdiff -u -r1.4 -r1.5 src/tests/usr.bin/xlint/lint1/init_braces.c
cvs rdiff -u -r1.5 -r1.6 src/tests/usr.bin/xlint/lint1/msg_102.c
cvs rdiff -u -r1.441 -r1.442 src/usr.bin/xlint/lint1/cgram.y
cvs rdiff -u -r1.38 -r1.39 src/usr.bin/xlint/lint1/debug.c
cvs rdiff -u -r1.328 -r1.329 src/usr.bin/xlint/lint1/decl.c
cvs rdiff -u -r1.181 -r1.182 src/usr.bin/xlint/lint1/externs1.h
cvs rdiff -u -r1.242 -r1.243 src/usr.bin/xlint/lint1/init.c
cvs rdiff -u -r1.536 -r1.537 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/expr_sizeof.c
diff -u src/tests/usr.bin/xlint/lint1/expr_sizeof.c:1.11 src/tests/usr.bin/xlint/lint1/expr_sizeof.c:1.12
--- src/tests/usr.bin/xlint/lint1/expr_sizeof.c:1.11	Fri Jun 30 16:39:17 2023
+++ src/tests/usr.bin/xlint/lint1/expr_sizeof.c	Fri Jun 30 21:06:18 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: expr_sizeof.c,v 1.11 2023/06/30 16:39:17 rillig Exp $	*/
+/*	$NetBSD: expr_sizeof.c,v 1.12 2023/06/30 21:06:18 rillig Exp $	*/
 # 3 "expr_sizeof.c"
 
 /*
@@ -152,8 +152,7 @@ anonymous_struct_and_union(void)
 			unsigned char uc32[32];
 		};
 	} su_16_32;
-	/* FIXME: Must be 32, not 48. */
-	/* expect+1: error: negative array dimension (-48) [20] */
+	/* expect+1: error: negative array dimension (-32) [20] */
 	typedef int sizeof_su_16_32[-(int)sizeof(su_16_32)];
 
 	union {

Index: src/tests/usr.bin/xlint/lint1/init_braces.c
diff -u src/tests/usr.bin/xlint/lint1/init_braces.c:1.4 src/tests/usr.bin/xlint/lint1/init_braces.c:1.5
--- src/tests/usr.bin/xlint/lint1/init_braces.c:1.4	Fri Jun 30 09:21:52 2023
+++ src/tests/usr.bin/xlint/lint1/init_braces.c	Fri Jun 30 21:06:18 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: init_braces.c,v 1.4 2023/06/30 09:21:52 rillig Exp $	*/
+/*	$NetBSD: init_braces.c,v 1.5 2023/06/30 21:06:18 rillig Exp $	*/
 # 3 "init_braces.c"
 
 /*
@@ -86,8 +86,6 @@ init_anonymous_struct_and_union(void)
 	struct outer var = {	/* struct outer */
 		{		/* anonymous union */
 			{	/* anonymous struct */
-/* FIXME: GCC and Clang both compile this initializer. */
-/* expect+1: error: type 'struct time' does not have member 'times' [101] */
 				.times = {
 					.t0 = { .ns = 0, },
 					.t1 = { .ns = 0, },

Index: src/tests/usr.bin/xlint/lint1/msg_102.c
diff -u src/tests/usr.bin/xlint/lint1/msg_102.c:1.5 src/tests/usr.bin/xlint/lint1/msg_102.c:1.6
--- src/tests/usr.bin/xlint/lint1/msg_102.c:1.5	Fri Jun 30 09:21:52 2023
+++ src/tests/usr.bin/xlint/lint1/msg_102.c	Fri Jun 30 21:06:18 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_102.c,v 1.5 2023/06/30 09:21:52 rillig Exp $	*/
+/*	$NetBSD: msg_102.c,v 1.6 2023/06/30 21:06:18 rillig Exp $	*/
 # 3 "msg_102.c"
 
 // Test for message: illegal use of member '%s' [102]
@@ -31,14 +31,8 @@ static struct bit_fields_and_bits *b1, *
 static inline _Bool
 eq(int x)
 {
-	/*
-	 * TODO: Once this is fixed, enable lint in
-	 * external/mit/xorg/lib/dri.old/Makefile again.
-	 */
-
 	if (x == 0)
-		/* expect+2: error: illegal use of member 'bits' [102] */
-		/* expect+1: error: illegal use of member 'bits' [102] */
+		/* Accessing a member from an unnamed struct member. */
 		return u1->bits == u2->bits;
 
 	/*

Index: src/usr.bin/xlint/lint1/cgram.y
diff -u src/usr.bin/xlint/lint1/cgram.y:1.441 src/usr.bin/xlint/lint1/cgram.y:1.442
--- src/usr.bin/xlint/lint1/cgram.y:1.441	Fri Jun 30 19:10:49 2023
+++ src/usr.bin/xlint/lint1/cgram.y	Fri Jun 30 21:06:18 2023
@@ -1,5 +1,5 @@
 %{
-/* $NetBSD: cgram.y,v 1.441 2023/06/30 19:10:49 rillig Exp $ */
+/* $NetBSD: cgram.y,v 1.442 2023/06/30 21:06:18 rillig Exp $ */
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
@@ -35,7 +35,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: cgram.y,v 1.441 2023/06/30 19:10:49 rillig Exp $");
+__RCSID("$NetBSD: cgram.y,v 1.442 2023/06/30 21:06:18 rillig Exp $");
 #endif
 
 #include <limits.h>
@@ -120,14 +120,6 @@ restore_warning_flags_loc(const char *fi
 #define save_warning_flags()	save_warning_flags_loc(__FILE__, __LINE__)
 #define restore_warning_flags()	restore_warning_flags_loc(__FILE__, __LINE__)
 
-/* unbind the anonymous struct members from the struct */
-static void
-anonymize(sym_t *s)
-{
-	for ( ; s != NULL; s = s->s_next)
-		s->u.s_member.sm_sou_type = NULL;
-}
-
 static bool
 is_either(const char *s, const char *a, const char *b)
 {
@@ -968,11 +960,9 @@ struct_declaration:		/* C99 6.7.2.1 */
 		if (!allow_c11 && !allow_gcc)
 			/* anonymous struct/union members is a C11 feature */
 			warning(49);
-		if (is_struct_or_union(dcs->d_type->t_tspec)) {
-			$$ = dcs->d_type->t_sou->sou_first_member;
-			/* add all the members of the anonymous struct/union */
-			anonymize($$);
-		} else {
+		if (is_struct_or_union(dcs->d_type->t_tspec))
+			$$ = declare_unnamed_member();
+		else {
 			/* syntax error '%s' */
 			error(249, "unnamed member");
 			$$ = NULL;

Index: src/usr.bin/xlint/lint1/debug.c
diff -u src/usr.bin/xlint/lint1/debug.c:1.38 src/usr.bin/xlint/lint1/debug.c:1.39
--- src/usr.bin/xlint/lint1/debug.c:1.38	Fri Jun 30 14:39:23 2023
+++ src/usr.bin/xlint/lint1/debug.c	Fri Jun 30 21:06:18 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: debug.c,v 1.38 2023/06/30 14:39:23 rillig Exp $ */
+/* $NetBSD: debug.c,v 1.39 2023/06/30 21:06:18 rillig Exp $ */
 
 /*-
  * Copyright (c) 2021 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: debug.c,v 1.38 2023/06/30 14:39:23 rillig Exp $");
+__RCSID("$NetBSD: debug.c,v 1.39 2023/06/30 21:06:18 rillig Exp $");
 #endif
 
 #include <stdlib.h>
@@ -367,8 +367,9 @@ debug_sym(const char *prefix, const sym_
 		debug_printf(" value=%s",
 		    sym->u.s_bool_constant ? "true" : "false");
 
-	if (is_member(sym) && sym->u.s_member.sm_sou_type != NULL) {
+	if (is_member(sym)) {
 		struct_or_union *sou_type = sym->u.s_member.sm_sou_type;
+		lint_assert(sou_type != NULL);
 		const char *tag = sou_type->sou_tag->s_name;
 		const sym_t *def = sou_type->sou_first_typedef;
 		if (tag == unnamed && def != NULL)

Index: src/usr.bin/xlint/lint1/decl.c
diff -u src/usr.bin/xlint/lint1/decl.c:1.328 src/usr.bin/xlint/lint1/decl.c:1.329
--- src/usr.bin/xlint/lint1/decl.c:1.328	Fri Jun 30 19:43:00 2023
+++ src/usr.bin/xlint/lint1/decl.c	Fri Jun 30 21:06:18 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: decl.c,v 1.328 2023/06/30 19:43:00 rillig Exp $ */
+/* $NetBSD: decl.c,v 1.329 2023/06/30 21:06:18 rillig Exp $ */
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
@@ -38,7 +38,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: decl.c,v 1.328 2023/06/30 19:43:00 rillig Exp $");
+__RCSID("$NetBSD: decl.c,v 1.329 2023/06/30 21:06:18 rillig Exp $");
 #endif
 
 #include <sys/param.h>
@@ -1016,6 +1016,49 @@ check_bit_field(sym_t *dsym, tspec_t *in
 	}
 }
 
+/* Add a member to the struct or union type that is being built in 'dcs'. */
+static void
+dcs_add_member(sym_t *mem)
+{
+	type_t *tp = mem->s_type;
+
+	unsigned int union_offset = 0;
+	if (dcs->d_kind == DK_UNION_MEMBER) {
+		union_offset = dcs->d_offset_in_bits;
+		dcs->d_offset_in_bits = 0;
+	}
+
+	if (mem->s_bitfield) {
+		dcs_align(alignment_in_bits(tp), tp->t_bit_field_width);
+		// XXX: Why round down?
+		mem->u.s_member.sm_offset_in_bits = dcs->d_offset_in_bits
+		    - dcs->d_offset_in_bits % size_in_bits(tp->t_tspec);
+		tp->t_bit_field_offset = dcs->d_offset_in_bits
+		    - mem->u.s_member.sm_offset_in_bits;
+		dcs->d_offset_in_bits += tp->t_bit_field_width;
+	} else {
+		dcs_align(alignment_in_bits(tp), 0);
+		mem->u.s_member.sm_offset_in_bits = dcs->d_offset_in_bits;
+		dcs->d_offset_in_bits += type_size_in_bits(tp);
+	}
+
+	if (union_offset > dcs->d_offset_in_bits)
+		dcs->d_offset_in_bits = union_offset;
+}
+
+sym_t *
+declare_unnamed_member(void)
+{
+
+	sym_t *mem = block_zero_alloc(sizeof(*mem));
+	mem->s_name = unnamed;
+	mem->s_type = dcs->d_type;
+
+	dcs_add_member(mem);
+	bitfieldtype_ok = false;
+	return mem;
+}
+
 sym_t *
 declare_member(sym_t *dsym)
 {
@@ -1056,25 +1099,7 @@ declare_member(sym_t *dsym)
 		c99ism(39, dsym->s_name);
 	}
 
-	unsigned int union_offset = 0;
-	if (dcs->d_kind == DK_UNION_MEMBER) {
-		union_offset = dcs->d_offset_in_bits;
-		dcs->d_offset_in_bits = 0;
-	}
-	if (dsym->s_bitfield) {
-		dcs_align(alignment_in_bits(tp), tp->t_bit_field_width);
-		dsym->u.s_member.sm_offset_in_bits = dcs->d_offset_in_bits -
-		    dcs->d_offset_in_bits % size_in_bits(t);
-		tp->t_bit_field_offset = dcs->d_offset_in_bits -
-		    dsym->u.s_member.sm_offset_in_bits;
-		dcs->d_offset_in_bits += tp->t_bit_field_width;
-	} else {
-		dcs_align(alignment_in_bits(tp), 0);
-		dsym->u.s_member.sm_offset_in_bits = dcs->d_offset_in_bits;
-		dcs->d_offset_in_bits += sz;
-	}
-	if (union_offset > dcs->d_offset_in_bits)
-		dcs->d_offset_in_bits = union_offset;
+	dcs_add_member(dsym);
 
 	check_function_definition(dsym, false);
 
@@ -1683,6 +1708,20 @@ storage_class_name(scl_t sc)
 	/* NOTREACHED */
 }
 
+static bool
+has_named_member(const type_t *tp)
+{
+	for (const sym_t *mem = tp->t_sou->sou_first_member;
+	     mem != NULL; mem = mem->s_next) {
+		if (mem->s_name != unnamed)
+			return true;
+		if (is_struct_or_union(mem->s_type->t_tspec)
+		    && has_named_member(mem->s_type))
+			return true;
+	}
+	return false;
+}
+
 type_t *
 complete_struct_or_union(sym_t *first_member)
 {
@@ -1705,27 +1744,7 @@ complete_struct_or_union(sym_t *first_me
 	if (sp->sou_size_in_bits == 0) {
 		/* zero sized %s is a C99 feature */
 		c99ism(47, tspec_name(tp->t_tspec));
-	}
-
-	bool has_named_member = false;
-	for (sym_t *mem = first_member; mem != NULL; mem = mem->s_next) {
-		if (mem->s_name != unnamed)
-			has_named_member = true;
-		/* bind anonymous members to the structure */
-		if (mem->u.s_member.sm_sou_type == NULL) {
-			mem->u.s_member.sm_sou_type = sp;
-			if (mem->s_type->t_bitfield) {
-				sp->sou_size_in_bits +=
-				    bit_fields_width(&mem, &has_named_member);
-				if (mem == NULL)
-					break;
-			}
-			sp->sou_size_in_bits +=
-			    type_size_in_bits(mem->s_type);
-		}
-	}
-
-	if (!has_named_member && sp->sou_size_in_bits != 0) {
+	} else if (!has_named_member(tp)) {
 		/* '%s' has no named members */
 		warning(65, type_name(tp));
 	}

Index: src/usr.bin/xlint/lint1/externs1.h
diff -u src/usr.bin/xlint/lint1/externs1.h:1.181 src/usr.bin/xlint/lint1/externs1.h:1.182
--- src/usr.bin/xlint/lint1/externs1.h:1.181	Fri Jun 30 19:10:49 2023
+++ src/usr.bin/xlint/lint1/externs1.h	Fri Jun 30 21:06:18 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: externs1.h,v 1.181 2023/06/30 19:10:49 rillig Exp $	*/
+/*	$NetBSD: externs1.h,v 1.182 2023/06/30 21:06:18 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -203,6 +203,7 @@ int	length_in_bits(const type_t *, const
 unsigned int alignment_in_bits(const type_t *);
 sym_t	*concat_symbols(sym_t *, sym_t *);
 void	check_type(sym_t *);
+sym_t	*declare_unnamed_member(void);
 sym_t	*declare_member(sym_t *);
 sym_t	*set_bit_field_width(sym_t *, int);
 qual_ptr *merge_qualified_pointer(qual_ptr *, qual_ptr *);

Index: src/usr.bin/xlint/lint1/init.c
diff -u src/usr.bin/xlint/lint1/init.c:1.242 src/usr.bin/xlint/lint1/init.c:1.243
--- src/usr.bin/xlint/lint1/init.c:1.242	Mon May 22 17:53:27 2023
+++ src/usr.bin/xlint/lint1/init.c	Fri Jun 30 21:06:18 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: init.c,v 1.242 2023/05/22 17:53:27 rillig Exp $	*/
+/*	$NetBSD: init.c,v 1.243 2023/06/30 21:06:18 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -38,7 +38,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: init.c,v 1.242 2023/05/22 17:53:27 rillig Exp $");
+__RCSID("$NetBSD: init.c,v 1.243 2023/06/30 21:06:18 rillig Exp $");
 #endif
 
 #include <stdlib.h>
@@ -209,12 +209,19 @@ can_init_character_array(const type_t *l
 	    : lst == WCHAR;
 }
 
-/* C99 6.7.8p9 */
+/*
+ * C11 6.7.9p9 seems to say that all unnamed members are skipped. C11 6.7.2.1p8
+ * suggests an exception to that rule, and together with C11 6.7.2.1p13, it
+ * says that the members from an anonymous struct/union member are "considered
+ * to be members of the containing structure or union", thereby preventing that
+ * the containing structure or union has only unnamed members.
+ */
 static const sym_t *
 skip_unnamed(const sym_t *m)
 {
 
-	while (m != NULL && m->s_name == unnamed)
+	while (m != NULL && m->s_name == unnamed
+	    && !is_struct_or_union(m->s_type->t_tspec))
 		m = m->s_next;
 	return m;
 }

Index: src/usr.bin/xlint/lint1/tree.c
diff -u src/usr.bin/xlint/lint1/tree.c:1.536 src/usr.bin/xlint/lint1/tree.c:1.537
--- src/usr.bin/xlint/lint1/tree.c:1.536	Fri Jun 30 12:21:25 2023
+++ src/usr.bin/xlint/lint1/tree.c	Fri Jun 30 21:06:18 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: tree.c,v 1.536 2023/06/30 12:21:25 rillig Exp $	*/
+/*	$NetBSD: tree.c,v 1.537 2023/06/30 21:06:18 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.536 2023/06/30 12:21:25 rillig Exp $");
+__RCSID("$NetBSD: tree.c,v 1.537 2023/06/30 21:06:18 rillig Exp $");
 #endif
 
 #include <float.h>
@@ -1882,6 +1882,26 @@ all_members_compatible(const sym_t *msym
 	return true;
 }
 
+static sym_t *
+find_member(const type_t *tp, const char *name)
+{
+	for (sym_t *mem = tp->t_sou->sou_first_member;
+	     mem != NULL; mem = mem->s_next) {
+		if (strcmp(mem->s_name, name) == 0)
+			return mem;
+	}
+	for (sym_t *mem = tp->t_sou->sou_first_member;
+	     mem != NULL; mem = mem->s_next) {
+		if (is_struct_or_union(mem->s_type->t_tspec) &&
+		    mem->s_name == unnamed) {
+			sym_t *nested_mem = find_member(mem->s_type, name);
+			if (nested_mem != NULL)
+				return nested_mem;
+		}
+	}
+	return NULL;
+}
+
 /*
  * Returns a symbol which has the same name as the msym argument and is a
  * member of the struct or union specified by the tn argument.
@@ -1913,13 +1933,14 @@ struct_or_union_member(tnode_t *tn, op_t
 		return msym;
 	}
 
-	/* Set str to the tag of which msym is expected to be a member. */
-	struct_or_union *str = NULL;
+	/* Determine the tag type of which msym is expected to be a member. */
+	const type_t *tp = NULL;
 	if (op == POINT && is_struct_or_union(tn->tn_type->t_tspec))
-		str = tn->tn_type->t_sou;
+		tp = tn->tn_type;
 	if (op == ARROW && tn->tn_type->t_tspec == PTR
 	    && is_struct_or_union(tn->tn_type->t_subt->t_tspec))
-		str = tn->tn_type->t_subt->t_sou;
+		tp = tn->tn_type->t_subt;
+	struct_or_union *str = tp != NULL ? tp->t_sou : NULL;
 
 	/*
 	 * If this struct/union has a member with the name of msym, return it.
@@ -1934,6 +1955,12 @@ struct_or_union_member(tnode_t *tn, op_t
 		}
 	}
 
+	if (tp != NULL) {
+		sym_t *nested_mem = find_member(tp, msym->s_name);
+		if (nested_mem != NULL)
+			return nested_mem;
+	}
+
 	bool eq = all_members_compatible(msym);
 
 	/*

Reply via email to