Assertion failure in `clang::CFG::buildCFG()` when processing function with backward `goto`

Hi,

Apologies if this is the wrong place for this question.

I’m working on a program that uses libclang (13.0.0 on Linux) to
generate an AST and CFG. It does the latter by calling
clang::CFG::buildCFG() and this leads to an assertion failure in
LocalScope::const_iterator::distance() when finding the distance
between a goto and its target in the CFG.

Here is the source file I’m running it on:

struct bloop {int a; char *b; };

int foo(int a, int c, int d) {
    if (a) {
        struct bloop t1;
    lbl1:
        t1.a = 42;
    }

    if (d)
        goto lbl1;

    return c;
}

CFGBuilder::prependAutomaticObjLifetimeWithTerminator() calls
LocalScope::const_iterator::distance() on the jump and its target
but it only searches forward. The jump is backward so it never finds
it, triggering the assertion failure:

Assertion `F != const_iterator() && “L iterator is not reachable from F iterator.”’ failed.

This looks like a bug in libclang to me, but I’m still pretty new to
the codebase and API so it could well be due to something on my end.

Has anyone seen anything like this elsewhere? Is there perhaps
something I need to check before assuming it’s a bug?

Thanks!

[Edited to fix an error in the code snippet].

Could you please double-check if the code is right? I cannot see the declaration of c anywhere. Anyway, it definitely should not crash.
You could also check if it crashes on Compiler Explorer (CE) by using the clang with “assertions”.
Link the CE link if you managed to reproduce it there.

D’oh! I accidentally removed it while shortening the code. It’s fixed now.

Also: thanks for the pointer to CE. I hadn’t thought of that.

I’ve tried it with CE and the assertions build and it works fine. This is not really unexpected to me though since (I think) the clang compiler doesn’t go through clang::CFG. clang-tidy (which does, AFAICT) also succeeds on the sources, though.

I think it would be great to have the flags by which the CFG was constructed.
You can probably find those somewhere around AnalysisDeclContextManager::getCFGBuildOptions() calls.
Without an end-to-end reproducer, we probably cannot help you.

I may be able to do better than that. I’ve managed to recreate the problem in a small C++ program that (mostly) uses the libs in the same way the main project does. I’ve only tested it against my specific build of Clang 13 though so I want to confirm it’s still an issue against a more recent version before I post it. I should have it up early next week, though.

As mentioned above, I’ve managed to boil it down to a small example that recreates the failure on the current main branch as of a few days ago. The clang libs were built with Clang 8.0.0 on Ubuntu 18.04.

I’m reasonably convinced now that this is due to a bug in Clang, but it’s always possible I’m doing something wrong.

Anyway, thanks for any help or insight anyone can provide.

simple2.c (187 Bytes)
clang_error.cpp (4.1 KB)
Makefile.txt (1.6 KB)

Has anyone had a chance to look at this? Should this be reported as a bug?

I’ll have a look at this (in a week or so :smiley: ). Thanks for reporting.

I managed to crash the Clang Static Analyzer in the same way: https://godbolt.org/z/5vKsMEevn by setting cfg-lifetime=true and also disabling cfg-implicit-dtors - because they cannot be enabled at the same time:
--analyze -Xclang -analyzer-checker=core -Xclang -analyzer-config -Xclang cfg-lifetime=true -Xclang -analyzer-config -Xclang cfg-implicit-dtors=false

void sideffect();
void foo() {
  int jumped = 0;
  {
    int t1;
  mylabel:
    sideffect();
  }
  if (jumped == 0) {
    jumped = 1;
    goto mylabel;
  }
}

I’m not sure how mature the AddLifetime option is, but I’d recommend setting that to false to workaround the crash.
For me, the crash heavily correlates to setting AddLifetime to true.

Maybe @Xazax-hun knows something about this. I don’t know anyone who would use that option, hence I cannot ping anyone for fixing it.