semantic analysis
Imagine reading a sentence that is grammatically perfect but nonsense, like 'the number ate Tuesday'. The grammar is fine; the meaning is broken. Parsing a program is like checking the grammar — it confirms the shape is legal. Semantic analysis is the next step: it checks whether the legal-looking program actually MEANS something, given the rules of the language.
Semantic analysis runs after parsing, over the abstract syntax tree the parser built. It does two big things. Name resolution figures out what each identifier refers to: when you write count, which declaration of count is that, in which scope, of what type. Type checking then verifies every operation is allowed for the types involved: you may add two ints, but assigning a struct to an int, or calling a non-function, or returning the wrong type is rejected here. Along the way it enforces the language's other static rules — that you do not use a variable before declaring it, that a switch label is a constant, that const data is not assigned to. The output is a fully type-annotated, checked tree ready to lower into IR.
It matters because this is the stage that produces the most useful compile errors: 'undeclared identifier', 'incompatible types', 'too few arguments'. It is the compiler catching real mistakes before any code runs. But be honest about its limits: semantic analysis is a STATIC check. It confirms the program is consistent under the type rules; it cannot tell whether your loop terminates, whether a pointer is valid at run time, or whether your logic is correct. Passing semantic analysis means well-typed, not bug-free.
int n = 3; char *s = n; // sema error: assigning int to char * (incompatible types) undeclared = 5; // sema error: 'undeclared' was never declared int r = add(1); // sema error if add() needs two arguments
Each line parses fine as grammar; semantic analysis is what rejects them for meaning.
Well-typed is not the same as correct: semantic analysis proves your program obeys the type and scope rules, not that it computes the right thing or avoids run-time faults and undefined behavior.