vlang / v

Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io
MIT License
35.66k stars 2.15k forks source link

Anonymous generic function inside of a generic function #19045

Open Dracks opened 1 year ago

Dracks commented 1 year ago

Describe the bug

Code: https://vosca.dev/p/fb9c42f729

When you have a generic function that accepts two types, and has an anonymous function inside, If you put only one type (and not the second, as this case that is used both), it give you a weird error.

I hope the people that knew better V-lang, can explain better the problem

struct Test{
    something string
}

struct Dependency {}

struct Factory {
    build fn()!
}
fn  inject[T](mut serv T)!{
    $for field in T.fields {
        if field.typ is string {
            serv.$(field.name) = "Hello world!"
        }
    }
}

fn use_factory[F, E](factory fn(dep F) !E) Factory{
    return Factory{
        build: fn [factory][F]()!{
                mut dep := F{}
                inject[F](mut dep)!
                dump(factory(dep)!)
        }
    }
}

use_factory[Dependency, Test](fn (dep Dependency)!Test {
    return Test{
        something: 'daleks!'
    }
}).build()!

Expected Behavior

Complain that the anonymous function misses the second generic parameter

Current Behavior

Output:

code.v:11:16: error: unknown type `F`
    9 | }
   10 | fn  inject[T](mut serv T)!{
   11 |     $for field in T.fields {
      |                   ^
   12 |         if field.typ is string {
   13 |             serv.$(field.name) = "Hello world!"
If the code of your project is in multiple files, try with `v .` instead of `v code.v`
Exited with error status 1

Reproduction Steps

For what I found you need a function, that creates an anonymous function that handles both types

Possible Solution

No response

Additional Information/Context

No response

V version

V 0.4.0 8ee1667

Environment details (OS name and version, etc.)

V full version: V 0.4.0 fc4c431.8ee1667
OS: linux, Ubuntu 22.04.2 LTS
Processor: 2 cpus, 64bit, little endian, Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz

getwd: /home/pmakhnev/playground
vexe: /home/pmakhnev/v/v
vexe mtime: 2023-08-02 12:00:10

vroot: OK, value: /home/pmakhnev/v
VMODULES: OK, value: /root/.vmodules
VTMP: OK, value: /tmp/v_0

Git version: git version 2.34.1
Git vroot status: weekly.2023.30-63-g8ee1667a
.git/config present: true

CC version: cc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
thirdparty/tcc status: thirdparty-linux-amd64 12f392c3
shove70 commented 1 year ago
struct Test{
    something string
}

struct Dependency {}

// struct Factory {
//  build fn()!
//}

struct Factory[F, E] {
    build fn(F) !E       // NOTE: function signatures need to be consistent.
}

fn  inject[T](mut serv T)!{
    $for field in T.fields {
        if field.typ is string {
            serv.$(field.name) = "Hello world!"
        }
    }
}

fn use_factory[F, E](factory fn(dep F) !E) Factory{
    return Factory{
        build: fn [factory][F]()!{
                mut dep := F{}
                inject[F](mut dep)!
                dump(factory(dep)!)
        }
    }
}

use_factory[Dependency, Test](fn (dep Dependency)!Test {
    return Test{
        something: 'daleks!'
    }
}).build()!
Dracks commented 1 year ago

Hi, first what you did is not what i wish, i don't know why you put the same function in the anonymous that the parameter.

The fix of the problem is pass the two generics to the anonymous function, but the problem/bug is the error of v, as is complettly missdirection

On Thursday, 3 August 2023, shove wrote:

struct Test{
  something string
}

struct Dependency {}

// struct Factory {
//    build fn()!
//}

struct Factory[F, E] {
  build fn(F) !E       // NOTE: function signatures need to be consistent.
}

fn  inject[T](mut serv T)!{
  $for field in T.fields {
      if field.typ is string {
          serv.$(field.name) = "Hello world!"
      }
  }
}

fn use_factory[F, E](factory fn(dep F) !E) Factory{
  return Factory{
      build: fn [factory][F]()!{
              mut dep := F{}
              inject[F](mut dep)!
              dump(factory(dep)!)
      }
  }
}

use_factory[Dependency, Test](fn (dep Dependency)!Test {
  return Test{
      something: 'daleks!'
  }
}).build()!

-- Reply to this email directly or view it on GitHub: https://github.com/vlang/v/issues/19045#issuecomment-1663755851 You are receiving this because you authored the thread.

Message ID: @.***

-- Sent from my Sailfish device

KamkoAmoh commented 10 months ago

I tried to reproduce, but now it works fine

Some test code:

fn (mut db Database[T]) remove_by_id(id int) {  
    db.data = db.data.filter(fn [id][T](ele T) bool {
        return ele.id != id
    })
}
JalonSolov commented 10 months ago

With latest V, I see both a notice and an error (that doesn't make sense):

bug.v:8:2: notice: uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or `[required]` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)
    6 | 
    7 | struct Factory {
    8 |     build fn()!
      |     ~~~~~~~~~~~
    9 | }
   10 | fn  inject[T](mut serv T)!{
bug.v:11:16: error: unknown type `F`
    9 | }
   10 | fn  inject[T](mut serv T)!{
   11 |     $for field in T.fields {
      |                   ^
   12 |         if field.typ is string {
   13 |             serv.$(field.name) = "Hello world!"
If the code of your project is in multiple files, try with `v .` instead of `v bug.v`
Dracks commented 10 months ago

@KamkoAmoh the problem is when you have multiple generics.

@JalonSolov yes there was this warning/error, but is missleading, as the problem is the caller that only has 1 generic parameter, when you put both works. (you can see this is the same as the original, with only adding second generic in the anonymous function: https://vosca.dev/p/074d669643 )