Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add a flag to make CHECK failures non-fatal for debugging. #4835

Merged
merged 6 commits into from
Jan 23, 2025

Conversation

zygoloid
Copy link
Contributor

toolchain/autoupdate_testdata.py --allow-check-fail can now be used to perform an autoupdate even if some CARBON_CHECKs are failing. What this does will depend on how the toolchain behaves after the CHECK failure, and of course there's no guarantees there, but this can be useful if it's easier to debug the CHECK failure by looking at the produced SemIR.

Internally, this uses bazel build --config=non-fatal-checks, which in turn specifies a --per_file_copt for check_internal.cpp. The intent here is that the rebuild required to enable or disable this mode is as small as reasonably possible.

This mode is not compatible with -c opt, as it's important that check failure calls are [[noreturn]] in -c opt mode.

`toolchain/autoupdate_testdata.py --allow-check-fail` can now be used to
perform an autoupdate even if some `CARBON_CHECK`s are failing. What
this does will depend on how the toolchain behaves after the `CHECK`
failure, and of course there's no guarantees there, but this can be
useful if it's easier to debug the `CHECK` failure by looking at the
produced SemIR.

Internally, this uses `bazel build --config=non-fatal-checks`, which in
turn specifies a `--per_file_copt` for `check_internal.cpp`. The intent
here is that the rebuild required to enable or disable this mode is as
small as reasonably possible.

This mode is not compatible with `-c opt`, as it's important that check
failure calls are `[[noreturn]]` in `-c opt` mode.
Copy link
Contributor

@jonmeow jonmeow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just suggesting if you want to address the TODO, I think it may be a smaller change than you're expecting (maybe just needs a little familiarity)

@@ -37,11 +38,22 @@ def main() -> None:
else:
exit(f"Build mode not found in `bazel-bin` symlink: {link}")

# TODO: Add proper argument parsing.
if "--allow-check-fail" in args:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had you considered --non-fatal-checks to match the --config?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I've gone back and forth on this. I found this flag name more intuitive when running autoupdate_testdata, but using a single name also seems nice, and this name doesn't work so well for the config. If you have a strong opinion I'm happy to defer to that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a slight leaning towards the same name because it's niche, so my instinct is keeping the same name in spots would make it clearer that they're closely related. I wouldn't call it a strong opinion though.

Perhaps a different name would help -- maybe no-abort-on-check (or disable-check-abort, or similar) or log[ging]-checks? Mirroring something similar to the #ifdef... I don't know if any of those names sound better to you in the various spots.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A slight leaning is more than I have, so I've gone with non-fatal-checks throughout.

toolchain/autoupdate_testdata.py Outdated Show resolved Hide resolved
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
common/check_internal.h Outdated Show resolved Hide resolved
zygoloid and others added 3 commits January 22, 2025 23:44
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
@jonmeow
Copy link
Contributor

jonmeow commented Jan 23, 2025

BTW, LG with the argparse changes, sorry about my * vs + mixup

@chandlerc
Copy link
Contributor

I don't really want to block this landing, but I do feel very nervous about it.

Specifically, I'm very nervous about something that is only sometimes no-return. For example, won't this build mode be flaky with -Wuninitialized warnings? Without this config, a CHECK can be used to clearly and forcibly cut off a control flow path, but with this build that isn't true.

This is debug only, and so I'm fine if we want to experiment with it and see if its useful. But I think this is even more brittle than it is described. Mostly, I don't want us to start slowly accumulating changes to work around when CHECK isn't actually no-return, as I don't think source level changes like that will be sustainable long-term.

@jonmeow
Copy link
Contributor

jonmeow commented Jan 23, 2025

For example, won't this build mode be flaky with -Wuninitialized warnings?

I think zygoloid is taking advantage of a very peculiar trick: --per_file_copt=common/check_internal.cpp@-DCARBON_NON_FATAL_CHECKS

This means only the implementation of CheckFail sees the behavior change. Users of CHECK won't see it, meaning uninitialized warnings shouldn't be affected.

It's weird and potentially risky, and yes brittle, but when it's mainly for debug (and also default-off) I'm kind of okay with that if it helps zygoloid.

I think it's better than the alternative mentioned of adding another CHECK variant that explicitly isn't noreturn and then auditing current CHECK calls to migrate. That to me has a higher cost, because it creates CHECK more variants that are intended to work (but untested).

@zygoloid
Copy link
Contributor Author

I'm very nervous about something that is only sometimes no-return. For example, won't this build mode be flaky with -Wuninitialized warnings? Without this config, a CHECK can be used to clearly and forcibly cut off a control flow path, but with this build that isn't true.

CARBON_CHECK can't be used to cut off control flow to the following code. We have compile-time checking that the condition of a CARBON_CHECK is neither a constant false nor a constant true, so the code after the CARBON_CHECK is always reachable regardless of build mode. And for CARBON_FATAL, this PR injects a call to std::abort() after the check failure in the build modes where CheckFail can return.

So this should not in any way affect flow-sensitive reachability. It does affect path-sensitive reachability, but I'm assuming that that (a) won't affect diagnostics, and (b) doesn't really matter outside -c opt.

@zygoloid zygoloid enabled auto-merge January 23, 2025 19:09
@zygoloid zygoloid added this pull request to the merge queue Jan 23, 2025
Merged via the queue into carbon-language:trunk with commit 58fba07 Jan 23, 2025
8 checks passed
@zygoloid zygoloid deleted the toolchain-non-fatal-checks branch January 23, 2025 22:25
jonmeow added a commit to jonmeow/carbon-lang that referenced this pull request Jan 24, 2025
jonmeow added a commit to jonmeow/carbon-lang that referenced this pull request Jan 24, 2025
github-merge-queue bot pushed a commit that referenced this pull request Jan 24, 2025
# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants