This is an archive of the discontinued LLVM Phabricator instance.

[Sema] Preserve invalid CXXCtorInitializers using RecoveryExpr in initializer
ClosedPublic

Authored by sammccall on Apr 30 2021, 8:22 AM.

Details

Summary

Before this patch, CXXCtorInitializers that don't typecheck get discarded in
most cases. In particular:

  • typos that can't be corrected don't turn into RecoveryExpr. The full expr disappears instead, and without an init expr we discard the node.
  • initializers that fail initialization (e.g. constructor overload resolution) are discarded too.

This patch addresses both these issues (a bit clunkily and repetitively, for
member/base/delegating initializers)

It does not preserve any AST nodes when the member/base can't be resolved or
other problems of that nature. That breaks invariants of CXXCtorInitializer
itself, and we don't have a "weak" RecoveryCtorInitializer like we do for Expr.

I believe the changes to diagnostics in existing tests are improvements.
(We're able to do some analysis on the non-broken parts of the initializer)

Diff Detail

Event Timeline

sammccall requested review of this revision.Apr 30 2021, 8:22 AM
sammccall created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptApr 30 2021, 8:22 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
hokein added a comment.May 3 2021, 1:32 PM

thanks, this code looks pretty solid, just a question.

clang/lib/Sema/SemaDeclCXX.cpp
4491–4496

it is unclear to me why we need a isDependentContext() here? looks like this would special-case RecoveryExpr, the following dependent code-path would not be executed for RecoveryExpr case?

sammccall updated this revision to Diff 364846.Aug 6 2021, 11:10 AM

Rebase and add clarifying comment

Doh, I forgot this never landed :-(

clang/lib/Sema/SemaDeclCXX.cpp
4491–4496

Right, if the init expr is type-dependent because it contains errors, but we're not actually in a template, we don't want to take the dependent path.

The reason is that the dependent path produces an "as-written" form of the CXXCtorInitializer, which has weaker invariants, because we know we're not going to examine it much until template instantiation time.
e.g. an initializer that is actually delegating is stored as a base initializer instead. (Thus avoiding the impossible requirement to check whether two dependent types are the same).
Later on in Sema::SetCtorInitializers, we check Constructor->isDependentContext() to decide whether to propagate the "as-written" initializers without checking, but of course this is false. So then we go and shuffle the initializers into the correct order, build default initializers for the uninitialized members etc, and the delegating initializer is just... lost in the shuffle. We think it's a base init, so it's put in a map of type->init, and then it never gets consumed from there because the type is not actually a base class.

I'll try to add a comment that hints at this, without getting into the concrete details...

hokein accepted this revision.Aug 9 2021, 12:51 AM
hokein added inline comments.
clang/lib/Sema/SemaDeclCXX.cpp
4491–4496

I see, fair enough, thanks for the classifications.

clang/test/AST/ast-dump-recovery.cpp
305

For completeness, I would add case 'x(undefine())', even though it already works without this patch.

This revision is now accepted and ready to land.Aug 9 2021, 12:51 AM