This is an archive of the discontinued LLVM Phabricator instance.

[ASTImporter] Typedef import brings in the complete type
ClosedPublic

Authored by martong on Oct 25 2018, 5:45 AM.

Details

Summary

When we already have an incomplete underlying type of a typedef in the
"To" context, and the "From" context has the same typedef, but the
underlying type is complete, then the imported type should be complete.

Fixes an assertion in Xerces:
Assertion `DD && "queried property of class with no definition"' failed.
This assert is happening in the analyzer engine, because that attempts
to query an underlying type of a typedef, which happens to be
incomplete.

Diff Detail

Event Timeline

martong created this revision.Oct 25 2018, 5:45 AM

Hi Gabor,
Thank you for the patch. I looks reasonable but I have some questions.

lib/AST/ASTImporter.cpp
2309

We can move these two vars upper and use them in the condition.

2313

This condition omits the case where both types are complete. Should we use FromUT->isIncompleteType == FoundUT-isIncompleteType() instead?

martong updated this revision to Diff 171478.Oct 29 2018, 3:45 AM
martong marked 2 inline comments as done.
  • Move FromUT upper, add break
martong added inline comments.Nov 22 2018, 8:59 AM
lib/AST/ASTImporter.cpp
2309

Good point, changed it.

2313

I think you wanted to say "both types are INcomplete".
The condition indeed omits the case where both types are incomplete and that is a potential bug, thanks for finding it!
I think the best way to handle this case is to do the something similar what is done in case of functions, i.e break the for loop once we know that the found and the to be imported Decl are structurally equivalent.
So, I added a break to do that and to be similar what we do in case of functions.

This is what we have with functions:

if (IsStructuralMatch(D, FoundFunction)) {
  const FunctionDecl *Definition = nullptr;
  if (D->doesThisDeclarationHaveABody() &&
      FoundFunction->hasBody(Definition)) {
    return Importer.MapImported(
        D, const_cast<FunctionDecl *>(Definition));
  }
  FoundByLookup = FoundFunction;
  break;
}

I'd like to have something similar with typedefs. The goal is to have only one TypedefNameDecl with a complete type. A TypedefNameDecl with a complete type is analogous to a FunctionDecl with a definition, we want to have only one in the "To" context.
A TypedefNameDecl is also redeclarable, so on a long term we should connect a new Decl to the found previous one.

a_sidorin accepted this revision.Nov 25 2018, 11:21 AM

Thanks!

This revision is now accepted and ready to land.Nov 25 2018, 11:21 AM
This revision was automatically updated to reflect the committed changes.

Apologies for my late comments.

cfe/trunk/unittests/AST/ASTImporterTest.cpp
3780 ↗(On Diff #175422)

As far as I can tell S<int> is complete in Code, am I missing something?

3792 ↗(On Diff #175422)

Given my previous comment, I don't see why the addition code would change where S<int> was complete or not.

martong marked an inline comment as done.Nov 28 2018, 1:52 PM
martong added inline comments.
cfe/trunk/unittests/AST/ASTImporterTest.cpp
3780 ↗(On Diff #175422)

No, S<int> is incomplete at this point. The specialization is indeed created, but its type is incomplete. There is no need for the type to be complete because there is not any operation which would require the size of the type.
This is how the AST looks like:

|-ClassTemplateDecl 0x1f5b378 <output.cc:2:7, line:5:7> line:3:14 S
| |-TemplateTypeParmDecl 0x1f5b228 <line:2:17, col:26> col:26 typename depth 0 index 0 T
| |-CXXRecordDecl 0x1f5b2e0 <line:3:7, line:5:7> line:3:14 struct S definition
| | |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveConstructor exists simple trivial needs_implicit
| | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveAssignment exists simple trivial needs_implicit
| | | `-Destructor simple irrelevant trivial needs_implicit
| | |-CXXRecordDecl 0x1f5b5e0 <col:7, col:14> col:14 implicit struct S
| | `-CXXMethodDecl 0x1f5b700 <line:4:9, col:18> col:14 foo 'void ()'
| `-ClassTemplateSpecializationDecl 0x1f5b7d8 <line:2:7, line:5:7> line:3:14 struct S
|   `-TemplateArgument type 'int'
`-TypeAliasDecl 0x1f5b988 <line:6:7, col:22> col:13 U 'S<int>':'S<int>'
  `-TemplateSpecializationType 0x1f5b8e0 'S<int>' sugar S
    |-TemplateArgument type 'int'
    `-RecordType 0x1f5b8c0 'S<int>'
      `-ClassTemplateSpecialization 0x1f5b7d8 'S'

However, below by using the member access operator (->) we require the completion of the type. Alas, the specialization has a definition there, not just a fwd declaration.
The AST of FromTU looks like this:

|-ClassTemplateDecl 0x2033c28 <input.cc:2:7, line:5:7> line:3:14 S
| |-TemplateTypeParmDecl 0x2033ad8 <line:2:17, col:26> col:26 typename depth 0 index 0 T
| |-CXXRecordDecl 0x2033b90 <line:3:7, line:5:7> line:3:14 struct S definition
| | |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveConstructor exists simple trivial needs_implicit
| | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveAssignment exists simple trivial needs_implicit
| | | `-Destructor simple irrelevant trivial needs_implicit
| | |-CXXRecordDecl 0x2033e90 <col:7, col:14> col:14 implicit struct S
| | `-CXXMethodDecl 0x2033fb0 <line:4:9, col:18> col:14 foo 'void ()'
| `-ClassTemplateSpecializationDecl 0x2034088 <line:2:7, line:5:7> line:3:14 struct S definition
|   |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
|   | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
|   | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
|   | |-MoveConstructor exists simple trivial needs_implicit
|   | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
|   | |-MoveAssignment exists simple trivial needs_implicit
|   | `-Destructor simple irrelevant trivial needs_implicit
|   |-TemplateArgument type 'int'
|   |-CXXRecordDecl 0x2034598 prev 0x2034088 <col:7, col:14> col:14 implicit struct S
|   `-CXXMethodDecl 0x2034630 <line:4:9, col:18> col:14 used foo 'void ()'
|-TypeAliasDecl 0x2034238 <line:6:7, col:22> col:13 referenced U 'S<int>':'S<int>'
| `-TemplateSpecializationType 0x2034190 'S<int>' sugar S
|   |-TemplateArgument type 'int'
|   `-RecordType 0x2034170 'S<int>'
|     `-ClassTemplateSpecialization 0x2034088 'S'
`-FunctionDecl 0x2034420 <line:8:7, line:10:7> line:8:12 foo 'void (U *)'
  |-ParmVarDecl 0x2034318 <col:16, col:19> col:19 used u 'U *'
  `-CompoundStmt 0x2061a38 <col:22, line:10:7>
    `-CXXMemberCallExpr 0x2061a10 <line:9:9, col:16> 'void'
      `-MemberExpr 0x2034718 <col:9, col:12> '<bound member function type>' ->foo 0x2034630
        `-ImplicitCastExpr 0x2034700 <col:9> 'U *' <LValueToRValue>
          `-DeclRefExpr 0x2034508 <col:9> 'U *' lvalue ParmVar 0x2034318 'u' 'U *'