From 5c9244ff43ee8644ddb09ca9bf8e78ad89fbaa22 Mon Sep 17 00:00:00 2001 From: Ehab Maali <107338078+71iq@users.noreply.github.com> Date: Sat, 7 Nov 2026 23:24:01 +0200 Subject: [PATCH] C: diagnose pointer conversions that add qualifiers below the first level [GH #514] In C, an implicit conversion such as "int **" to "const int *const *" is valid in C++ but violates the constraints of simple assignment, because the types pointed to are not compatible (C23 6.5.17.2), so a diagnostic is required (C23 5.1.1.3). The front end accepted it as an extension without one. Issue the caller's default warning when no more specific warning applies; the conversion is still accepted. --- .../gh-514-c-mode-cv-qual-conv.txt | 14 +++++++ src/types.c | 6 ++- .../.gh-514-cpp.rto/default.1.1.txt | 1 + .../.gh-514-positive.rto/default.1.1.txt | 1 + .../regressions/.gh-514.rto/default.1.1.txt | 21 ++++++++++ .../regressions/.gh-514.rto/default.2.1.txt | 21 ++++++++++ tests/tests/regressions/gh-514-cpp.sft.cpp | 18 ++++++++ tests/tests/regressions/gh-514-positive.sft.c | 17 ++++++++ tests/tests/regressions/gh-514.sft.c | 42 +++++++++++++++++++ 9 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/pending_changes/gh-514-c-mode-cv-qual-conv.txt create mode 100644 tests/tests/regressions/.gh-514-cpp.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-514-positive.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-514.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-514.rto/default.2.1.txt create mode 100644 tests/tests/regressions/gh-514-cpp.sft.cpp create mode 100644 tests/tests/regressions/gh-514-positive.sft.c create mode 100644 tests/tests/regressions/gh-514.sft.c diff --git a/src/pending_changes/gh-514-c-mode-cv-qual-conv.txt b/src/pending_changes/gh-514-c-mode-cv-qual-conv.txt new file mode 100644 index 0000000000..292ba488a3 --- /dev/null +++ b/src/pending_changes/gh-514-c-mode-cv-qual-conv.txt @@ -0,0 +1,14 @@ +[GH #514] +C: pointer conversions adding qualifiers below the first level + +In C, the front end accepted an implicit conversion between pointer types +that adds type qualifiers below the first level, such as "int **" to +"const int *const *", without a diagnostic. Such a conversion is valid in +C++, but in C the types pointed to are not compatible, so it violates a +constraint and requires a diagnostic (C23 6.5.17.2). A warning is now +issued; the conversion is still accepted. For example: + + void f(int **p) { + const int *const *q; + q = p; // Now a warning. + } diff --git a/src/types.c b/src/types.c index 3e8a3aed1b..bd9f53ab6f 100644 --- a/src/types.c +++ b/src/types.c @@ -11682,11 +11682,15 @@ operators), 3.3.15 (?: operator), and 3.3.16.1 (simple assignment). /* Allow conversion between pointers where type qualifiers are being added at levels other than the first, e.g., "int **" -> "const int * const *". These are the const-safe - cases. This is an extension in C mode. */ + cases. In C mode, this is an extension, and a diagnostic is + issued since the types pointed to are not "compatible". */ okay = TRUE; std_conv->nontrivial_conversion = FALSE; std_conv->type_qualifiers_added = qualifiers_added; std_conv->warning_suggested = warning_suggested; + if (C_mode() && warning_suggested == ec_no_error) { + std_conv->warning_suggested = default_warning_code; + } /* if */ qualifiers_checked = TRUE; } else if ((!suppress_extensions || any_cfront_mode()) && same_type_with_added_qualifiers( diff --git a/tests/tests/regressions/.gh-514-cpp.rto/default.1.1.txt b/tests/tests/regressions/.gh-514-cpp.rto/default.1.1.txt new file mode 100644 index 0000000000..8aa33ab790 --- /dev/null +++ b/tests/tests/regressions/.gh-514-cpp.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++20 --strict Test_name.c diff --git a/tests/tests/regressions/.gh-514-positive.rto/default.1.1.txt b/tests/tests/regressions/.gh-514-positive.rto/default.1.1.txt new file mode 100644 index 0000000000..89c79baec6 --- /dev/null +++ b/tests/tests/regressions/.gh-514-positive.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c --c23 --strict Test_name.c diff --git a/tests/tests/regressions/.gh-514.rto/default.1.1.txt b/tests/tests/regressions/.gh-514.rto/default.1.1.txt new file mode 100644 index 0000000000..f264da309f --- /dev/null +++ b/tests/tests/regressions/.gh-514.rto/default.1.1.txt @@ -0,0 +1,21 @@ +fe_only -DTEST_NUMBER=1 --c --c23 --no_strict_gnu --gnu_version=130300 Test_name.c +"Test_name.c", line 16: warning: a value of type "int **" cannot be assigned to an entity of type "const int *const *" + q = p; + ^ + +"Test_name.c", line 22: warning: a value of type "int **" cannot be used to initialize an entity of type "const int *const *" + const int *const *q = p; + ^ + +"Test_name.c", line 29: warning: argument of type "int **" is incompatible with parameter of type "const int *const *" + take(p); + ^ + +"Test_name.c", line 34: warning: return value type does not match the function type + return p; + ^ + +"Test_name.c", line 39: warning: a value of type "int ***" cannot be used to initialize an entity of type "const int *const *const *" + const int *const *const *q = p; + ^ + diff --git a/tests/tests/regressions/.gh-514.rto/default.2.1.txt b/tests/tests/regressions/.gh-514.rto/default.2.1.txt new file mode 100644 index 0000000000..ea22553afc --- /dev/null +++ b/tests/tests/regressions/.gh-514.rto/default.2.1.txt @@ -0,0 +1,21 @@ +fe_only -DTEST_NUMBER=2 --c --c23 --strict_warnings Test_name.c +"Test_name.c", line 16: warning: a value of type "int **" cannot be assigned to an entity of type "const int *const *" + q = p; + ^ + +"Test_name.c", line 22: warning: a value of type "int **" cannot be used to initialize an entity of type "const int *const *" + const int *const *q = p; + ^ + +"Test_name.c", line 29: warning: argument of type "int **" is incompatible with parameter of type "const int *const *" + take(p); + ^ + +"Test_name.c", line 34: warning: return value type does not match the function type + return p; + ^ + +"Test_name.c", line 39: warning: a value of type "int ***" cannot be used to initialize an entity of type "const int *const *const *" + const int *const *const *q = p; + ^ + diff --git a/tests/tests/regressions/gh-514-cpp.sft.cpp b/tests/tests/regressions/gh-514-cpp.sft.cpp new file mode 100644 index 0000000000..7b7a9d9796 --- /dev/null +++ b/tests/tests/regressions/gh-514-cpp.sft.cpp @@ -0,0 +1,18 @@ +//type:fp +//options_all:--c++20 --strict +//remark:C++: impl. ptr conv. w/ qualifiers below first level + +// The const-safe conversions that need a diagnostic in C are standard +// qualification conversions in C++. + +void take(const int *const *); +void take3(const int *const *const *); + +void add_deep_const(int **p, int ***p3) +{ + const int *const *q = p; + + take(q); + take(p); + take3(p3); +} diff --git a/tests/tests/regressions/gh-514-positive.sft.c b/tests/tests/regressions/gh-514-positive.sft.c new file mode 100644 index 0000000000..5c2b7c6bff --- /dev/null +++ b/tests/tests/regressions/gh-514-positive.sft.c @@ -0,0 +1,17 @@ +//type:fp +//options_all:--c --c23 --strict +//remark:C: impl. ptr conv. adding qualifiers at the first level only + +void add_first_level_const(int **p) +{ + int *const* q = p; + + (void)q; +} + +void explicit_deep_qualification(int **p) +{ + const int *const* q = (const int *const* )p; + + (void)q; +} diff --git a/tests/tests/regressions/gh-514.sft.c b/tests/tests/regressions/gh-514.sft.c new file mode 100644 index 0000000000..b647c1ccfa --- /dev/null +++ b/tests/tests/regressions/gh-514.sft.c @@ -0,0 +1,42 @@ +//type:fp +//options_all:--c --c23 +//options:--no_strict_gnu --gnu_version=130300:--strict_warnings +//remark:C: impl. ptr conv. w/ qualifiers below first level + +// C++ permits these conversions, but in C the types pointed to are not +// compatible, so each one requires a diagnostic. + +void take(const int *const *); +void take3(const int *const *const *); + +void assignment(int **p) +{ + const int *const *q; + + q = p; + take(q); +} + +void initialization(int **p) +{ + const int *const *q = p; + + take(q); +} + +void argument(int **p) +{ + take(p); +} + +const int *const *return_value(int **p) +{ + return p; +} + +void three_levels(int ***p) +{ + const int *const *const *q = p; + + take3(q); +}