11.13Srillig/*	$NetBSD: msg_168.c,v 1.13 2024/03/30 17:12:26 rillig Exp $	*/
21.1Srillig# 3 "msg_168.c"
31.1Srillig
41.13Srillig// Test for message: array subscript %ju cannot be > %d [168]
51.1Srillig
61.10Srillig/* lint1-extra-flags: -X 351 */
71.10Srillig
81.2Srilligvoid print_string(const char *);
91.2Srilligvoid print_char(char);
101.2Srillig
111.2Srilligvoid
121.2Srilligexample(void)
131.2Srillig{
141.4Srillig	char buf[20] = {};	/* empty initializer is a GCC extension */
151.2Srillig
161.2Srillig	print_string(buf + 19);	/* inside the array */
171.2Srillig
181.2Srillig	/*
191.2Srillig	 * It is valid to point at the end of the array, but reading a
201.2Srillig	 * character from there invokes undefined behavior.
211.2Srillig	 *
221.2Srillig	 * The pointer to the end of the array is typically used in (begin,
231.2Srillig	 * end) tuples.  These are more common in C++ than in C though.
241.2Srillig	 */
251.2Srillig	print_string(buf + 20);
261.2Srillig
271.2Srillig	print_string(buf + 21);	/* undefined behavior, not detected */
281.2Srillig
291.2Srillig	print_char(buf[19]);
301.12Srillig	/* expect+1: warning: array subscript 20 cannot be > 19 [168] */
311.9Srillig	print_char(buf[20]);
321.2Srillig}
331.3Srillig
341.3Srilligvoid
351.3Srilligarray_with_c99_initializer(void)
361.3Srillig{
371.3Srillig	static const char *const to_roman[] = {
381.3Srillig	    ['0'] = "undefined",
391.3Srillig	    ['5'] = "V",
401.3Srillig	    ['9'] = "IX"
411.3Srillig	};
421.3Srillig
431.5Srillig	print_string(to_roman['9']);
441.12Srillig	/* expect+1: warning: array subscript 58 cannot be > 57 [168] */
451.9Srillig	print_string(to_roman[':']);
461.3Srillig}
471.6Srillig
481.6Srillig
491.8Srillig/*
501.8Srillig * In its expression tree, lint represents pointer addition as 'ptr + off',
511.8Srillig * where 'off' is the offset in bytes, regardless of the pointer type.
521.8Srillig *
531.8Srillig * In the below code, the member 'offset_8' has type 'short', and the
541.8Srillig * expression 's->offset_8' is represented as '&s + 8', or more verbose:
551.8Srillig *
561.8Srillig *	'+' type 'pointer to short'
571.8Srillig *		'&' type 'pointer to struct s'
581.8Srillig *			'name' 's' with auto 'array[1] of struct s', lvalue
591.8Srillig *		'constant' type 'long', value 8
601.8Srillig *
611.8Srillig * The constant 8 differs from the usual model of pointer arithmetics.  Since
621.8Srillig * the type of the '&' expression is 'pointer to struct s', adding a constant
631.8Srillig * would rather be interpreted as adding 'constant * sizeof(struct s)', and
641.8Srillig * to access a member, the pointer to 'struct s' would need to be converted
651.8Srillig * to 'pointer of byte' first, then adding the offset 8, then converting the
661.8Srillig * pointer to the target type 'pointer to short'.
671.8Srillig *
681.8Srillig * Lint uses the simpler representation, saving a few conversions on the way.
691.8Srillig * Without this pre-multiplied representation, the below code would generate
701.8Srillig * warnings about out-of-bounds array access, starting with offset_1.
711.8Srillig */
721.6Srilligstruct s {
731.6Srillig	char offset_0;
741.6Srillig	char offset_1;
751.6Srillig	int offset_4;
761.6Srillig	short offset_8;
771.6Srillig	char offset_10;
781.6Srillig};
791.6Srillig
801.6Srilligstruct s
811.6Srilligs_init(void)
821.6Srillig{
831.6Srillig	struct s s[1];
841.6Srillig	s->offset_0 = 1;
851.6Srillig	s->offset_1 = 2;
861.6Srillig	s->offset_4 = 3;
871.6Srillig	s->offset_8 = 4;
881.6Srillig	s->offset_10 = 5;
891.6Srillig	return s[0];
901.6Srillig}
91