[PATCH v2 1/4] include: Fix undefined char16_t in C11.

Kevin Puetz PuetzKevinA at JohnDeere.com
Thu Sep 17 17:49:02 CDT 2020


In c++11 char16_t is a distinct fundamental type,
but in c11 it is merely a typedef in <uchar.h>.

Explicitly mention char16_t only in c++11 (where it is built-in),
otherwise define WCHAR to match u"...", without naming char16_t.

Remove WINE_UNICODE_CHAR16; it is now the default when supported.

Signed-off-by: Kevin Puetz <PuetzKevinA at JohnDeere.com>
---

Per https://www.winehq.org/pipermail/wine-devel/2020-July/170502.html
Jacek wants to avoid including <uchar.h>; without that C11 has to make
some assumptions the standard doesn't guarantee. But that's far from
wine's first foray into implementation-defined behavior, so OK :-)

c11 defines char16_t as a typedef to uint_least16_t, which is likely
(though not guaranteed) the same as unsigned short (e.g. it could be
int if that is a 16-bit type). I do see that basetsd has a comment
saying only ILP32, LP64, or P64 typemodels are supported, though, so
probably wine would already not work on a platform with 16-bit int.

However, GCC and clang provide a macro __CHAR16_TYPE__ which is easy to
test and, if defined, will definitely match <uchar.h>. This patch uses
it if available, else falls back on unsigned short as before.

One *could* delete the #if defined(__CHAR16_TYPE__) cases too
and just asume unsigned short is right, which in practice it is.
But IMO using the gcc/clang #define makes the goal clearer.

---

Removing WINE_UNICODE_CHAR1 changes little for C or C++98/C++03,
or anything using -fshort-wchar/WINE_UNICODE_NATIVE, but it has
some ABI implications for wineg++ -std=c++11 -fno-short-wchar.

On the plus side:

* TEXT(), OLESTR() macros "just work" in UNICODE (for c11 and c++11)
* We revert an #ifdef with ABI implications, now there's just
  -f(no-)short-wchar and -std=c++*, which already mattered
* in C++11, WCHAR overloads are no longer ambiguous vs integral types
  and WCHAR is recognizable as text rather than promoted to int.
  This is much more like the situation on MSVC where WCHAR == wchar_t;
  char16_t is also distinct from uint16_t, unsigned short, etc.
* We avoid ever having the situation where TEXT("...") -> u"..."
  is accepted by the compiler and produces char16_t[],
  but that isn't compatible with TCHAR[] or LPCTSTR.

  Silently getting the wrong type is obnoxious in C++, since templates
  can move the type-mismatch compile error far away from the offending
  TEXT("...") macro, or even compile but use an unexpected overload.
  Now u"..." either doesn't compile, or it's correct, so we can drop
  https://www.winehq.org/pipermail/wine-devel/2020-July/170227.html

On the minus side:

* any libraries built with wineg++ -std=c++11 -fno-short-wchar
  change their mangled names for functions with WCHAR/LPCWSTR
  parameters because char16_t is a distinct fundamental type.

This doesn't affect compatibility of wine itself (which always exports
things under their MSVC-ish mangling as-if using wchar_t), but wineg++
could fail to link to a .dll.so built with wine 5.0.x headers.
I don't know to what extent wineg++ tries to promise that this works,
but IMO we are headed into a major version 6, and binaries are already
not fully interchangeable due to winecrt0/__wine_spec_init changes.
So I think think it seems preferable to just have good defaults
that are MSVC-like, and fewer possible mistakes.

But it's certainly possible to keep the WINE_UNICODE_CHAR16 opt-in,
(or add a WINE_NO_UNICODE_CHAR16 opt-out), if we must.
Or we could follow gcc's lead on the upcoming c++20 -f(no-)char8_t
and winegcc could have -f(no-)char16_t, or -fchar16_t-wchar
(synthesizing the macro like it does with WINE_UNICODE_NATIVE).
But I like the simplicity of "just works if the compiler has support"
unless someone objects.
---
 include/sqltypes.h | 4 +++-
 include/tchar.h    | 4 +++-
 include/winnt.h    | 4 +++-
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/include/sqltypes.h b/include/sqltypes.h
index 0923f6b362..08b6fc2f7b 100644
--- a/include/sqltypes.h
+++ b/include/sqltypes.h
@@ -30,8 +30,10 @@ extern "C" {
 typedef unsigned char   SQLCHAR;
 #if defined(WINE_UNICODE_NATIVE)
 typedef wchar_t         SQLWCHAR;
-#elif defined(WINE_UNICODE_CHAR16)
+#elif __cpp_unicode_literals >= 200710
 typedef char16_t        SQLWCHAR;
+#elif defined(__CHAR16_TYPE__)
+typedef __CHAR16_TYPE__ SQLWCHAR;
 #else
 typedef unsigned short  SQLWCHAR;
 #endif
diff --git a/include/tchar.h b/include/tchar.h
index 9fc4c72099..e1e21df272 100644
--- a/include/tchar.h
+++ b/include/tchar.h
@@ -240,8 +240,10 @@ typedef unsigned short wctype_t;
 #ifndef __TCHAR_DEFINED
 #if defined(WINE_UNICODE_NATIVE)
 typedef wchar_t       _TCHAR;
-#elif defined(WINE_UNICODE_CHAR16)
+#elif __cpp_unicode_literals >= 200710
 typedef char16_t      _TCHAR;
+#elif defined(__CHAR16_TYPE__)
+typedef __CHAR16_TYPE__ _TCHAR;
 #else
 typedef unsigned short _TCHAR;
 #endif
diff --git a/include/winnt.h b/include/winnt.h
index 63567ba62e..1874d53430 100644
--- a/include/winnt.h
+++ b/include/winnt.h
@@ -462,8 +462,10 @@ typedef int             LONG,       *PLONG;
 /* Some systems might have wchar_t, but we really need 16 bit characters */
 #if defined(WINE_UNICODE_NATIVE)
 typedef wchar_t         WCHAR;
-#elif defined(WINE_UNICODE_CHAR16)
+#elif __cpp_unicode_literals >= 200710
 typedef char16_t        WCHAR;
+#elif defined(__CHAR16_TYPE__)
+typedef __CHAR16_TYPE__ WCHAR;
 #else
 typedef unsigned short  WCHAR;
 #endif



More information about the wine-devel mailing list