Semantic Analysis — Type System과 Type Checking
Douglas Thain, Introduction to Compilers and Language Design (2nd ed., 2023) · pp.99–118
📌단원 개요
AST 구성을 마쳤으니, 이제 프로그램의 구조가 아니라 실제 의미(semantics)를 분석한다. 이 단계가 semantic analysis이며 핵심은 type checking이다.
- ① name resolution — expression 안의 각 identifier가 어떤 정의(local / parameter / global)를 가리키는지 결정한다. 각 정의를 symbol table에 등록한다.
- ② type checking — name resolution이 끝나면 모든 정보가 모이므로, standard conversion rule에 따라 복합 expression의 type을 계산하고 검증한다.
- ③ 그 외 correctness 검사 — array 범위, bad pointer traversal, control flow 등도 semantic analysis에 포함된다. 언어 설계에 따라 compile time 혹은 runtime에 검출된다.
- type system은 programmer가 verifiable assertion을 하게 해주고, 그것을 compiler가 자동으로 검사한다.
- 덕분에 error를 runtime이 아니라 compile-time에 검출할 수 있다.
- 언어마다 접근이 다르다: C는 weak type system(잘못하면 심각한 error 가능), Ada는 strong type system(대신 compile 자체가 까다로움).
7.1Overview of Type Systems
대부분의 언어는 모든 value(literal, constant, variable)에 type을 부여한다. type은 그 data를 어떻게 해석할지를 나타낸다 — integer, floating point, boolean, string, pointer 등.
이런 atomic type들은 enumeration, structure, variant type 같은 higher-order type으로 결합되어 복잡한 제약을 표현할 수 있다.
type system의 3가지 목적
| 목적 | 설명 |
|---|---|
| Correctness | 부적절한 동작을 시도하면 warning/error를 발생. 예: pointer variable에 integer 할당은 거의 항상 error. runtime error를 compile time에 미리 걸러냄. |
| Performance | type 정보를 이용해 가장 효율적인 구현을 찾음. 예: 변수가 constant임을 알면 register에 한 번 load해 여러 번 재사용. |
| Expressiveness | type system으로 추론 가능한 사실은 생략 가능 → 더 간결. 예: B-Minor의 print는 integer/string/boolean을 명시할 필요 없이 type을 inference해 알맞게 출력. |
type system 분류의 3축
- unsafe: 프로그램의 기본 구조를 위반하는 wildly undefined behavior를 작성 가능. 예: C에서 임의의 pointer로 메모리 아무 word나 수정 가능. OS·driver 같은 low-level 코드엔 필요하지만 일반 코드엔 문제.
- safe: 어떤 입력에도 항상 well-defined 방식으로 실행되어 언어의 abstraction을 보존. array 경계, pointer 사용, type 할당을 강제. Perl, Python, Java 같은 대부분의 interpreted language가 safe.
unsafe — C (배열 경계 침범, 결과 미정)
/* This is C code */
int i;
int a[10];
for(i=0;i<100;i++) a[i] = i;
safe — C# (runtime에 bounds checking → exception)
/* This is C-sharp code */
a = new int[10];
for(int i=0;i<100;i++) a[i] = i;
// IndexOutOfRangeException 발생
- static: 모든 typechecking을 compile-time에 수행. 실행 시 type 정보를 유지할 필요가 없어 가장 high performance code를 생성. 단, 일부 편리한 idiom은 불가능.
- static typing은 integer vs floating point 구분에 자주 쓰임.
(a+b)가 integer면 X86에서ADDL, floating point면FADD명령으로 번역됨 →a,b의 type을 먼저 알아야+의 의미를 결정. - dynamic: type 정보를 runtime에 data와 함께 메모리에 저장. 실행 중 각 연산의 안전성을 operand의 type 비교로 검사하고, 호환되지 않으면 runtime type error로 중단. 변수의 type을 명시적으로 검사 가능(예: Java의
instanceof).
dynamic 예 — Java instanceof
/* This is Java code */
public void sit( Furniture f ) {
if (f instanceof Chair) {
System.out.println("Sit up straight!");
} else if ( f instanceof Couch ) {
System.out.println("You may slouch.");
} else {
System.out.println("You may sit normally.");
}
}
- explicit: programmer가 변수 등의 type을 직접 명시. 노력은 더 들지만 예기치 못한 error 가능성을 줄임. 예: C에서
int x = 32.5;는 precision loss로 error/warning 가능. 같은 표현이지만 의미가 다른 pointer 간 할당(int *i; float *f = i;)도 error/warning. - implicit: compiler가 가능한 범위에서 type을 inference. 더 간결하지만 의도치 않은 동작 가능. 예: C++11의
auto x = 32.5;→32.5가double이므로x도double로 추론.
/* This is C code */
int x = 32.5; // precision loss
/* This is C code */
int *i;
float *f = i; // pointer 타입 불일치
/* This is C++11 code */
auto x = 32.5; // x 는 double 로 추론
cout << x << endl;
- safe/unsafe = 언어 구조 위반(undefined behavior) 가능 여부.
- static/dynamic = typecheck를 compile-time에 하는가 runtime에 하는가.
- explicit/implicit = type을 programmer가 쓰는가 compiler가 inference하는가.
- 이 셋은 독립적인 축이다. (예: C = unsafe·static·explicit)
7.2Designing a Type System
type system을 기술하려면 atomic type, compound type, 그리고 type 간 assigning·converting rule을 설명해야 한다.
- 개별 변수를 기술하는 단순 type. 보통 assembly에서 single register에 저장됨: integer, floating point, boolean 등.
- 각 atomic type마다 지원 range를 명확히 정의해야 함. 예: integer는 signed/unsigned, 8/16/32/64 bit; floating point는 32/40/64 bit; character는 ASCII/Unicode.
user-defined types
atomic type으로 구현하되 range를 제한해 새 의미를 부여한다.
Ada — range 제한이 가능 (강함)
-- This is Ada code
type Day is range 1..31;
type Month is range 1..12;
Day와 Month를 서로 할당하거나, Month에 13을 주는 것을 방지.
C — typedef (약함)
/* This is C code */
typedef int Month;
typedef int Day;
Month m = 10;
Day d = m; /* 둘 다 int 라 허용됨 */
typedef는 새 이름만 줄 뿐 range 제한·할당 방지를 못 함.
enumerations
변수가 가질 수 있는 finite set의 symbolic value를 선언하는 user-defined type. 내부적으로는 integer지만 가독성을 높이고 불법 값 할당을 막는다. (C는 enumeration 선언은 되지만 integer와 섞어 쓰는 것을 막지 못함.)
/* This is Rust code */
enum Fuzzy { True, False, Uncertain };
compound types
structure (record) type — Go
/* This is Go code */
type coordinates struct {
latitude float64
longitude float64
}
여러 값을 하나의 큰 단위로 묶음.
union type — C
/* This is C code */
union number {
int i;
float f;
};
union number n;
n.i = 10;
n.f = 3.14;
n.i와 n.f가 같은 메모리를 차지 → 한쪽에 쓰고 다른 쪽을 읽으면 garbage. device driver 등에 가끔 유용.
- 여러 variant를 명시적으로 기술하며 각각 다른 field를 가짐. union과 비슷하지만 unsafe access를 방지한다.
- 선택된 type에 해당하는 field만 사용 가능 (예:
ADD면left/right,NAME이면name).
/* This is Rust code */
enum Expression {
ADD{ left: Expression, right: Expression },
MULTIPLY{ left: Expression, right: Expression },
INTEGER{ value: i32 },
NAME{ name: string }
}
unlike type을 함께 쓸 때의 4가지 선택지
integer i를 floating point f에 할당하거나, 함수 인자로 다른 type을 넘길 때 언어가 할 수 있는 일:
| 선택지 | 설명 |
|---|---|
| Disallow the assignment | 매우 엄격한 언어(B-Minor)는 error를 내고 compile을 막음. 정말 필요하면 IntToFloat 같은 built-in conversion function 호출을 요구. |
| Perform a bitwise copy | 저장 크기가 같으면 bit를 그대로 복사. 보통 나쁜 생각(의미 보장 없음). C의 서로 다른 pointer type 할당 등 일부에서만 발생. |
| Convert to an equivalent value | compiler의 built-in conversion으로 값을 변환(integer↔floating point, signed↔unsigned). 단 safe하다는 뜻은 아님 — 정보 손실로 까다로운 bug 가능. |
| Interpret the value in a different way | 동등하진 않지만 유용한 다른 값으로 변환. 예: Perl에서 list를 scalar context로 복사하면 list 내용 대신 길이가 들어감. |
@days = ("Monday", "Tuesday", "Wednesday", ... );
@a = @days; # copies the array to array a
$b = @days; # puts the length of the array into b
- type system 기술의 3요소: atomic type · compound type · assign/convert rule.
- union은 같은 메모리를 공유(unsafe), variant는 선택된 field만 노출(safe).
- Ada는 range 제한이 되는 strong user-defined type, C의
typedef는 이름만 바꾸는 weak한 것. - implicit conversion이 일어난다고 해서 그것이 safe한 것은 아니다(정보 손실 가능).
7.3The B-Minor Type System
B-Minor의 type system은 safe, static, explicit이다. 그래서 기술이 간결하고 구현이 직관적이며 많은 error를 제거한다. 다만 엄격해서 검출해야 할 error가 많다.
atomic types
integer— 64 bit signed integerboolean—true또는falsechar— ASCII 값string— ASCII, null terminatedvoid— 값을 반환하지 않는 function에만 사용
compound types
array [size] type
function type ( a: type, b: type, ... )
- value는 같은 type의 variable에만 할당 가능.
- function parameter는 같은 type의 value만 받음.
returnstatement의 type은 function return type과 일치해야 함.- 모든 binary operator는 좌·우변이 같은 type이어야 함.
- equality operator
!===:void·array·function을 제외한 모든 type에 적용, 항상 boolean 반환. - comparison operator
< <= >= >: integer에만 적용, 항상 boolean 반환. - boolean operator
! && ||: boolean에만 적용, 항상 boolean 반환. - arithmetic operator
+ - * / % ˆ ++ --: integer에만 적용, 항상 integer 반환.
7.4The Symbol Table
symbol table은 프로그램의 모든 declared variable(및 function 같은 named item)에 대해 알아야 할 정보를 기록한다. 각 entry는 struct symbol이다.
Figure 7.1 — The Symbol Structure
struct symbol {
symbol_t kind;
struct type *type;
char *name;
int which;
};
typedef enum {
SYMBOL_LOCAL,
SYMBOL_PARAM,
SYMBOL_GLOBAL
} symbol_t;
각 field의 의미
kind— local variable / global variable / function parameter 구분type— 변수의 type을 가리키는 type structure 포인터name— 이름which— local variable·parameter의 ordinal position(순서)
factory function
struct symbol * symbol_create( symbol_t kind,
struct type *type,
char *name ) {
struct symbol *s = malloc(sizeof(*s));
s->kind = kind;
s->type = type;
s->name = name;
return s;
}
- 개념적으로 symbol table은 변수 이름 → symbol structure의 map.
- 하지만 대부분의 언어는 같은 변수 이름을 서로 다른 scope에서 여러 번 쓸 수 있게 허용한다.
- C 계열(B-Minor 포함)의 scope: global scope, function parameter·local variable의 scope, 그리고 curly brace마다 생기는 nested scope.
같은 이름 x가 세 scope에 등장하는 예
아래 프로그램은 x를 type·storage class를 달리하며 세 번 정의한다. 실행 시 10 hello false를 출력해야 한다.
x: integer = 10;
f: function void ( x: string ) =
{
print x, "\n";
{
x: boolean = false;
print x, "\n";
}
}
main: function void () =
{
print x, "\n";
f("hello");
}
- symbol table을 hash table들의 stack으로 구성한다. 각 hash table은 한 scope의 이름→symbol을 map.
- 같은
x가 여러 scope에 충돌 없이 공존 가능. - scope에 진입하면 새 table을 push, scope를 떠나면 table을 pop.
Symbol Table API (Figure 7.3) — 6개 연산
void scope_enter();
void scope_exit();
int scope_level();
void scope_bind( const char *name, struct symbol *sym );
struct symbol *scope_lookup( const char *name );
struct symbol *scope_lookup_current( const char *name );
| 연산 | 의미 |
|---|---|
scope_enter() | 새 hash table을 stack top에 push (새 scope 시작) |
scope_exit() | 최상단 hash table 제거 |
scope_level() | 현재 stack의 hash table 개수 반환 (global scope 여부 판단에 유용) |
scope_bind(name,sym) | 최상단 table에 name → sym entry 추가 |
scope_lookup(name) | stack을 top → bottom으로 탐색, name이 정확히 일치하는 첫 entry 반환. 없으면 null |
scope_lookup_current(name) | scope_lookup과 같되 최상단 table만 탐색. 현재 scope에 이미 정의됐는지 판단용 |
- symbol table = stack of hash tables. scope 진입 시 push, 탈출 시 pop.
scope_lookup은 안쪽(top)부터 바깥(bottom)으로 탐색 → inner scope가 outer scope를 가린다(shadowing).scope_lookup_current는 최상단만 봄 → 중복 선언(redeclaration) 검사에 사용.symbol_t의 세 kind:SYMBOL_LOCAL,SYMBOL_PARAM,SYMBOL_GLOBAL.
7.5Name Resolution
symbol table이 준비되면, 변수 이름의 각 use를 그에 맞는 definition에 연결한다. 이 과정이 name resolution이다.
- AST의 각 구조에 대해
resolvemethod를 작성:decl_resolve(),stmt_resolve(),expr_resolve()등. - 이들이 모여 AST 전체를 순회하며 변수의 declaration과 use를 찾는다.
- declared: symbol table에 등록하고 symbol structure를 AST에 연결.
- used: symbol table에서 lookup하여 symbol structure를 AST에 연결.
- 같은 scope에서 두 번 선언되거나 선언 없이 사용되면 적절한 error message를 출력.
- 전체 AST에 대한 name resolution은 root node에서
decl_resolve()를 한 번 호출하면 된다(하위 함수들이 트리 전체를 순회).
선언 resolve (Figure 7.4)
각 decl은 변수 선언이므로 새 symbol을 만들어 현재 scope의 이름에 bind한다. expression 선언(d->value가 not null)이면 그 expression도 resolve하고, function 선언(d->code가 not null)이면 새 scope를 만들어 parameter와 code를 resolve한다.
void decl_resolve( struct decl *d )
{
if(!d) return;
symbol_t kind = scope_level() > 1 ?
SYMBOL_LOCAL : SYMBOL_GLOBAL;
d->symbol = symbol_create(kind,d->type,d->name);
expr_resolve(d->value);
scope_bind(d->name,d->symbol);
if(d->code) {
scope_enter();
param_list_resolve(d->type->params);
stmt_resolve(d->code);
scope_exit();
}
decl_resolve(d->next);
}
표현식 resolve (Figure 7.5)
EXPR_NAME이면 symbol table에서 lookup해 연결하고, 그 외엔 left·right 자식을 재귀적으로 resolve한다.
void expr_resolve( struct expr *e )
{
if(!e) return;
if( e->kind==EXPR_NAME ) {
e->symbol = scope_lookup(e->name);
} else {
expr_resolve( e->left );
expr_resolve( e->right );
}
}
stmt_resolve()— 각 sub-component에 알맞은 resolve를 호출.STMT_BLOCK의 경우 새 scope를 enter/leave 한다.param_list_resolve()— function의 각 parameter를 새 변수 선언으로 등록 → function의 code에서 그 정의들을 쓸 수 있게 함.
scope_level() > 1→ local, 아니면 global로 kind 결정.- declaration은 bind, use(
EXPR_NAME)는 lookup. - function 선언 처리:
scope_enter→ parameter resolve → code resolve →scope_exit. - name resolution 자체는 type을 검사하지 않는다 — 이름과 정의를 연결할 뿐. type 검사는 다음 단계.
7.6Implementing Type Checking
expression을 검사하기 전에 type structure를 다루는 helper 함수가 필요하다: equality·copy·delete.
type helper 함수들 (pseudo-code)
type_equals
boolean type_equals(
struct type *a, struct type *b )
{
if( a->kind == b->kind ) {
if( a and b are atomic types ){
Return true
} else if ( both are array ) {
Return true if subtype is
recursively equal
} else if ( both are function ) {
Return true if both subtype
and params are recursively equal
}
} else {
Return false
}
}
type_copy / type_delete
struct type * type_copy( struct type *t )
{
Return a duplicate copy of t,
making sure to duplicate subtype
and params recursively.
}
void type_delete( struct type *t )
{
Free all the elements of t
recursively.
}
- expression의 올바른 type을 계산해 반환한다.
- non-null expr에 호출되면 항상 newly-allocated type structure를 반환한다(코드 단순화).
- type 조합이 invalid해도 error를 출력하되 valid type을 반환한다 → compiler가 계속 진행하며 가능한 한 많은 error를 찾게 함.
- 방식: expression tree의 recursive post-order traversal.
- leaf: node의 kind가 곧 type (integer literal → integer, string literal → string). 변수 이름이면 symbol pointer를 따라가 symbol structure의 type을 copy해 반환.
- interior node: left·right subtree의 type을 비교해 7.3의 rule과 호환되는지 확인. 안 되면 error 출력 후 global error counter 증가. 어느 쪽이든 operator에 맞는 type을 반환하고, 더 이상 필요 없는 left·right type은 delete.
기본 구조
struct type * expr_typecheck( struct expr *e )
{
if(!e) return 0;
struct type *lt = expr_typecheck(e->left);
struct type *rt = expr_typecheck(e->right);
struct type *result;
switch(e->kind) {
case EXPR_INTEGER_LITERAL:
result = type_create(TYPE_INTEGER,0,0);
break;
case EXPR_STRING_LITERAL:
result = type_create(TYPE_STRING,0,0);
break;
/* more cases here */
}
type_delete(lt);
type_delete(rt);
return result;
}
operator별 case 예시
arithmetic — integer만, integer 반환
case EXPR_ADD:
if( lt->kind!=TYPE_INTEGER ||
rt->kind!=TYPE_INTEGER ) {
/* display an error */
}
result = type_create(TYPE_INTEGER,0,0);
break;
equality — 양쪽 동일 type, boolean 반환
case EXPR_EQ:
case EXPR_NE:
if(!type_equals(lt,rt)) {
/* display an error */
}
if(lt->kind==TYPE_VOID ||
lt->kind==TYPE_ARRAY ||
lt->kind==TYPE_FUNCTION) {
/* display an error */
}
result = type_create(TYPE_BOOLEAN,0,0);
break;
array dereference a[i] — a는 array, i는 integer, subtype 반환
case EXPR_DEREF:
if(lt->kind==TYPE_ARRAY) {
if(rt->kind!=TYPE_INTEGER) {
/* error: index not an integer */
}
result = type_copy(lt->subtype);
} else {
/* error: not an array */
/* but we need to return a valid type */
result = type_copy(lt);
}
break;
declaration / statement typecheck
대부분의 작업은 expr_typecheck에서 일어나지만, declaration·statement 등 다른 AST 요소도 typecheck해야 한다. 이들은 AST를 순회하며 expression type을 계산해 declaration이나 제약과 비교한다.
decl_typecheck
void decl_typecheck( struct decl *d )
{
if(d->value) {
struct type *t;
t = expr_typecheck(d->value);
if(!type_equals(t,d->symbol->type)) {
/* display an error */
}
}
if(d->code) {
stmt_typecheck(d->code);
}
}
선언이 initializer와 일치하는지 확인하고, function이면 body를 typecheck.
stmt_typecheck
void stmt_typecheck( struct stmt *s )
{
struct type *t;
switch(s->kind) {
case STMT_EXPR:
t = expr_typecheck(s->expr);
type_delete(t);
break;
case STMT_IF_THEN:
t = expr_typecheck(s->expr);
if(t->kind!=TYPE_BOOLEAN) {
/* display an error */
}
type_delete(t);
stmt_typecheck(s->body);
stmt_typecheck(s->else_body);
break;
/* more cases here */
}
}
if-then의 control expression은 boolean이어야 함.
expr_typecheck는 post-order traversal: 자식 type을 먼저 계산 후 부모에서 결합.- error가 있어도 valid type을 반환해 여러 error를 한 번에 검출(error counter 증가).
- leaf의 type은 node kind로 결정, name은 symbol의 type을 copy.
- 다 쓴
lt·rt는type_delete로 해제(메모리 관리). ==/!=는void·array·function제외 + 양쪽 type 동일 → boolean.if-thencontrol expression은 boolean type이어야 함.
7.7Error Messages
compiler는 형편없는 error message로 악명 높다. 하지만 지금까지 만든 구조 덕분에, 어떤 type이 발견됐고 무엇이 문제인지 정확히 설명하는 message를 쉽게 출력할 수 있다.
type 문제가 가득한 B-Minor 코드
s: string = "hello";
b: boolean = false;
i: integer = s + (b<5);
error: type compatibility in expression
error: cannot compare a boolean (b)
to an integer (5)
error: cannot add a boolean (b<5)
to a string (s)
문제 발견 시 관련된 expression과 type을 정성껏 출력하면 된다.
printf("error: cannot add a ");
type_print(lt);
printf(" (");
expr_print(e->left);
printf(") to a ");
type_print(rt);
printf(" (");
expr_print(e->right);
printf(")\n");
- 좋은 error message = 발견된 type과 문제의 expression을 함께 보여줌(
type_print+expr_print). - 앞서 만든 AST·type 구조 덕분에 상세한 message 출력이 어렵지 않다.
7.8Exercises (연습문제)
- 기존 hash table 구현을 출발점으로
symbol.c·scope.c의 symbol·scope 함수를 구현하라. stmt_resolve()·param_list_resolve()등 필요한 코드를 작성해 name resolution을 완성하라.decl_resolve()·expr_resolve()를 수정해 같은 이름 중복 선언 또는 선언 없는 사용 시 error를 표시하라.expr_typecheck를 완성해 모든 종류의 expression의 type을 검사·반환하라.stmt_typecheck를 완성해 각 statement 종류별 제약을 강제하라.%T(type),%E(expression) 같은 기호를 지원하는myprintf를 작성해 error message 출력을 쉽게 하라.stdarg.h의 variadic function을 참고하라.
myprintf(
"error: cannot add a %T (%E) to a %T (%E)\n",
lt,e->left,rt,e->right
);
7.9Further Reading
| 문헌 | 비고 |
|---|---|
| H. Abelson, G. Sussman, J. Sussman, Structure and Interpretation of Computer Programs | MIT Press, 1996 |
| B. Pierce, Types and Programming Languages | MIT Press, 2002 |
| D. Friedman, D. Christiansen, The Little Typer | MIT Press, 2018 |
⭐핵심 정리 / 시험 포인트
- semantic analysis = AST의 구조가 아닌 의미 분석. 핵심은 type checking.
- 순서: name resolution → symbol table 구축 → type checking.
- type system 3축: safe/unsafe · static/dynamic · explicit/implicit (서로 독립).
- static = compile-time typecheck, dynamic = runtime에 type 정보 보관·검사.
- type system 3목적: correctness · performance · expressiveness.
- B-Minor = safe · static · explicit. integer는 64-bit signed.
- union은 메모리 공유(unsafe), variant는 선택 field만 노출(safe).
- symbol table = stack of hash tables. scope 진입 push, 탈출 pop.
scope_lookup=top→bottom 탐색(shadowing),scope_lookup_current=top만(중복 선언 검사).- name resolution: declaration은 bind, use는 lookup.
expr_typecheck는 post-order, error 시에도 valid type 반환(여러 error 검출).- arithmetic·comparison·boolean operator는 operand type이 고정, 반환 type도 고정(7.3 rule).
| operator | 입력 → 출력 |
|---|---|
+ - * / % ˆ ++ -- | integer → integer |
< <= >= > | integer → boolean |
! && || | boolean → boolean |
== != | 동일 type(void/array/function 제외) → boolean |
SYMBOL_LOCAL/SYMBOL_PARAM/SYMBOL_GLOBALscope_level() > 1→ local, 아니면 globalwhich= local·parameter의 ordinal position
- Q. name resolution이란? → 변수 이름의 각 use를 그 definition에 연결하고 symbol table에 등록하는 과정.
- Q. symbol table은 왜 단순 map이 아닌 stack of hash table인가? → 같은 이름을 다른 scope에서 재사용하도록 허용하기 위해.
- Q.
scope_lookup과scope_lookup_current의 차이는? → 전자는 top→bottom 전체 탐색, 후자는 최상단 table만 → 중복 선언 검사용. - Q.
expr_typecheck가 error 시에도 valid type을 반환하는 이유는? → compiler가 멈추지 않고 가능한 한 많은 error를 한 번에 찾기 위해. - Q.
s + (b<5)의 type error를 설명하라 → boolean(b)과 integer(5) 비교 불가, boolean(b<5)과 string(s) 덧셈 불가.
📋한눈에 보기 — Cheat Sheet
| 키워드 / 항목 | 의미 |
|---|---|
| semantic analysis | 프로그램의 semantic을 검증. 핵심 = type checking |
| name resolution | 변수 use를 definition에 연결, symbol table에 등록 |
| type checking | conversion rule로 expression type 계산·검증 |
| safe / unsafe | 언어 구조 위반(undefined behavior) 가능 여부 |
| static / dynamic | typecheck를 compile-time / runtime에 수행 |
| explicit / implicit | type을 programmer가 명시 / compiler가 inference |
| atomic type | integer, boolean, char, string, void … |
| compound type | array, function, structure, union, variant |
| union | 같은 메모리 공유(unsafe access 가능) |
| variant type | 선택된 type의 field만 노출(safe) |
| symbol table | stack of hash tables. scope마다 push/pop |
| struct symbol | kind · type · name · which |
| symbol_t | SYMBOL_LOCAL / SYMBOL_PARAM / SYMBOL_GLOBAL |
| scope_enter / exit | 새 scope table push / 최상단 pop |
| scope_bind | 최상단 table에 name → symbol 추가 |
| scope_lookup | top→bottom 전체 탐색(shadowing) |
| scope_lookup_current | 최상단만 탐색(중복 선언 검사) |
| decl_resolve / expr_resolve | declaration bind / use lookup |
| expr_typecheck | post-order로 type 계산, error에도 valid type 반환 |
| type_equals / copy / delete | type 동등 비교 / 복제 / 해제(재귀) |
| B-Minor type system | safe · static · explicit, integer=64-bit signed |