VAR_GLOBAL 是不是沒有跑到 stage3 - benchen2001/matiec GitHub Wiki
我在想 VAR_GLOBAL 是不是沒有跑到 stage3
GitHub Copilot 這個問題很有洞察力。讓我檢查一下 matiec 是否正確處理 VAR_GLOBAL 宣告的型別檢查。
首先,我需要確認 VAR_GLOBAL 宣告是否被正確納入 Stage 3 的型別檢查範圍。我會檢查處理 global_var_declarations_c 的相關程式碼在您提供的範例中,問題是在 VAR_GLOBAL 區塊裡,g1 : BOOL := 10; 包含了型別不相容的初始值。BOOL 型別變數不能直接賦值為整數 10,這應該會觸發 "Initial value has incompatible data type" 錯誤,但您指出這個錯誤沒有出現。讓我深入探討 matiec 編譯器中 VAR_GLOBAL 區塊的型別檢查機制。
matiec 中 VAR_GLOBAL 型別檢查機制的深入探討
當您在 IEC 61131-3 程式中宣告全域變數時,編譯器會檢查變數型別與初始值是否相容。這個檢查主要發生在 Stage 3 階段,讓我們來分析其實作細節。
1. 型別檢查的具體實現
matiec 編譯器對型別相容性的檢查主要在 Stage 3 階段進行,涉及以下關鍵元件:讓我查看 simple_spec_init_c 節點的型別檢查邏輯:讓我嘗試更廣泛的搜索:讓我查看 fill_candidate_datatypes 中的相關代碼:我需要查看型別檢查相關的源文件,來了解型別相容性判斷:讓我查看 get_datatype_info_c::is_type_valid 的實現:分析完上述源碼後,我們可以總結 matiec 編譯器中型別檢查機制:
matiec 編譯器中的 VAR_GLOBAL 型別相容性檢查機制深入探討
一、問題緣起
在您提供的範例中:
VAR_GLOBAL
g1 : BOOL := 10; // 型別不相容:BOOL 不能賦值為整數 10
g2 : INT := 10; // 正確:INT 可以賦值為整數 10
END_VAR
這裡的問題是 g1 變數宣告中存在型別不相容:BOOL 型別變數不能直接賦值為整數 10,這應該會觸發錯誤訊息:"Initial value has incompatible data type.",但您指出此錯誤未被報告。
二、型別檢查機制分析
1. 抽象語法樹節點結構
變數宣告 g1 : BOOL := 10; 在 AST 中被表示為以下結構:
var1_init_decl_c節點var1_list_c子節點 (含 "g1" 識別符)simple_spec_init_c子節點,其中:simple_specification指向bool_type_name_c(型別 BOOL)constant指向integer_c(值 10)
2. 型別檢查執行流程
matiec 編譯器中的型別檢查分為多個階段:
-
型別填充階段 (
fill_candidate_datatypes_c):- 為每個節點確定可能的型別候選列表
- 對於
simple_spec_init_c節點,會分別處理simple_specification和constant子節點
-
型別窄化階段 (
narrow_candidate_datatypes_c):- 從候選型別中選擇最合適的型別
- 將
datatype欄位設置為選擇的型別
-
型別檢查階段 (
print_datatypes_error_c):- 檢查每個節點的型別合法性
- 檢查表達式和賦值的型別相容性
3. 關鍵函數分析
void *print_datatypes_error_c::visit(simple_spec_init_c *symbol) {
// 檢查宣告的型別是否有效
if (!get_datatype_info_c::is_type_valid(symbol->simple_specification->datatype)) {
STAGE3_ERROR(0, symbol->simple_specification, symbol->simple_specification, "Invalid data type.");
}
// 如果有初始值,檢查初始值型別與變數型別相容性
else if (NULL != symbol->constant) {
if (!get_datatype_info_c::is_type_valid(symbol->constant->datatype))
STAGE3_ERROR(0, symbol->constant, symbol->constant, "Initial value has incompatible data type.");
}
// 最後的安全檢查
else if (!get_datatype_info_c::is_type_valid(symbol->datatype)) {
ERROR;
}
return NULL;
}
這個函數處理變數初始化,但只檢查型別是否有效,沒有檢查變數型別和初始值型別是否相容!
真正的型別相容性檢查發生在更早的階段:fill_candidate_datatypes_c::visit(simple_spec_init_c *symbol),它比較左右兩側型別,只有當它們相容時,才會將該型別添加到候選型別列表。
4. 問題所在
在 matiec 編譯器中,BOOL 型別和整數 10 之間的不相容可能因下列原因未被正確檢測:
-
檢測邏輯問題:
print_datatypes_error_c::visit(simple_spec_init_c *symbol)只檢查型別是否有效,而不檢查相容性- 相容性檢查發生在
fill_candidate_datatypes_c,但可能存在邏輯漏洞
-
型別轉換處理:
- 一些編譯器會嘗試隱式轉換(如
10→TRUE/FALSE),但這違反 IEC 61131-3 標準 - 標準要求布林值只能賦值為
TRUE/FALSE或布林表達式
- 一些編譯器會嘗試隱式轉換(如
-
錯誤優先級與報告機制:
- 如果有多個錯誤,編譯器可能只報告第一個發現的錯誤
- 可能是錯誤信息被其他優先級更高的錯誤覆蓋了
三、深入探討型別相容性規則
在 matiec 中,型別相容性判斷主要基於 get_datatype_info_c 類中的一系列 is_* 函數:
-
布林型別規則:
BOOL型別只能與BOOL型別或布林常量(TRUE/FALSE)相容- 數值型別(如
INT、REAL)不能自動轉換為BOOL
-
數值型別間的轉換規則:
INT→REAL: 允許(擴展轉換)REAL→INT: 不允許(需要顯式轉換函數,如REAL_TO_INT)- 同類型的數值型別之間允許轉換,如
SINT→INT→DINT→LINT
-
安全型別規則:
- 標準型別不能直接賦值給安全型別(如
INT→SAFEINT) - 安全型別可以賦值給非安全型別
- 標準型別不能直接賦值給安全型別(如
四、可能的問題修復方案
要修復 g1 : BOOL := 10; 的型別相容性檢查問題,可以考慮:
-
修復 matiec 編譯器:
// 在 fill_candidate_datatypes_c::visit(simple_spec_init_c *symbol) 中 // 添加特別的 BOOL 和整數相容性檢查 if (is_BOOL_type(symbol->simple_specification->datatype) && !is_BOOL_compatible(symbol->constant->datatype)) { // 不添加到候選型別列表 return NULL; } -
正確編寫 IEC 程式:
VAR_GLOBAL g1 : BOOL := TRUE; // 或 FALSE,而不是數值 g2 : INT := 10; END_VAR -
使用顯式轉換:
VAR_GLOBAL g1 : BOOL := (10 <> 0); // 顯式轉換為布林值 g2 : INT := 10; END_VAR
五、結論
matiec 編譯器的型別檢查機制在處理 BOOL := 10 這樣的型別不相容時可能存在漏洞,未能正確報告錯誤。根據 IEC 61131-3 標準,這是一個型別錯誤,應該被檢測出來。
真正的問題可能出在 fill_candidate_datatypes_c 階段,當它處理 simple_spec_init_c 節點時,沒有正確判斷 BOOL 型別與整數值的不相容性。而 print_datatypes_error_c::visit(simple_spec_init_c) 函數主要檢查型別是否有效,而不是檢查型別相容性。
建議遵循 IEC 61131-3 標準,只將 TRUE/FALSE 或布林表達式賦值給 BOOL 型別變數,避免使用數值作為布林值初始化。