You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
MSVC: cxx_runtime does not reach the std module build — /MD project against a /MT std.ifc fails with C2375 #422
With cxx_runtime = "host-coupled" in [build], the project's TUs are compiled /MD but the std module interface they import was built /MT. cl warns and
then fails for real:
warning C5050: Possible incompatible environment while importing module 'std':
_MSVC_MT is defined in module command line and not in current command line
warning C5050: Possible incompatible environment while importing module 'std':
_MSVC_MD is defined in current command line and not in module command line
C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt\corecrt_malloc.h(89):
error C2375: 'free': redefinition; different linkage
Why it is not a stale artifact
I first assumed the CI cache. It is not:
the cache key changed and there are no restore-keys, so ~/.mcpp came in cold
the build log contains no command that builds std.ifc at all — it is
served from mcpp's own global std cache
the project TU command line does carry /MD, so cxx_runtime reached the
project; it just did not reach std
So the std module is built with mcpp's default runtime regardless of what the
manifest asks for, and a host-coupled project can never agree with it on MSVC.
Expected
cxx_runtime is described as a contract for the produced artifact, and on MSVC
the runtime selection is an ABI-level property that the std module has to share
with everything importing it. Either the std module build should honour cxx_runtime, or the std cache should be keyed by it so the two cannot diverge
silently.
Environment
mcpp 2026.8.11.3 (installed via xlings install mcpp)
Setting cxx_runtime = "self-contained" — mcpp's own default, /MT on MSVC —
puts the project on the same runtime as the std module it is handed. That is
what this project does today, at the cost of differing from its xmake build,
which uses set_runtimes("MD").
Sharper than the original report, and mcpp says it itself. Setting cxx_runtime = "self-contained" produces:
warning: cxx_runtime: distributable target: cxx_runtime = "self-contained" is not
implemented for the MSVC runtime yet (it would need the /MT runtime);
using host-coupled — the artifact needs the VC++ redistributable
So on MSVC both settings end at the same place. Verified on cold caches:
cxx_runtime
project TUs
std module
host-coupled
/MD
/MT
self-contained
/MD (warned, falls back)
/MT
The project side is pinned to /MD by design-in-progress, and the std module is
handed over built /MT. Nothing in the manifest can bring them together, so any
MSVC project that does import std; and reaches a CRT declaration fails:
corecrt_malloc.h(89): error C2375: 'free': redefinition; different linkage
The inconsistency is internal to mcpp: the same build produces /MD TUs and a /MT std.ifc. Until self-contained lands on MSVC, the std module presumably
wants to be built /MD to match where everything else already is.
Fixed in 2026.8.15.1 (commit c459cf2). Closing late; recording the evidence here.
One correction to the report, which strengthens rather than weakens it. The attribution to cxx_runtime is not what the measurement showed: the CRT model is derived from linkage, and the command that built the std module carried no /M switch at all, so it took cl's default /MT while the project compiled /MD. The mismatch was therefore present in the DEFAULT configuration, not only under host-coupled — the report found it through host-coupled, but nothing about that setting caused it.
Both remedies the issue offered as alternatives landed, and they turn out to be one change:
toolchain::stdmod::ensure_built takes an msvc_crt_flag, shaped after macos_deployment_target and carrying the same contract: it must be the RESOLVED value the project's own TUs use.
That flag enters std_build_commands, which is part of the cache identity, so two CRT models cannot share a std cache directory.
The flag has ONE derivation — msvc_wants_static_crt in mcpp.toolchain.dialect — read by flags.cppm for the project TUs and by prepare.cppm for the std build. That is what keeps the two sides from drifting apart again, and it is the part a fix that only passed the right flag would have left open.
Unit test: tests/unit/test_toolchain_msvc.cpp. It pins two properties, and the second is what makes the first stay true: the flag must be IN the command so the module is built correctly, and it must be in the command STRING so it reaches std_build_commands and therefore the cache key.
Activity
Sunrisepeak commentedon Aug 12, 2026
Sharper than the original report, and mcpp says it itself. Setting
cxx_runtime = "self-contained"produces:So on MSVC both settings end at the same place. Verified on cold caches:
cxx_runtimehost-coupled/MD/MTself-contained/MD(warned, falls back)/MTThe project side is pinned to
/MDby design-in-progress, and the std module ishanded over built
/MT. Nothing in the manifest can bring them together, so anyMSVC project that does
import std;and reaches a CRT declaration fails:The inconsistency is internal to mcpp: the same build produces
/MDTUs and a/MTstd.ifc. Untilself-containedlands on MSVC, the std module presumablywants to be built
/MDto match where everything else already is.Run: https://github.com/Sunrisepeak/xrgui/actions/runs/31594701447
docs: 七个 issue(#412 #415 #416 #417 #418 #421 #422)的逐条核实与修复方案
fix(msvc,manifest,docs): #421-A #412-1 #412-2 #422 #418; bump to 2026…
feat(bench): cross-platform benchmark suite, and the defects it expos…
feat(toolchain): MSVC gets a version axis — `msvc@<toolset>` alongsid…
docs: cxx_runtime was inert on MSVC until 2026.8.16, and upgrading is…
speak-agent commentedon Aug 31, 2026
Fixed in 2026.8.15.1 (commit
c459cf2). Closing late; recording the evidence here.One correction to the report, which strengthens rather than weakens it. The attribution to
cxx_runtimeis not what the measurement showed: the CRT model is derived fromlinkage, and the command that built the std module carried no/Mswitch at all, so it took cl's default/MTwhile the project compiled/MD. The mismatch was therefore present in the DEFAULT configuration, not only underhost-coupled— the report found it throughhost-coupled, but nothing about that setting caused it.Both remedies the issue offered as alternatives landed, and they turn out to be one change:
toolchain::stdmod::ensure_builttakes anmsvc_crt_flag, shaped aftermacos_deployment_targetand carrying the same contract: it must be the RESOLVED value the project's own TUs use.std_build_commands, which is part of the cache identity, so two CRT models cannot share a std cache directory.The flag has ONE derivation —
msvc_wants_static_crtinmcpp.toolchain.dialect— read byflags.cppmfor the project TUs and byprepare.cppmfor the std build. That is what keeps the two sides from drifting apart again, and it is the part a fix that only passed the right flag would have left open.Unit test:
tests/unit/test_toolchain_msvc.cpp. It pins two properties, and the second is what makes the first stay true: the flag must be IN the command so the module is built correctly, and it must be in the command STRING so it reachesstd_build_commandsand therefore the cache key.