diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt --- a/libcxx/CMakeLists.txt +++ b/libcxx/CMakeLists.txt @@ -57,6 +57,9 @@ by users in their own code regardless of this option." OFF) option(LIBCXX_ENABLE_SHARED "Build libc++ as a shared library." ON) option(LIBCXX_ENABLE_STATIC "Build libc++ as a static library." ON) +cmake_dependent_option(LIBCXX_ENABLE_EXPERIMENTAL_DLL + "Enable building of Windows c++experimental.dll." OFF + "LIBCXX_ENABLE_SHARED" OFF) option(LIBCXX_ENABLE_FILESYSTEM "Whether to include support for parts of the library that rely on a filesystem being available on the platform. This includes things like most parts of and @@ -483,17 +486,22 @@ set(LIBCXX_DEBUG_BUILD OFF) endif() +if (LIBCXX_TARGETING_MSVC) + set(LIBCXX_MSVC_DYNAMIC_RUNTIME ON) + if (CMAKE_MSVC_RUNTIME_LIBRARY) + string(FIND ${CMAKE_MSVC_RUNTIME_LIBRARY} "DLL" idx) + if (${idx} EQUAL -1) + set(LIBCXX_MSVC_DYNAMIC_RUNTIME OFF) + endif() + endif() +endif() + #=============================================================================== # Setup Compiler Flags #=============================================================================== include(HandleLibCXXABI) # Setup the ABI library flags -# FIXME: Remove all debug flags and flags that change which Windows -# default libraries are linked. Currently we only support linking the -# non-debug DLLs -remove_flags("/D_DEBUG" "/MTd" "/MDd" "/MT" "/Md") - # FIXME(EricWF): See the FIXME on LIBCXX_ENABLE_PEDANTIC. # Remove the -pedantic flag and -Wno-pedantic and -pedantic-errors # so they don't get transformed into -Wno and -errors respectively. @@ -509,6 +517,12 @@ CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) + if (LIBCXX_TARGETING_MSVC) + cmake_parse_arguments(ARG "DYNAMIC_RUNTIME" "" "" ${ARGN}) + set_property(TARGET ${target} PROPERTY + MSVC_RUNTIME_LIBRARY "MultiThreaded$<${LIBCXX_DEBUG_BUILD}:Debug>$<$:DLL>") + endif() + # When building the dylib, don't warn for unavailable aligned allocation # functions based on the deployment target -- they are always available # because they are provided by the dylib itself with the exception of z/OS. @@ -718,16 +732,30 @@ endif() if (LIBCXX_TARGETING_MSVC) + cmake_parse_arguments(ARG "DYNAMIC_RUNTIME" "" "" ${ARGN}) + if (LIBCXX_DEBUG_BUILD) set(LIB_SUFFIX "d") else() set(LIB_SUFFIX "") endif() - target_link_libraries(${target} PRIVATE ucrt${LIB_SUFFIX}) # Universal C runtime - target_link_libraries(${target} PRIVATE vcruntime${LIB_SUFFIX}) # C++ runtime - target_link_libraries(${target} PRIVATE msvcrt${LIB_SUFFIX}) # C runtime startup files - target_link_libraries(${target} PRIVATE msvcprt${LIB_SUFFIX}) # C++ standard library. Required for exception_ptr internals. + if (ARG_DYNAMIC_RUNTIME OR LIBCXX_MSVC_DYNAMIC_RUNTIME) + set(UCRT_LIB "ucrt") + set(CXXRT_LIB "vcruntime") + set(CRT_LIB "msvcrt") + set(CXX_LIB "msvcprt") + else() + set(UCRT_LIB "libucrt") + set(CXXRT_LIB "libvcruntime") + set(CRT_LIB "libcmt") + set(CXX_LIB "libcpmt") + endif() + + target_link_libraries(${target} PRIVATE ${UCRT_LIB}${LIB_SUFFIX}) # Universal C runtime + target_link_libraries(${target} PRIVATE ${CXXRT_LIB}${LIB_SUFFIX}) # C++ runtime + target_link_libraries(${target} PRIVATE ${CRT_LIB}${LIB_SUFFIX}) # C runtime startup files + target_link_libraries(${target} PRIVATE ${CXX_LIB}${LIB_SUFFIX}) # C++ standard library. Required for exception_ptr internals. # Required for standards-complaint wide character formatting functions # (e.g. `printfw`/`scanfw`) target_link_libraries(${target} PRIVATE iso_stdio_wide_specifiers) @@ -841,13 +869,13 @@ # Setup all common build flags ================================================= function(cxx_add_common_build_flags target) - cxx_add_basic_build_flags(${target}) + cxx_add_basic_build_flags(${target} ${ARGN}) cxx_add_warning_flags(${target} ${LIBCXX_ENABLE_WERROR} ${LIBCXX_ENABLE_PEDANTIC}) cxx_add_windows_flags(${target}) cxx_add_exception_flags(${target}) cxx_add_rtti_flags(${target}) cxx_add_module_flags(${target}) - cxx_link_system_libraries(${target}) + cxx_link_system_libraries(${target} ${ARGN}) endfunction() #=============================================================================== diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt --- a/libcxx/src/CMakeLists.txt +++ b/libcxx/src/CMakeLists.txt @@ -193,7 +193,7 @@ SOVERSION "${LIBCXX_ABI_VERSION}" DEFINE_SYMBOL "" ) - cxx_add_common_build_flags(cxx_shared) + cxx_add_common_build_flags(cxx_shared DYNAMIC_RUNTIME) if(ZOS) add_custom_command(TARGET cxx_shared POST_BUILD @@ -267,6 +267,10 @@ set_target_properties(cxx_shared PROPERTIES APPEND_STRING PROPERTY LINK_FLAGS " /MANIFEST:NO") endif() + if(LIBCXX_TARGETING_MSVC AND LIBCXX_ENABLE_EXPERIMENTAL_DLL) + target_link_libraries(cxx_shared PRIVATE cxx_experimental_stub) + set(LIBCXX_MSVC_EXPERIMENTAL_SHARED ON) + endif() endif() set(CMAKE_STATIC_LIBRARY_PREFIX "lib") @@ -335,6 +339,34 @@ cxx_add_common_build_flags(cxx_experimental) target_compile_options(cxx_experimental PUBLIC -D_LIBCPP_ENABLE_EXPERIMENTAL) +if (LIBCXX_MSVC_EXPERIMENTAL_SHARED) + add_library(cxx_experimental_shared SHARED ${LIBCXX_EXPERIMENTAL_SOURCES}) + target_link_libraries(cxx_experimental_shared PUBLIC cxx-headers) + target_link_libraries(cxx_experimental_shared PRIVATE cxx_shared) + + set_target_properties(cxx_experimental_shared + PROPERTIES + COMPILE_FLAGS "${LIBCXX_COMPILE_FLAGS}" + OUTPUT_NAME "c++experimental" + ) + cxx_add_common_build_flags(cxx_experimental_shared DYNAMIC_RUNTIME) + target_compile_options(cxx_experimental_shared PUBLIC -D_LIBCPP_ENABLE_EXPERIMENTAL) + + add_library(cxx_experimental_stub SHARED ${LIBCXX_EXPERIMENTAL_SOURCES}) + target_link_libraries(cxx_experimental_stub PUBLIC cxx-headers) + + set_target_properties(cxx_experimental_stub + PROPERTIES + COMPILE_FLAGS "${LIBCXX_COMPILE_FLAGS}" + LINK_FLAGS "/FORCE:UNRESOLVED" + RUNTIME_OUTPUT_NAME "c++experimental" + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + ) + cxx_add_common_build_flags(cxx_experimental_stub DYNAMIC_RUNTIME) + target_compile_options(cxx_experimental_stub PUBLIC -D_LIBCPP_ENABLE_EXPERIMENTAL) +endif() if (LIBCXX_BUILD_EXTERNAL_THREAD_LIBRARY) set(LIBCXX_EXTERNAL_THREADING_SUPPORT_SOURCES @@ -357,21 +389,22 @@ endif() if (LIBCXX_INSTALL_SHARED_LIBRARY) - install(TARGETS cxx_shared - ARCHIVE DESTINATION ${LIBCXX_INSTALL_LIBRARY_DIR} COMPONENT cxx - LIBRARY DESTINATION ${LIBCXX_INSTALL_LIBRARY_DIR} COMPONENT cxx - RUNTIME DESTINATION ${LIBCXX_INSTALL_RUNTIME_DIR} COMPONENT cxx) + list(APPEND install_targets cxx_shared) endif() if (LIBCXX_INSTALL_STATIC_LIBRARY) - install(TARGETS cxx_static - ARCHIVE DESTINATION ${LIBCXX_INSTALL_LIBRARY_DIR} COMPONENT cxx - LIBRARY DESTINATION ${LIBCXX_INSTALL_LIBRARY_DIR} COMPONENT cxx - RUNTIME DESTINATION ${LIBCXX_INSTALL_RUNTIME_DIR} COMPONENT cxx) + list(APPEND install_targets cxx_static) endif() if (LIBCXX_INSTALL_LIBRARY) - install(TARGETS cxx_experimental + list(APPEND install_targets cxx_experimental) + if (LIBCXX_MSVC_EXPERIMENTAL_SHARED) + list(APPEND install_targets cxx_experimental_shared) + endif() +endif() + +if (install_targets) + install(TARGETS ${install_targets} LIBRARY DESTINATION ${LIBCXX_INSTALL_LIBRARY_DIR} COMPONENT cxx ARCHIVE DESTINATION ${LIBCXX_INSTALL_LIBRARY_DIR} COMPONENT cxx RUNTIME DESTINATION ${LIBCXX_INSTALL_RUNTIME_DIR} COMPONENT cxx) @@ -388,6 +421,9 @@ if (NOT CMAKE_CONFIGURATION_TYPES) if(LIBCXX_INSTALL_LIBRARY) set(lib_install_target "cxx;cxx_experimental") + if (LIBCXX_MSVC_EXPERIMENTAL_SHARED) + string(APPEND lib_install_target ";experimental_shared") + endif() endif() if(LIBCXX_INSTALL_HEADERS) set(header_install_target install-cxx-headers) diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp --- a/libcxx/src/filesystem/operations.cpp +++ b/libcxx/src/filesystem/operations.cpp @@ -414,6 +414,7 @@ } win_error_mapping[] = { {ERROR_ACCESS_DENIED, errc::permission_denied}, {ERROR_ALREADY_EXISTS, errc::file_exists}, + {ERROR_BAD_NET_NAME, errc::no_such_file_or_directory}, {ERROR_BAD_NETPATH, errc::no_such_file_or_directory}, {ERROR_BAD_PATHNAME, errc::no_such_file_or_directory}, {ERROR_BAD_UNIT, errc::no_such_device}, diff --git a/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in b/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in --- a/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in +++ b/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in @@ -1,14 +1,24 @@ # This testing configuration handles running the test suite against LLVM's libc++ # using a DLL, with Clang-cl on Windows. +runtime_lib = 'msvcprt' +fms_runtime_lib = 'dll' +dbg_include = '' + +if '@LIBCXX_DEBUG_BUILD@' == 'ON': + fms_runtime_lib += '_dbg' + runtime_lib += 'd' + dbg_include = ' -include set_windows_crt_report_mode.h' + lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg') config.substitutions.append(('%{flags}', '--driver-mode=g++')) config.substitutions.append(('%{compile_flags}', - '-nostdinc++ -I %{include} -I %{target-include} -I %{libcxx}/test/support -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_STDIO_ISO_WIDE_SPECIFIERS -DNOMINMAX' + '-fms-runtime-lib=' + fms_runtime_lib + ' -nostdinc++ -I %{include} -I %{target-include} -I %{libcxx}/test/support -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_STDIO_ISO_WIDE_SPECIFIERS -DNOMINMAX' + dbg_include )) +link_flags_idx = len(config.substitutions) config.substitutions.append(('%{link_flags}', - '-nostdlib -L %{lib} -lc++ -lmsvcrt -lmsvcprt -loldnames' + '-nostartfiles -nostdlib++ -L %{lib} -lc++ -l' + runtime_lib )) config.substitutions.append(('%{exec}', '%{executor} --execdir %T --prepend_env PATH=%{lib} -- ' @@ -17,6 +27,11 @@ import os, site site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils')) import libcxx.test.params, libcxx.test.config + +if not '_LIBCPP_HAS_NO_INT128' in libcxx.test.dsl.compilerMacros(config): + subst = config.substitutions[link_flags_idx] + config.substitutions[link_flags_idx] = (subst[0], '-rtlib=compiler-rt ' + subst[1]) + libcxx.test.config.configure( libcxx.test.params.DEFAULT_PARAMETERS, libcxx.test.features.DEFAULT_FEATURES, diff --git a/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in b/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in --- a/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in +++ b/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in @@ -1,15 +1,25 @@ # This testing configuration handles running the test suite against LLVM's libc++ # using a DLL, with Clang-cl on Windows. This variant sets _HAS_EXCEPTIONS = 0 -# which removes exception class definitions from the vcruntime. +# which removes exception class definitions from the vcruntime. + +runtime_lib = 'msvcprt' +fms_runtime_lib = 'dll' +dbg_include = '' + +if '@LIBCXX_DEBUG_BUILD@' == 'ON': + fms_runtime_lib += '_dbg' + runtime_lib += 'd' + dbg_include = ' -include set_windows_crt_report_mode.h' lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg') config.substitutions.append(('%{flags}', '--driver-mode=g++')) config.substitutions.append(('%{compile_flags}', - '-nostdinc++ -I %{include} -I %{target-include} -I %{libcxx}/test/support -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_STDIO_ISO_WIDE_SPECIFIERS -DNOMINMAX -D_HAS_EXCEPTIONS=0' + '-fms-runtime-lib=' + fms_runtime_lib + ' -nostdinc++ -I %{include} -I %{target-include} -I %{libcxx}/test/support -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_STDIO_ISO_WIDE_SPECIFIERS -DNOMINMAX -D_HAS_EXCEPTIONS=0' + dbg_include )) +link_flags_idx = len(config.substitutions) config.substitutions.append(('%{link_flags}', - '-nostdlib -L %{lib} -lc++ -lmsvcrt -lmsvcprt -loldnames' + '-nostartfiles -nostdlib++ -L %{lib} -lc++ -l' + runtime_lib )) config.substitutions.append(('%{exec}', '%{executor} --execdir %T --prepend_env PATH=%{lib} -- ' @@ -18,6 +28,11 @@ import os, site site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils')) import libcxx.test.params, libcxx.test.config + +if not '_LIBCPP_HAS_NO_INT128' in libcxx.test.dsl.compilerMacros(config): + subst = config.substitutions[link_flags_idx] + config.substitutions[link_flags_idx] = (subst[0], '-rtlib=compiler-rt ' + subst[1]) + libcxx.test.config.configure( libcxx.test.params.DEFAULT_PARAMETERS, libcxx.test.features.DEFAULT_FEATURES, diff --git a/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in b/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in --- a/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in +++ b/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in @@ -1,14 +1,29 @@ # This testing configuration handles running the test suite against LLVM's libc++ # using a static library, with Clang-cl on Windows. +if '@LIBCXX_MSVC_DYNAMIC_RUNTIME@' == 'ON': + fms_runtime_lib = 'dll' + runtime_lib = 'msvcprt' +else: + fms_runtime_lib = 'static' + runtime_lib = 'libcpmt' + +dbg_include = '' + +if '@LIBCXX_DEBUG_BUILD@' == 'ON': + fms_runtime_lib += '_dbg' + runtime_lib += 'd' + dbg_include = ' -include set_windows_crt_report_mode.h' + lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg') config.substitutions.append(('%{flags}', '--driver-mode=g++')) config.substitutions.append(('%{compile_flags}', - '-nostdinc++ -I %{include} -I %{target-include} -I %{libcxx}/test/support -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_STDIO_ISO_WIDE_SPECIFIERS -DNOMINMAX' + '-fms-runtime-lib=' + fms_runtime_lib + ' -nostdinc++ -I %{include} -I %{target-include} -I %{libcxx}/test/support -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_STDIO_ISO_WIDE_SPECIFIERS -DNOMINMAX -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS=' + dbg_include )) +link_flags_idx = len(config.substitutions) config.substitutions.append(('%{link_flags}', - '-nostdlib -L %{lib} -llibc++ -lmsvcrt -lmsvcprt -loldnames' + '-nostartfiles -nostdlib++ -L %{lib} -llibc++ -l' + runtime_lib )) config.substitutions.append(('%{exec}', '%{executor} --execdir %T -- ' @@ -17,6 +32,11 @@ import os, site site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils')) import libcxx.test.params, libcxx.test.config + +if not '_LIBCPP_HAS_NO_INT128' in libcxx.test.dsl.compilerMacros(config): + subst = config.substitutions[link_flags_idx] + config.substitutions[link_flags_idx] = (subst[0], '-rtlib=compiler-rt ' + subst[1]) + libcxx.test.config.configure( libcxx.test.params.DEFAULT_PARAMETERS, libcxx.test.features.DEFAULT_FEATURES, diff --git a/libcxx/test/std/experimental/memory/memory.resource.global/new_delete_resource.pass.cpp b/libcxx/test/std/experimental/memory/memory.resource.global/new_delete_resource.pass.cpp --- a/libcxx/test/std/experimental/memory/memory.resource.global/new_delete_resource.pass.cpp +++ b/libcxx/test/std/experimental/memory/memory.resource.global/new_delete_resource.pass.cpp @@ -88,13 +88,12 @@ void *ret = r1.allocate(50); assert(ret); - assert(globalMemCounter.checkOutstandingNewEq(1)); - assert(globalMemCounter.checkLastNewSizeEq(50)); + ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(1)); + ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkLastNewSizeEq(50)); r1.deallocate(ret, 1); - assert(globalMemCounter.checkOutstandingNewEq(0)); - assert(globalMemCounter.checkDeleteCalledEq(1)); - + ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0)); + ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkDeleteCalledEq(1)); } int main(int, char**) diff --git a/libcxx/test/support/set_windows_crt_report_mode.h b/libcxx/test/support/set_windows_crt_report_mode.h --- a/libcxx/test/support/set_windows_crt_report_mode.h +++ b/libcxx/test/support/set_windows_crt_report_mode.h @@ -10,27 +10,32 @@ #ifndef SUPPORT_SET_WINDOWS_CRT_REPORT_MODE_H #define SUPPORT_SET_WINDOWS_CRT_REPORT_MODE_H -#ifndef _DEBUG -#error _DEBUG must be defined when using this header -#endif +#if defined(__cplusplus) && __cplusplus > 199711L +# ifndef _DEBUG +# error _DEBUG must be defined when using this header +# endif -#ifndef _WIN32 -#error This header can only be used when targeting Windows -#endif +# ifndef _WIN32 +# error This header can only be used when targeting Windows +# endif -#include +# include // On Windows in debug builds the default assertion handler opens a new dialog // window which must be dismissed manually by the user. This function overrides // that setting and instead changes the assertion handler to log to stderr // instead. -inline int init_crt_report_mode() { - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); +inline int init_crt_report_mode(void) { + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); return 0; } static int init_crt_anchor = init_crt_report_mode(); +#endif #endif // SUPPORT_SET_WINDOWS_CRT_REPORT_MODE_H diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py --- a/libcxx/utils/libcxx/test/features.py +++ b/libcxx/utils/libcxx/test/features.py @@ -21,6 +21,9 @@ cfg ) and "__clang__" not in compilerMacros(cfg) _isMSVC = lambda cfg: "_MSC_VER" in compilerMacros(cfg) +_isMSVCStatic = lambda cfg: _isMSVC( + cfg +) and "_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS" in compilerMacros(cfg) _msvcVersion = lambda cfg: ( int(compilerMacros(cfg)["_MSC_VER"]) // 100, int(compilerMacros(cfg)["_MSC_VER"]) % 100, diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py --- a/libcxx/utils/libcxx/test/params.py +++ b/libcxx/utils/libcxx/test/params.py @@ -7,7 +7,7 @@ # ===----------------------------------------------------------------------===## from libcxx.test.dsl import * -from libcxx.test.features import _isMSVC +from libcxx.test.features import _isMSVCStatic import re _warningFlags = [ @@ -257,7 +257,7 @@ AddFeature("c++experimental"), PrependLinkFlag( lambda cfg: "-llibc++experimental" - if _isMSVC(cfg) + if _isMSVCStatic(cfg) else "-lc++experimental" ), AddCompileFlag("-D_LIBCPP_ENABLE_EXPERIMENTAL"),