This is an archive of the discontinued LLVM Phabricator instance.

[clang] adapt c++17 behavior for dependent decltype-specifiers
AbandonedPublic

Authored by hokein on Sep 9 2020, 12:23 AM.

Details

Diff Detail

Event Timeline

hokein created this revision.Sep 9 2020, 12:23 AM
Herald added a project: Restricted Project. · View Herald TranscriptSep 9 2020, 12:23 AM
hokein requested review of this revision.Sep 9 2020, 12:23 AM

We should check if the mangling matches GCC (or if it depends on -std), if we can.

clang/lib/AST/Type.cpp
3404–3408

isn't this redudant with toDependence(E->getDependence)?

clang/test/SemaCXX/invalid-template-base-specifier.cpp
16

nice!

Ugh, the hole goes deeper, because the Itanium ABI unambiguously follows the C++11 (instantiation-dependent) behavior.
Definitely need Richard's input here as I'm fairly sure this will break things as-is.

clang/test/CodeGenCXX/mangle-exprs.cpp
215 ↗(On Diff #290653)

Amusingly, both GCC and MSVC reject this valid code.

GCC says: "sorry, unimplemented: mangling noexcept_expr"

MSVC says: "'void test5::a(bool)': no function template defined that matches forced instantiation"
MSVC seems to require the expression to be exactly the same as that in the template declaration in order to compile (even when that's not possible)...

clang/test/CodeGenCXX/mangle.cpp
805

GCC mangles this as _ZN6test341fIiEEvDTstDTplcvT__EcvS1__EEE, so we're breaking compat here :-\

And in fact we're just incorrect. https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling:

If the operand expression of decltype is not instantiation-dependent then the resulting type is encoded directly.

Here, instantiation-dependent *is* explicitly defined as "type-dependent or value-dependent or ...". And therefore these cases must not be "encoded directly", including the motivating case (decltype(N) where N is a typed constant whose initializer is value-dependent).

We could consider not two but *three* types of decltypetypes:

  • decltype(type-dependent), which is sugar for a canonical DependentDeclTypeType
  • decltype(instantiation-but-not-type-dependent), which is still sugar for a concrete canonical type (per C++17) but mangles using the expr (per cxxabi)
  • decltype(non-dependent), which is sugar for a concrete canonical type and mangles directly

This only works if it's OK for mangling to depend on non-canonical type details - I don't know whether this is the case. @rsmith - any hints here?

clang/test/SemaTemplate/dependent-expr.cpp
132

Took me a while to understand this, I think this is an improvement as we now diagnose when we see the template rather than when we see the instantiation.

sammccall added inline comments.Sep 16 2020, 7:04 AM
clang/test/CodeGenCXX/mangle.cpp
805

Hmm, my naive reading of the mangle code matches what I described.

The big comment in ItaniumMangle talks about related issues: https://github.com/llvm/llvm-project/blob/24238f09edb98b0f460aa41139874ae5d4e5cd8d/clang/lib/AST/ItaniumMangle.cpp#L2541-L2572

I don't really understand what's going on here, sorry.

rsmith added inline comments.Sep 16 2020, 4:37 PM
clang/test/CodeGenCXX/mangle-exprs.cpp
218 ↗(On Diff #290653)

As with the other case, the expression here is instantiation-dependent so should be mangled.

clang/test/CodeGenCXX/mangle.cpp
805

Looks like we need the single-step-desugaring loop below the big comment you quoted to stop when it hits a decltype type. That's presumably where we step from the instantiation-dependent-but-not-type-dependent decltype node to its desugared type.

clang/test/SemaTemplate/dependent-expr.cpp
132

Right. We treat statement-expressions in a template as always being value- and instantiation-dependent, in part to match GCC and in part to avoid needing to define value- and instantiation-dependence for all statements and block-scope declarations, so decltype(statement expression) would have been a dependent type before.

rsmith added inline comments.Sep 16 2020, 4:42 PM
clang/test/CodeGenCXX/mangle.cpp
805

Also... the FIXME from that comment will apply here too. Given:

template<typename T> void f(decltype(sizeof(T)), decltype(sizeof(T))) {}
template void f<int>(unsigned long, unsigned long);

we currently (correctly, as far as I can see) use a substitution for the second parameter type, but with the change here, I don't think we will do so any more. We could fix that by deduplicating DecltypeTypes when they're instantiation dependent but not dependent; that's what we do for ConstantArrayType's size expression, for example. If we do that, we'll need to store the actual expression on the DecltypeTypeLoc, since we can't rely on the DecltypeType having the right expression any more (only an expression that's equivalent to it).

hokein updated this revision to Diff 292784.Sep 18 2020, 7:20 AM
hokein marked 2 inline comments as done.

address comments:

  • don't break existing mangling tests
  • deduplicate instantiation-dependent-but-not-type-dependent DecltypeType node (for name mangling substitution)
  • fix the dependence bit of DecltypeType
hokein added inline comments.Sep 18 2020, 7:22 AM
clang/lib/AST/Type.cpp
3404–3408

I think I must have missed to reset the dependent bit from the result of toTypeDependence. toTypeDependence will set the dependent bit if E is value-dependent, but it is wrong for decltype.

clang/test/CodeGenCXX/mangle.cpp
805

Thanks for the hints and explanations.

we currently (correctly, as far as I can see) use a substitution for the second parameter type, but with the change here, I don't think we will do so any more.

thanks for the example, yeah, the behavior was changed with the prior change of this patch.

We could fix that by deduplicating DecltypeTypes when they're instantiation dependent but not dependent; that's what we do for ConstantArrayType's size expression, for example. If we do that, we'll need to store the actual expression on the DecltypeTypeLoc, since we can't rely on the DecltypeType having the right expression any more (only an expression that's equivalent to it).

It took me sometime to understand the whole piece here, but I'm afraid that I still don't fully understand the second part -- any reason why we can't rely on the underlying expression of DecltypeType when doing the deduplication? If we store the actual expression on DecltypeTypeLoc, how do we retrieve it in ASTContext::getDecltypeType(), I didn't find any connection between DecltypeTypeLoc and ASTContext::getDecltypeType.

I updated the patch using the DecltypeTypeLoc to do the duplication, it passes the example you gave above, but it might be wrong.

rsmith added inline comments.Sep 18 2020, 5:58 PM
clang/include/clang/AST/Type.h
4478

We shouldn't store the context here. To provide it to the Profile implementation, use a ContextualFoldingSet.

4499–4508

Can we remove this class now?

clang/lib/AST/ASTContext.cpp
5342–5347

Maybe fold these two paragraphs together

Unlike many "get<Type>" functions, we don't unique DecltypeType nodes unless they are instantiation-dependent. [...]

clang/test/CodeGenCXX/mangle.cpp
805

The problem with the patch as it now stands is that for a case such as:

int x;
template<typename T> void f(decltype(sizeof(T.f(x))));
template<typename T> void g(decltype(sizeof(T.f(x))));

... there is only one reference to x reachable in the AST, on line #2. The expression containing the DeclRefExpr on line #3 was discarded entirely. So (for example) any tooling that's looking to find references to x will not find the one on line #3.

The best way to fix this would be to store the Expr* for the actual expression in the DecltypeTypeLoc information. We'll also need to make sure that TreeTransform uses that Expr* rather than the one from the type when transforming a DecltypeType with source info.

hokein updated this revision to Diff 294143.Sep 24 2020, 1:00 PM
hokein marked 2 inline comments as done.

store the actual expression in DecltypeTypeLoc and address comments.

hokein added inline comments.Sep 24 2020, 1:03 PM
clang/include/clang/AST/Type.h
4499–4508

yeah I think it is possible, but I would rather do it in a follow-up patch -- the current patch seems large enough. Added a FIXME.

clang/test/CodeGenCXX/mangle.cpp
805

Thanks, now I get it.

I have updated this patch to implement what you suggested (storing the actual expression in DecltypeTypeLoc), it touches more files. Please take another look.

friendly ping @rsmith :)

hokein added a comment.Dec 1 2020, 6:27 AM

@rsmith I think it still makes sense to get this patch landed.