Reddit

C3 0.8.4: Parameter Reflection, Stack Protection, iOS Support—and One Last Renaming

C3 0.8.4: 매개변수 리플렉션·스택 보호·iOS 지원, 마지막 키워드 변경

C3 0.8.4는 매개변수 리플렉션과 계약 기능을 확장하고, 스택 보호 옵션과 iOS 타깃 지원을 추가한 릴리스입니다. 0.9.0에서 문법을 동결하기 전 마지막 키워드 변경을 시험하며, 표준 라이브러리와 컴파일러의 여러 기능 및 버그 수정도 포함합니다.

AI 요약

C3 0.8.4는 언어 기능과 컴파일러·표준 라이브러리를 함께 다듬은 릴리스입니다. ‘The Last Renaming’이라는 제목은 0.9.0에서 문법을 동결하기 전에 키워드 구성을 정리하려는 목적을 담았습니다. 기존 키워드는 0.8.x에서 계속 사용할 수 있지만, 새 이름을 권장합니다. 새 이름은 0.9.0부터 기본이 될지 그때까지 검토합니다.

키워드와 계약 기능

기존 faultdef, attrdef, constdef를 각각 excuse, attrmacro, constset으로 바꾸는 안을 시험합니다. faultdef는 빈 optional에서 반환하는 fault를 가리키는 문서 용어 excuse를 키워드로 채택했습니다. 새 excuse 문법은 기존 faultdef가 지원하지 않던 중괄호 형식도 지원합니다. attrdef는 매개변수를 받고 별칭보다는 매크로에 가까운 동작을 하므로 attrmacro로 바꿉니다. constdef는 C 방식 enum으로 오해할 수 있는 cenum 대신 독립 기능이라는 점을 드러내는 constset을 선택했습니다.

@require 조건에 매개변수를 지정하는 문법도 추가했습니다. 예를 들어 @require [a] a > 0 : "a must be greater than zero"처럼 쓰면 사전 조건이 실패했을 때 진단이 문제를 일으킨 인수를 직접 가리킵니다.

매개변수 리플렉션과 별칭

$reflect(foo).param_struct와 Foo::param_struct로 함수 매개변수를 구조체 형태로 조회합니다. 매크로는 각 필드에 값을 채운 뒤 struct splat으로 함수 호출을 구성할 수 있습니다. 매개변수에 @tag를 붙일 수 있으며, 리플렉션 결과에서 get_tag, tags, has_tag로 태그를 조회합니다. .default_value로 기본 인수 값도 확인합니다. 이 과정에서 FooFn::params가 ReflectedParam 값 대신 리플렉션 참조를 반환하도록 바뀌었으므로 해당 API를 쓰는 코드는 수정이 필요할 수 있습니다.

별칭 정의에는 삼항식을 사용할 수 있습니다. 따라서 조건부 선언으로 별칭을 감싸지 않고도 $feat(ABC) 같은 기능 플래그에 따라 구현을 선택할 수 있습니다.

컴파일러와 플랫폼

명령행 옵션 --stack-probe, --stack-probe-size, --stack-protector로 스택 프로빙과 스택 카나리 생성을 설정합니다. 함수와 람다에는 @stackprobe(level), @nostackprobe, @stackprotector(level), @nostackprotector 속성을 지정해 설정을 덮어쓸 수 있습니다. naked 함수에는 기본적으로 스택 보호기를 넣지 않습니다. x86-64 red zone을 끄는 @noredzone도 추가했습니다. 임시 안전 조치로 ELF x86-64 함수는 red zone을 사용하지 않도록 출력합니다. ELF와 COFF 출력에는 dso_local 속성 지원도 들어갔습니다.

저수준 타깃에서 원치 않는 부동소수점 명령어 생성을 제어하도록 --implicit-float 설정을 추가했습니다. iOS 타깃도 지원합니다. LLVM 병렬 컴파일에서는 큰 모듈부터 빌드하도록 순서를 바꿨습니다. --keep-obj는 빌드와 링크 뒤에도 오브젝트 파일을 남기며, --warn-unusedlocal과 --warn-unusedparam은 사용하지 않은 지역 변수와 매개변수를 경고합니다. 프로젝트 이름에 하이픈을 허용하고, benchmark와 test 타깃이 c3c benchmark 및 c3c test에서 올바르게 동작하도록 수정했습니다. 특정 benchmark 함수 실행도 지원합니다.

