步骤2:VAR_TEMP 链接层(FB 布局排除 Temp 字段 + 顶层防御校验 + scope 符号映射 Local)+ linker_test

This commit is contained in:
2026-08-26 17:55:24 +08:00
parent aa0c66780c
commit 94634dc0b0
2 changed files with 75 additions and 5 deletions
+13 -5
View File
@@ -214,6 +214,11 @@ namespace {
b.vars.empty() ? 0 : b.vars[0].line,
b.vars.empty() ? 0 : b.vars[0].col);
}
if (b.section == VarSection::Temp) {
return fail(u.path, "VAR_TEMP only allowed in POU",
b.vars.empty() ? 0 : b.vars[0].line,
b.vars.empty() ? 0 : b.vars[0].col);
}
for (const VarDecl& d : b.vars) {
if (out_->global_index.count(d.name)) {
return fail(u.path, "duplicate global '" + d.name + "'",
@@ -266,9 +271,11 @@ namespace {
FbLayout lay;
lay.type_name = p.name;
for (const VarBlock& b : p.blocks) {
// 字段 = input / output / 内部 varexternalglobal 不是字段
// 字段 = input / output / 内部 varexternal/global/temp 不是字段
//(VAR_TEMP 不持久,每次调用重新初始化,走帧寄存器)
if (b.section == VarSection::External ||
b.section == VarSection::Global) {
b.section == VarSection::Global ||
b.section == VarSection::Temp) {
continue;
}
for (const VarDecl& d : b.vars) {
@@ -331,9 +338,10 @@ namespace {
/**
* @brief 建一个 POU 的局部符号
* @details VAR/INPUT/OUTPUT → 对应种类;VAR_EXTERNAL → 查全局表接同一槽
* (类型必须一致);FB 类型 → FbInstance + 布局(内建取冻结表,
* 用户类型查 fb_types);POU 内 VAR_GLOBAL → 拒绝
* @details VAR/INPUT/OUTPUT → 对应种类;VAR_TEMP → Local(帧寄存器,
* 不持久);VAR_EXTERNAL → 查全局表接同一槽(类型必须一致);
* FB 类型 → FbInstance + 布局(内建取冻结表,用户类型查 fb_types);
* POU 内 VAR_GLOBAL → 拒绝
* @param file 出错文件
* @param p POU AST
* @param sc 输出作用域(syms / fb_instances 被填充)
+62
View File
@@ -70,6 +70,67 @@ static bool expect_link_err(const char* dir, const char* keyword) {
return true;
}
// ---- 0. VAR_TEMPFB 布局排除 Temp、scope 符号为 Local ----
static bool test_var_temp() {
using namespace compiler;
const std::string toml = std::string(REPO_ROOT) + "/tests/cases/13_function_call/project.toml";
Project p;
std::string err;
CHECK(parse_project(toml, &p, &err));
SourceUnit u;
u.path = "main.st";
CHECK(parse_pous("main.st",
"FUNCTION_BLOCK Foo\n"
"VAR_INPUT\n in : BOOL;\nEND_VAR\n"
"VAR_OUTPUT\n out : BOOL;\nEND_VAR\n"
"VAR\n state : BOOL;\nEND_VAR\n"
"VAR_TEMP\n tmp : INT;\nEND_VAR\n"
" out := tmp > 0;\n"
"END_FUNCTION_BLOCK\n"
"PROGRAM MAIN\nEND_PROGRAM",
&u.ast, &err));
compiler::MachineConfig cfg;
std::string cerr;
CHECK(cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &cerr));
LinkResult r;
std::vector<SourceUnit> units;
units.push_back(std::move(u));
CHECK(link_project(p, units, cfg, &r, &err));
// FB 布局:字段 = input/output/内部 varVAR_TEMP 不是字段
const auto it = r.fb_types.find("foo");
CHECK(it != r.fb_types.end());
CHECK(it->second.fields.size() == 3);
CHECK(it->second.fields[0].name == "in");
CHECK(it->second.fields[1].name == "out");
CHECK(it->second.fields[2].name == "state");
for (const FbField& f : it->second.fields) {
CHECK(f.name != "tmp");
}
// scope 符号:VAR_TEMP → Local(在 Foo 作用域)
bool saw_tmp = false;
bool saw_main = false;
for (const auto& sc : r.scopes) {
if (sc.name == "foo") {
for (const Symbol& s : sc.syms) {
if (s.name == "tmp") {
saw_tmp = true;
CHECK(s.kind == SymbolKind::Local);
CHECK(s.type_name == "int");
}
}
}
if (sc.name == "main") {
saw_main = true;
}
}
CHECK(saw_tmp);
CHECK(saw_main);
return true;
}
// ---- 1. 用例 09GVL + VAR_EXTERNAL 同一槽 ----
static bool test_case09() {
@@ -229,6 +290,7 @@ static bool test_builtin() {
}
int main() {
if (!test_var_temp()) return 1;
if (!test_case09()) return 1;
if (!test_neg_cases()) return 1;
if (!test_case16()) return 1;