edubart / nelua-lang

Minimal, efficient, statically-typed and meta-programmable systems programming language heavily inspired by Lua, which compiles to C and native code.
https://nelua.io
MIT License
1.99k stars 64 forks source link

(@span(byte)){...} ordered field cast permits invalid, rejects valid construction #236

Closed jrfondren closed 10 months ago

jrfondren commented 10 months ago

Code example

require 'span'
local s1: string = "hello"
local s2: span(byte)
s2 = s1
s1 = {s2.data, s2.size} -- ok
-- s2 = (@span(byte)){s1.data, s1.size} -- not accepted, but right
-- ^error: in array literal at index 1: no viable type conversion from 'pointer(array(uint8, 0))' to 'uint8'
s2 = (@span(byte)){s1.size, s1.size} -- accepted, but wrong!
print((@usize)(s2.data), (@usize)(s2.size))
print((@usize)(s1.data), (@usize)(s1.size))
s1 = {s2.data, s2.size}
print(s1)

Workaround

Name the fields instead of relying on ordered fields, and it works:

require 'span'
local s1: string = "hello"
local s2: span(byte)
-- s2 = (@span(byte)){s1.data, s1.size} -- error
s2 = (@span(byte)){data=s1.data, size=s1.size} -- ok
s1 = {s2.data, s2.size}
print(s1) -- output: hello

Environment

x86_64 linux Nelua 0.2.0-dev Build number: 1588 Git date: 2023-09-16 16:20:44 -0300 Git hash: 596fcca5c77932da8a07c249de59a9dff3099495 Semantic version: 0.2.0-dev.1588+596fcca5 Copyright (C) 2019-2022 Eduardo Bart (https://nelua.io/)

edubart commented 10 months ago

This is not a bug, but a feature.

For container like records (span, vector and sequence) a list of values has different meaning than a list with named fields when initializing. An example demonstrate this better:

require 'span'
require 'vector'

local a = (@span(byte)){1,2,3,4}
for i=0,<#a do
  print(a[i]) -- outputs 1,2,3,4
end

local v = (@vector(byte)){5,6,7,8}
for i=0,<#v do
  print(v[i]) -- outputs 5,6,7,8
end

But at the moment you initialize using named fields, the meaning is different, then you are really initializing its record fields. I clarified this in commit https://github.com/edubart/nelua-lang/commit/31270b586a2b83abeaaee57f36dba7e4a38b94a0

If you wonder why this is a feature, it's because span can be used a placeholder for functions that accept any kind of container, and even lists of values, for example:

require 'span'
require 'vector'

local function f(a: span(byte))
  for i=0,<#a do
    print(a[i]) -- outputs 5,6,7,8
  end
end

f{1,2,3,4} -- I can call here from a list of values

local v: vector(byte) = {5,6,7,8}
f(v)

local a: array(byte) = {9,10}
f(a)