This is an archive of the discontinued LLVM Phabricator instance.

[clang][Interp] Handle undefined functions better
ClosedPublic

Authored by tbaeder on Oct 28 2022, 3:07 AM.

Details

Summary

Make sure we add body-less functions to our program as well, and add the actual body later. We then need to compile functions again later once we have the definition with a body.

We do a bunch of work in ByteCodeEmitter::compileFunc() to prepare the values passed to Program::createFunction() even if we don't need them now, that's a possible optimization for a later commit.

Diff Detail

Event Timeline

tbaeder created this revision.Oct 28 2022, 3:07 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 28 2022, 3:07 AM
tbaeder requested review of this revision.Oct 28 2022, 3:07 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 28 2022, 3:07 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
aaron.ballman added inline comments.Nov 1 2022, 7:24 AM
clang/lib/AST/Interp/ByteCodeEmitter.cpp
24–27

hasBody() returns true if any body in the redeclaration chain already has a body, so I'm surprised to see this change -- I don't know if we reset willHaveBody() when we give the function a body, but the logic here seems wrong to me.

clang/lib/AST/Interp/Program.cpp
207–208

Same question here about canonical decl.

clang/lib/AST/Interp/Program.h
96

Are you trying to get the FunctionDecl which represents the function definition, or do you really mean you want the first declaration in the redecl chain?

tbaeder added inline comments.Nov 1 2022, 10:25 PM
clang/lib/AST/Interp/ByteCodeEmitter.cpp
24–27

I was confused by your question, but it's just about hasBody() vs !hasBody(), right? The old code used a negation and of course that way it makes sense, at least the way I read it. Yes I forgot the negation. :)

clang/lib/AST/Interp/Program.h
96

Whether or not the function has a body is irrelevant here, I think. The Def here is just used for caching. So the first one makes sense, doesn't it?

aaron.ballman added inline comments.Nov 2 2022, 11:41 AM
clang/lib/AST/Interp/ByteCodeEmitter.cpp
24–27

Heh, you managed to decipher my word salad correctly -- I was tying to figure out if you dropped the ! for a reason. :-D

tbaeder updated this revision to Diff 472853.Nov 3 2022, 12:01 AM
aaron.ballman accepted this revision.Nov 3 2022, 11:46 AM

LGTM!

clang/lib/AST/Interp/ByteCodeEmitter.cpp
23–27

.... negating the Boolean calculation and applying deMorgan's law did not make that code more clear, did it (assuming I did everything right)? If you agree, then I'm fine with the more complicated form and letting the optimizer make it faster.

clang/lib/AST/Interp/Program.h
96

Hmmm, this may actually be fine. The situation I had in mind is where a redeclaration adds more semantic information to the function declaration that may disqualify it from being a constexpr function due to UB. For example, declare a function with no attribute, then redeclare the function with an attribute accepting an expression argument (like __attribute__((enable_if)) or __attribute__((diagnose_if)) and put the UB in the expression argument.

But I can't devise a test case that hits the problem because any *use* of the constexpr function causes us to claim it's non-constexpr if the function is declared but not defined.

This revision is now accepted and ready to land.Nov 3 2022, 11:46 AM
tbaeder added inline comments.Nov 4 2022, 12:28 AM
clang/lib/AST/Interp/ByteCodeEmitter.cpp
23–27

I can see that going either way. I think your version is a more confusing though because the two body conditions are coupled with the isDefined condition. E.g. HasBody is true if isDefined() && !willHaveBody(), which doesn't make sense to me I think.

I think I just read this code too many times now. How does being defined even relate to the function having a body? Should that code just be HasBody = hasBody() || willHaveBody()?

aaron.ballman added inline comments.Nov 4 2022, 11:43 AM
clang/lib/AST/Interp/ByteCodeEmitter.cpp
23–27

I can see that going either way. I think your version is a more confusing though because the two body conditions are coupled with the isDefined condition. E.g. HasBody is true if isDefined() && !willHaveBody(), which doesn't make sense to me I think.

I'm glad we both are confused about the same thing!

I think I just read this code too many times now. How does being defined even relate to the function having a body? Should that code just be HasBody = hasBody() || willHaveBody()?

I've not dug back in time to see how we got to that predicate in the first place, but my *hunch* is that this may have to do with implicit function definitions for things like special member functions. That's a case where you can have a function definition without a body. e.g.,

struct S {
  S();
};

S::S() = default;
tbaeder added inline comments.Nov 5 2022, 1:00 AM
clang/lib/AST/Interp/ByteCodeEmitter.cpp
23–27

Well, any sort of digging usually just gets you to the one commit that introduced all of the new constant interpreter :P

willHaveBody() isn't even relevant here, is it?
I think simply

bool hasBody = FuncDecl->isDefined() || FuncDecl->hasBody();

makes the most sense.

aaron.ballman added inline comments.Nov 8 2022, 9:30 AM
clang/lib/AST/Interp/ByteCodeEmitter.cpp
23–27

I think it's one or the other?

isDefined searches for a function that's been defined by looking for something in the redecl chain that: is deleted as written, is defaulted, has a body, will have a body, has a body that's skipped, or has an attribute that causes the declaration to be a definition.

By contrast, hasBody searches the redecl chain looking for a declaration that has not been defaulted and has a body or is template late parsed.

So I *think* what you want is FuncDecl->hasBody(FuncDecl) to do the check and get the specific declaration with the body.

tbaeder marked an inline comment as done.Nov 11 2022, 3:54 AM
tbaeder added inline comments.
clang/lib/AST/Interp/ByteCodeEmitter.cpp
23–27

FuncDecl->hasBody(FuncDecl) sounds like it makes sense. None of the variations break any of the existing tests, so I will trust you. :) Thanks

This revision was landed with ongoing or failed builds.Nov 30 2022, 1:22 AM
This revision was automatically updated to reflect the committed changes.
tbaeder marked an inline comment as done.