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 編譯器中的型別檢查分為多個階段:

  1. 型別填充階段 (fill_candidate_datatypes_c):

    • 為每個節點確定可能的型別候選列表
    • 對於 simple_spec_init_c 節點,會分別處理 simple_specificationconstant 子節點
  2. 型別窄化階段 (narrow_candidate_datatypes_c):

    • 從候選型別中選擇最合適的型別
    • datatype 欄位設置為選擇的型別
  3. 型別檢查階段 (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 之間的不相容可能因下列原因未被正確檢測:

  1. 檢測邏輯問題

    • print_datatypes_error_c::visit(simple_spec_init_c *symbol) 只檢查型別是否有效,而不檢查相容性
    • 相容性檢查發生在 fill_candidate_datatypes_c,但可能存在邏輯漏洞
  2. 型別轉換處理

    • 一些編譯器會嘗試隱式轉換(如 10TRUE/FALSE),但這違反 IEC 61131-3 標準
    • 標準要求布林值只能賦值為 TRUE/FALSE 或布林表達式
  3. 錯誤優先級與報告機制

    • 如果有多個錯誤,編譯器可能只報告第一個發現的錯誤
    • 可能是錯誤信息被其他優先級更高的錯誤覆蓋了

三、深入探討型別相容性規則

在 matiec 中,型別相容性判斷主要基於 get_datatype_info_c 類中的一系列 is_* 函數:

  1. 布林型別規則:

    • BOOL 型別只能與 BOOL 型別或布林常量(TRUE/FALSE)相容
    • 數值型別(如 INTREAL)不能自動轉換為 BOOL
  2. 數值型別間的轉換規則:

    • INTREAL: 允許(擴展轉換)
    • REALINT: 不允許(需要顯式轉換函數,如 REAL_TO_INT
    • 同類型的數值型別之間允許轉換,如 SINTINTDINTLINT
  3. 安全型別規則

    • 標準型別不能直接賦值給安全型別(如 INTSAFEINT
    • 安全型別可以賦值給非安全型別

四、可能的問題修復方案

要修復 g1 : BOOL := 10; 的型別相容性檢查問題,可以考慮:

  1. 修復 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;
    }
    
  2. 正確編寫 IEC 程式

    VAR_GLOBAL
        g1      : BOOL := TRUE;  // 或 FALSE,而不是數值
        g2      : INT := 10;
    END_VAR
    
  3. 使用顯式轉換

    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 型別變數,避免使用數值作為布林值初始化。