This is an archive of the discontinued LLVM Phabricator instance.

[MS ABI] Prevent some expressions from evaluating to a constant
Needs ReviewPublic

Authored by ehsan on Jan 22 2016, 7:15 AM.

Details

Reviewers
rnk
rsmith
Summary

In the Microsoft ABI, some expressions containing references to variables
cannot be evaluated as a constant. These are expressions containing a
conditional, logical and/or, or comma operator with an operand that is a
variable name.

This is observable at the ABI level by whether the compiler would emit a
static initializer for such expressions where normally it would emit a
readonly constant data. See PR26210 for more details.

Diff Detail

Event Timeline

ehsan updated this revision to Diff 45686.Jan 22 2016, 7:15 AM
ehsan retitled this revision from to [MS ABI] Prevent some expressions from evaluating to a constant.
ehsan updated this object.
ehsan added a reviewer: rnk.
ehsan added a subscriber: cfe-commits.
ehsan added inline comments.Jan 22 2016, 7:17 AM
lib/AST/ExprConstant.cpp
4113

Oops, sorry, just noticed this whitespace issue, will fix when landing!

majnemer added a subscriber: majnemer.

Adding @rsmith as a reviewer.

rnk edited edge metadata.Jan 22 2016, 10:43 AM

Your code won't catch this test case:

static int x;
extern inline const bool *f() {
  static const bool p = !&x;
  return &p;
}

Getting this exactly right is going to be a challenge. =/

include/clang/Basic/DiagnosticASTKinds.td
151

We should add this test case and decide what to do with it:

static int x;
inline int **f() {
  static constexpr int *p = true ? 0 : &x;
  return &p;
}

Currently, in your patch, this diagnostic will come out. MSVC compiles this to guarded, dynamic initialization, despite the constexpr. ;_;

David thinks we should just give the user the real deal constexpr behavior, even though it's ABI incompatible.

152–153

This isn't an extension, so we should say something else. Maybe:

"in the Microsoft ABI static local variables cannot contain references to variables"
lib/AST/ExprConstant.cpp
9008

This should be limited in scope to only apply to static locals. We should be able to statically initialize globals.

In D16465#333688, @rnk wrote:

Your code won't catch this test case:

static int x;
extern inline const bool *f() {
  static const bool p = !&x;
  return &p;
}

Getting this exactly right is going to be a challenge. =/

Oh you're right. I need to spend some more time comparing our behavior with cl's, it seems...

include/clang/Basic/DiagnosticASTKinds.td
151

One solution to this would be to create a variation of evaluateValue() which activates this new behavior and only use it from CodeGenModule::EmitConstantInit(), so that the behavior of evaluateValue() in this context doesn't change. How does that sound?

By the way, I just realized that CheckPotentialExpressionContainingDeclRefExpr() eats this diagnostic because of the SpeculativeLookForDeclRefExprRAII object. Should I propagate it up from CheckPotentialExpressionContainingDeclRefExpr() to make it user visible? Right now the test case above only emits "constexpr variable 'p' must be initialized by a constant expression" without any notes.

lib/AST/ExprConstant.cpp
9008

Right, my bad!

ehsan updated this revision to Diff 47021.Feb 5 2016, 9:00 AM
ehsan edited edge metadata.

Addressed the review comments.

rnk added a comment.Feb 26 2016, 3:22 PM

Richard really is the constexpr owner so I'd like him to chime in if he can.

include/clang/AST/Decl.h
1092 ↗(On Diff #47021)

Maybe this parameter would be better named IsStaticLocalInit or something.

include/clang/Basic/DiagnosticASTKinds.td
151

One solution to this would be to create a variation of evaluateValue() which activates this new behavior and only use it from CodeGenModule::EmitConstantInit(), so that the behavior of evaluateValue() in this context doesn't change. How does that sound?

Right, only having this behavior change in the awkward static local case is good.

By the way, I just realized that CheckPotentialExpressionContainingDeclRefExpr() eats this diagnostic because of the SpeculativeLookForDeclRefExprRAII object. Should I propagate it up from CheckPotentialExpressionContainingDeclRefExpr() to make it user visible? Right now the test case above only emits "constexpr variable 'p' must be initialized by a constant expression" without any notes.

Yeah, I'd like it if we could get that note out. I'm surprised we can't use the normal recursive traversal to find the DeclRefExprs...

rsmith edited edge metadata.Feb 26 2016, 6:22 PM

We stand no hope of being bug-for-bug compatible with MSVC's constant expression evaluator -- we should assume there will be expressions that we evaluate as a constant and they do not, and vice versa. For reference, we have the same problem between code built by Clang and by GCC, and in some cases for linking together multiple object files compiled by clang (an initializer for a static local in an inline function can be constant in one TU but not another). But those have never been enough of a problem for us to actually fix the root cause.

Why is this set of MSVC behavior the right set to be compatible with? Is there some important code for which this makes us ABI-compatible? I really don't believe the heuristics you've got here are an exact match for the MSVC semantics...

This feels like patching the symptoms of a bigger issue; is there something we can do to address the underlying problem? Perhaps we could always emit a mutable variable for a static local, even if we are able to constant-initialize it.

lib/AST/ExprConstant.cpp
9008

This also needs to apply for implicitly-instantiated static data members of class templates.

test/CodeGenCXX/static-init-msvc.cpp
7

These should all be inline functions. There's no need to apply any special behavior in any of these cases, as only one object file will contain a definition of the static local.