Description
Summary
The bionic C header <uchar.h> cannot be parsed as C++98/C++03: it suppresses
its char16_t/char32_t typedefs in all C++ modes (assuming they are built-in
language types), but they only became built-in types in C++11. In pre-C++11
the type names used by c16rtomb, c32rtomb, mbrtoc16, and mbrtoc32
resolve to nothing, causing four parse errors.
This is normally masked because the NDK's default C++ include path resolves
<uchar.h> to the libc++ wrapper (c++/v1/uchar.h), which is intentionally a
no-op before C++11 and never reaches the bionic header. The bionic header is
reached — and fails — in any configuration without the libc++ include path,
including the supported ndk-build configuration APP_STL := none, which adds
-nostdinc++ itself (build/core/stl.mk:22-23).
Environment
- NDK: r30 (30.0.16248370)
- Host: Linux x86_64
- Compiler: the NDK toolchain's
clang++
- Target: any (tested aarch64)
Reproducer
ndk-build (APP_STL := none)
// jni/test.cpp
#include <uchar.h>
int foo() {
return 0;
}
# jni/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_SRC_FILES := test.cpp
include $(BUILD_SHARED_LIBRARY)
# jni/Application.mk
APP_ABI := arm64-v8a
APP_STL := none
APP_CPPFLAGS := -std=c++98
$ ndk-build
[arm64-v8a] Compile++ : test <= test.cpp
.../sysroot/usr/include/uchar.h:65:40: error: unknown type name 'char16_t'
.../sysroot/usr/include/uchar.h:74:40: error: unknown type name 'char32_t'
.../sysroot/usr/include/uchar.h:80:17: error: unknown type name 'char16_t'
.../sysroot/usr/include/uchar.h:86:17: error: unknown type name 'char32_t'
4 errors generated.
Command line (equivalent)
$ aarch64-linux-android29-clang++ -std=c++98 -nostdinc++ -c test.cpp
For comparison, the same project builds fine with the default APP_STL
(c++_static) and with APP_STL := system, because in both cases
<uchar.h> resolves to the libc++ wrapper. (Note that APP_STL := system
currently also uses the libc++ headers per the long-standing TODO in
build/core/stl.mk.)
Actual result (r30)
.../sysroot/usr/include/uchar.h:65:40: error: unknown type name 'char16_t'
size_t c16rtomb(char* __buf, char16_t __ch16, mbstate_t* __ps) ...
^
.../sysroot/usr/include/uchar.h:74:40: error: unknown type name 'char32_t'
.../sysroot/usr/include/uchar.h:80:17: error: unknown type name 'char16_t'
.../sysroot/usr/include/uchar.h:86:17: error: unknown type name 'char32_t'
4 errors generated.
Same failure with -std=c++03. C11 and C++11 and later compile fine.
Expected result
The bionic header should either compile in C++98/C++03, or hide its
declarations in pre-C++11 modes so that it at least remains includable in any
configuration.
Root cause
sysroot/usr/include/uchar.h (from bionic), lines 45-49 in r30:
#if !defined(__cplusplus)
/** The UTF-16 character type. */
typedef __CHAR16_TYPE__ char16_t;
/** The UTF-32 character type. */
typedef __CHAR32_TYPE__ char32_t;
#endif
The typedefs are suppressed in all C++ modes because char16_t/char32_t
are keywords in C++11 and later. But in C++98/C++03 they are not keywords,
and the header no longer typedefs them either, so the type names in the
function declarations below fail to resolve.
By contrast, the libc++ wrapper (c++/v1/uchar.h) treats <uchar.h> as a
C++11-only header: for pre-C++11 it includes nothing (or the frozen C++03
header when _LIBCPP_USE_FROZEN_CXX03_HEADERS is set). That is why default
builds never see the problem — and why the bionic header should not be
reached in that mode, which a __cplusplus guard would enforce.
Suggested fix
Hide the declarations in pre-C++11 modes, consistent with how libc++ treats
the header:
#if !defined(__cplusplus) || __cplusplus >= 201103L
size_t c16rtomb(char* __buf, char16_t __ch16, mbstate_t* __ps) ...;
size_t c32rtomb(char* __buf, char32_t __ch32, mbstate_t* __ps) ...;
size_t mbrtoc16(char16_t* __ch16, const char* __s, size_t __n, mbstate_t* __ps) ...;
size_t mbrtoc32(char32_t* __ch32, const char* __s, size_t __n, mbstate_t* __ps) ...;
#endif
(Alternatively, provide the typedefs for __cplusplus < 201103L so the
declarations parse and the C11 functions remain usable from C++98 — keeping
the keywords untouched for C++11+.)
Notes
The header originates from platform/bionic, so the fix likely needs to land
upstream there and then roll into the NDK sysroot.
I am using a supported NDK
Affected versions
r30
Description
Summary
The bionic C header
<uchar.h>cannot be parsed as C++98/C++03: it suppressesits
char16_t/char32_ttypedefs in all C++ modes (assuming they are built-inlanguage types), but they only became built-in types in C++11. In pre-C++11
the type names used by
c16rtomb,c32rtomb,mbrtoc16, andmbrtoc32resolve to nothing, causing four parse errors.
This is normally masked because the NDK's default C++ include path resolves
<uchar.h>to the libc++ wrapper (c++/v1/uchar.h), which is intentionally ano-op before C++11 and never reaches the bionic header. The bionic header is
reached — and fails — in any configuration without the libc++ include path,
including the supported ndk-build configuration
APP_STL := none, which adds-nostdinc++itself (build/core/stl.mk:22-23).Environment
clang++Reproducer
ndk-build (
APP_STL := none)Command line (equivalent)
For comparison, the same project builds fine with the default
APP_STL(
c++_static) and withAPP_STL := system, because in both cases<uchar.h>resolves to the libc++ wrapper. (Note thatAPP_STL := systemcurrently also uses the libc++ headers per the long-standing TODO in
build/core/stl.mk.)
Actual result (r30)
Same failure with
-std=c++03. C11 and C++11 and later compile fine.Expected result
The bionic header should either compile in C++98/C++03, or hide its
declarations in pre-C++11 modes so that it at least remains includable in any
configuration.
Root cause
sysroot/usr/include/uchar.h(from bionic), lines 45-49 in r30:The typedefs are suppressed in all C++ modes because
char16_t/char32_tare keywords in C++11 and later. But in C++98/C++03 they are not keywords,
and the header no longer typedefs them either, so the type names in the
function declarations below fail to resolve.
By contrast, the libc++ wrapper (
c++/v1/uchar.h) treats<uchar.h>as aC++11-only header: for pre-C++11 it includes nothing (or the frozen C++03
header when
_LIBCPP_USE_FROZEN_CXX03_HEADERSis set). That is why defaultbuilds never see the problem — and why the bionic header should not be
reached in that mode, which a
__cplusplusguard would enforce.Suggested fix
Hide the declarations in pre-C++11 modes, consistent with how libc++ treats
the header:
(Alternatively, provide the typedefs for
__cplusplus < 201103Lso thedeclarations parse and the C11 functions remain usable from C++98 — keeping
the keywords untouched for C++11+.)
Notes
The header originates from platform/bionic, so the fix likely needs to land
upstream there and then roll into the NDK sysroot.
I am using a supported NDK
Affected versions
r30