A Compile-Flag Dialect Tag That Propagates (Scottish Fold)

Importing the Standard Library and every chapter since have quietly needed CXX_EXTENSIONS ON to work around Clang’s synthesized std BMI carrying a GNU-extensions dialect tag that has to match everywhere the BMI is consumed. GCC has an analogous problem of its own, triggered by a different, common project setting: disabling exceptions.

An icon indicating this blurb contains information

Plenty of codebases — embedded targets, some performance-sensitive services, anything following Google’s C++ style guide — build with -fno-exceptions throughout. This chapter imagines exactly that kind of project reaching for import std; for the first time.

The Module

The module itself is unremarkable — it’s the build configuration that matters here.

1 export module ScottishFold;
2 
3 import std;
4 
5 export auto meow() -> void {
6    std::println("The Scottish Fold cat says 'Meow.'");
7 }

What Goes Wrong

Apply -fno-exceptions project-wide, the way such a codebase would, and the build fails immediately — not on anything in ScottishFold.cppm itself, but on the synthesized std module CMake builds behind the scenes:

1 In module imported at main.cpp:1:1:
2 std: error: language dialect differs 'C++23', expected 'C++23/no-exceptions'
3 std: error: failed to read compiled module: Bad file data
4 std: note: compiled module file is 'CMakeFiles/__CMAKE__CXX23@.../1f708004f604.bmi'
5 std: fatal error: returning to the gate for a mechanical issue

GCC’s synthesized std BMI is always built with exceptions enabled, independent of whatever the rest of the project requests. A target compiled with -fno-exceptions expects a BMI tagged C++23/no-exceptions; the actual std BMI is tagged plain C++23. GCC treats that as a hard configuration mismatch, not a warning.

The Fix, and Why It Has to Be Applied Twice

The fix is a -fexceptions override, applied after -fno-exceptions on the command line so the last flag wins — but it has to go on every target that imports std, directly or transitively, exactly the way CXX_EXTENSIONS ON needed to reach both the library and the executable in Importing the Standard Library.

 1 cmake_minimum_required(VERSION 3.30.5)
 2 
 3 project(ScottishFold LANGUAGES CXX)
 4 set(module_name scottish_fold)
 5 
 6 # Imagine a codebase that disables exceptions project-wide for performance
 7 # or embedded-target reasons -- a common convention, and the trigger for
 8 # the dialect mismatch this chapter demonstrates.
 9 add_compile_options(-fno-exceptions)
10 
11 add_library(${module_name})
12 target_compile_features(${module_name} PUBLIC cxx_std_23)
13 # GCC's synthesized std BMI is always built WITH exceptions enabled,
14 # regardless of the project's own -fno-exceptions default -- so this
15 # target, which imports std, needs an explicit override layered after
16 # -fno-exceptions so it wins.
17 target_compile_options(${module_name} PRIVATE -fexceptions)
18 set_target_properties(${module_name} PROPERTIES
19         CXX_MODULE_STD ON
20         CXX_EXTENSIONS ON
21 )
22 target_sources(${module_name}
23                PUBLIC
24                FILE_SET cxx_modules TYPE CXX_MODULES
25                FILES ScottishFold.cppm
26 )
27 
28 add_executable(${PROJECT_NAME} main.cpp)
29 target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)
30 set_target_properties(${PROJECT_NAME} PROPERTIES CXX_EXTENSIONS ON)
31 # scottish_fold's BMI carries the -fexceptions override too (see above),
32 # and that tag propagates transitively -- main.cpp only imports
33 # ScottishFold, never std directly, but still needs the same override.
34 target_compile_options(${PROJECT_NAME} PRIVATE -fexceptions)
35 
36 target_link_libraries(${PROJECT_NAME} ${module_name})

Line 9 sets the project-wide default. Line 17 overrides it back on for scottish_fold, the target that actually says import std; — without this, the library target fails exactly like the error above. But scottish_fold’s own BMI, once built with exceptions enabled, now carries that dialect tag forward; the executable target, which only does import ScottishFold; and never touches std directly, fails the identical way one step later unless it also gets the override (line 33). The tag follows the import chain all the way to the top, the same way the GNU-extensions requirement in Importing the Standard Library did.

An icon indicating this blurb contains a warning

Clang has no equivalent problem — the same project, same -fno-exceptions default, same override pattern, builds cleanly under Clang with no dialect complaint at any step. This is a GCC-specific consequence of how it synthesizes the std BMI, not a general rule about exceptions and modules.

Building With Raw Compiler Commands

The raw-script mechanics match Importing the Standard Library exactly; only the flags differ.

 1 # GCC's synthesized std BMI is always built with exceptions enabled, so it
 2 # doesn't matter whether -fno-exceptions is passed here -- but any TU that
 3 # imports std, directly or transitively, must be compiled WITHOUT
 4 # -fno-exceptions (or with an explicit -fexceptions override) or GCC
 5 # rejects the BMI with "language dialect differs".
 6 g++-15 -std=c++23 -fmodules-ts -c -x c++ /usr/include/c++/15/bits/std.cc -o bin/std.o
 7 
 8 g++-15 -std=c++23 -fmodules-ts -c -x c++ ScottishFold.cppm -o bin/ScottishFold.o
 9 
10 g++-15 -std=c++23 -fmodules-ts -c main.cpp -o bin/main.o
11 g++-15 -std=c++23 -fmodules-ts  bin/main.o bin/ScottishFold.o bin/std.o -o bin/main

None of these lines pass -fno-exceptions at all — the whole point of the demonstration is what happens when a project’s own default introduces it. Left alone, GCC’s std BMI and everything consuming it agree, and the build just works, which is exactly why this failure mode is easy to miss until a real project’s build flags collide with it.