On Wed, Jul 31, 2024 at 09:50:56AM +0200, Richard Biener wrote: > I wonder if > > int foo (uintrptr_t x) { *(int *)x = 1; return 1; } > > is considered "noptr" by the standard but then by making a pointer out of > 'x' invokes UB?
I don't know. The paper claims same behavior as const for functions without pointer/array arguments (but arrays decay to pointers); that claim is most likely false because of the infinite loops, but still, I'm not sure about the struct S { int *p; }; int bar (struct S x) [[unsequenced]] { x.p[0] = 1; return 1; } or int baz (...) [[unsequenced]] { va_list ap; va_start (ap); int *p = va_arg (ap, int *); va_end (ap); *p = 1; return 1; } or typedef union { int *p; long long *q; } U __attribute__((transparent_union)); int qux (int x, U y) [[unsequenced]] { if (x) y.p[0] = 1; else y.q[1] = 2; return 3; } etc. cases too. > > +/* Handle an "unsequenced" attribute; arguments as in > > + struct attribute_spec.handler. */ > > + > > +tree > > +handle_unsequenced_attribute (tree *node, tree name, tree ARG_UNUSED > > (args), > > + int flags, bool *no_add_attrs) > > +{ > > + tree fntype = *node; > > + for (tree argtype = TYPE_ARG_TYPES (fntype); argtype; > > + argtype = TREE_CHAIN (argtype)) > > + if (argtype == void_list_node) > > I think this warrants a comment that the attribute on variadic functions > is treated as receiving pointers. Ok (though, iff we actually need to handle variadic functions that way). > > + { > > + if (VOID_TYPE_P (TREE_TYPE (fntype))) > > + warning (OPT_Wattributes, "%qE attribute on function type " > > + "without pointer arguments returning %<void%>", name); > > + const char *name2; > > + if (IDENTIFIER_LENGTH (name) == sizeof ("unsequenced") - 1) > > + name2 = "unsequenced noptr"; > > + else > > + name2 = "reproducible noptr"; > > + if (!lookup_attribute (name2, TYPE_ATTRIBUTES (fntype))) > > + { > > + *no_add_attrs = true; > > shouldn't you set *no_add_attrs also when the noptr attribute is > already there? Because otherwise you'll get the non-noptr attr added? I think that isn't needed. The reason for this (ugly) dance is to avoid building 2 separate FUNCTION_TYPEs with build_type_attribute_variant, one with just "* noptr" attribute and another with both. If "* noptr" is already there, then just return NULL_TREE; without *no_add_attrs = true; will in the caller try to lookup the attribute and if it is found (and have same args, this one doesn't have any), will not add anything further, otherwise it will add it and build_type_attribute_variant. Guess I should add a comment. > > + gcc_assert ((flags & (int) ATTR_FLAG_TYPE_IN_PLACE) == 0); > > + tree attr = tree_cons (get_identifier (name2), NULL_TREE, > > + TYPE_ATTRIBUTES (fntype)); > > + if (!lookup_attribute (IDENTIFIER_POINTER (name), > > + TYPE_ATTRIBUTES (fntype))) > > + attr = tree_cons (name, NULL_TREE, attr); > > + *node = build_type_attribute_variant (*node, attr); > > + } > > + return NULL_TREE; > > + } > > + else if (c_maybe_contains_pointers_p (TREE_VALUE (argtype))) > > + break; > > + return NULL_TREE; > > +} Jakub