@setRuntimeSafety

@setRuntimeSafety

도입

함수가 실행되는 스코프에서 런타임 안전성 검사를 켤지 끌지를 정해요. ReleaseFast나 ReleaseSmall처럼 빌드 모드에 따라 안전성 검사가 꺼져 있어도, 특정 블록 안에서는 다시 켜고 싶을 때 유용해요.

출처: Zig Documentation

본문

@setRuntimeSafety(comptime safety_on: bool) void

이 함수가 호출된 스코프에서 런타임 안전성 검사를 켤지(true) 끌지(false)를 설정해요.

직접 예시로 확인해 볼게요.

test "@setRuntimeSafety" {
    // The builtin applies to the scope that it is called in. So here, integer overflow
    // will not be caught in ReleaseFast and ReleaseSmall modes:
    // var x: u8 = 255;
    // x += 1; // Unchecked Illegal Behavior in ReleaseFast/ReleaseSmall modes.
    {
        // However this block has safety enabled, so safety checks happen here,
        // even in ReleaseFast and ReleaseSmall modes.
        @setRuntimeSafety(true);
        var x: u8 = 255;
        x += 1;

        {
            // The value can be overridden at any scope. So here integer overflow
            // would not be caught in any build mode.
            @setRuntimeSafety(false);
            // var x: u8 = 255;
            // x += 1; // Unchecked Illegal Behavior in all build modes.
        }
    }
}

첫 번째 점은 이 빌트인이 호출된 스코프에 적용된다는 거예요. 바깥쪽 테스트 스코프에서는 설정을 건드리지 않았으니, ReleaseFast나 ReleaseSmall 모드에서는 정수 오버플로를 잡아내지 못해요(위의 주석 처리된 x += 1은 바로 그 상황이에요).

하지만 안쪽 블록에서는 @setRuntimeSafety(true)로 다시 켰기 때문에, ReleaseFast나 ReleaseSmall 모드에서조차 이 블록 안에서는 안전성 검사가 동작해요. 그래서 그 아래의 x += 1은 정수 오버플로 검사에 걸려요.

두 번째 점은 어느 스코프에서든 값을 다시 덮어쓸 수 있다는 거예요. 가장 안쪽 블록에서 @setRuntimeSafety(false)로 끄면, 그 바깥 블록에서 켰던 설정과 무관하게 어떤 빌드 모드에서도 오버플로를 잡지 않아요. 이렇게 스코프가 중첩될 때마다 현재 위치의 설정이 우선해요.

-Ofast 모드로 실제 실행하면 어떤 결과가 나오는지 볼까요.

$ zig test test_setRuntimeSafety_builtin.zig -Ofast
1/1 [email protected] 975313 panic: integer overflow
/home/ci/work/zig-bootstrap/out/zig-local-cache/tmp/15625dde49aa511e/../../../../zig/doc/langref/test_setRuntimeSafety_builtin.zig:11:11: 0x107c3c8 in test.@setRuntimeSafety (test)
        x += 1;
          ^
/home/ci/work/zig-bootstrap/out/host/lib/zig/compiler/test_runner.zig:295:25: 0x106eb62 in mainTerminal (test)
        if (test_fn.func()) |_| {
                        ^
/home/ci/work/zig-bootstrap/out/host/lib/zig/std/start.zig:789:88: 0x106d066 in callMain (test)
    if (fn_info.param_types[0].? == std.process.Init.Minimal) return wrapMain(root.main(.{
                                                                                       ^
/home/ci/work/zig-bootstrap/out/host/lib/zig/std/start.zig:248:5: 0x106ccfd in _start (test)
    asm volatile (switch (native_arch) {
    ^
error: the following test command terminated with signal ABRT:
/home/ci/work/zig-bootstrap/out/zig-local-cache/o/1053d260722683c1c582e293d115d193/test --seed=0xf0b4385

빌드 모드가 ReleaseFast(-Ofast)인데도 안쪽 블록에서 안전성 검사를 켰기 때문에, x += 1에서 panic: integer overflow가 발생해요. 이게 바로 @setRuntimeSafety의 핵심이에요. 빌드 모드가 아니라 호출된 스코프가 안전성 검사 여부를 결정해요.

참고로 이 빌트인은 앞으로 @optimizeFor로 대체될 예정이에요.

더 알아보기