diff mbox series

[committed] d: Warn when declared size of a special enum does not match its intrinsic type.

Message ID 20230605165531.1009946-1-ibuclaw@gdcproject.org
State New
Headers show
Series [committed] d: Warn when declared size of a special enum does not match its intrinsic type. | expand

Commit Message

Iain Buclaw June 5, 2023, 4:55 p.m. UTC
Hi,

All special enums have declarations in the D runtime library, but the
compiler will recognize and treat them specially if declared in any
module.  When the underlying base type of a special enum is a different
size to its matched intrinsic, then this can cause undefined behavior at
runtime.  Detect and warn about when such a mismatch occurs.

This was found when merging the D front-end with the v2.103.1 release,
splitting this out of the merge patch into its own standalone change.

Bootstrapped and regression tested on x86_64-linux-gnu, committed to
mainline and backported to the releases/gcc-13 branch.

Regards,
Iain.

---
gcc/d/ChangeLog:

	* gdc.texi (Warnings): Document -Wextra and -Wmismatched-special-enum.
	* implement-d.texi (Special Enums): Add reference to warning option
	-Wmismatched-special-enum.
	* lang.opt: Add -Wextra and -Wmismatched-special-enum.
	* types.cc (TypeVisitor::visit (TypeEnum *)): Warn when declared
	special enum size mismatches its intrinsic type.

gcc/testsuite/ChangeLog:

	* gdc.dg/Wmismatched_enum.d: New test.
---
 gcc/d/gdc.texi                          | 17 +++++++++++++++++
 gcc/d/implement-d.texi                  |  5 +++++
 gcc/d/lang.opt                          |  8 ++++++++
 gcc/d/types.cc                          | 15 +++++++++++++++
 gcc/testsuite/gdc.dg/Wmismatched_enum.d |  4 ++++
 5 files changed, 49 insertions(+)
 create mode 100644 gcc/testsuite/gdc.dg/Wmismatched_enum.d
diff mbox series

Patch

diff --git a/gcc/d/gdc.texi b/gcc/d/gdc.texi
index 24b6ee00478..6f81967a83d 100644
--- a/gcc/d/gdc.texi
+++ b/gcc/d/gdc.texi
@@ -699,6 +699,23 @@  Do not warn about usage of deprecated features and symbols with
 @item -Werror
 Turns all warnings into errors.
 
+@opindex Wextra
+@opindex Wno-extra
+@item -Wextra
+This enables some extra warning flags that are not enabled by
+@option{-Wall}.
+
+@gccoptlist{-Waddress
+-Wcast-result
+-Wmismatched-special-enum
+-Wunknown-pragmas}
+
+@opindex Wmismatched-special-enum
+@opindex Wno-mismatched-special-enum
+@item -Wmismatched-special-enum
+Warn when an enum the compiler recognizes as special is declared with a
+different size to the built-in type it is representing.
+
 @opindex Wspeculative
 @opindex Wno-speculative
 @item -Wspeculative
diff --git a/gcc/d/implement-d.texi b/gcc/d/implement-d.texi
index 039e5fbd24e..6f33bc192fe 100644
--- a/gcc/d/implement-d.texi
+++ b/gcc/d/implement-d.texi
@@ -2085,6 +2085,11 @@  for convenience: @code{c_complex_double}, @code{c_complex_float},
 @code{c_complex_real}, @code{cpp_long}, @code{cpp_longlong},
 @code{c_long_double}, @code{cpp_ulong}, @code{cpp_ulonglong}.
 
+It may cause undefined behavior at runtime if a special enum is declared with a
+base type that has a different size to the target C/C++ type it is
+representing.  The GNU D compiler will catch such declarations and emit a
+warning when the @option{-Wmismatched-special-enum} option is seen on the
+command-line.
 
 @c --------------------------------------------------------
 
diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt
index bb0a3dcc911..26ca92c4c17 100644
--- a/gcc/d/lang.opt
+++ b/gcc/d/lang.opt
@@ -134,6 +134,14 @@  Werror
 D
 ; Documented in common.opt
 
+Wextra
+D Warning
+; Documented in common.opt
+
+Wmismatched-special-enum
+D Warning Var(warn_mismatched_special_enum) LangEnabledBy(D, Wextra)
+Warn when a special enum is declared with the wrong base type.
+
 Wpsabi
 D
 ; Documented in C
diff --git a/gcc/d/types.cc b/gcc/d/types.cc
index beaf2a61af9..a4c05bfb75f 100644
--- a/gcc/d/types.cc
+++ b/gcc/d/types.cc
@@ -1067,6 +1067,21 @@  public:
 	gcc_assert (underlying != NULL);
 
 	t->ctype = build_variant_type_copy (build_ctype (underlying));
+
+	/* When the size of the declared enum base type doesn't match the target
+	   C type that this enum is being used as a placeholder for, we can't
+	   use the generated underlying type as it'll conflict with all sizes
+	   the front-end has computed during semantic.  */
+	if (TYPE_SIZE (t->ctype) != TYPE_SIZE (basetype))
+	  {
+	    warning_at (make_location_t (t->sym->loc),
+			OPT_Wmismatched_special_enum,
+			"size of %qs (%wd) differ from its declared size (%wd)",
+			t->sym->ident->toChars (), int_size_in_bytes (t->ctype),
+			int_size_in_bytes (basetype));
+	    t->ctype = basetype;
+	  }
+
 	build_type_decl (t->ctype, t->sym);
       }
     else if (t->sym->ident == NULL
diff --git a/gcc/testsuite/gdc.dg/Wmismatched_enum.d b/gcc/testsuite/gdc.dg/Wmismatched_enum.d
new file mode 100644
index 00000000000..54f47988c2b
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/Wmismatched_enum.d
@@ -0,0 +1,4 @@ 
+// { dg-do compile }
+// { dg-options "-Wmismatched-special-enum" }
+
+enum __c_longlong : byte; // { dg-warning "differ from its declared size" }