Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make implicit cast syntax different than function call syntax #1757

Closed
andrewrk opened this issue Nov 19, 2018 · 18 comments
Closed

make implicit cast syntax different than function call syntax #1757

andrewrk opened this issue Nov 19, 2018 · 18 comments
Labels
accepted This proposal is planned. breaking Implementing this issue could cause existing code to no longer compile or have different behavior. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@andrewrk
Copy link
Member

andrewrk commented Nov 19, 2018

Accepted Proposal


Given: A(B) and no other information it is impossible to tell if this is a function call or an implicit cast.

Instead @cast(A, B) would be implicit cast syntax, and A(B) would then be unambiguously function call syntax.

This is arguably a readability improvement for this example:
const a = ([*]const u32)((*const [1]u32)(&foo[20])); becomes
const a = @cast([*]const u32, @cast(*const [1]u32, &foo[20]));

And makes this example worse:
i32(1234) becomes
@cast(i32, 1234)

@andrewrk andrewrk added breaking Implementing this issue could cause existing code to no longer compile or have different behavior. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. labels Nov 19, 2018
@andrewrk andrewrk added this to the 0.5.0 milestone Nov 19, 2018
@Hejsil
Copy link
Contributor

Hejsil commented Nov 19, 2018

This also removes the choice of var a: u8 = 0 or var a = u8(0)

@andrewrk
Copy link
Member Author

How so? The choice becomes var a: u8 = 0 or var a = @cast(u8, 0). I think it's another good question whether to remove the ability to specify the type of a variable since it's only syntactic sugar for an implicit cast, but that's a separate issue which I can't remember whether is already filed.

@Hejsil
Copy link
Contributor

Hejsil commented Nov 19, 2018

I meant, that we currently have these two, very similar, choices when there should really only be one way to do this. At least with this new @cast builtin, var a: u8 = 0 is easier to type and is therefore encouraged. Ofc, giving an error when the decl type can be inferred is also a way to solve this.

@ghost
Copy link

ghost commented Nov 20, 2018

This is tangential, but is "implicit" the right word for this? i32(0) or @cast(i32, 0) looks pretty explicit to me.

@andrewrk
Copy link
Member Author

Ofc, giving an error when the decl type can be inferred is also a way to solve this.

I don't think we would do that because the decision to put a type or to allow inference has implications for maintenance of the software. For example, if you want your code to be generic friendly you could purposefully omit a type; however if some code was brittle and you wanted to get a compile error if anything changed, you could put the type there.

This is tangential, but is "implicit" the right word for this? i32(0) or @cast(i32, 0) looks pretty explicit to me.

Feel free to suggest alternative names. It's called "implicit" because it's the same cast semantically as what happens when you do this:

fn foo(x: i32) void {}
test "a" {
    foo(1); // 1 is implicitly casted to i32
}

This is exactly equivalent to (in status quo zig) foo(i32(1)).

@ghost
Copy link

ghost commented Nov 20, 2018

I would say:

  • funcThatTakesI32(1) - coercion (implicit cast)
  • const x: i32 = 1; - coercion (implicit cast)
  • i32(1) or @intCast(i32, 1) - explicit cast
  • @truncate, @floatToInt, etc. - lossy/destructive conversions, maybe don't call them casts at all

@andrewrk
Copy link
Member Author

These are semantically the same as each other

  • funcThatTakesI32(1) - coercion (implicit cast)
  • const x: i32 = 1; - coercion (implicit cast)
  • i32(1)

