[lldb][tests][gmodules] Test for expression evaluator crash for types referencing the same template

The problem here is that the ASTImporter adds
the template class member FieldDecl to
the DeclContext twice. This happens because
we don't construct a `LookupPtr` for decls
that originate from modules and thus the
ASTImporter never realizes that the FieldDecl
has already been imported. These duplicate
decls then break the assumption of the LayoutBuilder
which expects only a single member decl to
exist.

The test will be fixed by a follow-up revision
and is thus skipped for now.

Differential Revision: https://reviews.llvm.org/D133944
This commit is contained in:
Michael Buch 2022-09-14 22:36:07 -04:00
parent 4475470529
commit 3004a759bc
10 changed files with 139 additions and 0 deletions

View File

@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp module1.cpp module2.cpp base_module.cpp
include Makefile.rules

View File

@ -0,0 +1,72 @@
"""
Tests the scenario where we evaluate expressions
of two types in different modules that reference
a class template instantiated with the same
template argument.
Note that,
1. Since the decls originate from modules, LLDB
marks them as such and Clang doesn't create
a LookupPtr map on the corresponding DeclContext.
This prevents regular DeclContext::lookup from
succeeding.
2. Because we reference the same class template
from two different modules we get a redeclaration
chain for the class's ClassTemplateSpecializationDecl.
The importer will import all FieldDecls into the
same DeclContext on the redeclaration chain. If
we don't do the bookkeeping correctly we end up
with duplicate decls on the same DeclContext leading
to crashes down the line.
"""
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class TestTemplateWithSameArg(TestBase):
def setUp(self):
TestBase.setUp(self)
self.build()
self.main_source_file = lldb.SBFileSpec("main.cpp")
@add_test_categories(["gmodules"])
@skipIf(bugnumber='rdar://96581048')
def test_same_template_arg(self):
lldbutil.run_to_source_breakpoint(self, "Break here", self.main_source_file)
self.expect_expr("FromMod1", result_type="ClassInMod1", result_children=[
ValueCheck(name="VecInMod1", children=[
ValueCheck(name="Member", value="137")
])
])
self.expect_expr("FromMod2", result_type="ClassInMod2", result_children=[
ValueCheck(name="VecInMod2", children=[
ValueCheck(name="Member", value="42")
])
])
@add_test_categories(["gmodules"])
@skipIf(bugnumber='rdar://96581048')
def test_duplicate_decls(self):
lldbutil.run_to_source_breakpoint(self, "Break here", self.main_source_file)
self.expect_expr("(intptr_t)&FromMod1 + (intptr_t)&FromMod2")
# Make sure we only have a single 'Member' decl on the AST
self.filecheck("target module dump ast", __file__)
# CHECK: ClassTemplateSpecializationDecl {{.*}} imported in Module2 struct ClassInMod3 definition
# CHECK-NEXT: |-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial
# CHECK-NEXT: | |-DefaultConstructor exists trivial needs_implicit
# CHECK-NEXT: | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
# CHECK-NEXT: | |-MoveConstructor exists simple trivial needs_implicit
# CHECK-NEXT: | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
# CHECK-NEXT: | |-MoveAssignment exists simple trivial needs_implicit
# CHECK-NEXT: | `-Destructor simple irrelevant trivial needs_implicit
# CHECK-NEXT: |-TemplateArgument type 'int'
# CHECK-NEXT: | `-BuiltinType {{.*}} 'int'
# CHECK-NEXT: `-FieldDecl {{.*}} imported in Module2 Member 'int'

View File

@ -0,0 +1,3 @@
#include "base_module.h"
namespace crash {} // namespace crash

View File

@ -0,0 +1,6 @@
#ifndef MOD3_H_IN
#define MOD3_H_IN
template <typename SIZE_T> struct ClassInMod3 { int Member = 0; };
#endif // _H_IN

View File

@ -0,0 +1,15 @@
#include "module1.h"
#include "module2.h"
#include <cstdio>
int main() {
ClassInMod1 FromMod1;
ClassInMod2 FromMod2;
FromMod1.VecInMod1.Member = 137;
FromMod2.VecInMod2.Member = 42;
std::puts("Break here");
return 0;
}

View File

@ -0,0 +1,14 @@
module Module1 {
header "module1.h"
export *
}
module Module2 {
header "module2.h"
export *
}
module BaseModule {
header "base_module.h"
export *
}

View File

@ -0,0 +1,3 @@
#include "module1.h"
namespace crash {} // namespace crash

View File

@ -0,0 +1,10 @@
#ifndef MOD1_H_IN
#define MOD1_H_IN
#include "base_module.h"
struct ClassInMod1 {
ClassInMod3<int> VecInMod1;
};
#endif // _H_IN

View File

@ -0,0 +1,3 @@
#include "module2.h"
namespace crash {} // namespace crash

View File

@ -0,0 +1,10 @@
#ifndef MOD2_H_IN
#define MOD2_H_IN
#include "base_module.h"
struct ClassInMod2 {
ClassInMod3<int> VecInMod2;
};
#endif