[lldb] Don't reject empty or unnamed template parameter packs

We currently reject all templates that have either zero args or that have a
parameter pack without a name. Both cases are actually allowed in C++, so
rejecting them leads to LLDB instead falling back to a dummy 'void' type. This
leads to all kind of errors later on (most notable, variables that have such
template types appear to be missing as we can't have 'void' variables and
inheriting from such a template type will cause Clang to hit some asserts when
finding that the base class is 'void').

This just removes the too strict tests and adds a few tests for this stuff (+
some combinations of these tests with preceding template parameters).

Things that I left for follow-up patches:
* All the possible interactions with template-template arguments which seem like a whole new source of possible bugs.
* Function templates which completely lack sanity checks.
* Variable templates are not implemented.
* Alias templates are not implemented too.
* The rather strange checks that just make sure that the separate list of
  template arg names and values always have the same length. I believe those
  ought to be asserts, but my current plan is to move both those things into a
  single list that can't end up in this inconsistent state.

Reviewed By: JDevlieghere, shafik

Differential Revision: https://reviews.llvm.org/D92425
This commit is contained in:
Raphael Isemann 2020-12-02 10:35:07 +01:00
parent 99eb0f16c3
commit c526426f5c
9 changed files with 305 additions and 4 deletions

View File

@ -1944,8 +1944,6 @@ bool DWARFASTParserClang::ParseTemplateParameterInfos(
break;
}
}
if (template_param_infos.args.empty())
return false;
return template_param_infos.args.size() == template_param_infos.names.size();
}

View File

