본문으로 건너뛰기
피드

Go 1.26의 타입 생성(Type Construction)과 순환 감지(Cycle Detection) 개선

backend 약 5분
vote
0
댓글
북마크

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

  • 1

    타입 완전성(completeness)이 타입 분해(deconstruction)의 전제 조건

  • 2

    재귀 타입에서는 루프 내 타입이 동시에 완성되는 트릭으로 해결

  • 3

    배열 크기에 unsafe.Sizeof(T{}) 같은 순환 정의는 교착 상태 유발

  • 4

    불완전한 값의 업스트림(변환, 함수 호출 등)에서 완전성 즉시 체크

  • 5

    컴파일러 패닉 #75918, #76383 등 다수 해결

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

기본 타입 생성 과정

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

  • 타입 완전성(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의 내부를 몰라도 알 수 있기 때문 (모든 포인터 크기는 동일)

중요

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

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

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

댓글

댓글

댓글을 불러오는 중...

backend

잘못된 추상화보다 중복이 낫다는 샌디 메츠의 고전 조언

샌디 메츠는 중복을 없애려다 잘못된 추상화를 만들면 코드가 조건문과 파라미터로 부풀어 더 위험해진다고 말한다. 이미 틀어진 추상화는 억지로 보존하지 말고, 다시 호출부에 인라인해서 중복을 되살린 뒤 현재 요구사항에 맞는 새 구조를 찾는 편이 빠르다는 주장이다.

backend

리눅스 커널, 6년·360개 넘는 패치 끝에 strncpy 제거

리눅스 커널이 오랫동안 버그의 원인이던 strncpy API 사용을 Linux 7.2에서 제거했어. NUL 종료 동작이 직관적이지 않고 불필요한 zero-fill로 성능 문제도 있던 API를 6년 동안 약 362개 커밋으로 걷어낸 작업임.

backend

덕디비는 왜 빠를까: 서버 없는 분석 엔진의 내부 구조 뜯어보기

DuckDB가 단일 바이너리, 인프로세스 실행, 컬럼형 저장, 최적화 패스, Parquet 푸시다운으로 빠른 분석 쿼리를 처리하는 방식을 깊게 설명한 글이다. 6GB Parquet 파일을 노트북에서 바로 SQL로 읽는 경험 뒤에 어떤 설계가 깔려 있는지 따라간다.

backend

피지독, 포스트그레스를 수평 확장시키겠다고 550만 달러 투자 유치

피지독은 포스트그레스 앞단에 프록시를 두고 샤딩과 라우팅을 처리해 수평 확장을 가능하게 하겠다는 오픈소스 프로젝트다. 이미 프로덕션에서 초당 200만 건이 넘는 쿼리를 처리하고, 확인된 규모만 20테라바이트 이상을 샤딩했다고 밝히며 550만 달러 투자를 공개했다.

backend

펜타시스템, EDB 포스트그레SQL로 국내 엔터프라이즈 DB 전환 시장 공략

펜타시스템테크놀러지가 EDB와 파트너 계약을 맺고 국내에 EDB 포스트그레SQL 기반 데이터 플랫폼을 공급한다. 기존 상용 DBMS 정책 변화로 비용 부담이 커진 기업들을 겨냥해, 오픈소스 기반 엔터프라이즈 데이터 플랫폼 전환 수요를 잡겠다는 전략이다. 금융, 공공, 제조, 유통, 클라우드, AI 데이터 분석 환경까지 적용 범위를 넓히려는 움직임이다.