|
17 | 17 | // coroutine diagnostic can run (measured, probe PR #788). |
18 | 18 | // |
19 | 19 | // ASKED ONLY AFTER A FAILURE, OF THE COMPILER THAT FAILED. The command is read |
20 | | -// from the failure output itself — the std module's `command:` line, or the |
21 | | -// command ninja prints after `FAILED:` — so the plan path and the fast path |
22 | | -// share this one function and no record has to be written in advance. A |
23 | | -// successful build never reaches it, and when the compiler or the STL changes, |
24 | | -// either the symptom or the probe stops matching and the note disappears. |
| 20 | +// from the failure itself, so no record has to be written in advance, and a |
| 21 | +// successful build never reaches this module: |
| 22 | +// |
| 23 | +// * a compile ninja ran: the output carries the command after `FAILED:` and |
| 24 | +// the compiler's diagnostics, so the symptom is read from it (`advice`); |
| 25 | +// the plan path and the fast path share that one function; |
| 26 | +// * the std module precompile: its error carries the `command:` line but not |
| 27 | +// the compiler's diagnostics, which reach the terminal directly (measured |
| 28 | +// on #781's Windows CI). The answer there rests on the command alone |
| 29 | +// (`std_module_advice`): the MSVC STL's `std.ixx` at C++23 or later |
| 30 | +// includes <generator>, which cannot compile without the macro. |
| 31 | +// |
| 32 | +// When the compiler or the STL changes, the probe stops matching and the note |
| 33 | +// disappears. |
25 | 34 |
|
26 | 35 | export module mcpp.toolchain.msvc_coroutines; |
27 | 36 |
|
@@ -63,6 +72,13 @@ std::optional<FailedCommand> failed_command_in(std::string_view output); |
63 | 72 | // only after the symptom and the command have both matched. |
64 | 73 | std::string advice(std::string_view output, const ProbeFn& probe); |
65 | 74 |
|
| 75 | +// The note for a failed std module precompile, from its error message: the |
| 76 | +// `command:` line compiles the MSVC STL's `std.ixx` or `std.compat.ixx` at |
| 77 | +// C++23 or later for a `*-windows-msvc` target, and the probe finds the macro |
| 78 | +// undefined. Empty otherwise. |
| 79 | +std::string std_module_advice(std::string_view message, const ProbeFn& probe); |
| 80 | +std::string std_module_advice(std::string_view message); |
| 81 | + |
66 | 82 | // The probe that runs the compiler: `-dM -E` of an empty unit, with the same |
67 | 83 | // target and standard and without the driver's configuration file. |
68 | 84 | std::optional<Probe> run_probe(const FailedCommand& cmd); |
@@ -169,25 +185,74 @@ std::optional<FailedCommand> failed_command_in(std::string_view output) { |
169 | 185 | return found; |
170 | 186 | } |
171 | 187 |
|
| 188 | +namespace { |
| 189 | + |
| 190 | +bool targets_msvc_abi(const FailedCommand& cmd) { |
| 191 | + auto triple = mcpp::toolchain::triple::parse(cmd.target); |
| 192 | + return triple && triple->os == "windows" && triple->env == "msvc"; |
| 193 | +} |
| 194 | + |
| 195 | +// C++23 or later: `c++23`, `gnu++2b`, `c++26`, `c++2c`, `c++latest`. |
| 196 | +bool at_least_cxx23(std::string_view standard) { |
| 197 | + auto plus = standard.find("++"); |
| 198 | + if (plus == std::string_view::npos) return false; |
| 199 | + auto level = standard.substr(plus + 2); |
| 200 | + if (level == "latest") return true; |
| 201 | + if (level.size() != 2) return false; |
| 202 | + if (level[0] == '2' && level[1] >= 'b' && level[1] <= 'z') return true; |
| 203 | + return level >= "23" && level <= "99"; |
| 204 | +} |
| 205 | + |
| 206 | +std::string render(Symptom symptom, const FailedCommand& cmd, const Probe& probed); |
| 207 | + |
| 208 | +} // namespace |
| 209 | + |
172 | 210 | std::string advice(std::string_view output, const ProbeFn& probe) { |
173 | 211 | const auto symptom = symptom_in(output); |
174 | 212 | if (symptom == Symptom::None) return {}; |
175 | 213 | auto cmd = failed_command_in(output); |
176 | | - if (!cmd) return {}; |
177 | | - auto triple = mcpp::toolchain::triple::parse(cmd->target); |
178 | | - if (!triple || triple->os != "windows" || triple->env != "msvc") return {}; |
| 214 | + if (!cmd || !targets_msvc_abi(*cmd)) return {}; |
| 215 | + auto probed = probe(*cmd); |
| 216 | + if (!probed || probed->predefinesImplCoroutine) return {}; |
| 217 | + return render(symptom, *cmd, *probed); |
| 218 | +} |
| 219 | + |
| 220 | +std::string std_module_advice(std::string_view message, const ProbeFn& probe) { |
| 221 | + std::optional<FailedCommand> cmd; |
| 222 | + bool stlModule = false; |
| 223 | + for_each_line(message, [&](std::string_view line) { |
| 224 | + if (!line.starts_with("command: ")) return; |
| 225 | + if (auto c = failed_command_in(line)) { |
| 226 | + cmd = std::move(c); |
| 227 | + for (auto const& w : split_words(line.substr(9))) |
| 228 | + if (w.ends_with("std.ixx") || w.ends_with("std.compat.ixx")) stlModule = true; |
| 229 | + } |
| 230 | + }); |
| 231 | + if (!cmd || !stlModule || !targets_msvc_abi(*cmd) || !at_least_cxx23(cmd->standard)) |
| 232 | + return {}; |
179 | 233 | auto probed = probe(*cmd); |
180 | 234 | if (!probed || probed->predefinesImplCoroutine) return {}; |
| 235 | + return render(Symptom::StdModuleGenerator, *cmd, *probed); |
| 236 | +} |
181 | 237 |
|
182 | | - const std::string compiler = probed->version.empty() |
183 | | - ? std::string("this clang") : std::format("clang {}", probed->version); |
184 | | - const std::string section = triple->str(); |
| 238 | +std::string std_module_advice(std::string_view message) { |
| 239 | + return std_module_advice(message, run_probe); |
| 240 | +} |
| 241 | + |
| 242 | +namespace { |
| 243 | + |
| 244 | +std::string render(Symptom symptom, const FailedCommand& cmd, const Probe& probed) { |
| 245 | + auto triple = mcpp::toolchain::triple::parse(cmd.target); |
| 246 | + |
| 247 | + const std::string compiler = probed.version.empty() |
| 248 | + ? std::string("this clang") : std::format("clang {}", probed.version); |
| 249 | + const std::string section = triple ? triple->str() : cmd.target; |
185 | 250 | std::string note = std::format( |
186 | 251 | "\n" |
187 | 252 | "note: {} does not support C++20 coroutines on {} (this Microsoft\n" |
188 | 253 | " ABI): it does not predefine __cpp_impl_coroutine there, so the\n" |
189 | 254 | " MSVC STL's <coroutine> is empty.", |
190 | | - compiler, cmd->target); |
| 255 | + compiler, cmd.target); |
191 | 256 | if (symptom == Symptom::StdModuleGenerator) { |
192 | 257 | note += |
193 | 258 | " The C++23 std module includes\n" |
@@ -226,6 +291,8 @@ std::string advice(std::string_view output, const ProbeFn& probe) { |
226 | 291 | return note; |
227 | 292 | } |
228 | 293 |
|
| 294 | +} // namespace |
| 295 | + |
229 | 296 | std::optional<Probe> run_probe(const FailedCommand& cmd) { |
230 | 297 | std::error_code ec; |
231 | 298 | const auto empty = std::filesystem::temp_directory_path(ec) |
|
0 commit comments