These are not semantically the same as the items in the previous list

  • @intCast(i32, 1) <-- this is also a lossy/destructive conversion (well it would be if 1 wasn't comptime known)
  • @truncate
  • @floatToInt

So what we need is names for each category. How about the first category is called "casting", and the second category is called "conversion"?

@ghost
Copy link

ghost commented Nov 21, 2018

I don't really want to call the third example (i32(1) / @cast(i32, 1)) a cast, because you're basically annotating a literal with a type for the first time. Is there another option? Maybe some new syntax specifically for literals? For working on variables you could probably get along fine using intCast/et al.

Also, this would be a user (programmer) experience downgrade:

const x = if (cond) @cast(i32, 1) else @cast(i32, 0);

@andrewrk
Copy link
Member Author

It is a cast though. @typeOf(1) == comptime_int and the cast turns it into a different type.

For the other example, see #137. After copy elision is finished (#1682) it can look like this:

const x: i32 = if (cond) 1 else 0;

@tgschultz
Copy link
Contributor

Let's say we eliminated the T() implicit cast syntax, but didn't replace it with anything and used @intCast @floatCast and @ptrCast. What does adding @cast buy us?

@ghost
Copy link

ghost commented Nov 21, 2018

I don’t really see the issue to begin with.
The first example looks very artificial and I do not see why it’s a real problem.
If you cast everything all over the place something else is probably wrong anyway.

@andrewrk
Copy link
Member Author

What does adding @cast buy us?

This kind of cast is always preferable to any of the others, because it's always safe (will never emit a safety check), never mangles information, and always works at compile time. It's the first kind of cast that coders should reach for, before resorting to the others.

If we didn't have expression syntax for it, one would have to create a variable in order to get the behavior, but this has different semantics. When it's in an expression, the result location (this is a copy elision concept) is propagated. With a separate variable, there is separate memory allocated.

Some particularly useful expressions that use this construct:

  • OptionalType(null)
  • SomeOtherType(undefined)

@williamcol3
Copy link

If this type of cast is will only ever be available/possible with built in types (numeric coercions, etc.) and not usable for user-types[1], then I think it would be more consistent to use the @-prefix syntax. This would seem to agree with the "Favour reading code over writing code" and "Communicate intent precisely" insofar as the source of behaviour (compiler defined vs. user/std lib defined) goes.

Unambiguous function call syntax would also make tooling easier to write, albeit marginally.

[1] That is, this will never be valid:
const unit_j = MyComplexType(1);

@andrewrk
Copy link
Member Author

andrewrk commented Jul 5, 2019

I'm pretty sure that I want this - make implicit cast syntax different than function call syntax - but I'm not satisfied with any of the syntax ideas so far. I want to press "accept" but I want a satisfying syntax proposal first.

I suppose one option is how Rust does it: a as b. I don't like that the precedence is less clear.

@andrewrk
Copy link
Member Author

A slight alternative to @cast: @as.

i32(1234) becomes @as(i32, 1234).

@zimmi
Copy link
Contributor

zimmi commented Jul 11, 2019

What about reusing the type declaration syntax?

const i = 1 :i32;
const a = (&foo[20] :*const [1]u32) :[*]const u32;

Might need some playing with the whitespace placement.

Pro: Concept has only one syntax, less to learn.
Con: break :blk 42 :u8;

shawnl added a commit to shawnl/zig that referenced this issue Jul 30, 2019
…unc), with safety checks.

Finishing this depends on ziglang#1757. I'd rather not re-work ir_gen_node_raw for explicit casts
(signed to unsigned, and safe narrowing casts) when that is upcoming.
shawnl added a commit to shawnl/zig that referenced this issue Aug 5, 2019
…unc), with safety checks.

Finishing this depends on ziglang#1757. I'd rather not re-work ir_gen_node_raw for explicit casts
(signed to unsigned, and safe narrowing casts) when that is upcoming.
shawnl added a commit to shawnl/zig that referenced this issue Sep 8, 2019
…unc), with safety checks.

Finishing this depends on ziglang#1757. I'd rather not re-work ir_gen_node_raw for explicit casts
(signed to unsigned, and safe narrowing casts) when that is upcoming.
@andrewrk andrewrk added the accepted This proposal is planned. label Sep 19, 2019
@andrewrk
Copy link
Member Author

This is now accepted with @as.

shawnl added a commit to shawnl/zig that referenced this issue Nov 5, 2019
…unc), with safety checks.

Finishing this depends on ziglang#1757. I'd rather not re-work ir_gen_node_raw for explicit casts
(signed to unsigned, and safe narrowing casts) when that is upcoming.

v2: @truncate can now take a scalar type
andrewrk added a commit that referenced this issue Nov 8, 2019
This commit also hooks up type coercion (previously called implicit
casting) into the result location mechanism, and additionally hooks up
variable declarations, maintaining the property that:

    var a: T = b;

is semantically equivalent to:

    var a = @as(T, b);

See #1757
@andrewrk
Copy link
Member Author

andrewrk commented Nov 8, 2019

Landed in 6d5abf8

@andrewrk andrewrk closed this as completed Nov 8, 2019
shawnl added a commit to shawnl/zig that referenced this issue Nov 21, 2019
…unc), with safety checks.

Finishing this depends on ziglang#1757. I'd rather not re-work ir_gen_node_raw for explicit casts
(signed to unsigned, and safe narrowing casts) when that is upcoming.

v2: @truncate can now take a scalar type
yvt pushed a commit to TZmCFI/TZmCFI that referenced this issue May 14, 2020
The implicit cast syntax was changed in
<ziglang/zig#1757> in a breaking way.
yvt pushed a commit to TZmCFI/TZmCFI that referenced this issue May 14, 2020
The implicit cast syntax was changed in
<ziglang/zig#1757> in a breaking way.
yvt pushed a commit to TZmCFI/TZmCFI that referenced this issue May 14, 2020
The implicit cast syntax was changed in
<ziglang/zig#1757> in a breaking way.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted This proposal is planned. breaking Implementing this issue could cause existing code to no longer compile or have different behavior. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

5 participants