@ -332,10 +332,11 @@ public:
class TemplateParameterInfos {
public:
bool IsValid() const {
if (args.empty())
// Having a pack name but no packed args doesn't make sense, so mark
// these template parameters as invalid.
if (pack_name && !packed_args)
return false;
return args.size() == names.size() &&
((bool)pack_name == (bool)packed_args) &&
(!packed_args || !packed_args->packed_args);
}

View File

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

View File

@ -0,0 +1,76 @@
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class TestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@no_debug_info_test
def test(self):
self.build()
self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
self.expect_expr("emptyNonTypePack", result_type="NonTypePack<>",
result_children=[ValueCheck(name="a", type="int")])
self.expect_expr("oneElemNonTypePack", result_type="NonTypePack<1>",
result_children=[ValueCheck(name="a", type="int")])
self.expect_expr("twoElemNonTypePack", result_type="NonTypePack<1, 2>",
result_children=[ValueCheck(name="a", type="int")])
self.expect_expr("emptyAnonNonTypePack", result_type="AnonNonTypePack<>",
result_children=[ValueCheck(name="b", type="int")])
self.expect_expr("oneElemAnonNonTypePack", result_type="AnonNonTypePack<1>",
result_children=[ValueCheck(name="b", type="int")])
self.expect_expr("twoElemAnonNonTypePack", result_type="AnonNonTypePack<1, 2>",
result_children=[ValueCheck(name="b", type="int")])
self.expect_expr("emptyAnonNonTypePackAfterTypeParam", result_type="AnonNonTypePackAfterTypeParam<int>",
result_children=[ValueCheck(name="c", type="int")])
self.expect_expr("oneElemAnonNonTypePackAfterTypeParam", result_type="AnonNonTypePackAfterTypeParam<int, 1>",
result_children=[ValueCheck(name="c", type="int")])
self.expect_expr("emptyAnonNonTypePackAfterAnonTypeParam", result_type="AnonNonTypePackAfterAnonTypeParam<int>",
result_children=[ValueCheck(name="d", type="float")])
self.expect_expr("oneElemAnonNonTypePackAfterAnonTypeParam", result_type="AnonNonTypePackAfterAnonTypeParam<int, 1>",
result_children=[ValueCheck(name="d", type="float")])
self.expect_expr("emptyNonTypePackAfterAnonTypeParam", result_type="NonTypePackAfterAnonTypeParam<int>",
result_children=[ValueCheck(name="e", type="int")])
self.expect_expr("oneElemNonTypePackAfterAnonTypeParam", result_type="NonTypePackAfterAnonTypeParam<int, 1>",
result_children=[ValueCheck(name="e", type="int")])
self.expect_expr("emptyNonTypePackAfterTypeParam", result_type="NonTypePackAfterTypeParam<int>",
result_children=[ValueCheck(name="f", type="int")])
self.expect_expr("oneElemNonTypePackAfterTypeParam", result_type="NonTypePackAfterTypeParam<int, 1>",
result_children=[ValueCheck(name="f", type="int")])
self.expect_expr("emptyAnonNonTypePackAfterNonTypeParam", result_type="AnonNonTypePackAfterNonTypeParam<1>",
result_children=[ValueCheck(name="g", type="int")])
self.expect_expr("oneElemAnonNonTypePackAfterNonTypeParam", result_type="AnonNonTypePackAfterNonTypeParam<1, 2>",
result_children=[ValueCheck(name="g", type="int")])
self.expect_expr("emptyAnonNonTypePackAfterAnonNonTypeParam", result_type="AnonNonTypePackAfterAnonNonTypeParam<1>",
result_children=[ValueCheck(name="h", type="float")])
self.expect_expr("oneElemAnonNonTypePackAfterAnonNonTypeParam", result_type="AnonNonTypePackAfterAnonNonTypeParam<1, 2>",
result_children=[ValueCheck(name="h", type="float")])
self.expect_expr("emptyNonTypePackAfterAnonNonTypeParam", result_type="NonTypePackAfterAnonNonTypeParam<1>",
result_children=[ValueCheck(name="i", type="int")])
self.expect_expr("oneElemNonTypePackAfterAnonNonTypeParam", result_type="NonTypePackAfterAnonNonTypeParam<1, 2>",
result_children=[ValueCheck(name="i", type="int")])
self.expect_expr("emptyNonTypePackAfterNonTypeParam", result_type="NonTypePackAfterNonTypeParam<1>",
result_children=[ValueCheck(name="j", type="int")])
self.expect_expr("oneElemNonTypePackAfterNonTypeParam", result_type="NonTypePackAfterNonTypeParam<1, 2>",
result_children=[ValueCheck(name="j", type="int")])

View File

@ -0,0 +1,69 @@
// Named type parameter pack.
template <int... Is>
struct NonTypePack { int a; };
NonTypePack<> emptyNonTypePack;
NonTypePack<1> oneElemNonTypePack;
NonTypePack<1, 2> twoElemNonTypePack;
// Unnamed type parameter pack.
template <int... >
struct AnonNonTypePack { int b; };
AnonNonTypePack<> emptyAnonNonTypePack;
AnonNonTypePack<1> oneElemAnonNonTypePack;
AnonNonTypePack<1, 2> twoElemAnonNonTypePack;
// Test type parameter packs combined with non-pack type template parameters.
// Unnamed non-type parameter pack behind a named type parameter.
template <typename T, int... >
struct AnonNonTypePackAfterTypeParam { T c; };
AnonNonTypePackAfterTypeParam<int> emptyAnonNonTypePackAfterTypeParam;
AnonNonTypePackAfterTypeParam<int, 1> oneElemAnonNonTypePackAfterTypeParam;
// Unnamed non-type parameter pack behind an unnamed type parameter.
template <typename, int... >
struct AnonNonTypePackAfterAnonTypeParam { float d; };
AnonNonTypePackAfterAnonTypeParam<int> emptyAnonNonTypePackAfterAnonTypeParam;
AnonNonTypePackAfterAnonTypeParam<int, 1> oneElemAnonNonTypePackAfterAnonTypeParam;
// Named non-type parameter pack behind an unnamed type parameter.
template <typename, int... Is>
struct NonTypePackAfterAnonTypeParam { int e; };
NonTypePackAfterAnonTypeParam<int> emptyNonTypePackAfterAnonTypeParam;
NonTypePackAfterAnonTypeParam<int, 1> oneElemNonTypePackAfterAnonTypeParam;
// Named non-type parameter pack behind a named type parameter.
template <typename T, int... Is>
struct NonTypePackAfterTypeParam { int f; };
NonTypePackAfterTypeParam<int> emptyNonTypePackAfterTypeParam;
NonTypePackAfterTypeParam<int, 1> oneElemNonTypePackAfterTypeParam;
// Test non-type parameter packs combined with non-pack non-type template parameters.
// Unnamed non-type parameter pack behind a named non-type parameter.
template <int I, int... >
struct AnonNonTypePackAfterNonTypeParam { int g; };
AnonNonTypePackAfterNonTypeParam<1> emptyAnonNonTypePackAfterNonTypeParam;
AnonNonTypePackAfterNonTypeParam<1, 2> oneElemAnonNonTypePackAfterNonTypeParam;
// Unnamed non-type parameter pack behind an unnamed non-type parameter.
template <int, int... >
struct AnonNonTypePackAfterAnonNonTypeParam { float h; };
AnonNonTypePackAfterAnonNonTypeParam<1> emptyAnonNonTypePackAfterAnonNonTypeParam;
AnonNonTypePackAfterAnonNonTypeParam<1, 2> oneElemAnonNonTypePackAfterAnonNonTypeParam;
// Named non-type parameter pack behind an unnamed non-type parameter.
template <int, int... Is>
struct NonTypePackAfterAnonNonTypeParam { int i; };
NonTypePackAfterAnonNonTypeParam<1> emptyNonTypePackAfterAnonNonTypeParam;
NonTypePackAfterAnonNonTypeParam<1, 2> oneElemNonTypePackAfterAnonNonTypeParam;
// Named non-type parameter pack behind an unnamed non-type parameter.
template <int I, int... Is>
struct NonTypePackAfterNonTypeParam { int j; };
NonTypePackAfterNonTypeParam<1> emptyNonTypePackAfterNonTypeParam;
NonTypePackAfterNonTypeParam<1, 2> oneElemNonTypePackAfterNonTypeParam;
int main() {
return 0; // break here
}

View File

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

View File

@ -0,0 +1,76 @@
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class TestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@no_debug_info_test
def test(self):
self.build()
self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
self.expect_expr("emptyTypePack", result_type="TypePack<>",
result_children=[ValueCheck(name="a", type="int")])
self.expect_expr("oneElemTypePack", result_type="TypePack<int>",
result_children=[ValueCheck(name="a", type="int")])
self.expect_expr("twoElemTypePack", result_type="TypePack<int, float>",
result_children=[ValueCheck(name="a", type="int")])
self.expect_expr("emptyAnonTypePack", result_type="AnonTypePack<>",
result_children=[ValueCheck(name="b", type="int")])
self.expect_expr("oneElemAnonTypePack", result_type="AnonTypePack<int>",
result_children=[ValueCheck(name="b", type="int")])
self.expect_expr("twoElemAnonTypePack", result_type="AnonTypePack<int, float>",
result_children=[ValueCheck(name="b", type="int")])
self.expect_expr("emptyAnonTypePackAfterTypeParam", result_type="AnonTypePackAfterTypeParam<int>",
result_children=[ValueCheck(name="c", type="int")])
self.expect_expr("oneElemAnonTypePackAfterTypeParam", result_type="AnonTypePackAfterTypeParam<int, float>",
result_children=[ValueCheck(name="c", type="int")])
self.expect_expr("emptyAnonTypePackAfterAnonTypeParam", result_type="AnonTypePackAfterAnonTypeParam<int>",
result_children=[ValueCheck(name="d", type="float")])
self.expect_expr("oneElemAnonTypePackAfterAnonTypeParam", result_type="AnonTypePackAfterAnonTypeParam<int, float>",
result_children=[ValueCheck(name="d", type="float")])
self.expect_expr("emptyTypePackAfterAnonTypeParam", result_type="TypePackAfterAnonTypeParam<int>",
result_children=[ValueCheck(name="e", type="int")])
self.expect_expr("oneElemTypePackAfterAnonTypeParam", result_type="TypePackAfterAnonTypeParam<int, float>",
result_children=[ValueCheck(name="e", type="int")])
self.expect_expr("emptyTypePackAfterTypeParam", result_type="TypePackAfterTypeParam<int>",
result_children=[ValueCheck(name="f", type="int")])
self.expect_expr("oneElemTypePackAfterTypeParam", result_type="TypePackAfterTypeParam<int, float>",
result_children=[ValueCheck(name="f", type="int")])
self.expect_expr("emptyAnonTypePackAfterNonTypeParam", result_type="AnonTypePackAfterNonTypeParam<1>",
result_children=[ValueCheck(name="g", type="int")])
self.expect_expr("oneElemAnonTypePackAfterNonTypeParam", result_type="AnonTypePackAfterNonTypeParam<1, int>",
result_children=[ValueCheck(name="g", type="int")])
self.expect_expr("emptyAnonTypePackAfterAnonNonTypeParam", result_type="AnonTypePackAfterAnonNonTypeParam<1>",
result_children=[ValueCheck(name="h", type="float")])
self.expect_expr("oneElemAnonTypePackAfterAnonNonTypeParam", result_type="AnonTypePackAfterAnonNonTypeParam<1, int>",
result_children=[ValueCheck(name="h", type="float")])
self.expect_expr("emptyTypePackAfterAnonNonTypeParam", result_type="TypePackAfterAnonNonTypeParam<1>",
result_children=[ValueCheck(name="i", type="int")])
self.expect_expr("oneElemTypePackAfterAnonNonTypeParam", result_type="TypePackAfterAnonNonTypeParam<1, int>",
result_children=[ValueCheck(name="i", type="int")])
self.expect_expr("emptyTypePackAfterNonTypeParam", result_type="TypePackAfterNonTypeParam<1>",
result_children=[ValueCheck(name="j", type="int")])
self.expect_expr("oneElemTypePackAfterNonTypeParam", result_type="TypePackAfterNonTypeParam<1, int>",
result_children=[ValueCheck(name="j", type="int")])

View File

@ -0,0 +1,69 @@
// Named type parameter pack.
template <typename... Is>
struct TypePack { int a; };
TypePack<> emptyTypePack;
TypePack<int> oneElemTypePack;
TypePack<int, float> twoElemTypePack;
// Unnamed type parameter pack.
template <typename... >
struct AnonTypePack { int b; };
AnonTypePack<> emptyAnonTypePack;
AnonTypePack<int> oneElemAnonTypePack;
AnonTypePack<int, float> twoElemAnonTypePack;
// Test type parameter packs combined with non-pack type template parameters.
// Unnamed type parameter pack behind a named type parameter.
template <typename T, typename... >
struct AnonTypePackAfterTypeParam { T c; };
AnonTypePackAfterTypeParam<int> emptyAnonTypePackAfterTypeParam;
AnonTypePackAfterTypeParam<int, float> oneElemAnonTypePackAfterTypeParam;
// Unnamed type parameter pack behind an unnamed type parameter.
template <typename, typename... >
struct AnonTypePackAfterAnonTypeParam { float d; };
AnonTypePackAfterAnonTypeParam<int> emptyAnonTypePackAfterAnonTypeParam;
AnonTypePackAfterAnonTypeParam<int, float> oneElemAnonTypePackAfterAnonTypeParam;
// Named type parameter pack behind an unnamed type parameter.
template <typename, typename... Ts>
struct TypePackAfterAnonTypeParam { int e; };
TypePackAfterAnonTypeParam<int> emptyTypePackAfterAnonTypeParam;
TypePackAfterAnonTypeParam<int, float> oneElemTypePackAfterAnonTypeParam;
// Named type parameter pack behind a named type parameter.
template <typename T, typename... Ts>
struct TypePackAfterTypeParam { int f; };
TypePackAfterTypeParam<int> emptyTypePackAfterTypeParam;
TypePackAfterTypeParam<int, float> oneElemTypePackAfterTypeParam;
// Test type parameter packs combined with non-pack non-type template parameters.
// Unnamed type parameter pack behind a named type parameter.
template <int I, typename... >
struct AnonTypePackAfterNonTypeParam { int g; };
AnonTypePackAfterNonTypeParam<1> emptyAnonTypePackAfterNonTypeParam;
AnonTypePackAfterNonTypeParam<1, int> oneElemAnonTypePackAfterNonTypeParam;
// Unnamed type parameter pack behind an unnamed type parameter.
template <int, typename... >
struct AnonTypePackAfterAnonNonTypeParam { float h; };
AnonTypePackAfterAnonNonTypeParam<1> emptyAnonTypePackAfterAnonNonTypeParam;
AnonTypePackAfterAnonNonTypeParam<1, int> oneElemAnonTypePackAfterAnonNonTypeParam;
// Named type parameter pack behind an unnamed type parameter.
template <int, typename... Ts>
struct TypePackAfterAnonNonTypeParam { int i; };
TypePackAfterAnonNonTypeParam<1> emptyTypePackAfterAnonNonTypeParam;
TypePackAfterAnonNonTypeParam<1, int> oneElemTypePackAfterAnonNonTypeParam;
// Named type parameter pack behind an unnamed type parameter.
template <int I, typename... Ts>
struct TypePackAfterNonTypeParam { int j; };
TypePackAfterNonTypeParam<1> emptyTypePackAfterNonTypeParam;
TypePackAfterNonTypeParam<1, int> oneElemTypePackAfterNonTypeParam;
int main() {
return 0; // break here
}

View File

@ -516,6 +516,12 @@ TEST_F(TestTypeSystemClang, TemplateArguments) {
}
}
TEST_F(TestTypeSystemClang, OnlyPackName) {
TypeSystemClang::TemplateParameterInfos infos;
infos.pack_name = "A";
EXPECT_FALSE(infos.IsValid());
}
static QualType makeConstInt(clang::ASTContext &ctxt) {
QualType result(ctxt.IntTy);
result.addConst();