#print

1 messages · Page 1 of 1 (latest)

minor pike
#

I read that comptime_ints cannot be used at runtime.

fn tt(comptime x: comptime_int, y: i8) i8 {
    return x + y;
}

test "comptime_int" {
    const a = 2;
    var y: i8 = 3;
    y += 1;
    try expect(tt(a, y) == 6);
}

Wouldn't the tt function above execute at runtime, and therefore the comptime_int is getting used at runtime?

crisp ermine
#

comptime arguments cause the function to be instantiated for every comptime value, so the compiler actually compiles a function that looks like this:

fn tt_with_x_2(y: i8) i8 {
    const x = 2;
    return x + y;
}
minor pike
#

But conxt x = 2, is also a comptime_int?

#

Also, this means that the comptime_int isn't really getting erased.

crisp ermine
#

yes, comptime_int is the type of integer literals

minor pike
#

The Comptime section here: https://ziglearn.org/chapter-1/#comptime, indicates that they get erased at runtime, and cannot be used at runtime.

crisp ermine
#

x + y is comptime_int + i8 which coerces the comptime_int to an i8 and then performs safety-checked addition

#

if you want to see a compile error when trying to use a comptime-only type at runtime, try storing it to a function-local var

minor pike
#

x + y is comptime_int + i8 which coerces the comptime_int to an i8 and then performs safety-checked addition
Coerces at runtime?

crisp ermine
#

no, at compile time, the addition is runtime

minor pike
#

So, it wouldn't be

fn tt_with_x_2(y: i8) i8 {
    const x = 2;
    return x + y;
}

Because the two would get erased at runtime. Instead, it would be:

fn tt_with_x_2(y: i8) i8 {
    const x: i8 = 2;
    return x + y;
}

I guess I don't see the point of saying that the comptime_int cannot be used at runtime. We're clearyly using it at runtime, by declaring some space for it in the stack.

#

Thanks for the patience, btw.

crisp ermine
#

storing a comptime-known value to a function-local const does not allocate any stack space

#

my example was how comptime arguments work, not how it looks at runtime

#

var x = 2; is the easiest way to see the consequences of a comptime-only type not being allowed at runtime

minor pike
#

So, the value is directly stored into a register during function execution?

crisp ermine
#

unlikely

#

a small constant that is only used once and for a simple operation like addition should never appear in a register in an optimized build

minor pike
#

Then how is it added to the variable y during function execution, in my example?

crisp ermine
#

by being added to it, not sure what you are asking

#

here's what it looks like part way through the compiler:

  %18 = interned(i8, 1)
...
  %16 = load(i8, %10)
...
  %27 = add_safe(%16!, %18)
minor pike
#

I guess my question is:

  1. The value associated with the comptime int is stored somewhere, to be accessed and added to y during function execution.

  2. The compiler generates some code for the x + y line. What does that look like?

crisp ermine
#

you'll see there's already no comptime_int and we haven't even gotten to codegen yet

#

as far as the backend is concerned, you might as well have said return y + @as(i8, 2);

zinc hazel
#

The same principle applies to comptime_ints

crisp ermine
#

another consequence of not being able to use comptime-only types at runtime:

test {
    var x = true;
    var y = if (x) 1 else 2;
    _ = y;
}
minor pike
#

What is the type of 2 in return y + @as(i8, 2)?

crisp ermine
#

interned(i8, 1) means the type is i8 and the value is 1

zinc hazel
#

@TypeOf(2) == comptime_int,
@TypeOf(@as(i8, 2)) == i8

crisp ermine
#

the compiler needs to store the 1 and 2 on the stack in order to select based on a runtime condition, and since comptime_int doesn't have an upper bound on size, there's no way to allocate stack for it

#

oops

minor pike
#
They aren't stored anywhere except in the compiler's memory during compilation

But we need to access the value during runtime? To add it to y.

zinc hazel
#

They compile down into constants, which are stored in places like rom.

minor pike
#

Yea that makes sense.

zinc hazel
#

Although integers are usually just compiled inline into instructions

crisp ermine
#

I meant to copy this part:

  %11 = interned(i8, 2)
...
  %1 = arg(i8, 1)
...
  %12 = add_safe(%11, %1!)
minor pike
#

I think that also makes sense.

zinc hazel
#

E.g. if you do x + 1, it can generate the instruction to just increment by 1

crisp ermine
#

they are actually just stored in the instruction in the case of adding 2, but yeah

zinc hazel
#

Ye

crisp ermine
#

when the backend sees a constant, all it sees is the final value, not all of the intermediate steps, so in this case all it sees is an i8 with a value of 2, the comptime_int is long gone

zinc hazel
#

You can say the resulting code generated by comptime_ints does "put" the values somewhere, whether in rom or into the instructions, or encoded as specialized instructions, but semantically, a comptime_int doesn't have a size or anything - it doesn't describe a type with a layout

minor pike
#

Right. So when we say a comptime_int cannot be used at runtime, we're saying that the value will not longer have type comptime_int. But it can technically still and probably must be used at runtime.

zinc hazel
#

No, you can't use the comptime int at runtime

crisp ermine
#

it can be coerced to a runtime type and then that can be used at runtime

zinc hazel
#

If you need runtime control flow, you have to coerce to a runtime type

minor pike
#

But it can technically still and probably must be used at runtime.
I meant the value

crisp ermine
#

a comptime_int doesn't have any storage

zinc hazel
#

There's no mov for comptime_int

crisp ermine
#

check out @compileLog(@sizeOf(comptime_int));

zinc hazel
#

In impl it's a big int

crisp ermine
#

an i8 with a value of 2 and a comptime_int with a value of 2 are two different values, as far as the compiler is concerned, but because an implicit conversion exists, it's relatively easy to get the compiler to the second to the first without extra syntax

minor pike
#

Right. If the value associated with a comptime_int is used at runtime, then it must coerce. So, it must fit into whatever type it's coercing into.

crisp ermine
#

there are plenty of ways of using comptime_int at runtime that don't coerce, and so will just be a compile error

minor pike
#

Makes sense. Thanks.

crisp ermine
#

yeah, since comptime_int is a comptime-only type, its value must be comptime-known, which means it can coerce to precisely the types in which it fits

#

if it could be used at runtime, then the compiler would have no way of knowing what value it has, and it would not be safe to allow it to cast to any integer type, since it could always be too big