[PATCH] D135551: [clang] replace `assert(0)` with `llvm_unreachable` NFC

Aaron Ballman via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 11 12:22:54 PDT 2022


aaron.ballman added a comment.

In D135551#3850308 <https://reviews.llvm.org/D135551#3850308>, @dblaikie wrote:

> In D135551#3850266 <https://reviews.llvm.org/D135551#3850266>, @inclyc wrote:
>
>> This makes sense! However I think `assert(0)` should not be used in this case, we could expose another `llvm_unreachable`-like api and probably `llvm_report_error` shall be fine. Are there some changed assertions actually "Aspirationally unreachable" in this patch?
>
> No, I really don't think  we should go down that path.
>
> I believe these are not actually distinct cases - in either case, the program has UB if they violated the invariants/preconditions - whether or not they called through the C API.

The C Index test cases I commented on earlier in the review are a good example of when there's no UB but we still want to alert people to the problem of code they should not be reaching. The assumption that "reached here unexpectedly" == "UB" is invalid. Some things are logic bugs that exhibit no UB.

> unreachable is no more a guarantee/proven thing than an assertion - both are written by humans and a claim "if this is reached-or-false, there is a bug in some code, somewhere". The statement is not stronger in the unreachable case and the style guide supports that perspective and the way we triage/treat bugs is pretty consistent with that - we get bugs all the time when an unreachable is reached and that doesn't seem to surprise most/anyone - we treat it the same as a bug when an assertion fires.
>
> The discourse discussion, I think, supports this ^ perspective.
>
> As there's still disagreement, should this escalate to the RFC process to change the style guide, Aaron?

Yes, I would appreciate that. I don't think we're interpreting our policy the same way. Specifically "Use llvm_unreachable to mark a specific point in code that should never be reached." -- "should" is turning out to be interpreted in two ways:

- "used to indicate obligation, duty, or correctness, typically when criticizing someone's actions. e.g., he should have been careful": I am asserting it is impossible to reach this.
- "used to indicate what is probable. e.g., $348 million should be enough to buy him out": I am asserting you probably won't get here, but you won't be happy if you do.

In D135551#3850266 <https://reviews.llvm.org/D135551#3850266>, @inclyc wrote:

> This makes sense! However I think `assert(0)` should not be used in this case, we could expose another `llvm_unreachable`-like api and probably `llvm_report_error` shall be fine. Are there some changed assertions actually "Aspirationally unreachable" in this patch?

I'm totally fine not using `assert(0)` and using an `llvm_unreachable`-like API (or even using a macro to dispatch to `llvm_unreachable` under a different name).

There are more aspirationally unreachable issues in this patch, I've commented on the ones I spotted, but I stopped commenting pretty quickly because I think a lot of the cases are made slightly worse by switching to `llvm_unreachable` instead of more targeted changes. I'd be especially curious to hear what @dblaikie thinks of the suggestions I have though -- it might be easier to see the distinction with real world code (or it might not!).



================
Comment at: clang/include/clang/AST/Redeclarable.h:265
         if (PassedFirst) {
-          assert(0 && "Passed first decl twice, invalid redecl chain!");
+          llvm_unreachable("Passed first decl twice, invalid redecl chain!");
           Current = nullptr;
----------------
This looks like it should probably be: `assert(!PassedFirst && "Passed first decl twice, invalid redecl chain!");` rather than an `if` statement with recovery mechanisms.


================
Comment at: clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h:601
     assert(isa<ObjCMethodDecl>(FD));
-    assert(false && "Getting the type of ObjCMethod is not supported yet");
+    llvm_unreachable("Getting the type of ObjCMethod is not supported yet");
 
----------------
This looks like it possibly should have been an error reported to the user? If not, this is a wonderful example of what I mean by aspirationally unreachable code. We aspire to not getting here -- but we can get here just the same and there is not UB as a result (we return a valid object, UB might happen elsewhere based on invalid assumptions of what is returned).


================
Comment at: clang/lib/AST/ASTImporter.cpp:9976
   else {
-    assert(0 && "CompleteDecl called on a Decl that can't be completed");
+    llvm_unreachable("CompleteDecl called on a Decl that can't be completed");
   }
----------------
According to our style guide, this probably should have been `assert(isa<ObjCInterfaceDecl, ObjCProtocolDecl, TagDecl>(D) && "CompleteDecl called on a Decl that can't be completed");` but IMO that's worse than using `assert(0)` because it's less maintainable (any time you add a new else if chain you have to also update the assert). I think `llvm_unreachable` is wrong to use here.


================
Comment at: clang/lib/AST/ExprConstant.cpp:7561-7564
     if (Source == E) {
-      assert(0 && "OpaqueValueExpr recursively refers to itself");
+      llvm_unreachable("OpaqueValueExpr recursively refers to itself");
       return Error(E);
     }
----------------
Probably should be `assert(Source != E && "OpaqueValueExpr recursively refers to itself");`


================
Comment at: clang/lib/AST/Interp/ByteCodeExprGen.cpp:136-137
 
   default:
-    assert(false && "Cast not implemented");
+    llvm_unreachable("Cast not implemented");
   }
----------------
I think this can just be removed, right? There's another unreachable for falling out of the `switch` that seems to be covering the same situation.


================
Comment at: clang/lib/AST/Interp/ByteCodeExprGen.cpp:598
       } else {
-        assert(false && "Unhandled type in array initializer initlist");
+        llvm_unreachable("Unhandled type in array initializer initlist");
       }
----------------
The rest of the ones here are somewhat interesting in that the interpreter is an experiment under active development and is known to be incomplete. In all of these cases, I think the switch to unreachable is flat-out wrong -- these asserts serve explicitly to find unimplemented cases when we hit them.


================
Comment at: clang/lib/Basic/SourceManager.cpp:62-65
   if (Buffer == nullptr) {
-    assert(0 && "Buffer should never be null");
+    llvm_unreachable("Buffer should never be null");
     return llvm::MemoryBuffer::MemoryBuffer_Malloc;
   }
----------------
`assert(Buffer != nullptr && "Buffer should never be null");`

but that said, this one might be an optimization hint that suggests we should be using `__builtin_assume(Buffer != nullptr)`, I'm not certain.


================
Comment at: clang/lib/Basic/SourceManager.cpp:863-866
   if (SLocOffset < CurrentLoadedOffset) {
-    assert(0 && "Invalid SLocOffset or bad function choice");
+    llvm_unreachable("Invalid SLocOffset or bad function choice");
     return FileID();
   }
----------------
`assert(SLocOffset >= CurrentLoadedOffset && "Invalid SLocOffset or bad function choice");`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D135551/new/

https://reviews.llvm.org/D135551



More information about the cfe-commits mailing list