This is an archive of the discontinued LLVM Phabricator instance.

Allow dllimport non-type template arguments in C++17
ClosedPublic

Authored by rnk on Feb 14 2018, 2:54 PM.

Event Timeline

rnk created this revision.Feb 14 2018, 2:54 PM
rsmith added inline comments.Feb 14 2018, 4:38 PM
clang/lib/AST/ExprConstant.cpp
10377–10393

Neither EM_ConstantFold nor EM_IgnoreSideEffects seem like the right evaluation modes to be using to evaluate a non-type template argument. I would expect EM_ConstantExpression to be used for both cases.

rsmith added inline comments.Feb 14 2018, 4:42 PM
clang/lib/AST/ExprConstant.cpp
10377–10393

Oh, I see, we're allowing side-effects here and then issuing a constant-folding warning elsewhere if there actually were any. *sigh*

I would expect we can get away with not doing that for template arguments. It'd be worth testing whether GCC actually allows constant folding there, or requires a real constant expression.

clang/lib/Sema/SemaOverload.cpp
5411–5412

Don't we get here for CCEKinds other than the non-type template argument case?

rnk added inline comments.Feb 15 2018, 10:59 AM
clang/lib/AST/ExprConstant.cpp
10377–10393

"Allowing side effects" here looks like it really means allowing non-constant subexpressions when those subexpressions would be discarded:

template <int *N> struct Foo {};
int x;
Foo<(x, &x)> f;

constexpr bool is_true = true;

int *fn() {
  if constexpr (((void)x, &x) != nullptr) {
    return &x;
  } else {
    return nullptr;
  }

  if constexpr (is_true || x == 2) {
    return &x;
  } else {
    return nullptr;
  }
}

Clang accepts this, GCC rejects a few. I'm guessing we want that kind of behavior for if constexpr conditions, but maybe not for non-type template parameters.

So, it sounds like we should check the CCE kind in SemaOverload, and use a more restrictive constant expression evaluation mode in that case. Perhaps even a new one for non-type template arguments.

clang/lib/Sema/SemaOverload.cpp
5411–5412

You're right, but I wasn't able to construct a test case where we would call CheckConvertedConstantExpression and we would reject it today because it is dllimported. This was my best idea, using if constexpr:

struct __declspec(dllimport) Foo {
  static constexpr bool imported_foo = true;
};
const bool some_bool = false;
const bool *f() {
  if constexpr (Foo::imported_foo) {
    return &Foo::imported_foo;
  } else {
    return &some_bool;
  }
}

Any other ideas on how to get more coverage of this path through CheckConvertedConstantExpression?

rnk updated this revision to Diff 134510.Feb 15 2018, 2:25 PM
  • revise as discussed
rsmith added inline comments.Feb 15 2018, 6:45 PM
clang/include/clang/AST/Expr.h
103

If we end up moving this out of Sema into the clang namespace, it needs a longer name than this.

661–663

Seems strange to pass a converted constant expression kind to an 'evaluate as core constant expression' function. And it seems like we don't need the kind here, just an "evaluating for emission w/relocations" vs "evaluating for mangling" enum.

Also, we need to evaluate non-type template arguments as constant expressions, not just as core constant expressions, which the implementation does, but the name and comment here don't reflect. (The difference is that you can't result in a pointer/reference to a temporary or automatic / thread storage duration entity.)

rnk edited the summary of this revision. (Show Details)May 9 2018, 4:05 PM
rnk updated this revision to Diff 146025.May 9 2018, 4:06 PM
  • don't expose CCEK, use a bool
rnk added inline comments.May 9 2018, 4:09 PM
clang/include/clang/AST/Expr.h
661–663

So... what are these things? Converted constant expressions? What are we evaluating them as? I guess they're not rvalues or lvalues.

rsmith added inline comments.May 9 2018, 4:34 PM
clang/include/clang/AST/Expr.h
661–663

I think this should just be called EvaluateAsConstantExpr, and you should drop all the "core"s throughout. The difference between a constant expression and a core constant expression is that a core constant expression allows the evaluated value to refer to entities with automatic storage duration etc, which is exactly the case you want to disallow here :)

I also don't think you need the ParamType, and can instead just look at whether the expression itself is an rvalue.

663

I would prefer an enum { EvaluateForCodeGen, EvaluateForMangling } over a bool IsTemplateArg; I would not be surprised if we find other cases where we want to evaluate an expression for mangling purposes only.

clang/lib/AST/ExprConstant.cpp
707

No "core" here.

709

I don't think we need this. See below.

10384–10385

Instead of a new EvaluationMode which is actually not used by evaluation, we could pass a parameter into CheckLValueConstantExpression to indicate if we're in the "for-mangling" mode.

10397

If you switch from using ParamType->isReferenceType() to asking the expression for its value category, you don't need the implicit-lvalue-to-rvalue-conversion semantics of EvaluateAsRValue, and can just call ::Evaluate here followed by a call to CheckConstantExpression (passing the latter the IsTemplateArg flag).

In fact, you don't need the separate case for lvalues above, either, since ::Evaluate does the right thing for an lvalue expression by itself.

clang/lib/Sema/SemaOverload.cpp
5411–5412

All the other forms of CCEKind also set RequireInt to true, and so any case your check catches would also be caught on the line below.

rnk updated this revision to Diff 146039.May 9 2018, 5:29 PM
  • getting closer
rnk added inline comments.May 9 2018, 5:30 PM
clang/include/clang/AST/Expr.h
662

I expect we could come up with a better name, but is this closer to what you had in mind?

rsmith accepted this revision.May 9 2018, 6:08 PM
rsmith added inline comments.
clang/lib/AST/ExprConstant.cpp
1881–1912

Usage should be passed into these recursive calls to CheckConstantExpression, although that's NFC for now, until/unless we start allowing class types as template parameters.

This revision is now accepted and ready to land.May 9 2018, 6:08 PM
This revision was automatically updated to reflect the committed changes.
rnk added a comment.May 10 2018, 1:27 PM

Thanks for the guidance!

clang/lib/AST/ExprConstant.cpp
1881–1912

Makes sense, done.