This is an archive of the discontinued LLVM Phabricator instance.

consteval if does not form a discarded statement
ClosedPublic

Authored by aaron.ballman on Oct 19 2021, 12:06 PM.

Details

Summary

When we added support for if consteval, we accidentally formed a discarded statement evaluation context for the branch-not-taken. However, a discarded statement is a property of an if constexpr statement, not an if consteval statement (https://eel.is/c++draft/stmt.if#2.sentence-2). This turned out to cause issues when deducing the return type from a function with a consteval if statement -- we wouldn't consider the branch-not-taken when deducing the return type.

This fixes PR52206.

Diff Detail

Event Timeline

aaron.ballman requested review of this revision.Oct 19 2021, 12:06 PM
aaron.ballman created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptOct 19 2021, 12:06 PM
cor3ntin accepted this revision.Oct 19 2021, 1:59 PM

Oups, Sorry I missed that in my earlier PR.
This fix LGTM.
Thanks Aaron

This revision is now accepted and ready to land.Oct 19 2021, 1:59 PM
rsmith added inline comments.Oct 19 2021, 2:05 PM
clang/lib/Parse/ParseStmt.cpp
1445–1450

It looks to me like this is still incorrectly updating the context in some cases. We shouldn't enter an immediate function context if we're already in a discarded statement context. For example, Clang currently rejects this valid code:

auto f() {
    if constexpr (false) {
        if consteval {
            return 0;
        }
    }
    return 0.0;
}

... and it looks like it still will after this change. I think we should not enter a new context here if the existing context is a discarded statement context.

rsmith accepted this revision.Oct 19 2021, 2:10 PM
rsmith added inline comments.
clang/lib/Parse/ParseStmt.cpp
1445–1450

Hm, not updating the context also seems wrong; then we'd reject things like this:

consteval int *make() { return new int; }
auto f() {
  if constexpr (false) {
    if consteval {
      // Immediate function context, so call to `make()` is valid.
      // Discarded statement context, so `return 0;` is valid too.
      delete make();
      return 0;
    }
  }
  return 0.0;
}

Perhaps we need to track whether we're in a discarded statement and whether we're in an immediate function context separately rather than treating them as mutually exclusive. I think it makes sense to go ahead with this patch as-is and deal with the bug that we overwrite a discarded statement context with an immediate function context separately.

cor3ntin added inline comments.Oct 19 2021, 2:15 PM
clang/lib/Parse/ParseStmt.cpp
1445–1450

Hum, I missed these scenarios entirely.
But I agree with you, not entering an immediate context would be equally bad.
I think the solution in both cases if to test whether any of the parent context is discarded or immediate, instead of just looking at the current context

aaron.ballman closed this revision.Oct 20 2021, 4:28 AM

I've commit in ab2ca8496d54573de1c8bec204009567ba2b4086 and filed https://bugs.llvm.org/show_bug.cgi?id=52231 as the bug for the remaining bits, thanks for the reviews and discussion!

clang/lib/Parse/ParseStmt.cpp
1445–1450

Perhaps we need to track whether we're in a discarded statement and whether we're in an immediate function context separately rather than treating them as mutually exclusive. I think it makes sense to go ahead with this patch as-is and deal with the bug that we overwrite a discarded statement context with an immediate function context separately.

I think you may be correct here. I've added your example to the test case with a FIXME comment so it's clear we've still got some work left to do here. Thanks!