diff --git a/.git-hooks/pre-push.py b/.git-hooks/pre-push.py index aba11c4a77..da03e3c6c3 100755 --- a/.git-hooks/pre-push.py +++ b/.git-hooks/pre-push.py @@ -41,30 +41,36 @@ import subprocess import sys import tempfile +from pathlib import Path -import edgutil +# Git invokes this hook with the ambient environment, which does not +# necessarily have PYTHONPATH configured (e.g., on Windows). Locate +# "dev_tools/pylibs" relative to this script so "edgutil" can be imported. +sys.path.insert( + 0, str(Path(__file__).resolve().parents[1] / 'dev_tools' / 'pylibs') +) -from pathlib import Path +import edgutil if platform.system() == 'Windows': - # On Windows we need to use the kernel API to detect if this script is - # running in a standalone console window (if so, there will be only one - # processes at this point). - import ctypes - - kernel32 = ctypes.WinDLL('kernel32', use_last_error = True) - process_array = (ctypes.c_uint * 2)() - num_processes = kernel32.GetConsoleProcessList(process_array, 2) - if num_processes != 1: - # When not running in a standalone console window, the script relaunches - # itself to make sure the user has an interactive shell. Then it forwards - # the exit code from the delegate process. + # The standard input of this hook carries the push ref list supplied by + # git, so it cannot be used to prompt the user. When a console is + # attached, open the console input device directly ("CONIN$" is the + # Windows counterpart of "/dev/tty") so that all prompts and output stay + # in the user's current terminal. + try: + sys.stdin = open('CONIN$', 'r') + except OSError: + # No console is attached (e.g., a GUI git client or a pseudo terminal + # such as mintty). Relaunch in a standalone console window so that the + # user can still respond to interactive prompts. delegate_proc = subprocess.run( [ sys.executable, __file__ ], - creationflags = subprocess.CREATE_NEW_CONSOLE + creationflags = subprocess.CREATE_NEW_CONSOLE, + env = {**os.environ, 'EDG_PREPUSH_DELEGATE': '1'} ) sys.exit(delegate_proc.returncode) else: @@ -137,6 +143,7 @@ class PrePushChecker: def __init__(self): self.errors = 0 + self.abort = False def confirm_okay(self): '''Ask the user whether a policy warning is acceptable. @@ -159,7 +166,8 @@ def confirm_okay(self): return if response == 'q': self.errors = 1 - sys.exit(1) + self.abort = True + return print( "Please answer 'y' (to accept), 'n' (to continue check) or " @@ -168,15 +176,18 @@ def confirm_okay(self): tries += 1 if tries >= 10: print('Too many attempts.') - sys.exit(1) + self.errors = 1 + self.abort = True + return def check_commits(self): '''Walk commits until one already on a remote branch is found. As commits are walked, check to make sure they match formatting guidance. - Returns the oldest new commit hash, or None if there are no new commits. - Exits immediately if a commit subject fails the PR-number policy. + Returns the oldest new commit hash, or None if there are no new commits + (or if a commit subject fails the PR-number policy, in which case an + error is recorded and the check sequence is aborted). ''' first_new_commit = None @@ -200,7 +211,9 @@ def check_commits(self): print( " [POLICY] Commit message doesn't contain valid PR number (or [])." ) - sys.exit(1) + self.errors = 1 + self.abort = True + return None first_new_commit = commit @@ -285,6 +298,8 @@ def check_changed_files(self, first_new_commit): results = json.load(file_handle) for file_path_str, file_results in results.items(): + if self.abort: + break print(f" Checking file: {file_path_str}") misspellings = file_results['misspellings'] @@ -336,18 +351,32 @@ def check_changed_files(self, first_new_commit): print('=' * 79) self.confirm_okay() - def run(self) -> bool: - '''Execute the full pre-push check sequence.''' + def run(self) -> int: + '''Execute the full pre-push check sequence. Return the exit status.''' first_new_commit = self.check_commits() # Skip per file checks if we didn't actually detect a new commit. - if first_new_commit is not None: + if not self.abort and first_new_commit is not None: self.check_changed_files(first_new_commit) - return self.errors == 0 + return 0 if (self.errors == 0 and not self.abort) else 1 + +def pause_delegate_window() -> None: + '''When running as a relaunch delegate, wait for the user before the + standalone console window closes so that failure output can be read.''' + if os.environ.get('EDG_PREPUSH_DELEGATE') != '1': + return + + try: + input('Push checks failed; press Enter to close this window. ') + except EOFError: + pass def main(): checker = PrePushChecker() - sys.exit(0 if checker.run() else 1) + returncode = checker.run() + if returncode != 0: + pause_delegate_window() + sys.exit(returncode) main() diff --git a/HACKING.md b/HACKING.md index 0cb7261fdd..b0fb73714a 100644 --- a/HACKING.md +++ b/HACKING.md @@ -27,6 +27,28 @@ If direnv is not an option, manually add `/dev_tools/bin` (or variables respectively. This will make the EDG development tools and their supporting Python libraries available. +For Windows users, a PowerShell counterpart of the direnv setup is provided +at the root of the repository; from a PowerShell prompt dot source it to +configure the current session: + +``` +. .\dev-env.ps1 +``` + +(If PowerShell script execution is disabled, first run +`Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`, or use the manual +environment variable configuration described above.) + +> [!WARNING] +> +> Windows users should ensure their clone uses LF line endings, e.g., by +> setting `git config core.autocrlf input` before cloning (an existing clone +> can be repaired by setting that configuration and re-checking out the +> tree). The default Windows Git configuration (`core.autocrlf true`) +> produces a CRLF checkout, which breaks both the build (e.g., `mk_errinfo` +> cannot parse `src/error_msg.txt`) and the Docker based workflow (scripts +> mounted into the container fail on CRLF shebangs). + ## Docker Based (Recommended) To get started with Docker, it is recommended that Windows and MacOS users diff --git a/dev-env.ps1 b/dev-env.ps1 new file mode 100644 index 0000000000..cc336e9f51 --- /dev/null +++ b/dev-env.ps1 @@ -0,0 +1,40 @@ +# Part of the EDG Compiler Project, under the Apache License v2.0 with LLVM +# Exceptions. +# See https://edgcpp.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Windows counterpart of the ".envrc" file used with direnv on POSIX systems: +# from a PowerShell prompt, dot source this script to make the EDG +# development tools and their supporting Python libraries available in the +# current session: +# +# . .\dev-env.ps1 +# +# This prepends "dev_tools/win-bin" and "dev_tools/bin" to the PATH and +# "dev_tools/pylibs" to the PYTHONPATH. The script may be dot sourced from +# any directory and repeated dot sourcing is harmless. + +$edgRepoRoot = $PSScriptRoot + +$edgToolDirs = @( + (Join-Path $edgRepoRoot 'dev_tools\win-bin'), + (Join-Path $edgRepoRoot 'dev_tools\bin') +) + +if (-not (@($env:Path -split ';' | + Where-Object { $edgToolDirs -icontains $_ }))) { + $env:Path = "$($edgToolDirs -join ';');$env:Path" +} + +$edgPyLibs = Join-Path $edgRepoRoot 'dev_tools\pylibs' +if (-not (@($env:PYTHONPATH -split ';' | + Where-Object { $_ -ieq $edgPyLibs }))) { + if ($env:PYTHONPATH) { + $env:PYTHONPATH = "$edgPyLibs;$env:PYTHONPATH" + } else { + $env:PYTHONPATH = $edgPyLibs + } +} + +Remove-Variable -Name edgRepoRoot, edgToolDirs, edgPyLibs ` + -ErrorAction SilentlyContinue diff --git a/dev-init.py b/dev-init.py index 0f30b9e0e9..03861d2d72 100755 --- a/dev-init.py +++ b/dev-init.py @@ -214,7 +214,25 @@ async def check_edg_python() -> InitResult: if platform.system() == 'Windows': find_edgy.add_failure_commentary( - 'Open the Windows environment variable settings:' + 'For the current PowerShell session only, run (from the repository ' + 'root):' + ) + find_edgy.add_failure_commentary( + ' . .\\dev-env.ps1', + allow_wrap = False + ) + find_edgy.add_failure_commentary( + 'and re-run this script; or, to configure new terminals ' + 'permanently, run:' + ) + find_edgy.add_failure_commentary( + ' [Environment]::SetEnvironmentVariable("Path", ' + '[Environment]::GetEnvironmentVariable("Path", "User") ' + f"+ ';{script_path}', 'User')", + allow_wrap = False + ) + find_edgy.add_failure_commentary( + 'Alternatively, open the Windows environment variable settings:' ) find_edgy.add_failure_commentary( ' "C:\\Windows\\system32\\rundll32.exe" ' @@ -271,7 +289,24 @@ async def check_edg_python() -> InitResult: if platform.system() == 'Windows': find_edg_tools.add_failure_commentary( - 'Open the Windows environment variable settings:' + 'For the current PowerShell session only, run (from the repository ' + 'root):' + ) + find_edg_tools.add_failure_commentary( + ' . .\\dev-env.ps1', + allow_wrap = False + ) + find_edg_tools.add_failure_commentary( + 'and re-run this script; or, to configure new terminals ' + 'permanently, run:' + ) + find_edg_tools.add_failure_commentary( + ' [Environment]::SetEnvironmentVariable("PYTHONPATH", ' + f"'{pylibs_path}', 'User')", + allow_wrap = False + ) + find_edg_tools.add_failure_commentary( + 'Alternatively, open the Windows environment variable settings:' ) find_edg_tools.add_failure_commentary( ' "C:\\Windows\\system32\\rundll32.exe" ' diff --git a/dev_tools/pylibs/edgdocker/__init__.py b/dev_tools/pylibs/edgdocker/__init__.py index 36267a4b48..0bfeb4ab5b 100644 --- a/dev_tools/pylibs/edgdocker/__init__.py +++ b/dev_tools/pylibs/edgdocker/__init__.py @@ -241,7 +241,13 @@ def _check_parents(mapped_dirs: List[Path], workspace_dir: Path) -> None: This can be valid (from a Docker perspective), but it's an advanced use case and can be a dangerous mistake (so we don't permit it). ''' + workspace_drive = os.path.splitdrive(str(workspace_dir))[0] for mapped_dir in mapped_dirs: + if os.path.splitdrive(str(mapped_dir))[0] != workspace_drive: + # The two paths are on different drives (which can only happen on + # Windows), so neither path can contain the other and the check below + # does not apply. + continue common_dir = Path(os.path.commonpath([mapped_dir, workspace_dir])) if common_dir == workspace_dir: print('Current workspace is the parent of a mapped dir:') @@ -390,7 +396,7 @@ def exec_docker_cmd(image_name: str, workspace_dir: Path, f"EDG_USER_NAME={render_user_name()}" ] - if os.isatty(sys.stdin.fileno()): + if edgutil.is_interactive_terminal(): # Make the container interactive. docker_args.append('-it') else: @@ -473,13 +479,15 @@ def exec_docker_cmd(image_name: str, workspace_dir: Path, if platform.system() == 'Windows': # Windows does not properly handle the execvp. Instead, keep this process - # around, and simply forward on stdin, stdout, and stderr. - subprocess.run( + # around, and simply forward on stdin, stdout, and stderr. Forward the + # exit status of the container so that callers observe failures. + completed_proc = subprocess.run( docker_args, stdin = sys.stdin, stdout = sys.stdout, stderr = sys.stderr ) + sys.exit(completed_proc.returncode) else: # Execute the docker command, replacing the current process. os.execvp('docker', docker_args) diff --git a/dev_tools/pylibs/edgutil/__init__.py b/dev_tools/pylibs/edgutil/__init__.py index e198570ca7..d7168d68b1 100644 --- a/dev_tools/pylibs/edgutil/__init__.py +++ b/dev_tools/pylibs/edgutil/__init__.py @@ -203,6 +203,29 @@ def print_env_var(env_var: str) -> None: var_value = os.environ.get(env_var) or '' print(f"Using \"{env_var}\": \"{var_value}\"") +def is_interactive_terminal() -> bool: + '''Return TRUE if standard input is attached to an interactive terminal. + + On Windows, "os.isatty" (and "sys.stdin.isatty") returns TRUE for some + character devices that are not consoles (e.g., "NUL", which is used when + a caller passes "subprocess.DEVNULL" as standard input), which would + incorrectly request TTY attachment for a container. A real console + handle can be identified by "GetConsoleMode", which fails for all other + devices. + ''' + if platform.system() != 'Windows': + return os.isatty(sys.stdin.fileno()) + + import ctypes + import msvcrt + + kernel32 = ctypes.WinDLL('kernel32', use_last_error = True) + kernel32.GetConsoleMode.argtypes = [ctypes.c_void_p, ctypes.c_void_p] + kernel32.GetConsoleMode.restype = ctypes.c_int + stdin_handle = msvcrt.get_osfhandle(sys.stdin.fileno()) + console_mode = ctypes.c_uint(0) + return bool(kernel32.GetConsoleMode(stdin_handle, ctypes.byref(console_mode))) + def get_application_files_dir(app_name: str) -> Path: platform_name = platform.system() if platform_name == 'Linux': diff --git a/doc/source/testing.rst b/doc/source/testing.rst index c8db4bcfeb..e781be5a1d 100644 --- a/doc/source/testing.rst +++ b/doc/source/testing.rst @@ -245,6 +245,19 @@ IFC file should be force-generated for (this is unusual). These module source files are compiled under the ``--module_internal_partition`` flag to generate both an IFC and a ``.obj`` file. +Note that when testing modules in a compiler emulation mode (e.g., with +``--gnu_version=150100`` added to ``//options_all:``), the ``--modules`` +option must also be specified: emulation modes disable the module keywords +by default to match the emulated compiler (no version of g++ enables them +without ``-fmodules-ts``). For example: + +.. code:: text + + //options_all:--c++20 --gnu_version=150100 --modules + +See the ``modules/eifc/modules/import-function-gnu`` and +``modules/eifc/modules/export-float16-gnu`` tests for examples. + If ``//script:`` is specified, that script is executed instead of any other commands. In general, the test type does not matter for script files except for checking the return code. For example, a ``cp`` test will expect a zero diff --git a/src/Changes b/src/Changes index acf160a341..5990867dc4 100644 --- a/src/Changes +++ b/src/Changes @@ -1,5 +1,18 @@ Version 7.1 +10/16/26 [GH #303] +C++20 modules: gnu emulation modes and extended floating-point types + +Module interface units compiled in a gnu or clang emulation mode always +failed with a catastrophic error indicating that an IFC file could not be +produced: the predeclared typedefs of extended floating-point types +(e.g., "_Float16") entered by those modes have no IFC encoding. +Predeclared typedefs are now skipped when the members of a scope are +written to an IFC file (they are re-created by any importing translation +unit), and the "_Float16", "_Float32", "_Float64", and "_Float128" type +families can now be written to and read from IFC files. + + 10/16/26 [GH #229] Improper handling of "if constexpr" substatements diff --git a/src/ifc_modules_read.c b/src/ifc_modules_read.c index d48ace4d9f..096eecd0d7 100644 --- a/src/ifc_modules_read.c +++ b/src/ifc_modules_read.c @@ -16497,8 +16497,30 @@ no corresponding type, return an error type. } break; case ifc_tbs_float: - check_assertion(precision == ifc_tps_default); - result = float_type((a_float_kind)fk_float); + switch (precision) { + case ifc_tps_default: + result = float_type(fk_float); + break; + case ifc_tps_bit16: + result = float_type(float16_kind()); + break; + case ifc_tps_bit32: + result = float_type(fk_std_float32); + break; + case ifc_tps_bit64: + result = float_type(fk_std_float64); + break; + case ifc_tps_bit128: + result = float_type(fk_float128); + break; + default: + { a_string err_msg("Unexpected ", str_for(precision), + " for ", str_for(basis)); + + ifc_unexpected(module_of(itf), err_msg); + } + goto invalid; + } /* switch */ break; case ifc_tbs_double: if (precision == ifc_tps_long) { diff --git a/src/ifc_modules_write.c b/src/ifc_modules_write.c index 1cf1e51aa5..6e468600b7 100644 --- a/src/ifc_modules_write.c +++ b/src/ifc_modules_write.c @@ -2714,20 +2714,33 @@ for the float type. set_ifc_precision(&fund_type, ifc_tps_long); break; case fk_float128: + case fk_std_float128: set_ifc_basis(&fund_type, ifc_tbs_float); set_ifc_precision(&fund_type, ifc_tps_bit128); break; case fk_float16: + case fk_std_float16: + set_ifc_basis(&fund_type, ifc_tbs_float); + set_ifc_precision(&fund_type, ifc_tps_bit16); + break; + case fk_std_float32: + set_ifc_basis(&fund_type, ifc_tbs_float); + set_ifc_precision(&fund_type, ifc_tps_bit32); + break; + case fk_std_float64: + set_ifc_basis(&fund_type, ifc_tbs_float); + set_ifc_precision(&fund_type, ifc_tps_bit64); + break; case fk_fp16: case fk_float32x: case fk_float64x: case fk_float80: case fk_std_bfloat16: - case fk_std_float16: - case fk_std_float32: - case fk_std_float64: - case fk_std_float128: case fk_last: + /* These extended floating-point kinds have no IFC encoding that + distinguishes them from other floating-point types of the same + size ("__fp16" in particular has a different mangling than + "_Float16"). */ ifc_write_catastrophe(); break; default_is_unexpected(); @@ -6042,6 +6055,13 @@ Add all the types in the given scope to the given IL -> IFC mapping. information. */ continue; } /* if */ + if (type->kind == tk_typeref && type->variant.typeref.predeclared) { + /* Skip predeclared typedefs (e.g., "_Float16", which is entered in gnu + and clang emulation modes): they are re-created by the predefined + type entry processing of an importing translation unit, and the + types of some of them cannot currently be written to an IFC file. */ + continue; + } /* if */ (void)il_map->find_or_enter_type(type); } /* for */ } /* dump_scope_types */ diff --git a/src/sys_predef.c b/src/sys_predef.c index f07b9d8d70..2c470b09a8 100644 --- a/src/sys_predef.c +++ b/src/sys_predef.c @@ -3091,9 +3091,7 @@ Enter predeclared symbols as required by the implementation. if (float16_enabled) { (void)enter_predefined_typedef( "_Float16", - ((gnu_version_is(>= 130000) || - clang_version_is(>= 150000)) ? float_type(fk_std_float16) - : float_type(fk_float16))); + float_type(float16_kind())); } /* if */ if (float80_enabled) { (void)enter_predefined_typedef( diff --git a/src/types.h b/src/types.h index 3b9bebbe91..b56b8aa700 100644 --- a/src/types.h +++ b/src/types.h @@ -54,6 +54,19 @@ this routine should not be used when checking type qualifiers. } /* skip_typerefs */ +INLINE a_float_kind float16_kind(void) +/* +Return the floating-point kind used for the type name "_Float16" in the +current mode: GNU versions 13 and later and Clang versions 15 and later +use the standard "_Float16" type, and other modes use the GNU extension +type. +*/ +{ + return (gnu_version_is(>= 130000) || clang_version_is(>= 150000)) ? + fk_std_float16 : fk_float16; +} /* float16_kind */ + + INLINE a_boolean is_lexical_typeref(a_type_ptr tp) /* Return TRUE if tp is a trk_template_arg_list or trk_name_qualifier typeref, diff --git a/tests/tests/modules/eifc/modules/export-float16-gnu/.test.rto/default.1.1.txt b/tests/tests/modules/eifc/modules/export-float16-gnu/.test.rto/default.1.1.txt new file mode 100644 index 0000000000..4882e1c979 --- /dev/null +++ b/tests/tests/modules/eifc/modules/export-float16-gnu/.test.rto/default.1.1.txt @@ -0,0 +1 @@ +cpfe -DTEST_NUMBER=1 --module_import_diagnostics -d-module_report --c++20 --gnu_version=150100 --modules --module_interface foo.ixx diff --git a/tests/tests/modules/eifc/modules/export-float16-gnu/.test.rto/default.1.2.txt b/tests/tests/modules/eifc/modules/export-float16-gnu/.test.rto/default.1.2.txt new file mode 100644 index 0000000000..45d0d4cfe4 --- /dev/null +++ b/tests/tests/modules/eifc/modules/export-float16-gnu/.test.rto/default.1.2.txt @@ -0,0 +1,2 @@ +cpfe -DTEST_NUMBER=1 --module_import_diagnostics -d-module_report --c++20 --gnu_version=150100 --modules Test_name.c +2 modules declarations processed (0 failed). diff --git a/tests/tests/modules/eifc/modules/export-float16-gnu/.test.rto/edg_x86_64_cp.1.2.txt b/tests/tests/modules/eifc/modules/export-float16-gnu/.test.rto/edg_x86_64_cp.1.2.txt new file mode 100644 index 0000000000..31335fc08b --- /dev/null +++ b/tests/tests/modules/eifc/modules/export-float16-gnu/.test.rto/edg_x86_64_cp.1.2.txt @@ -0,0 +1,3 @@ +cpfe -DTEST_NUMBER=1 --module_import_diagnostics -d-module_report --c++20 --gnu_version=150100 --modules Test_name.c +2 modules declarations processed (0 failed). +2 modules declarations processed (0 failed). diff --git a/tests/tests/modules/eifc/modules/export-float16-gnu/foo.ixx b/tests/tests/modules/eifc/modules/export-float16-gnu/foo.ixx new file mode 100644 index 0000000000..aabf552a96 --- /dev/null +++ b/tests/tests/modules/eifc/modules/export-float16-gnu/foo.ixx @@ -0,0 +1,5 @@ +export module foo; + +export _Float16 half(_Float16 v) { + return v / 2; +} diff --git a/tests/tests/modules/eifc/modules/export-float16-gnu/test.mft.cpp b/tests/tests/modules/eifc/modules/export-float16-gnu/test.mft.cpp new file mode 100644 index 0000000000..f91c3878c1 --- /dev/null +++ b/tests/tests/modules/eifc/modules/export-float16-gnu/test.mft.cpp @@ -0,0 +1,10 @@ +//type:cp +//module_files:foo.ixx +//options_all:--module_import_diagnostics -d-module_report --c++20 --gnu_version=150100 --modules + +import foo; + +int main() { + _Float16 x = half(8); + return 0; +} diff --git a/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/default.1.1.txt b/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/default.1.1.txt new file mode 100644 index 0000000000..1fe269d1e7 --- /dev/null +++ b/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/default.1.1.txt @@ -0,0 +1 @@ +eccp -DTEST_NUMBER=1 --module_import_diagnostics -d-module_report --c++20 --gnu_version=150100 --modules --module_interface foo.ixx diff --git a/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/default.1.2.txt b/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/default.1.2.txt new file mode 100644 index 0000000000..d9e7fd62c5 --- /dev/null +++ b/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/default.1.2.txt @@ -0,0 +1,2 @@ +eccp -DTEST_NUMBER=1 --module_import_diagnostics -d-module_report --c++20 --gnu_version=150100 --modules foo.o Test_name.c +1 modules declarations processed (0 failed). diff --git a/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/default.1.3.txt b/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/default.1.3.txt new file mode 100644 index 0000000000..ff2642c6d5 --- /dev/null +++ b/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/default.1.3.txt @@ -0,0 +1 @@ +./a.out diff --git a/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/edg_x86_64_cp.1.2.txt b/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/edg_x86_64_cp.1.2.txt new file mode 100644 index 0000000000..7e1abe9ab1 --- /dev/null +++ b/tests/tests/modules/eifc/modules/import-function-gnu/.test.rto/edg_x86_64_cp.1.2.txt @@ -0,0 +1,3 @@ +eccp -DTEST_NUMBER=1 --module_import_diagnostics -d-module_report --c++20 --gnu_version=150100 --modules foo.o Test_name.c +1 modules declarations processed (0 failed). +1 modules declarations processed (0 failed). diff --git a/tests/tests/modules/eifc/modules/import-function-gnu/foo.ixx b/tests/tests/modules/eifc/modules/import-function-gnu/foo.ixx new file mode 100644 index 0000000000..966a87da51 --- /dev/null +++ b/tests/tests/modules/eifc/modules/import-function-gnu/foo.ixx @@ -0,0 +1,5 @@ +export module foo; + +export int bar() { + return 0; +} diff --git a/tests/tests/modules/eifc/modules/import-function-gnu/test.mft.cpp b/tests/tests/modules/eifc/modules/import-function-gnu/test.mft.cpp new file mode 100644 index 0000000000..47af7310c7 --- /dev/null +++ b/tests/tests/modules/eifc/modules/import-function-gnu/test.mft.cpp @@ -0,0 +1,9 @@ +//type:rp +//module_files:foo.ixx +//options_all:--module_import_diagnostics -d-module_report --c++20 --gnu_version=150100 --modules + +import foo; + +int main() { + return bar(); +}