nim-lang / Nim

Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula. Its design focuses on efficiency, expressiveness, and elegance (in that order of priority).
https://nim-lang.org
Other
16.44k stars 1.47k forks source link

std/strutils split return type differs when being set as a variable compared to used as an argument #22263

Open ericvw opened 1 year ago

ericvw commented 1 year ago

Description

When trying to collect a table from parsing some string input with split from std/strutils, I encountered a compiler error I don't fully understand. Is this a bug or something I need help understanding about Nim?

I distilled my original code down to the following, which reproduces the issue:

import std/strutils
import std/sugar
import std/tables

type
    MyObject = object
        a: int
        b: seq[string]

let table = collect:
    for x in 0..1:
        {x: MyObject(a: 1, b: "".split(", "))}

Nim Version

Nim Compiler Version 1.6.14 [MacOSX: amd64] Compiled at 2023-07-12 Copyright (c) 2006-2023 by Andreas Rumpf

active boot switches: -d:release

Current Output

nim c repo.nim
Hint: used config file '/Users/eric/.choosenim/toolchains/nim-1.6.14/config/nim.cfg' [Conf]
Hint: used config file '/Users/eric/.choosenim/toolchains/nim-1.6.14/config/config.nims' [Conf]
................................................................................
/Users/eric/repos/nim.git/repo.nim(10, 13) template/generic instantiation of `collect` from here
/Users/eric/repos/nim.git/repo.nim(12, 29) Error: type mismatch: got 'string' for 'split("", ", ", -1)' but expected 'seq[string]'

Expected Output

A successful compilation.

Possible Solution

No response

Additional Information

Further investigating, I found something else odd when moving call the split outside of the object creation:

import std/strutils
import std/sugar
import std/tables

type
    MyObject = object
        a: int
        b: seq[string]

let table = collect:
    for x in 0..1:
        let s = "".split(", ")
        {x: MyObject(a: 1, b: s)}

The compiler hints the variable s is unused and the program links.

[I] eric@phosphorus ~/r/nim.git (devel)> nvim repo.nim
[I] eric@phosphorus ~/r/nim.git (devel)> nim c repo.nim
Hint: used config file '/Users/eric/.choosenim/toolchains/nim-1.6.14/config/nim.cfg' [Conf]
Hint: used config file '/Users/eric/.choosenim/toolchains/nim-1.6.14/config/config.nims' [Conf]
................................................................................
/Users/eric/repos/nim.git/repo.nim(12, 13) Hint: 's' is declared but not used [XDeclaredButNotUsed]
/Users/eric/repos/nim.git/repo.nim(10, 5) Hint: 'table' is declared but not used [XDeclaredButNotUsed]
Hint:  [Link]
Hint: gc: refc; opt: none (DEBUG BUILD, `-d:release` generates faster code)
43570 lines; 1.157s; 60.762MiB peakmem; proj: /Users/eric/repos/nim.git/repo.nim; out: /Users/eric/repos/nim.git/repo [SuccessX]

To understand my original issue further, I created the following to understand what type Nim thinks the return value of split is:

import std/strutils

let x: seq[string] = "".split(", ")
echo x
echo type(x)

let y = "".split(", ")
echo y
echo type(y)

echo "".split(", ")
echo type("".split(", "))

To my surprise, in the last set of echos, it prints as a sequence of strings, but the type is string:

./repo 
@[""]
seq[string]
@[""]
seq[string]
@[""]
string

I am beyond my depth, and I don't know if this is a bug or a known behavior I don't understand. Thanks for looking into this in advance :).

rnbwdsh commented 1 year ago

I'm rather new to nim, but I found the question/behavior interesting, so I looked into it.

Your examples made it super easy:

I used ctrl+hover in vscode and there was the solution: image image

There is a split iterator and a split proc. They live in the same namespace. You can even view them when you navigate into them (ctrl+click in vscode/intellij) type() has the weird property to "prefer" the iter-type.

There was a whole thread about it https://github.com/nim-lang/Nim/issues/8901 Something something assignment, inline..ing, i think it's a side effect of the zero-overhead-iterators.

Hotfix for your specific issue: Put a @() around it, or a .toSeq after it.