diff options
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | asm/assembler.h | 18 | ||||
-rw-r--r-- | asm/intel64/encode.cpp | 24 | ||||
-rw-r--r-- | asm/intel64/mov.cpp | 6 | ||||
-rw-r--r-- | asm/intel64/xor.cpp | 15 | ||||
-rw-r--r-- | cpp.cpp | 79 | ||||
-rw-r--r-- | flowgraph/graph.cpp | 60 | ||||
-rw-r--r-- | flowgraph/graph.h | 22 | ||||
-rw-r--r-- | flowgraph/node.cpp | 8 | ||||
-rw-r--r-- | flowgraph/node.h | 16 | ||||
-rw-r--r-- | flowgraph/scope.cpp | 28 | ||||
-rw-r--r-- | flowgraph/scope.h | 29 | ||||
-rw-r--r-- | flowgraph/storage.cpp | 9 | ||||
-rw-r--r-- | flowgraph/storage.h | 16 |
14 files changed, 267 insertions, 70 deletions
@@ -75,6 +75,7 @@ PROGSRC=\ flowgraph/data.cpp \ flowgraph/graph.cpp \ flowgraph/node.cpp \ + flowgraph/scope.cpp \ flowgraph/storage.cpp \ file.cpp \ grammer.cpp \ @@ -101,10 +102,10 @@ all: mcc unittest systemtest # Tests on C++ level unittest: test-$(PROJECTNAME) - ./test-$(PROJECTNAME) --gtest_filter='CppTest.compile_2_times' + ./test-$(PROJECTNAME) # --gtest_filter='CppTest.compile_2_times' # Testing mcc executable and compiled elf programs -systemtest: +systemtest: mcc ./mcc systemtest/mcc-execute.tests/test-return-1.cpp ./mcc systemtest/mcc-execute.tests/test-addition.cpp runtest --srcdir systemtest --tool mcc # --all @@ -121,7 +122,7 @@ dep: $(MAKE) $(TESTSRC:.cpp=.d) mcc.d %.d: %.cpp - $(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -MM -MP -MF $@ -c $< + $(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -MM -MP -MF $@ -MT $(*D)/$(*F).o -c $< %.o: %.cpp %.d $(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -c $< -o $@ diff --git a/asm/assembler.h b/asm/assembler.h index aa886b5..bd291b8 100644 --- a/asm/assembler.h +++ b/asm/assembler.h @@ -4,6 +4,8 @@ #include "chunk.h" +#include "../minicc.h" + #include <boost/endian/conversion.hpp> #include <any> @@ -93,6 +95,22 @@ public: std::string m_name; }; + // 64 bit Ptr to 32 bit Memory + class Mem32Ptr64 + { + public: + Mem32Ptr64(const std::string& reg, int32_t offs = 0): m_reg(reg), m_offs(offs) {} + Mem32Ptr64(const std::string& reg, const std::string& reg2 = ""s, int32_t offs = 0): m_reg(reg), m_reg2(reg2), m_offs(offs) {} + std::string reg() { return m_reg; } + std::string reg2() { return m_reg2; } + int32_t offs() { return m_offs; } + + private: + std::string m_reg; + std::string m_reg2; + int32_t m_offs; + }; + class Label { public: diff --git a/asm/intel64/encode.cpp b/asm/intel64/encode.cpp index 123dff2..03a7897 100644 --- a/asm/intel64/encode.cpp +++ b/asm/intel64/encode.cpp @@ -37,7 +37,7 @@ void Asm::toMachineCode(const FlowGraph::Graph& graph, Segment& segment) std::runtime_error("Bad value for operand 1: Constant expected"); } - if (op.type() == FlowGraph::UnaryOperationType::Store) { + if (op.type() == FlowGraph::UnaryOperationType::Negate) { Asm::Args args1{{Asm::Args::Register32("edi"), Asm::Args::Immediate32(immediate1)}}; segment.push_back(makeOp("mov", args1)); } else @@ -84,16 +84,14 @@ void Asm::toMachineCode(const FlowGraph::Graph& graph, Segment& segment) std::runtime_error("Bad value for operand 2: Constant expected"); } - Asm::Args args1{{Asm::Args::Register32("edi"), Asm::Args::Immediate32(immediate1)}}; - segment.push_back(makeOp("mov", args1)); + segment.push_back(makeOp("mov", Asm::Args{{Asm::Args::Register32("edi"), Asm::Args::Immediate32(immediate1)}})); - Asm::Args args2{{Asm::Args::Register32("edi"), Asm::Args::Immediate32(immediate2)}}; - - if (op.type() == FlowGraph::BinaryOperationType::Add) - segment.push_back(makeOp("add", args2)); - else if (op.type() == FlowGraph::BinaryOperationType::Multiply) - segment.push_back(makeOp("mul", args2)); - else + if (op.type() == FlowGraph::BinaryOperationType::Add) { + segment.push_back(makeOp("add", Asm::Args{{Asm::Args::Register32("edi"), Asm::Args::Immediate32(immediate2)}})); + } else if (op.type() == FlowGraph::BinaryOperationType::Multiply) { + segment.push_back(makeOp("mov", Asm::Args{{Asm::Args::Register32("ebx"), Asm::Args::Immediate32(immediate2)}})); + segment.push_back(makeOp("mul", Asm::Args{{Asm::Args::Register32("ebx")}})); + } else throw std::runtime_error("ICE: Asm: Unsupported binary operation type: "s + std::to_string(static_cast<int>(op.type()))); } else if (typeid(node_deref) == typeid(FlowGraph::CreateScopeOp)) { @@ -104,6 +102,12 @@ void Asm::toMachineCode(const FlowGraph::Graph& graph, Segment& segment) } else if (typeid(node_deref) == typeid(FlowGraph::DestroyScopeOp)) { //FlowGraph::DestroyScopeOp& op {dynamic_cast<FlowGraph::DestroyScopeOp&>(*node)}; segment.push_back(makeOp("pop", Asm::Args{{Asm::Args::Register64("rbp")}})); + + // Move + segment.push_back(makeOp("xor", Asm::Args{{Asm::Args::Register64("rdi"), Asm::Args::Register64("rdi")}})); + segment.push_back(makeOp("mov", Asm::Args{{Asm::Args::Register32("edi"), Asm::Args::Register32("eax")}})); + } else if (typeid(node_deref) == typeid(FlowGraph::DataNode)) { + // ignore: Immediate data is used in subsequent nodes } else { throw std::runtime_error("ICE: Encoding: Unsupported node: "s + demangle(typeid(node_deref))); } diff --git a/asm/intel64/mov.cpp b/asm/intel64/mov.cpp index 5b224c1..6426238 100644 --- a/asm/intel64/mov.cpp +++ b/asm/intel64/mov.cpp @@ -43,6 +43,12 @@ bool registered { registerOp(mangleName<Asm::Args::Register8, Asm::Args::Register8>("mov"), [](const Asm::Args& args) -> std::shared_ptr<Op>{ return std::make_shared<Op_mov>(args); }) && + registerOp(mangleName<Asm::Args::Register32, Asm::Args::Register32>("mov"), [](const Asm::Args& args) -> std::shared_ptr<Op>{ + return std::make_shared<Op_mov>(args); + }) && + registerOp(mangleName<Asm::Args::Register64, Asm::Args::Register64>("mov"), [](const Asm::Args& args) -> std::shared_ptr<Op>{ + return std::make_shared<Op_mov>(args); + }) && registerOp(mangleName<Asm::Args::Register32, Asm::Args::Immediate32>("mov"), [](const Asm::Args& args) -> std::shared_ptr<Op>{ return std::make_shared<Op_mov>(args); }) && diff --git a/asm/intel64/xor.cpp b/asm/intel64/xor.cpp index aba6fb5..1628558 100644 --- a/asm/intel64/xor.cpp +++ b/asm/intel64/xor.cpp @@ -12,9 +12,14 @@ using namespace std::string_literals; Op_xor::Op_xor(const Asm::Args& args) { if (args[0].type() == typeid(Asm::Args::Register8) && args[1].type() == typeid(Asm::Args::Register8)) { // xor reg8, reg8 - // r8, r/m8: ModRM:reg (w), ModRM:r/m (r) - machine_code = std::vector<uint8_t>{ 0x32 } + + machine_code = std::vector<uint8_t>{ 0x30 } + ModRM(std::any_cast<Asm::Args::Register8>(args[0]).name(), std::any_cast<Asm::Args::Register8>(args[1]).name()); + } else if (args[0].type() == typeid(Asm::Args::Register32) && args[1].type() == typeid(Asm::Args::Register32)) { // xor reg32, reg32 + machine_code = std::vector<uint8_t>{ 0x31 } + + ModRM(std::any_cast<Asm::Args::Register32>(args[0]).name(), std::any_cast<Asm::Args::Register32>(args[1]).name()); + } else if (args[0].type() == typeid(Asm::Args::Register64) && args[1].type() == typeid(Asm::Args::Register64)) { // xor reg64, reg64 + machine_code = REX("W") + std::vector<uint8_t>{ 0x31 } + + ModRM(std::any_cast<Asm::Args::Register64>(args[0]).name(), std::any_cast<Asm::Args::Register64>(args[1]).name()); } else { throw std::runtime_error("Unimplemented: xor "s + args[0].type().name() + " "s + args[1].type().name()); } @@ -25,6 +30,12 @@ namespace { bool registered { registerOp(mangleName<Asm::Args::Register8, Asm::Args::Register8>("xor"), [](const Asm::Args& args) -> std::shared_ptr<Op>{ return std::make_shared<Op_xor>(args); + }) && + registerOp(mangleName<Asm::Args::Register32, Asm::Args::Register32>("xor"), [](const Asm::Args& args) -> std::shared_ptr<Op>{ + return std::make_shared<Op_xor>(args); + }) && + registerOp(mangleName<Asm::Args::Register64, Asm::Args::Register64>("xor"), [](const Asm::Args& args) -> std::shared_ptr<Op>{ + return std::make_shared<Op_xor>(args); }) }; @@ -400,18 +400,29 @@ std::unordered_map<std::string, std::function<std::any(index_t)>> CPP::getNodeEv { "multiplicative-expression", [&](index_t index) -> std::any { if (childTypesOfNodeMatch(index, {"multiplicative-expression", "*", "pm-expression"})) { - if (getValue(index, 0).type() != typeid(FlowGraph::Data)) + if (getValue(index, 0).type() != typeid(FlowGraph::Graph)) throw std::runtime_error("ICE: multiplicative-expression: Bad data type for argument 1: "s + demangle(getValue(index, 0).type())); - if (getValue(index, 2).type() != typeid(FlowGraph::Data)) + if (getValue(index, 2).type() != typeid(FlowGraph::Graph)) throw std::runtime_error("ICE: multiplicative-expression: Bad data type for argument 3: "s + demangle(getValue(index, 2).type())); - FlowGraph::LocalScope scope; // TODO: move to context! - FlowGraph::Data destination{FlowGraph::MakeTemporaryInt(scope)}; - FlowGraph::Data value0 {std::any_cast<FlowGraph::Data>(getValue(index, 0))}; - FlowGraph::Data value1 {std::any_cast<FlowGraph::Data>(getValue(index, 2))}; + FlowGraph::Graph value0{std::any_cast<FlowGraph::Graph>(getValue(index, 0))}; + std::shared_ptr<FlowGraph::Node> lastOp0{value0.lastOp()}; - std::shared_ptr<FlowGraph::Node> node {std::make_shared<FlowGraph::BinaryOperation>(FlowGraph::BinaryOperationType::Multiply, destination, value0, value1)}; - return node; + FlowGraph::Graph value1{std::any_cast<FlowGraph::Graph>(getValue(index, 2))}; + std::shared_ptr<FlowGraph::Node> lastOp1{value1.lastOp()}; + + FlowGraph::Graph result{value0}; + result.append(value1); + + FlowGraph::Data destination{FlowGraph::MakeTemporaryInt(result.scope())}; + + std::shared_ptr<FlowGraph::Node> node {std::make_shared<FlowGraph::BinaryOperation>(FlowGraph::BinaryOperationType::Multiply, + destination, + lastOp0->destination(), lastOp1->destination())}; + + result.append(node); + + return result; } if (childTypesOfNodeMatch(index, {"pm-expression"})) return getValue(index, 0); @@ -421,18 +432,29 @@ std::unordered_map<std::string, std::function<std::any(index_t)>> CPP::getNodeEv { "additive-expression", [&](index_t index) -> std::any { if (childTypesOfNodeMatch(index, {"additive-expression", "+", "multiplicative-expression"})) { - if (getValue(index, 0).type() != typeid(FlowGraph::Data)) + if (getValue(index, 0).type() != typeid(FlowGraph::Graph)) throw std::runtime_error("ICE: additive-expression: Bad data type for argument 1: "s + demangle(getValue(index, 0).type())); - if (getValue(index, 2).type() != typeid(FlowGraph::Data)) + if (getValue(index, 2).type() != typeid(FlowGraph::Graph)) throw std::runtime_error("ICE: additive-expression: Bad data type for argument 3: "s + demangle(getValue(index, 2).type())); - FlowGraph::LocalScope scope; // TODO: move to context! - FlowGraph::Data destination{FlowGraph::MakeTemporaryInt(scope)}; - FlowGraph::Data value0 {std::any_cast<FlowGraph::Data>(getValue(index, 0))}; - FlowGraph::Data value1 {std::any_cast<FlowGraph::Data>(getValue(index, 2))}; + FlowGraph::Graph value0{std::any_cast<FlowGraph::Graph>(getValue(index, 0))}; + std::shared_ptr<FlowGraph::Node> lastOp0{value0.lastOp()}; + + FlowGraph::Graph value1{std::any_cast<FlowGraph::Graph>(getValue(index, 2))}; + std::shared_ptr<FlowGraph::Node> lastOp1{value1.lastOp()}; + + FlowGraph::Graph result{value0}; + result.append(value1); - std::shared_ptr<FlowGraph::Node> node {std::make_shared<FlowGraph::BinaryOperation>(FlowGraph::BinaryOperationType::Add, destination, value0, value1)}; - return node; + FlowGraph::Data destination{FlowGraph::MakeTemporaryInt(result.scope())}; + + std::shared_ptr<FlowGraph::Node> node {std::make_shared<FlowGraph::BinaryOperation>(FlowGraph::BinaryOperationType::Add, + destination, + lastOp0->destination(), lastOp1->destination())}; + + result.append(node); + + return result; } if (childTypesOfNodeMatch(index, {"multiplicative-expression"})) return getValue(index, 0); @@ -519,27 +541,13 @@ std::unordered_map<std::string, std::function<std::any(index_t)>> CPP::getNodeEv { "expression", [&](index_t index) -> std::any { if (childTypesOfNodeMatch(index, {"assignment-expression"})) { - std::shared_ptr<FlowGraph::CreateScopeOp> scope_node {std::make_shared<FlowGraph::CreateScopeOp>()}; - mCPPContext.graph.push_back(scope_node); - - FlowGraph::LocalScope& scope{scope_node->scope()}; - - if (getValue(index, 0).type() == typeid(FlowGraph::Data)) { // got Data -> make trivial Node out of it and return it - - FlowGraph::Data destination{FlowGraph::MakeTemporaryInt(scope)}; - FlowGraph::Data source {std::any_cast<FlowGraph::Data>(getValue(index, 0))}; - - std::shared_ptr<FlowGraph::Node> node {std::make_shared<FlowGraph::UnaryOperation>(FlowGraph::UnaryOperationType::Store, destination, source)}; - mCPPContext.graph.push_back(node); - return node; - } else if (getValue(index, 0).type() == typeid(std::shared_ptr<FlowGraph::Node>)) { - std::shared_ptr<FlowGraph::Node> node {std::any_cast<std::shared_ptr<FlowGraph::Node>>(getValue(index, 0))}; - mCPPContext.graph.push_back(node); - return getValue(index, 0); + if (getValue(index, 0).type() == typeid(FlowGraph::Graph)) { + FlowGraph::Graph graph {std::any_cast<FlowGraph::Graph>(getValue(index, 0))}; + mCPPContext.graph = graph; + return graph; } else { throw std::runtime_error("ICE: expression: Unsupported argument type: "s + demangle(getValue(index, 0).type())); } - mCPPContext.graph.push_back(std::make_shared<FlowGraph::DestroyScopeOp>(scope)); } throw std::runtime_error("ICE: Unsupported childs: "s + ruleString(index)); // TODO } @@ -576,7 +584,8 @@ void CPP::getValueOfToken(index_t index) if (m_tokens[index].type == "literal") { // TODO: also support other types, different from Int FlowGraph::Data data{FlowGraph::MakeConstantInt(stoi(m_tokens[index].value))}; - mValues.push_back(data); + FlowGraph::Graph graph{{std::make_shared<FlowGraph::DataNode>(data)}}; + mValues.push_back(graph); } else { mValues.push_back(std::any{}); } diff --git a/flowgraph/graph.cpp b/flowgraph/graph.cpp index e8b6b5e..6398c68 100644 --- a/flowgraph/graph.cpp +++ b/flowgraph/graph.cpp @@ -1 +1,61 @@ #include "graph.h" + +#include "minicc.h" +#include "node.h" + +#include <string> + +using namespace std::string_literals; + +FlowGraph::Graph::Graph() +{ +} + +FlowGraph::Graph::Graph(const std::deque<std::shared_ptr<Node>>& nodes): std::deque<std::shared_ptr<Node>>(nodes) +{ + auto createLocalScope {std::make_shared<FlowGraph::CreateScopeOp>()}; + this->push_front(createLocalScope); + + auto destroyLocalScope {std::make_shared<FlowGraph::DestroyScopeOp>(createLocalScope->scope())}; + this->push_back(destroyLocalScope); +} + +FlowGraph::Graph::~Graph() +{ +} + +// Assume first node of graph to be CreateScopeOp +FlowGraph::LocalScope& FlowGraph::Graph::scope() const +{ + if (this->empty()) + throw std::runtime_error("ICE: FlowGraph expected to be non-empty!"); + + auto& front_deref {*front()}; + + if (typeid(front_deref) != typeid(FlowGraph::CreateScopeOp)) + throw std::runtime_error("ICE: Bad type of first graph element: "s + demangle(typeid(front_deref))); + + FlowGraph::CreateScopeOp& createScope {dynamic_cast<FlowGraph::CreateScopeOp&>(front_deref)}; + + return createScope.scope(); +} + +void FlowGraph::Graph::append(const FlowGraph::Graph& other) +{ + this->insert(this->end() - 1, other.begin() + 1, other.end() - 1); + + this->scope().append(other.scope()); +} + +void FlowGraph::Graph::append(std::shared_ptr<Node> node) +{ + this->insert(this->end() - 1, node); +} + +std::shared_ptr<FlowGraph::Node> FlowGraph::Graph::lastOp() const +{ + if (size() >= 3) + return *(end() - 2); + + throw std::runtime_error("ICE: No last operation found in graph"); +} diff --git a/flowgraph/graph.h b/flowgraph/graph.h index 265a3bd..15c6aef 100644 --- a/flowgraph/graph.h +++ b/flowgraph/graph.h @@ -2,18 +2,32 @@ #include "node.h" +#include <deque> #include <exception> #include <memory> -#include <string> #include <stdexcept> -#include <vector> namespace FlowGraph { - class Graph: public std::vector<std::shared_ptr<Node>> + class Graph: public std::deque<std::shared_ptr<Node>> { public: - Graph() {} + Graph(); + Graph(const std::deque<std::shared_ptr<Node>>& nodes); + Graph(const Graph& other) = default; + ~Graph(); + + Graph& operator= (const Graph&) = default; + + // returns the outermost scope inside this graph + LocalScope& scope() const; + + // append other graph by joining the respective outermost scopes + void append(const Graph& other); + + void append(std::shared_ptr<Node> node); + + std::shared_ptr<Node> lastOp() const; }; } diff --git a/flowgraph/node.cpp b/flowgraph/node.cpp index 9b68d74..e0912dc 100644 --- a/flowgraph/node.cpp +++ b/flowgraph/node.cpp @@ -6,6 +6,14 @@ using namespace FlowGraph; +FlowGraph::Data& Node::destination() +{ + if (mOperands.size() < 1) + throw std::runtime_error("ICE: No destination operand available"); + + return mOperands[0]; +} + // 4 byte for now Data FlowGraph::MakeConstantInt(int i) { diff --git a/flowgraph/node.h b/flowgraph/node.h index 77395f0..5ea194d 100644 --- a/flowgraph/node.h +++ b/flowgraph/node.h @@ -19,8 +19,11 @@ namespace FlowGraph { public: Node(){} Node(std::vector<Data> operands): mOperands(operands) {} - std::vector<Data>& operands() { return mOperands; } virtual ~Node() {}; // force class to be polymorphic (e.g. in a container) + + std::vector<Data>& operands() { return mOperands; } + Data& destination(); // best-effort return of result/destination; else throw + private: std::vector<Data> mOperands; }; @@ -115,7 +118,6 @@ namespace FlowGraph { Increment, Decrement, Negate, - Store // just take Data as-is to store it at destination }; class UnaryOperation: public Node @@ -130,6 +132,16 @@ namespace FlowGraph { UnaryOperationType m_type; }; + // Just take a value e.g. Immediate and store it for later use. + // Should be optimized out later. + class DataNode: public Node + { + public: + DataNode(Data& value): + Node(std::vector<Data>({value})) + {} + }; + enum class BinaryOperationType: int { Add, diff --git a/flowgraph/scope.cpp b/flowgraph/scope.cpp new file mode 100644 index 0000000..6c2e30c --- /dev/null +++ b/flowgraph/scope.cpp @@ -0,0 +1,28 @@ +#include "scope.h" + +#include "storage.h" + +void FlowGraph::LocalScope::push_back(std::shared_ptr<Data> data) +{ + m_variables.push_back(data); +} + +void FlowGraph::LocalScope::append(const FlowGraph::LocalScope& other) +{ + m_variables.insert(m_variables.end(), other.m_variables.begin(), other.m_variables.end()); +} + +index_t FlowGraph::LocalScope::indexOfStorage(const TemporaryStorage& storage) const +{ + for (index_t i = 0; i < m_variables.size(); i++) { + FlowGraph::Storage& i_storage {*(m_variables[i]->storage())}; + + if (typeid(i_storage) == typeid(FlowGraph::TemporaryStorage)) { + FlowGraph::TemporaryStorage& temporaryStorage{dynamic_cast<FlowGraph::TemporaryStorage&>(i_storage)}; + if (&temporaryStorage == &storage) // compare addresses + return i; + } + } + + throw std::runtime_error("ICE: Storage not found"); +} diff --git a/flowgraph/scope.h b/flowgraph/scope.h new file mode 100644 index 0000000..50003f4 --- /dev/null +++ b/flowgraph/scope.h @@ -0,0 +1,29 @@ +#pragma once + +#include "data.h" + +#include "minicc.h" + +#include <cstddef> +#include <memory> +#include <vector> + +namespace FlowGraph { + + class TemporaryStorage; ///< Forward declaration + + // Provide a context for local temporaries name generation + class LocalScope + { + public: + LocalScope() = default; + + void push_back(std::shared_ptr<Data> data); + void append(const LocalScope& other); + index_t indexOfStorage(const TemporaryStorage& storage) const; + + private: + std::vector<std::shared_ptr<Data>> m_variables; + }; + +} diff --git a/flowgraph/storage.cpp b/flowgraph/storage.cpp index f78a65d..7e502de 100644 --- a/flowgraph/storage.cpp +++ b/flowgraph/storage.cpp @@ -3,6 +3,11 @@ using namespace std::string_literals; FlowGraph::TemporaryStorage::TemporaryStorage(LocalScope& scope): - m_name("__local_"s + std::to_string(scope.getNewIndex())) -{} + m_scope(scope) +{ +} +std::string FlowGraph::TemporaryStorage::name() const +{ + return "__local_"s + std::to_string(m_scope.indexOfStorage(*this)); +} diff --git a/flowgraph/storage.h b/flowgraph/storage.h index fd3c085..7f648b0 100644 --- a/flowgraph/storage.h +++ b/flowgraph/storage.h @@ -2,6 +2,7 @@ #pragma once #include "data.h" +#include "scope.h" #include <cstdint> #include <string> @@ -37,16 +38,7 @@ namespace FlowGraph { std::string m_name; }; - // Provide a context for local temporaries name generation - class LocalScope - { - public: - LocalScope() = default; - size_t getNewIndex() { return m_index++; } - private: - size_t m_index{ 0 }; - }; - + // named values class LocalStorage : public Storage { public: @@ -62,9 +54,9 @@ namespace FlowGraph { { public: TemporaryStorage(LocalScope& scope); - const std::string& name() const { return m_name; } + std::string name() const; private: - std::string m_name; + LocalScope& m_scope; }; // dereferenced pointer |