SafeC 소개
SafeC는 결정론적이고, 리전을 인식하며, 컴파일 타임을 우선시하는 시스템 프로그래밍 언어입니다. C의 진화형으로서 완전한 C ABI 호환성을 유지하면서도 메모리 안전성, 타입 안전성, 실시간 결정론을 컴파일 타임에, 런타임 오버헤드 없이 강제합니다.
왜 SafeC인가?
C는 여전히 시스템 프로그래밍의 기반입니다: 운영체제, 임베디드 펌웨어, 오디오 엔진, 네트워크 스택. 하지만 C의 메모리 안전성 부재는 버그, 취약점, 정의되지 않은 동작(UB)의 지속적인 원천입니다.
기존 대안들은 각각 상당한 트레이드오프를 요구합니다:
- **C++**는 안전 기능을 추가하지만 동시에 숨겨진 비용도 추가합니다: 암묵적 생성자, 소멸자, 예외, RTTI, vtable.
- Rust는 강력한 안전성 보장을 제공하지만 완전히 새로운 소유권 모델, 새로운 생태계, 그리고 C와 인터페이스하기 위한 만만치 않은 FFI 학습을 요구합니다.
- Zig는 숨겨진 동작을 제거하지만 리전이 아닌 할당자(allocator)에 기반한 근본적으로 다른 메모리 모델을 사용합니다.
SafeC는 다른 접근 방식을 취합니다: C에서 시작하여 숨겨진 비용 없이 안전성을 추가한다.
extern int printf(const char* fmt, ...);
int main() {
int x = 42;
&stack int ref = &x; // 명시적 리전 표기
printf("x = %d\n", *ref);
return 0;
}C를 알고 있다면 이미 SafeC의 대부분을 아는 것입니다. 추가된 부분들은 명시적이고, 눈에 보이며, 예측 가능합니다.
핵심 정체성
SafeC는 다섯 가지 타협 불가능한 원칙으로 정의됩니다:
1. 결정론
동일한 입력은 항상 동일한 출력을 만듭니다. 숨겨진 런타임 변동성도, 비결정론적 동작도 없습니다. 이 덕분에 SafeC는 실시간 시스템, 오디오 처리, 임베디드 펌웨어, 그리고 예측 가능성이 선택 사항이 아닌 모든 영역에 적합합니다.
2. 숨겨진 비용 없음
모든 연산에는 눈에 보이고 예측 가능한 비용이 있습니다. 암묵적인 힙 할당도, 암묵적인 복사도, 백그라운드 가비지 컬렉션도, 암묵적인 소멸자 체인도 없습니다. 작성한 것이 곧 실행되는 것입니다.
3. 명시적 제어
암묵적인 메모리 관리도, 암묵적인 타입 변환도, 암묵적인 에러 처리도 없습니다. 프로그래머가 할당, 라이프타임, 에러 전파를 명시적으로 제어합니다. 컴파일러는 정확성을 검증합니다.
4. C ABI 호환성
SafeC는 기본적으로 C 구조체 레이아웃, C 호출 규약, C 링키지를 사용합니다. SafeC 객체는 C 프로젝트에 직접 링크됩니다. C 헤더는 #include <stdio.h>로 네이티브하게 포함할 수 있습니다.
5. 컴파일 타임 우선 설계
컴파일 타임에 알 수 있는 것은 무엇이든 컴파일 타임에 해결됩니다. consteval 함수는 컴파일 중에 실행됩니다. static_assert는 코드가 실행되기 전에 오류를 잡아냅니다. if const는 컴파일 타임에 분기를 선택합니다. 제네릭은 단형화되어 런타임 디스패치가 없습니다.
결정론 계약
SafeC는 숨겨진 런타임 동작이 없음을 보장합니다:
- 숨겨진 힙 할당 없음 — 모든 할당은 명시적입니다 (
new<R>,malloc) - 숨겨진 런타임 없음 — 런타임 라이브러리가 필요하지 않습니다 (freestanding 모드 사용 가능)
- 숨겨진 패닉 없음 — bounds check(경계 검사)를 명시적으로 활성화하지 않는 한 없습니다
- 암묵적 예외 없음 — 에러는 던져지지 않고 반환됩니다
- 백그라운드 GC 없음 — 메모리는 리전과 명시적 해제를 통해 관리됩니다
- 암묵적 소멸자 호출 없음 — 리소스 정리는 명시적입니다 (
defer, 수동 free)
SafeC가 C에 추가하는 것
SafeC는 C의 실행 모델을 유지하면서 안전성과 현대적 기능을 확장합니다:
| 기능 | 설명 |
|---|---|
| 리전 | &stack T, &heap T, &arena<R> T, &static T, 또는 &T/?&T(리전 없음) — 컴파일 타임 메모리 안전성 |
| 경계 검사 | 정적 및 런타임 배열 경계 검증 |
| 대여 검사 | 컴파일 타임에 강제되는 가변/불변 참조 배타성 |
| 제네릭 | 단형화를 사용하는 generic<T> — 런타임 비용 없음 |
| 구조체 메서드 | 암묵적 self 리시버를 사용하는 Point::length() 구문 |
| 연산자 오버로딩 | Vec2 operator+(Vec2 other) const |
| 패턴 매칭 | 완전성 검사를 포함한 match 표현식 |
| 옵셔널 타입 | ?T (암묵적 T -> ?T 래핑), match, .is_null()/.is_none(), 또는 .default(fallback)으로 언래핑 |
| 슬라이스 | []int s = arr[0..3] — 배열에 대한 경계 검사가 이루어진 뷰 |
| Defer | defer close(fd) — 결정론적 정리 |
| 컴파일 타임 평가 | consteval, static_assert, if const |
| 튜플 | .0, .1 멤버 접근을 갖는 tuple(int, double) |
| 동시성 | spawn/join, 타입이 지정된 채널 |
| 베어메탈 지원 | naked, interrupt, asm, --freestanding |
SafeC가 추가하지 않는 것
SafeC가 의도적으로 생략한 것들도 마찬가지로 중요합니다:
- 가비지 컬렉터 없음
- 예외 없음
- RTTI나 가상 디스패치 없음
- 암묵적 생성자나 소멸자 없음
- 숨겨진 할당 없음
- 표준 런타임 요구사항 없음
- 클래스 계층 구조나 상속 없음
프로젝트 구성 요소
SafeC 프로젝트는 세 가지 주요 구성 요소로 이루어져 있습니다:
컴파일러 (safec) — .sc 파일을 LLVM IR로 컴파일하는 완전한 LLVM 프론트엔드입니다. 파이프라인은 다음과 같습니다: 전처리기, 렉서, 파서, AST, Sema(의미 분석), ConstEval(컴파일 타임 평가), CodeGen(LLVM IR 생성).
표준 라이브러리 (std/) — 메모리, I/O, 문자열, 수학, 컬렉션, 동시성, 시스템 인터페이스를 다루는 20개 이상의 모듈입니다. 시스템 콜을 위한 C FFI를 포함하여 모두 SafeC로 구현되었습니다.
패키지 매니저 (safeguard) — 빌드 도구이자 의존성 관리자입니다. 컴파일, 링킹, 의존성 가져오기, 표준 라이브러리 통합을 처리합니다.
파일 확장자
SafeC 소스 파일은 .sc 확장자를 사용합니다:
hello.sc
math_utils.sc
audio_engine.sc