編譯器與程式碼產生

語意分析(semantic analysis)

想像讀一句文法完美卻毫無意義的話,像「那個數字吃掉了星期二」。文法沒問題,意義卻壞了。剖析程式就像檢查文法——它確認形狀合法。語意分析是下一步:在語言規則下,它檢查這個看起來合法的程式到底有沒有「意義」。

語意分析在剖析之後執行,作用於剖析器建好的抽象語法樹上。它做兩件大事。名稱解析(name resolution)弄清每個識別字指向什麼:當你寫 count,那是哪一個 count 的宣告、在哪個範圍、是什麼型別。型別檢查(type checking)接著驗證每個操作對所涉型別都合法:你可以把兩個 int 相加,但把結構指派給 int、呼叫一個非函式、或回傳錯誤型別,都會在這裡被拒。一路上它也施行語言其他的靜態規則——不能在宣告前使用變數、switch 標籤必須是常數、const 資料不能被指派。輸出是一棵完整標註型別、檢查過的樹,準備好降階成 IR。

它重要在於:這個階段產生最有用的編譯錯誤:「未宣告的識別字」、「型別不相容」、「引數太少」。這是編譯器在任何程式碼執行前抓出真正的錯誤。但要誠實面對它的極限:語意分析是「靜態」檢查。它確認程式在型別規則下一致;它無法判斷你的迴圈是否會結束、指標在執行期是否有效、或你的邏輯是否正確。通過語意分析代表型別正確,不代表沒有 bug。

int n = 3; char *s = n; // 語意錯誤:把 int 指派給 char *(型別不相容) undeclared = 5; // 語意錯誤:'undeclared' 從未宣告 int r = add(1); // 若 add() 需要兩個引數,這是語意錯誤

每一行在文法上都剖析得過;是語意分析以「意義」為由拒絕它們。

型別正確不等於程式正確:語意分析證明你的程式遵守型別與範圍規則,而非證明它算出正確的結果、或避開執行期錯誤與未定義行為。

又稱
semastatic checkingtype checking and name resolution語意檢查