Skip to content

Commit 6042a51

Browse files
AkiSakuraiWerWolv
andauthored
evaluator: Implement eager binding for nested template arguments (#204)
* [PoC] Implement eager binding for nested template arguments This commit changes the evaluation of nested template arguments from lazy (pass-by-name) to eager (pass-by-value) to enforce correct lexical scoping. Motivation: Previously, in a nested template like `T<K<x>>`, the argument `x` was resolved lazily within the scope of `T` instead of the scope where it was declared. This pass-by-name behavior for nested types violated lexical scoping and caused unpredictable variable resolution. Implementation: The core of the fix was to separate a type's declaration from its application: 1. A new `ASTNodeTypeApplication` node was introduced to represent a specific instantiation of a type. It captures the fully resolved template arguments at the declaration site, effectively creating a closure over the surrounding scope. 2. The parser was updated to eagerly evaluate all arguments—including nested types—into this new `ASTNodeTypeApplication` node. 3. `ASTNodeTypeDecl` was simplified to only handle the static declaration of a type, leaving all instantiation-specific logic to `ASTNodeTypeApplication`. As a key benefit of this cleaner separation, forward-declared types can now be used in templates, enabling mutually recursive template definitions. 4. A new test suite (`test_pattern_template_parameters_scope.hpp`) was added to verify the corrected behavior with various nested and recursive template scenarios that previously failed. * Fix memory leak caused by retain cycle formed by recursive type. Break the cycle manually when the parser and parser manager get reset. * fix the issue that typenameof does not return the correct type name for template parameters. * Retain custom build types as they are global and have lifetimes beyond a single evaluation. * Fix formatting style --------- Co-authored-by: Nik <werwolv98@gmail.com>
1 parent 32b2928 commit 6042a51

38 files changed

Lines changed: 793 additions & 400 deletions

cli/source/subcommands/docs.cpp

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ namespace pl::cli::sub {
2121

2222
namespace {
2323

24-
std::string getTypeEndian(const core::ast::ASTNodeTypeDecl *typeDecl) {
25-
auto endian = typeDecl->getEndian();
24+
std::string getTypeEndian(const core::ast::ASTNodeTypeApplication *typeApp) {
25+
auto endian = typeApp->getEndian();
2626

2727
if (!endian.has_value())
2828
return "";
@@ -35,15 +35,12 @@ namespace pl::cli::sub {
3535
}
3636

3737
std::string getTypeName(const core::ast::ASTNode *type) {
38-
if (auto builtinType = dynamic_cast<const core::ast::ASTNodeBuiltinType*>(type))
39-
return core::Token::getTypeName(builtinType->getType());
40-
else if (auto typeDecl = dynamic_cast<const core::ast::ASTNodeTypeDecl*>(type)) {
41-
if (typeDecl->getName().empty())
42-
return getTypeEndian(typeDecl) + getTypeName(typeDecl->getType().get());
43-
else
44-
return getTypeEndian(typeDecl) + typeDecl->getName();
38+
if (auto typeApp = dynamic_cast<const core::ast::ASTNodeTypeApplication*>(type); typeApp != nullptr) {
39+
return fmt::format("{}{}", getTypeEndian(typeApp), getTypeName(typeApp->getType().get()));
40+
} else if (auto typeDecl = dynamic_cast<const core::ast::ASTNodeTypeDecl*>(type); typeDecl != nullptr) {
41+
return typeDecl->getName();
4542
} else {
46-
return "???";
43+
return "";
4744
}
4845
}
4946

@@ -72,12 +69,10 @@ namespace pl::cli::sub {
7269

7370
std::string result = "<";
7471
for (const auto &templateParam : templateParams) {
75-
if (auto typeDecl = dynamic_cast<const core::ast::ASTNodeTypeDecl*>(templateParam.get()); typeDecl != nullptr)
76-
result += typeDecl->getName();
77-
else if (auto lvalue = dynamic_cast<const core::ast::ASTNodeLValueAssignment*>(templateParam.get()); lvalue != nullptr)
78-
result += fmt::format("auto {}", lvalue->getLValueName());
79-
else
80-
continue;
72+
if (templateParam->isType())
73+
result += templateParam->getName().get();
74+
else
75+
result += fmt::format("auto {}", templateParam->getName().get());
8176

8277
result += ", ";
8378
}
@@ -89,8 +84,8 @@ namespace pl::cli::sub {
8984
}
9085

9186
std::string generateTypeDocumentation(const std::string &name, const core::ast::ASTNodeTypeDecl *type) {
92-
if (auto typeDecl = dynamic_cast<core::ast::ASTNodeTypeDecl*>(type->getType().get())) {
93-
return fmt::format("```rust\nusing {}{} = {}{};\n```", name, generateTemplateParams(type), getTypeName(typeDecl), generateAttributes(typeDecl));
87+
if (auto typeApp = dynamic_cast<core::ast::ASTNodeTypeApplication*>(type->getType().get())) {
88+
return fmt::format("```rust\nusing {}{} = {}{};\n```", name, generateTemplateParams(type), getTypeName(typeApp), generateAttributes(type));
9489
} else if (dynamic_cast<core::ast::ASTNodeStruct*>(type->getType().get())) {
9590
return fmt::format("```rust\nstruct {}{} {{ ... }}{};\n```", name, generateTemplateParams(type), generateAttributes(type));
9691
} else if (dynamic_cast<core::ast::ASTNodeUnion*>(type->getType().get())) {

lib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ add_library(libpl ${LIBRARY_TYPE}
4444
source/pl/core/ast/ast_node_struct.cpp
4545
source/pl/core/ast/ast_node_ternary_expression.cpp
4646
source/pl/core/ast/ast_node_try_catch_statement.cpp
47+
source/pl/core/ast/ast_node_type_application.cpp
4748
source/pl/core/ast/ast_node_type_decl.cpp
4849
source/pl/core/ast/ast_node_type_operator.cpp
4950
source/pl/core/ast/ast_node_union.cpp

lib/include/pl/core/ast/ast_node_array_variable_decl.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <pl/core/ast/ast_node.hpp>
44
#include <pl/core/ast/ast_node_attribute.hpp>
5+
#include <pl/core/ast/ast_node_type_appilication.hpp>
56

67
namespace pl::core::ast {
78

@@ -10,7 +11,7 @@ namespace pl::core::ast {
1011
class ASTNodeArrayVariableDecl : public ASTNode,
1112
public Attributable {
1213
public:
13-
ASTNodeArrayVariableDecl(std::string name, std::shared_ptr<ASTNodeTypeDecl> type, std::unique_ptr<ASTNode> &&size, std::unique_ptr<ASTNode> &&placementOffset = {}, std::unique_ptr<ASTNode> &&placementSection = {}, bool constant = false);
14+
ASTNodeArrayVariableDecl(std::string name, std::shared_ptr<ASTNodeTypeApplication> type, std::unique_ptr<ASTNode> &&size, std::unique_ptr<ASTNode> &&placementOffset = {}, std::unique_ptr<ASTNode> &&placementSection = {}, bool constant = false);
1415
ASTNodeArrayVariableDecl(const ASTNodeArrayVariableDecl &other);
1516

1617
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
@@ -24,7 +25,7 @@ namespace pl::core::ast {
2425
return this->m_name;
2526
}
2627

27-
[[nodiscard]] const std::shared_ptr<ASTNodeTypeDecl> &getType() const {
28+
[[nodiscard]] const std::shared_ptr<ASTNodeTypeApplication> &getType() const {
2829
return this->m_type;
2930
}
3031

@@ -42,7 +43,7 @@ namespace pl::core::ast {
4243

4344
private:
4445
std::string m_name;
45-
std::shared_ptr<ASTNodeTypeDecl> m_type;
46+
std::shared_ptr<ASTNodeTypeApplication> m_type;
4647
std::unique_ptr<ASTNode> m_size;
4748
std::unique_ptr<ASTNode> m_placementOffset, m_placementSection;
4849
bool m_constant;

lib/include/pl/core/ast/ast_node_bitfield_array_variable_decl.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include <pl/core/ast/ast_node_bitfield.hpp>
66
#include <pl/core/ast/ast_node_bitfield_field.hpp>
77
#include <pl/core/ast/ast_node_literal.hpp>
8-
#include <pl/core/ast/ast_node_type_decl.hpp>
8+
#include <pl/core/ast/ast_node_type_appilication.hpp>
99
#include <pl/core/ast/ast_node_while_statement.hpp>
1010

1111
#include <pl/patterns/pattern_array_dynamic.hpp>
@@ -15,7 +15,7 @@ namespace pl::core::ast {
1515
class ASTNodeBitfieldArrayVariableDecl : public ASTNode,
1616
public Attributable {
1717
public:
18-
ASTNodeBitfieldArrayVariableDecl(std::string name, std::shared_ptr<ASTNodeTypeDecl> type, std::unique_ptr<ASTNode> &&size);
18+
ASTNodeBitfieldArrayVariableDecl(std::string name, std::shared_ptr<ASTNodeTypeApplication> type, std::unique_ptr<ASTNode> &&size);
1919
ASTNodeBitfieldArrayVariableDecl(const ASTNodeBitfieldArrayVariableDecl &other);
2020

2121
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
@@ -28,7 +28,7 @@ namespace pl::core::ast {
2828
return this->m_name;
2929
}
3030

31-
[[nodiscard]] const std::shared_ptr<ASTNodeTypeDecl> &getType() const {
31+
[[nodiscard]] const std::shared_ptr<ASTNodeTypeApplication> &getType() const {
3232
return this->m_type;
3333
}
3434

@@ -38,7 +38,7 @@ namespace pl::core::ast {
3838

3939
private:
4040
std::string m_name;
41-
std::shared_ptr<ASTNodeTypeDecl> m_type;
41+
std::shared_ptr<ASTNodeTypeApplication> m_type;
4242
std::unique_ptr<ASTNode> m_size;
4343

4444
void createArray(Evaluator *evaluator, std::shared_ptr<ptrn::Pattern> &resultPattern) const;

lib/include/pl/core/ast/ast_node_bitfield_field.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <pl/core/ast/ast_node.hpp>
4-
#include <pl/core/ast/ast_node_type_decl.hpp>
4+
#include <pl/core/ast/ast_node_type_appilication.hpp>
55
#include <pl/core/ast/ast_node_attribute.hpp>
66

77
#include <pl/patterns/pattern_bitfield.hpp>
@@ -42,7 +42,7 @@ namespace pl::core::ast {
4242

4343
class ASTNodeBitfieldFieldSizedType : public ASTNodeBitfieldField {
4444
public:
45-
ASTNodeBitfieldFieldSizedType(std::string name, std::unique_ptr<ASTNodeTypeDecl> &&type, std::unique_ptr<ASTNode> &&size);
45+
ASTNodeBitfieldFieldSizedType(std::string name, std::unique_ptr<ASTNodeTypeApplication> &&type, std::unique_ptr<ASTNode> &&size);
4646
ASTNodeBitfieldFieldSizedType(const ASTNodeBitfieldFieldSizedType &other);
4747

4848
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
@@ -52,7 +52,7 @@ namespace pl::core::ast {
5252
[[nodiscard]] std::shared_ptr<ptrn::PatternBitfieldField> createBitfield(Evaluator *evaluator, u64 byteOffset, u8 bitOffset, u8 bitSize) const override;
5353

5454
private:
55-
std::unique_ptr<ASTNodeTypeDecl> m_type;
55+
std::unique_ptr<ASTNodeTypeApplication> m_type;
5656
};
5757

5858
}

lib/include/pl/core/ast/ast_node_cast.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
#pragma once
22

33
#include <pl/core/ast/ast_node.hpp>
4+
#include <pl/core/ast/ast_node_type_appilication.hpp>
45

56
namespace pl::core::ast {
67

78
class ASTNodeCast : public ASTNode {
89
public:
9-
ASTNodeCast(std::unique_ptr<ASTNode> &&value, std::unique_ptr<ASTNode> &&type, bool reinterpret);
10+
ASTNodeCast(std::unique_ptr<ASTNode> &&value, std::unique_ptr<ASTNodeTypeApplication> &&type, bool reinterpret);
1011
ASTNodeCast(const ASTNodeCast &other);
1112

1213
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
@@ -20,7 +21,7 @@ namespace pl::core::ast {
2021

2122
private:
2223
std::unique_ptr<ASTNode> m_value;
23-
std::unique_ptr<ASTNode> m_type;
24+
std::unique_ptr<ASTNodeTypeApplication> m_type;
2425
bool m_reinterpret;
2526
};
2627

lib/include/pl/core/ast/ast_node_pointer_variable_decl.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#include <pl/core/ast/ast_node.hpp>
44
#include <pl/core/ast/ast_node_attribute.hpp>
5-
5+
#include <pl/core/ast/ast_node_type_appilication.hpp>
66

77
namespace pl::core::ast {
88

@@ -11,7 +11,7 @@ namespace pl::core::ast {
1111
class ASTNodePointerVariableDecl : public ASTNode,
1212
public Attributable {
1313
public:
14-
ASTNodePointerVariableDecl(std::string name, std::shared_ptr<ASTNode> type, std::shared_ptr<ASTNodeTypeDecl> sizeType, std::unique_ptr<ASTNode> &&placementOffset = nullptr, std::unique_ptr<ASTNode> &&placementSection = nullptr);
14+
ASTNodePointerVariableDecl(std::string name, std::shared_ptr<ASTNode> type, std::shared_ptr<ASTNodeTypeApplication> sizeType, std::unique_ptr<ASTNode> &&placementOffset = nullptr, std::unique_ptr<ASTNode> &&placementSection = nullptr);
1515
ASTNodePointerVariableDecl(const ASTNodePointerVariableDecl &other);
1616

1717
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
@@ -20,15 +20,15 @@ namespace pl::core::ast {
2020

2121
[[nodiscard]] const std::string &getName() const { return this->m_name; }
2222
[[nodiscard]] constexpr const std::shared_ptr<ASTNode> &getType() const { return this->m_type; }
23-
[[nodiscard]] constexpr const std::shared_ptr<ASTNodeTypeDecl> &getSizeType() const { return this->m_sizeType; }
23+
[[nodiscard]] constexpr const std::shared_ptr<ASTNodeTypeApplication> &getSizeType() const { return this->m_sizeType; }
2424
[[nodiscard]] constexpr const std::unique_ptr<ASTNode> &getPlacementOffset() const { return this->m_placementOffset; }
2525

2626
void createPatterns(Evaluator *evaluator, std::vector<std::shared_ptr<ptrn::Pattern>> &resultPatterns) const override;
2727

2828
private:
2929
std::string m_name;
3030
std::shared_ptr<ASTNode> m_type;
31-
std::shared_ptr<ASTNodeTypeDecl> m_sizeType;
31+
std::shared_ptr<ASTNodeTypeApplication> m_sizeType;
3232
std::unique_ptr<ASTNode> m_placementOffset, m_placementSection;
3333
};
3434

lib/include/pl/core/ast/ast_node_scope_resolution.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
#pragma once
22

33
#include <pl/core/ast/ast_node.hpp>
4+
#include <pl/core/ast/ast_node_type_decl.hpp>
45

56
namespace pl::core::ast {
67

78
class ASTNodeScopeResolution : public ASTNode {
89
public:
9-
explicit ASTNodeScopeResolution(std::shared_ptr<ASTNode> &&type, std::string name);
10+
explicit ASTNodeScopeResolution(std::shared_ptr<ASTNodeTypeDecl> &&type, std::string name);
1011
ASTNodeScopeResolution(const ASTNodeScopeResolution &other);
1112

1213
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
@@ -16,7 +17,7 @@ namespace pl::core::ast {
1617
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override;
1718

1819
private:
19-
std::shared_ptr<ASTNode> m_type;
20+
std::shared_ptr<ASTNodeTypeDecl> m_type;
2021
std::string m_name;
2122
};
2223

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#pragma once
2+
3+
#include <pl/core/ast/ast_node.hpp>
4+
5+
namespace pl::core::ast {
6+
7+
class ASTNodeTemplateParameter : public ASTNode {
8+
public:
9+
explicit ASTNodeTemplateParameter(Token::Identifier name, bool isType)
10+
: m_name(std::move(name)), m_isType(isType) {}
11+
12+
ASTNodeTemplateParameter(const ASTNodeTemplateParameter &) = default;
13+
14+
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
15+
return std::unique_ptr<ASTNode>(new ASTNodeTemplateParameter(*this));
16+
}
17+
18+
[[nodiscard]] const auto &getName() const {
19+
return this->m_name;
20+
}
21+
22+
[[nodiscard]] bool isType() const {
23+
return this->m_isType;
24+
}
25+
26+
private:
27+
Token::Identifier m_name;
28+
bool m_isType;
29+
};
30+
31+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#pragma once
2+
3+
#include <pl/core/ast/ast_node.hpp>
4+
#include <pl/core/ast/ast_node_type_decl.hpp>
5+
6+
namespace pl::core::ast {
7+
8+
class ASTNodeTypeApplication : public ASTNode {
9+
public:
10+
explicit ASTNodeTypeApplication(std::shared_ptr<ASTNode> type);
11+
12+
ASTNodeTypeApplication(const ASTNodeTypeApplication &);
13+
14+
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
15+
return std::unique_ptr<ASTNode>(new ASTNodeTypeApplication(*this));
16+
}
17+
18+
void setTemplateArguments(std::vector<std::unique_ptr<ASTNode>> &&arguments) {
19+
this->m_templateArguments = std::move(arguments);
20+
}
21+
22+
std::vector<std::unique_ptr<ASTNode>> evaluateTemplateArguments(Evaluator *evaluator) const;
23+
24+
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override;
25+
void createPatterns(Evaluator *evaluator, std::vector<std::shared_ptr<ptrn::Pattern>> &resultPatterns) const override;
26+
27+
[[nodiscard]] const std::shared_ptr<ASTNode> &getType() const {
28+
return this->m_type;
29+
}
30+
31+
void setReference(bool reference) {
32+
this->m_reference = reference;
33+
}
34+
35+
[[nodiscard]] bool isReference() const {
36+
return this->m_reference;
37+
}
38+
39+
const ast::ASTNode* getTypeDefinition(Evaluator *evaluator) const;
40+
41+
[[nodiscard]] const std::string getTypeName() const;
42+
43+
void setEndian(std::endian endian) {
44+
this->m_endian = endian;
45+
}
46+
47+
[[nodiscard]] std::optional<std::endian> getEndian() const { return this->m_endian; }
48+
49+
void setTemplateParameterIndex(int index) {
50+
this->m_templateParameterIndex = index;
51+
}
52+
53+
[[nodiscard]] int getTemplateParameterIndex() const {
54+
return this->m_templateParameterIndex;
55+
}
56+
57+
private:
58+
std::shared_ptr<ASTNode> m_type;
59+
std::vector<std::unique_ptr<ASTNode>> m_templateArguments;
60+
bool m_reference = false;
61+
std::optional<std::endian> m_endian;
62+
size_t m_templateParameterIndex = 0;
63+
};
64+
65+
}

0 commit comments

Comments
 (0)