From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 7810) id E511938582BD; Mon, 13 Mar 2023 18:12:49 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E511938582BD DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1678731169; bh=xZ7if/4qYknqVYqQQ9NPuTwSJ9I7dIWM52iqt2a3aik=; h=From:To:Subject:Date:From; b=ZlrkXHgPahdBYFFxHegAx0fJmj8CM8ppANJ7ffzoVsLpm/PEtcmoWJwTtz2UPt5Uj /utFPxsayCiWVMTlhvNcw3iFRJxx4cXYpXjxpC4A6NHD+utb/xW5UIGXtko2jxnUKU jrxnX5Yq6fD2Vcm8UDpun5Mq8xCL4yt5d+CNGodE= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Alex Coplan To: gcc-cvs@gcc.gnu.org Subject: [gcc(refs/vendors/ARM/heads/morello)] c-family: Handle CHERI printf extensions in -Wformat X-Act-Checkin: gcc X-Git-Author: Alex Coplan X-Git-Refname: refs/vendors/ARM/heads/morello X-Git-Oldrev: 44079a7439fad52702678b4526bb60b90a577e7f X-Git-Newrev: a865594ef03c5b2b83fc37e9b0c441d39f47d2b4 Message-Id: <20230313181249.E511938582BD@sourceware.org> Date: Mon, 13 Mar 2023 18:12:49 +0000 (GMT) List-Id: https://gcc.gnu.org/g:a865594ef03c5b2b83fc37e9b0c441d39f47d2b4 commit a865594ef03c5b2b83fc37e9b0c441d39f47d2b4 Author: Alex Coplan Date: Tue Mar 7 12:47:31 2023 +0000 c-family: Handle CHERI printf extensions in -Wformat This patch teaches the printf format checking to handle the CHERI extensions described here: https://github.com/CTSRD-CHERI/cheri-c-programming/wiki/Displaying-Capabilities in particular, we add support for the '#' flag and the 'l' length modifier to be used with the 'p' format specifier. Since these extensions should only be recognized when CHERI is enabled (i.e. when compiling for a capability target), we make use of the existing dynamic_format_types mechanism to modify the format table at runtime to handle the CHERI extensions. The '#' flag is simple to handle in this way. The 'l' length modifier is not quite so straightforward to handle. The table that drives the checking has entries of type format_char_info. The two key members here are: int pointer_count; format_type_detail types[FMT_LEN_MAX]; Currently, the %p format specifier is handled with a pointer_count of 1 and an underlying type of void_type_node. The types array above is indexed by the format_lengths enum, so it is possible to vary the expected type based on length modifiers in this way. The problem is that we don't currently encode the pointer type in this array, the pointer type is instead implicit from the pointer_count field. Unfortunately, this doesn't allow us to distinguish between capability and integer pointers, which is necessary for hybrid CHERI. To fix this, we adjust the pointer_count for '%p' to 0, and encode the actual pointer type in the types array above (instead of void_type_node). This allows us to naturally distinguish between capability pointers (with the 'l' length modifier) and normal pointers (without the length modifiers). We then need to make a small change to check_format_types to handle pointer types encoded in this way. We continue to handle the existing pointer_count indirection mechanism here, as this is still suitable for use in other cases (e.g. for scanf where there can be multiple indirections). Diff: --- gcc/c-family/c-format.c | 68 +++++++++++- gcc/c-family/c-format.h | 2 + gcc/testsuite/gcc.dg/format/c90-printf-1.c | 5 +- gcc/testsuite/gcc.dg/format/c99-printf-1.c | 5 +- .../gcc.target/aarch64/morello/wformat-pedantic.c | 117 +++++++++++++++++++++ gcc/testsuite/gcc.target/aarch64/morello/wformat.c | 110 +++++++++++++++++++ 6 files changed, 301 insertions(+), 6 deletions(-) diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c index a41a1e32e0c..82a96ba6a8b 100644 --- a/gcc/c-family/c-format.c +++ b/gcc/c-family/c-format.c @@ -706,7 +706,7 @@ static const format_char_info print_char_table[] = { "eE", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#I", "", NULL }, { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, T94_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL }, { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "cR", NULL }, - { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "c", NULL }, + { "p", 0, STD_C89, { T89_PTR, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "c", NULL }, { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T9L_LL, BADLEN, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN }, "", "W", NULL }, /* C99 conversion specifiers. */ { "F", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "", NULL }, @@ -4201,6 +4201,19 @@ check_format_types (const substring_loc &fmt_loc, if (i < types->pointer_count) continue; + tree orig_wanted_type = wanted_type; + + /* If we're looking for a pointer type and we have a suitable + pointer type, look through to the target types. */ + if (POINTER_TYPE_P (wanted_type) + && POINTER_TYPE_P (cur_type) + && capability_type_p (wanted_type) == capability_type_p (cur_type)) + { + cur_type = TREE_TYPE (cur_type); + wanted_type = TREE_TYPE (wanted_type); + i++; + } + cur_type = TYPE_MAIN_VARIANT (cur_type); /* Check whether the argument type is a character type. This leniency @@ -4262,7 +4275,7 @@ check_format_types (const substring_loc &fmt_loc, continue; /* Now we have a type mismatch. */ format_type_warning (fmt_loc, param_loc, types, - wanted_type, orig_cur_type, fki, + orig_wanted_type, orig_cur_type, fki, offset_to_type_start, conversion_char); } } @@ -4796,6 +4809,44 @@ find_length_info_modifier_index (const format_length_info *fli, int c) gcc_unreachable (); } +static void +init_cheri_printf_info () +{ + static bool cheri_init_done = false; + if (cheri_init_done) + return; + + gcc_assert (dynamic_format_types[printf_format_type].conversion_specs + == print_char_table); + + format_char_info *new_print_char_table + = (format_char_info *) xmemdup (print_char_table, + sizeof (print_char_table), + sizeof (print_char_table)); + + const unsigned num_entries + = sizeof (print_char_table) / sizeof (format_char_info); + + format_char_info *p = new_print_char_table; + format_char_info *end = p + num_entries; + for (; p < end; p++) + { + if (strcmp (p->format_chars, "p")) + continue; + + /* Allow the '#' flag to be used on the 'p' conversion specifier. */ + gcc_assert (!strcmp (p->flag_chars, "-w")); + p->flag_chars = "-w#"; + p->types[FMT_LEN_l].type = &cap_ptr_type_node; + break; + } + gcc_assert (p < end); + + dynamic_format_types[printf_format_type].conversion_specs + = new_print_char_table; + cheri_init_done = true; +} + /* Determine the type of HOST_WIDE_INT in the code being compiled for use in GCC's __asm_fprintf__ custom format attribute. You must have set dynamic_format_types before calling this function. */ @@ -5254,6 +5305,19 @@ handle_format_attribute (tree *node, tree atname, tree args, gcc_unreachable (); } + /* Take CHERI printf extensions into account. */ + if (info.format_type == printf_format_type && cap_ptr_type_node) + { + /* Our first time through, we have to make sure that our + format_type data is allocated dynamically and is modifiable. */ + if (!dynamic_format_types) + format_types = dynamic_format_types = (format_kind_info *) + xmemdup (format_types_orig, sizeof (format_types_orig), + sizeof (format_types_orig)); + + init_cheri_printf_info (); + } + return NULL_TREE; } diff --git a/gcc/c-family/c-format.h b/gcc/c-family/c-format.h index ff8a9f988c3..130db5a0da5 100644 --- a/gcc/c-family/c-format.h +++ b/gcc/c-family/c-format.h @@ -301,11 +301,13 @@ struct format_kind_info #define T_UC &unsigned_char_type_node #define T99_UC { STD_C99, NULL, T_UC } #define T_V &void_type_node +#define T_PTR &ptr_type_node #define T89_G { STD_C89, NULL, &local_gimple_ptr_node } #define T_CGRAPH_NODE { STD_C89, NULL, &local_cgraph_node_ptr_node } #define T_EVENT_PTR { STD_C89, NULL, &local_event_ptr_node } #define T89_T { STD_C89, NULL, &local_tree_type_node } #define T89_V { STD_C89, NULL, T_V } +#define T89_PTR { STD_C89, NULL, T_PTR } #define T_W &wchar_type_node #define T94_W { STD_C94, "wchar_t", T_W } #define TEX_W { STD_EXT, "wchar_t", T_W } diff --git a/gcc/testsuite/gcc.dg/format/c90-printf-1.c b/gcc/testsuite/gcc.dg/format/c90-printf-1.c index c8652fc2252..a8ddced421a 100644 --- a/gcc/testsuite/gcc.dg/format/c90-printf-1.c +++ b/gcc/testsuite/gcc.dg/format/c90-printf-1.c @@ -91,7 +91,8 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p, printf (" %lE", d); /* { dg-warning "15:length|C" "bad use of %l" } */ printf (" %lg", d); /* { dg-warning "15:length|C" "bad use of %l" } */ printf (" %lG", d); /* { dg-warning "15:length|C" "bad use of %l" } */ - printf (" %lp", p); /* { dg-warning "15:length|C" "bad use of %l" } */ + printf (" %lp", p); /* { dg-warning "15:length|C" "bad use of %l" {target {! aarch64_capability_any}} } */ + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability'} "" {target {cheri_capability_hybrid}} .-1 } */ /* These next two were added in C94, but should be objected to in C90. For the first one, GCC has wanted wchar_t instead of the correct C94 and C99 wint_t. @@ -153,7 +154,7 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p, printf ("%#u", u); /* { dg-warning "14:flag" "bad use of # flag" } */ printf ("%#c", i); /* { dg-warning "14:flag" "bad use of # flag" } */ printf ("%#s", s); /* { dg-warning "14:flag" "bad use of # flag" } */ - printf ("%#p", p); /* { dg-warning "14:flag" "bad use of # flag" } */ + printf ("%#p", p); /* { dg-warning "14:flag" "bad use of # flag" {target {! aarch64_capability_any}} } */ printf ("%#n", n); /* { dg-warning "14:flag" "bad use of # flag" } */ /* Uses of the 0 flag. */ printf ("%08d%08i%08o%08u%08x%08X%08e%08E%08f%08g%08G", i, i, u, u, u, u, diff --git a/gcc/testsuite/gcc.dg/format/c99-printf-1.c b/gcc/testsuite/gcc.dg/format/c99-printf-1.c index 408ad49a680..62b7f6beabd 100644 --- a/gcc/testsuite/gcc.dg/format/c99-printf-1.c +++ b/gcc/testsuite/gcc.dg/format/c99-printf-1.c @@ -48,7 +48,8 @@ foo (int i, unsigned int u, double d, char *s, void *p, int *n, printf ("%lc", lc); printf ("%ls", ls); printf ("%lf%lF%le%lE%lg%lG%la%lA", d, d, d, d, d, d, d, d); - printf ("%lp", p); /* { dg-warning "length|C" "bad use of %l" } */ + printf ("%lp", p); /* { dg-warning "length|C" "bad use of %l" {target {! aarch64_capability_any}} } */ + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability'} "" {target {cheri_capability_hybrid}} .-1 } */ printf ("%lld%lli%llo%llu%llx%llX", ll, ll, ull, ull, ull, ull); printf ("%lln", lln); printf ("%llf", d); /* { dg-warning "length" "bad use of %ll" } */ @@ -154,7 +155,7 @@ foo (int i, unsigned int u, double d, char *s, void *p, int *n, printf ("%#u", u); /* { dg-warning "flag" "bad use of # flag" } */ printf ("%#c", i); /* { dg-warning "flag" "bad use of # flag" } */ printf ("%#s", s); /* { dg-warning "flag" "bad use of # flag" } */ - printf ("%#p", p); /* { dg-warning "flag" "bad use of # flag" } */ + printf ("%#p", p); /* { dg-warning "flag" "bad use of # flag" {target {! aarch64_capability_any}} } */ printf ("%#n", n); /* { dg-warning "flag" "bad use of # flag" } */ /* Uses of the 0 flag. */ printf ("%08d%08i%08o%08u%08x%08X%08e%08E%08f%08F%08g%08G%08a%08A", i, i, diff --git a/gcc/testsuite/gcc.target/aarch64/morello/wformat-pedantic.c b/gcc/testsuite/gcc.target/aarch64/morello/wformat-pedantic.c new file mode 100644 index 00000000000..621b78398ae --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/morello/wformat-pedantic.c @@ -0,0 +1,117 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-Wformat -pedantic" } */ +/* Test CHERI extensions are handled by -Wformat. */ + +void base1(void *p) { __builtin_printf ("%p", p); } +void base2(char *p) { __builtin_printf ("%p", p); } +void base3(const void *p) { __builtin_printf ("%p", p); } +void base4(const char *p) { __builtin_printf ("%p", p); } +void base5(const void * const p) { __builtin_printf ("%p", p); } +void base6(const char * const p) { __builtin_printf ("%p", p); } +void base_bad1(int *p) { + __builtin_printf ("%p", p); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'int \*'} "" {target *-*-*} .-1 } */ +} +void base_bad2(int x) +{ + __builtin_printf ("%p", x); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'int'} "" {target *-*-*} .-1 } */ +} +void hybrid_bad1 (void * __capability p) +{ + __builtin_printf ("%p", p); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'void \* __capability'} "" {target cheri_capability_hybrid} .-1 } */ +} +void hybrid_bad2 (char * __capability p) +{ + __builtin_printf ("%p", p); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'char \* __capability'} "" {target cheri_capability_hybrid} .-1 } */ +} +void hybrid_bad3 (int * __capability p) +{ + __builtin_printf ("%p", p); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'int \*( __capability)?'} "" {target *-*-*} .-1 } */ +} + +void alt1(void *p) { __builtin_printf ("%#p", p); } +void alt2(char *p) { __builtin_printf ("%#p", p); } +void alt3(const void *p) { __builtin_printf ("%#p", p); } +void alt4(const char *p) { __builtin_printf ("%#p", p); } +void alt_bad1(int *p) { + __builtin_printf ("%#p", p); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'int \*'} "" {target *-*-*} .-1 } */ +} +void alt_bad2(const int *p) +{ + __builtin_printf ("%#p", p); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'const int \*'} "" {target *-*-*} .-1 } */ +} +void alt_bad3(int x) +{ + __builtin_printf ("%#p", x); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'int'} "" {target *-*-*} .-1 } */ +} + +void ell1(void * __capability p) { __builtin_printf ("%lp", p); } +void ell2(char * __capability p) { __builtin_printf ("%lp", p); } +void ell3(const void * __capability p) { __builtin_printf ("%lp", p); } +void ell4(const char * __capability p) { __builtin_printf ("%lp", p); } +void ell_bad1(int * __capability p) +{ + __builtin_printf ("%lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \*( __capability)?', but argument 2 has type 'int \*( __capability)?'} "" {target *-*-*} .-1 } */ +} +void ell_bad2(const int * __capability p) +{ + __builtin_printf ("%lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \*( __capability)?', but argument 2 has type 'const int \*( __capability)?'} "" {target *-*-*} .-1 } */ +} +void ell_bad3(void *p) +{ + __builtin_printf ("%lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'void \*'} "" {target cheri_capability_hybrid} .-1 } */ +} +void ell_bad4(char *p) +{ + __builtin_printf ("%lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'char \*'} "" {target cheri_capability_hybrid} .-1 } */ +} +void ell_bad5(int *p) +{ + __builtin_printf ("%lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \*( __capability)?', but argument 2 has type 'int \*'} "" {target *-*-*} .-1 } */ +} + +void both1(void * __capability p) { __builtin_printf ("%#lp", p); } +void both2(char * __capability p) { __builtin_printf ("%#lp", p); } +void both3(const void * __capability p) { __builtin_printf ("%#lp", p); } +void both4(const char * __capability p) { __builtin_printf ("%#lp", p); } + +void both_bad1(int * __capability p) { + __builtin_printf ("%#lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \*( __capability)?', but argument 2 has type 'int \*( __capability)?'} "" {target *-*-*} .-1 } */ +} +void both_bad2(const int * __capability p) { + __builtin_printf ("%#lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \*( __capability)?', but argument 2 has type 'const int \*( __capability)?'} "" {target *-*-*} .-1 } */ +} +void both_bad3 (void *p) +{ + __builtin_printf ("%#lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'void \*'} "" {target cheri_capability_hybrid} .-1 } */ +} +void both_bad4 (char *p) +{ + __builtin_printf ("%#lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'char \*'} "" {target cheri_capability_hybrid} .-1 } */ +} +void both_bad5 (float *p) +{ + __builtin_printf ("%#lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \*( __capability)?', but argument 2 has type 'float \*( __capability)?'} "" {target *-*-*} .-1 } */ +} +void both_bad6 (const void *p) +{ + __builtin_printf ("%#lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'const void \*'} "" {target cheri_capability_hybrid} .-1 } */ +} diff --git a/gcc/testsuite/gcc.target/aarch64/morello/wformat.c b/gcc/testsuite/gcc.target/aarch64/morello/wformat.c new file mode 100644 index 00000000000..10c751f2234 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/morello/wformat.c @@ -0,0 +1,110 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-Wformat" } */ +/* Test CHERI extensions are handled by -Wformat. */ + +void base1(void *p) { __builtin_printf ("%p", p); } +void base2(char *p) { __builtin_printf ("%p", p); } +void base3(int *p) { __builtin_printf ("%p", p); } +void base4(const void *p) { __builtin_printf ("%p", p); } +void base5(const char *p) { __builtin_printf ("%p", p); } +void base6(const int *p) { __builtin_printf ("%p", p); } +void base7(const void * const p) { __builtin_printf ("%p", p); } +void base_bad(int x) +{ + __builtin_printf ("%p", x); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'int'} "" {target *-*-*} .-1 } */ +} +void hybrid_bad1 (void * __capability p) +{ + __builtin_printf ("%p", p); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'void \* __capability'} "" {target cheri_capability_hybrid} .-1 } */ +} +void hybrid_bad2 (char * __capability p) +{ + __builtin_printf ("%p", p); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'char \* __capability'} "" {target cheri_capability_hybrid} .-1 } */ +} +void hybrid_bad3 (int * __capability p) +{ + __builtin_printf ("%p", p); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'int \* __capability'} "" {target cheri_capability_hybrid} .-1 } */ +} +void hybrid_bad4 (const void * __capability p) +{ + __builtin_printf ("%p", p); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'const void \* __capability'} "" {target cheri_capability_hybrid} .-1 } */ +} + +void alt1(void *p) { __builtin_printf ("%#p", p); } +void alt2(char *p) { __builtin_printf ("%#p", p); } +void alt3(int *p) { __builtin_printf ("%#p", p); } +void alt_bad(int x) +{ + __builtin_printf ("%#p", x); + /* { dg-warning {format '%p' expects argument of type 'void \*', but argument 2 has type 'int'} "" {target *-*-*} .-1 } */ +} + +void ell1(void * __capability p) { __builtin_printf ("%lp", p); } +void ell2(char * __capability p) { __builtin_printf ("%lp", p); } +void ell3(int * __capability p) { __builtin_printf ("%lp", p); } +void ell4(const void * __capability p) { __builtin_printf ("%lp", p); } + +void ell_bad1(void *p) +{ + __builtin_printf ("%lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'void \*'} "" {target cheri_capability_hybrid} .-1 } */ +} +void ell_bad2(char *p) +{ + __builtin_printf ("%lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'char \*'} "" {target cheri_capability_hybrid} .-1 } */ +} +void ell_bad3(int *p) +{ + __builtin_printf ("%lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'int \*'} "" {target cheri_capability_hybrid} .-1 } */ +} +void ell_bad4(int x) +{ + __builtin_printf ("%lp", x); + /* { dg-warning {format '%lp' expects argument of type 'void \*( __capability)?', but argument 2 has type 'int'} "" {target *-*-*} .-1 } */ +} +void ell_bad5(const void *p) +{ + __builtin_printf ("%lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \*( __capability)?', but argument 2 has type 'const void \*'} "" {target cheri_capability_hybrid} .-1 } */ +} + + +void both1(void * __capability p) { __builtin_printf ("%#lp", p); } +void both2(char * __capability p) { __builtin_printf ("%#lp", p); } +void both3(int * __capability p) { __builtin_printf ("%#lp", p); } +void both4(const void * __capability p) { __builtin_printf ("%#lp", p); } +void both5(const char * __capability p) { __builtin_printf ("%#lp", p); } +void both6(const int * __capability p) { __builtin_printf ("%#lp", p); } + +void both_bad1 (void *p) +{ + __builtin_printf ("%#lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'void \*'} "" {target cheri_capability_hybrid} .-1 } */ +} +void both_bad2 (char *p) +{ + __builtin_printf ("%#lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'char \*'} "" {target cheri_capability_hybrid} .-1 } */ +} +void both_bad3 (float *p) +{ + __builtin_printf ("%#lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'float \*'} "" {target cheri_capability_hybrid} .-1 } */ +} +void both_bad4 (int x) +{ + __builtin_printf ("%#lp", x); + /* { dg-warning {format '%lp' expects argument of type 'void \*( __capability)?', but argument 2 has type 'int'} "" {target *-*-*} .-1 } */ +} +void both_bad5 (const void *p) +{ + __builtin_printf ("%#lp", p); + /* { dg-warning {format '%lp' expects argument of type 'void \* __capability', but argument 2 has type 'const void \*'} "" {target cheri_capability_hybrid} .-1 } */ +}