---
title: "Go 1.26의 타입 생성(Type Construction)과 순환 감지(Cycle Detection) 개선"
published: 2026-03-24T23:00:02.000Z
canonical: https://jeff.news/article/983
---
# Go 1.26의 타입 생성(Type Construction)과 순환 감지(Cycle Detection) 개선

Go 1.26에서 타입 체커의 타입 생성 알고리즘을 개선해 재귀 타입과 배열 크기 계산 시 발생하던 순환 감지 문제를 체계적으로 해결했다. 불완전한 값이 다운스트림으로 퍼지기 전에 업스트림에서 차단하는 새로운 접근법으로 여러 컴파일러 패닉을 수정.

- Go 컴파일러에서 타입 체커가 AST를 순회하면서 각 타입의 내부 표현을 구성하는 과정을 **타입 생성(type construction)**이라고 함. Go 1.26에서 이 부분을 상당히 개선했는데, 사용자 관점에서는 변화가 거의 없지만 미래 개선을 위한 기반 작업임

## 기본 타입 생성 과정

- `type T []U`와 `type U *int` 같은 단순 선언의 경우: T를 만나면 Defined 구조체를 생성하고, 아직 평가 안 된 `[]U`의 underlying은 nil. `U`를 찾아가서 `*int`를 평가하고, `int`는 미리 선언된(predeclared) 타입이라 이미 완성 상태. 그러면 역순으로 `*int` → `U` → `[]U` → `T`가 차례로 완성됨

- 타입 **완전성(completeness)**이 핵심 속성: 모든 내부 필드가 채워지고, 참조하는 타입들도 모두 완성된 상태를 의미함. 완전해야 타입을 분해(deconstruct)해서 내부를 들여다보는 게 안전함

## 재귀 타입에서 복잡해지는 부분

- `type T []U; type U *T` 같은 재귀 타입에서는 `*T`의 base 타입이 아직 완성 안 된 `T`를 가리키게 됨. 이때는 **"나중에 T가 완성되면 괜찮아질 것"이라 가정하고 일단 포인터만 연결**해둠. 루프 안의 타입들이 동시에 완성되면서 해결됨

- 재귀 타입이 등장하면 "평가 결과가 항상 완전한 타입"이라는 편리한 속성이 깨짐. 그래서 map 키의 comparable 체크 같은 분해가 필요한 검사는 **타입 체킹 마지막에 모든 타입이 완성된 후로 지연**시킴

## 진짜 문제: 배열 크기와 불완전한 값

- `type T [unsafe.Sizeof(T{})]int` 같은 경우가 진짜 골치아픔. 배열의 크기를 계산하려면 T의 크기를 알아야 하는데, T의 크기는 배열이 완성돼야 알 수 있음. **T는 배열 완성을 기다리고, 배열은 T 완성을 기다리는 교착 상태**

- 이건 순환 정의(cyclic definition) 에러. `type T T`도 같은 류의 문제임

- Go 1.26의 해법: 불완전한 값(incomplete value)이 생성되는 **업스트림 지점**(변환, 함수 호출, 타입 단언, 채널 수신, 맵 접근, 역참조 등)에서 결과 타입의 완전성을 즉시 체크. 불완전하면 순환 에러를 보고하고 invalid operand를 반환해서 불완전한 값이 다운스트림으로 퍼지는 걸 차단함

- 예외적으로 `type T [unsafe.Sizeof(new(T))]int`는 **유효**함. `*T` 타입인 값의 크기는 T의 내부를 몰라도 알 수 있기 때문 (모든 포인터 크기는 동일)

> [!IMPORTANT]
> 이 개선으로 기존에 발생하던 여러 컴파일러 패닉(#75918, #76383, #76384, #76478 등)이 해결됨. 기존의 복잡한 bespoke 순환 감지를 더 단순하고 체계적인 접근으로 교체한 것.

- 저자의 결론: Go가 단순한 타입 시스템으로 유명하지만, 재귀 타입 + 크기가 있는 배열 타입이 만나면 생각보다 복잡한 문제가 숨어 있다는 거임. 프로그래머로서 당연하게 쓰는 기능 뒤에 있는 미묘한 복잡성을 들여다보는 재미있는 글

## 핵심 포인트

- 타입 완전성(completeness)이 타입 분해(deconstruction)의 전제 조건
- 재귀 타입에서는 루프 내 타입이 동시에 완성되는 트릭으로 해결
- 배열 크기에 unsafe.Sizeof(T{}) 같은 순환 정의는 교착 상태 유발
- 불완전한 값의 업스트림(변환, 함수 호출 등)에서 완전성 즉시 체크
- 컴파일러 패닉 #75918, #76383 등 다수 해결

## 인사이트

Go의 단순한 타입 시스템 이면에 숨은 미묘한 복잡성을 보여주는 흥미로운 글. 재귀 타입 + 크기 있는 배열의 조합이 만드는 문제가 생각보다 깊음.
