From c526426f5cba5308782ea4f86822047ee2ee3818 Mon Sep 17 00:00:00 2001 From: Raphael Isemann Date: Wed, 2 Dec 2020 10:35:07 +0100 Subject: [PATCH] [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 --- .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 2 - .../TypeSystem/Clang/TypeSystemClang.h | 5 +- .../Makefile | 3 + .../TestClassTemplateNonTypeParameterPack.py | 76 +++++++++++++++++++ .../main.cpp | 69 +++++++++++++++++ .../Makefile | 3 + .../TestClassTemplateTypeParameterPack.py | 76 +++++++++++++++++++ .../main.cpp | 69 +++++++++++++++++ lldb/unittests/Symbol/TestTypeSystemClang.cpp | 6 ++ 9 files changed, 305 insertions(+), 4 deletions(-) create mode 100644 lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/Makefile create mode 100644 lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/TestClassTemplateNonTypeParameterPack.py create mode 100644 lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/main.cpp create mode 100644 lldb/test/API/lang/cpp/class-template-type-parameter-pack/Makefile create mode 100644 lldb/test/API/lang/cpp/class-template-type-parameter-pack/TestClassTemplateTypeParameterPack.py create mode 100644 lldb/test/API/lang/cpp/class-template-type-parameter-pack/main.cpp diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 6633acd70a50..2c045d6dc9c0 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -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(); } diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 5eb492f0dbc2..6879d2566183 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -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); } diff --git a/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/Makefile b/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/Makefile new file mode 100644 index 000000000000..99998b20bcb0 --- /dev/null +++ b/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/TestClassTemplateNonTypeParameterPack.py b/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/TestClassTemplateNonTypeParameterPack.py new file mode 100644 index 000000000000..d366d9d69222 --- /dev/null +++ b/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/TestClassTemplateNonTypeParameterPack.py @@ -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", + result_children=[ValueCheck(name="c", type="int")]) + self.expect_expr("oneElemAnonNonTypePackAfterTypeParam", result_type="AnonNonTypePackAfterTypeParam", + result_children=[ValueCheck(name="c", type="int")]) + + + + self.expect_expr("emptyAnonNonTypePackAfterAnonTypeParam", result_type="AnonNonTypePackAfterAnonTypeParam", + result_children=[ValueCheck(name="d", type="float")]) + self.expect_expr("oneElemAnonNonTypePackAfterAnonTypeParam", result_type="AnonNonTypePackAfterAnonTypeParam", + result_children=[ValueCheck(name="d", type="float")]) + + + self.expect_expr("emptyNonTypePackAfterAnonTypeParam", result_type="NonTypePackAfterAnonTypeParam", + result_children=[ValueCheck(name="e", type="int")]) + self.expect_expr("oneElemNonTypePackAfterAnonTypeParam", result_type="NonTypePackAfterAnonTypeParam", + result_children=[ValueCheck(name="e", type="int")]) + + + self.expect_expr("emptyNonTypePackAfterTypeParam", result_type="NonTypePackAfterTypeParam", + result_children=[ValueCheck(name="f", type="int")]) + self.expect_expr("oneElemNonTypePackAfterTypeParam", result_type="NonTypePackAfterTypeParam", + 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")]) \ No newline at end of file diff --git a/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/main.cpp b/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/main.cpp new file mode 100644 index 000000000000..a771a5d288ca --- /dev/null +++ b/lldb/test/API/lang/cpp/class-template-non-type-parameter-pack/main.cpp @@ -0,0 +1,69 @@ +// Named type parameter pack. +template +struct NonTypePack { int a; }; +NonTypePack<> emptyNonTypePack; +NonTypePack<1> oneElemNonTypePack; +NonTypePack<1, 2> twoElemNonTypePack; + +// Unnamed type parameter pack. +template +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 +struct AnonNonTypePackAfterTypeParam { T c; }; +AnonNonTypePackAfterTypeParam emptyAnonNonTypePackAfterTypeParam; +AnonNonTypePackAfterTypeParam oneElemAnonNonTypePackAfterTypeParam; + +// Unnamed non-type parameter pack behind an unnamed type parameter. +template +struct AnonNonTypePackAfterAnonTypeParam { float d; }; +AnonNonTypePackAfterAnonTypeParam emptyAnonNonTypePackAfterAnonTypeParam; +AnonNonTypePackAfterAnonTypeParam oneElemAnonNonTypePackAfterAnonTypeParam; + +// Named non-type parameter pack behind an unnamed type parameter. +template +struct NonTypePackAfterAnonTypeParam { int e; }; +NonTypePackAfterAnonTypeParam emptyNonTypePackAfterAnonTypeParam; +NonTypePackAfterAnonTypeParam oneElemNonTypePackAfterAnonTypeParam; + +// Named non-type parameter pack behind a named type parameter. +template +struct NonTypePackAfterTypeParam { int f; }; +NonTypePackAfterTypeParam emptyNonTypePackAfterTypeParam; +NonTypePackAfterTypeParam 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 +struct AnonNonTypePackAfterNonTypeParam { int g; }; +AnonNonTypePackAfterNonTypeParam<1> emptyAnonNonTypePackAfterNonTypeParam; +AnonNonTypePackAfterNonTypeParam<1, 2> oneElemAnonNonTypePackAfterNonTypeParam; + +// Unnamed non-type parameter pack behind an unnamed non-type parameter. +template +struct AnonNonTypePackAfterAnonNonTypeParam { float h; }; +AnonNonTypePackAfterAnonNonTypeParam<1> emptyAnonNonTypePackAfterAnonNonTypeParam; +AnonNonTypePackAfterAnonNonTypeParam<1, 2> oneElemAnonNonTypePackAfterAnonNonTypeParam; + +// Named non-type parameter pack behind an unnamed non-type parameter. +template +struct NonTypePackAfterAnonNonTypeParam { int i; }; +NonTypePackAfterAnonNonTypeParam<1> emptyNonTypePackAfterAnonNonTypeParam; +NonTypePackAfterAnonNonTypeParam<1, 2> oneElemNonTypePackAfterAnonNonTypeParam; + +// Named non-type parameter pack behind an unnamed non-type parameter. +template +struct NonTypePackAfterNonTypeParam { int j; }; +NonTypePackAfterNonTypeParam<1> emptyNonTypePackAfterNonTypeParam; +NonTypePackAfterNonTypeParam<1, 2> oneElemNonTypePackAfterNonTypeParam; + +int main() { + return 0; // break here +} diff --git a/lldb/test/API/lang/cpp/class-template-type-parameter-pack/Makefile b/lldb/test/API/lang/cpp/class-template-type-parameter-pack/Makefile new file mode 100644 index 000000000000..99998b20bcb0 --- /dev/null +++ b/lldb/test/API/lang/cpp/class-template-type-parameter-pack/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/class-template-type-parameter-pack/TestClassTemplateTypeParameterPack.py b/lldb/test/API/lang/cpp/class-template-type-parameter-pack/TestClassTemplateTypeParameterPack.py new file mode 100644 index 000000000000..48ea59d6e5eb --- /dev/null +++ b/lldb/test/API/lang/cpp/class-template-type-parameter-pack/TestClassTemplateTypeParameterPack.py @@ -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", + result_children=[ValueCheck(name="a", type="int")]) + self.expect_expr("twoElemTypePack", result_type="TypePack", + 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", + result_children=[ValueCheck(name="b", type="int")]) + self.expect_expr("twoElemAnonTypePack", result_type="AnonTypePack", + result_children=[ValueCheck(name="b", type="int")]) + + + self.expect_expr("emptyAnonTypePackAfterTypeParam", result_type="AnonTypePackAfterTypeParam", + result_children=[ValueCheck(name="c", type="int")]) + self.expect_expr("oneElemAnonTypePackAfterTypeParam", result_type="AnonTypePackAfterTypeParam", + result_children=[ValueCheck(name="c", type="int")]) + + + + self.expect_expr("emptyAnonTypePackAfterAnonTypeParam", result_type="AnonTypePackAfterAnonTypeParam", + result_children=[ValueCheck(name="d", type="float")]) + self.expect_expr("oneElemAnonTypePackAfterAnonTypeParam", result_type="AnonTypePackAfterAnonTypeParam", + result_children=[ValueCheck(name="d", type="float")]) + + + self.expect_expr("emptyTypePackAfterAnonTypeParam", result_type="TypePackAfterAnonTypeParam", + result_children=[ValueCheck(name="e", type="int")]) + self.expect_expr("oneElemTypePackAfterAnonTypeParam", result_type="TypePackAfterAnonTypeParam", + result_children=[ValueCheck(name="e", type="int")]) + + + self.expect_expr("emptyTypePackAfterTypeParam", result_type="TypePackAfterTypeParam", + result_children=[ValueCheck(name="f", type="int")]) + self.expect_expr("oneElemTypePackAfterTypeParam", result_type="TypePackAfterTypeParam", + 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")]) \ No newline at end of file diff --git a/lldb/test/API/lang/cpp/class-template-type-parameter-pack/main.cpp b/lldb/test/API/lang/cpp/class-template-type-parameter-pack/main.cpp new file mode 100644 index 000000000000..fd6cbac66f0c --- /dev/null +++ b/lldb/test/API/lang/cpp/class-template-type-parameter-pack/main.cpp @@ -0,0 +1,69 @@ +// Named type parameter pack. +template +struct TypePack { int a; }; +TypePack<> emptyTypePack; +TypePack oneElemTypePack; +TypePack twoElemTypePack; + +// Unnamed type parameter pack. +template +struct AnonTypePack { int b; }; +AnonTypePack<> emptyAnonTypePack; +AnonTypePack oneElemAnonTypePack; +AnonTypePack twoElemAnonTypePack; + +// Test type parameter packs combined with non-pack type template parameters. + +// Unnamed type parameter pack behind a named type parameter. +template +struct AnonTypePackAfterTypeParam { T c; }; +AnonTypePackAfterTypeParam emptyAnonTypePackAfterTypeParam; +AnonTypePackAfterTypeParam oneElemAnonTypePackAfterTypeParam; + +// Unnamed type parameter pack behind an unnamed type parameter. +template +struct AnonTypePackAfterAnonTypeParam { float d; }; +AnonTypePackAfterAnonTypeParam emptyAnonTypePackAfterAnonTypeParam; +AnonTypePackAfterAnonTypeParam oneElemAnonTypePackAfterAnonTypeParam; + +// Named type parameter pack behind an unnamed type parameter. +template +struct TypePackAfterAnonTypeParam { int e; }; +TypePackAfterAnonTypeParam emptyTypePackAfterAnonTypeParam; +TypePackAfterAnonTypeParam oneElemTypePackAfterAnonTypeParam; + +// Named type parameter pack behind a named type parameter. +template +struct TypePackAfterTypeParam { int f; }; +TypePackAfterTypeParam emptyTypePackAfterTypeParam; +TypePackAfterTypeParam oneElemTypePackAfterTypeParam; + +// Test type parameter packs combined with non-pack non-type template parameters. + +// Unnamed type parameter pack behind a named type parameter. +template +struct AnonTypePackAfterNonTypeParam { int g; }; +AnonTypePackAfterNonTypeParam<1> emptyAnonTypePackAfterNonTypeParam; +AnonTypePackAfterNonTypeParam<1, int> oneElemAnonTypePackAfterNonTypeParam; + +// Unnamed type parameter pack behind an unnamed type parameter. +template +struct AnonTypePackAfterAnonNonTypeParam { float h; }; +AnonTypePackAfterAnonNonTypeParam<1> emptyAnonTypePackAfterAnonNonTypeParam; +AnonTypePackAfterAnonNonTypeParam<1, int> oneElemAnonTypePackAfterAnonNonTypeParam; + +// Named type parameter pack behind an unnamed type parameter. +template +struct TypePackAfterAnonNonTypeParam { int i; }; +TypePackAfterAnonNonTypeParam<1> emptyTypePackAfterAnonNonTypeParam; +TypePackAfterAnonNonTypeParam<1, int> oneElemTypePackAfterAnonNonTypeParam; + +// Named type parameter pack behind an unnamed type parameter. +template +struct TypePackAfterNonTypeParam { int j; }; +TypePackAfterNonTypeParam<1> emptyTypePackAfterNonTypeParam; +TypePackAfterNonTypeParam<1, int> oneElemTypePackAfterNonTypeParam; + +int main() { + return 0; // break here +} diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp index 3303a45d756e..c1bbc552662f 100644 --- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp +++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp @@ -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();