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

Multiple variable decl followed by pointer type usage yields invalid type error #225

Closed leafi closed 10 months ago

leafi commented 11 months ago

Bug description

Declaring multiple variables of type primitive on one line (edit: when type is specified >1x), then declaring a variable of type *primitive on the next line, the compiler can't understand the type *primitive.

Code example

This fails mulv2:

local i1, i2: integer, integer = 1, 2
local p1: *integer = &i1
print(i1, i2, $p1, $(&i2))
$ nelua mulv2.nelua
mulv2.nelua:1:1: from: AST node Block
local i1, i2: integer, integer = 1, 2
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mulv2.nelua:2:12: error: invalid type
local p1: *integer = &i1
           ^~~~~~~

These work:

mulv2OneType Only specify type once:

local i1, i2: integer = 1, 2
local p1: *integer = &i1
print(i1, i2, $p1, $(&i2))
$ nelua mulv2OneType.nelua
1   2   1   2

mulv3 Split var decl:

local i1: integer = 1
local i2: integer = 2
local p1: *integer = &i1
print(i1, i2, $p1, $(&i2))
$ nelua mulv3.nelua
1   2   1   2

mulvAlias Use knowledge that integer == int64 & use different pointer type name:

local i1, i2: integer, integer = 1, 2
local p1: *int64 = &i1
print(i1, i2, $p1, $(&i2))
$ nelua mulvAlias.nelua
1   2   1   2

Bonus: mulvAlias2WhatType What are the types of 'local i1, i2: integer, uinteger'?

local i1, i2: integer, uinteger = 1, 2
local p1: *int64 = &i1
local p2: *uint64 = &i2
print(i1, i2, $p1, $p2)
$ nelua mulvAlias2WhatType.nelua
mulvAlias2WhatType.nelua:1:1: from: AST node Block
local i1, i2: integer, uinteger = 1, 2
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mulvAlias2WhatType.nelua:3:7: error: in variable 'p2' declaration: no viable type conversion from 'pointer(int64)' to 'pointer(uint64)'
local p2: *uint64 = &i2
      ^~~~~~~~~~~

Both i1 and i2 in i1, i2: integer, uinteger are integers.

Expected behavior

If multiple identical/differing types are banned in multiple variable declaration lines (multiple variable decls are shown in the Overview doc, but not with types), the compiler should ideally error out.

If that solves the ... integer, integer ... ; ... :*integer case, great. If that's a separate parser issue, it would be great to fix that behavior.

Add quick note about intended restrictions (if any) to relevant section of (incredibly useful) Overview doc

Workaround

Avoid multiple variable declarations on one line

Or, only specify type exactly once.

Environment

Provide relevant information about your environment:

$ nelua -v
Nelua 0.2.0-dev
Build number: 1588
Git date: 2023-09-16 16:20:44 -0300
Git hash: 596fcca5c77932da8a07c249de59a9dff3099495

$ uname -a
Linux alicorn 6.5.3-zen1-1-zen #1 ZEN SMP PREEMPT_DYNAMIC Wed, 13 Sep 2023 08:37:16 +0000 x86_64 GNU/Linux

$ head -n 4 /etc/os-release
NAME="Arch Linux"
PRETTY_NAME="Arch Linux"
ID=arch
BUILD_ID=rolling
leafi commented 11 months ago

Depending on your judgment, this may actually be a docs clarification request or even a feature enhancement request, not a bug. Writing these test cases, and another that goes local i1, i2: integer, integer, integer, integer, integer = 1, 2; ... really seems to suggest that the supported behavior is: Write exactly zero or one types, anything else is ignored and may confuse the parser.

Edit: No, I'm still confused. With one type, if and only if the variables aren't explicitly initialized:

local function f(p1: *integer, p2: *integer)
  print($p1, $p2)
end

local a, b: integer
f(&a, &b)
$ nelua mulvFunc.nelua
mulvFunc.nelua:1:1: from: AST node Block
local function f(p1: *integer, p2: *integer)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mulvFunc.nelua:6:2: error: in call of function 'f' at argument 1: no viable type conversion from 'pointer(any)' to 'pointer(int64)'
f(&a, &b)
 ^~~~~~~~
stefanos82 commented 11 months ago

From your first comment, the code

local i1, i2: integer, integer = 1, 2
local p1: *integer = &i1
print(i1, i2, $p1, $(&i2))

will work if you write your types properly (I understand you got confused):

local i1: integer, i2: integer = 1, 2
local p1: *integer = &i1
print(i1, i2, $p1, $(&i2))

Remember to declare the type right after the variable followed by a colon.

stefanos82 commented 11 months ago

I took this code of yours

local i1, i2: integer = 1, 2
local p1: *integer = &i1
print(i1, i2, $p1, $(&i2))

and wrote you a tiny example to clear things up for you:

do
  local function typenameof(x: auto): string
    return #[tostring(x.type)]#
  end

  local i1: integer, i2 = 1, 2.0
  local p1: *integer = &i1
  print(typenameof(i1), typenameof(i2))
  print(i1, i2, $p1, $(&i2))
end

I borrowed a runtime function @edubart shared with me in the past to clear things up about variable types and used it here to demonstrate for you the current output: we provide the type for variable i1 and let the compiler figure out the type based on the value we assigned to it.

The output should be this:

int64   float64
1   2.0 1   2.0

As we know from the official documentation, type integer is of C type int64_t and Nelua type int64 is exactly the same; basically integer should be seen as an alias for int64.

edubart commented 10 months ago

There is no bug here, just a misunderstanding on the syntax for declaring multiple typed variables.

In the first example 3 variables was declared, i1, i2 and integer, the integer shadows the original integer primitive, that is why the next line you have an error.

In Nelua, type notations must be immediately on the right for each variable declaration.