평탄한 타입으로 구성된 slice의 동등성 비교는 원소별 반복 대신 memcmp로 낮춥니다. 컴파일러 수정은 제네릭, 벡터, bitstruct, 기본 인수, defer 제어 흐름 등에 걸쳐 있습니다. 비활성화한 제네릭을 인스턴스화하던 문제와 함수 내부 제네릭 별칭에서 컴파일러가 멈추던 문제를 고쳤습니다. LLVM 23에서 기본 인수 디버그 정보와 bitstruct 필드 감소 연산이 컴파일러를 중단시키던 오류도 수정했습니다. 128개를 넘는 벡터를 뒤집을 때 힙이 손상되던 문제, 벡터 배열을 포함한 구조체 초기화의 정렬 오류, 부호 없는 정수의 $$mod가 부호 있는 나머지 연산을 내던 문제도 해결했습니다.

표준 라이브러리

CachedInStream, CachedOutStream, std::collections::Tree를 추가했습니다. InStream.read는 파일 끝에서 io::EOF를 던지는 대신 0을 반환하며, 비어 있지 않은 버퍼를 요구합니다. 기존에 io::EOF를 처리하던 코드는 반환 바이트 수를 확인하도록 바꿔야 합니다. range::slice 매크로와 심볼릭 링크 여부를 확인하는 Path.is_link도 추가했습니다.

Socket.peer_address, peer_port, local_address, local_port로 연결의 원격·로컬 주소와 포트를 확인합니다. log::get_logger도 추가했습니다. RefCounted는 객체의 종료 처리와 메모리 해제를 구분하도록 변경했습니다. 기존 pool::ThreadPool은 기본 비활성화하고 새 thread::ThreadPool을 기본으로 사용합니다. 기존 구현은 ThreadPoolOld로 제공하며, -D OLD_THREADPOOL을 지정하면 예전 이름을 유지할 수 있습니다.

그 밖에도 BigInt의 큰 왼쪽 시프트, DString 복사 시 할당자 보존, FixedBlockPool의 grow_capacity 변경 처리를 수정했습니다. UTF-16 서로게이트 쌍 처리, 가상 메모리 예약 페이지 크기, POSIX의 thread::current가 오래된 스레드 ID를 반환하던 문제도 고쳤습니다.

Reddit 반응

  • @u/Skaarj — “키워드 변경이 안정성을 보여주는 최선의 방법은 아니지만, 때로는 필요하다. faultdef → excuse라니, excuse가 언어 키워드라니? 문법에 솔직함을 담았네요. 마음에 듭니다.”
    • @u/sondr3_ — “Rust의 yeet 다음으로 좋은 언어 키워드일지도 모르겠네요. 자전거 보관소 논쟁을 피하려고 실험 기능으로 남겨두긴 했지만, 그래도 웃깁니다.”
  • @u/DanManPanther — “C3가 발전하는 모습을 보는 게 좋네요. 잘했습니다!”
  • @u/emonshr — “C와의 상호운용성은 어떤가요?”
    • @u/Nuoji — “헤더 파일을 가져오지는 못하지만, extern fn int printf(char*, ...) =>처럼 선언하면 C printf를 간단히 호출할 수 있습니다. C3 구조체와 유니온은 C 구조체·유니온과 레이아웃이 같습니다. C3에서 내보낸 함수도 거의 같은 선언으로 C에서 호출할 수 있습니다. 다만 C 헤더 생성은 아직 할 일 목록에 있습니다. 양방향 바이너리 상호운용성을 우선 목표로 삼고 있습니다.”
  • @u/captain_obvious_here — “C를 수십 년 동안 만지지 않았는데, 오늘 C3를 처음 알았습니다. 멋져 보이네요!”

원문: C3 / 번역·요약: Trawling