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.8k stars 2.16k forks source link

Missing generic method call generation at anonymous generic fn #18294

Closed Dracks closed 8 months ago

Dracks commented 1 year ago

Describe the bug

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

import v.reflection

const (
    provide_key = 'provide'
)

type InjectCb = fn () !

pub interface Object {}

pub struct Service {
    typ    int      [required]
    inject InjectCb [required]
    name   string   [required]
    // This will be a workarround as the reflection in V is not working correctly and is not unwrapping/wrapping
    // the type on assignation
    // TODO: https://github.com/vlang/v/issues/18256
    originalptr voidptr [required]
mut:
    instance &Object [required]
}

pub struct Module {
mut:
    services map[int]Service = map[int]Service{}
}

pub fn (mut self Module) inject_to_object[T](mut new_service T) ! {
    $for field in T.fields {
        mut service := self.get_service_from_field(field, T.name)!
        $if field.indirections >0 {
            if mut service is Service {
                if service.typ != field.typ  {
                    return error("Type of property '${field.name}' in '${T.name}' must be ${service.name} as Reference")
                }
                unsafe {
                    new_service.$(field.name) = service.originalptr
                }
            }
        } $else {
            if service is Service {
                println('Warning: field ${field.name} must be a reference to enable autoinject')
            }
        }
    }
}

pub fn (mut self Module) register[T]() &T {
    mut new_service := &T{}
    typ_idx := typeof[T]().idx
    typ := reflection.get_type(typ_idx) or { panic('Type not found ${T.name}') }

    if typ_idx in self.services {
        panic('Type ${typ.name} has been already registered')
    }

    self.services[typ_idx] = Service{
        name: T.name
        typ: typ_idx
        instance: new_service
        originalptr: new_service
        inject: fn [mut self, mut new_service] [T]() ! {
            self.inject_to_object[T](mut new_service)!
        }
    }
    return new_service
}

fn get_key(attrs []string) string {
    return attrs[0] or {'key'}
}

type ServiceOrNone = Service | bool

fn (mut self Module) get_service_from_field(field FieldData, t_name string) !ServiceOrNone {
    if 'inject' in field.attrs {
        return self.get_service(field.typ) or {
            return error('Invalid injection type for field ${field.name} in ${t_name}')
        }
    }
    return false
}

fn (mut self Module) get_service(service_idx int) !Service {
    return self.services[service_idx] or {
        service_info := reflection.get_type(service_idx) or { return err }
        return error('Service with name ${service_info.name} not available, see available: ${self.services.keys()}')
    }
}

struct Something {
    count int = 0
}

struct SomethingDifferent {
    text string
}

fn main(){
    mut mod := Module{}
    mod.register[Something]()
    mod.register[SomethingDifferent]()
}

When you try to run this code, raises a C code, if you transform it to C code, you can see that the function is generated for the struct Something but not for SomethingDifferent

Expected Behavior

The code runs well

Current Behavior

Output:

code.v:96:14: warning: unnecessary default value of `0`: struct fields are zeroed by default
   94 | 
   95 | struct Something {
   96 |     count int = 0
      |                 ^
   97 | }
   98 |
/tmp/v_60000/../../../../../../box/code.v:67: warning: implicit declaration of function 'main__Module_inject_to_object_T_main__SomethingDifferent'
/tmp/v_60000/../../../../../../box/code.v:67: error: '{' expected (got ";")
builder error: 
==================
C error. This should never happen.

This is a compiler bug, please report it using `v bug file.v`.

https://github.com/vlang/v/issues/new/choose

You can also use #help on Discord: https://discord.gg/vlang

Exited with error status 1

Reproduction Steps

I tried to do a small code from 0, and I was not able to reproducte, thats why I generate all this sample from my code (I already did some cleanup)

Possible Solution

No response

Additional Information/Context

No response

V version

V 0.3.4 fc4c431.45f16a2

Environment details (OS name and version, etc.)

V full version: V 0.3.4 fc4c431.80d404c
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-05-28 04:35:40

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.21-35-g80d404c2 (3 commit(s) behind V master)
.git/config present: true

CC version: cc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
thirdparty/tcc status: thirdparty-linux-amd64 12f392c3
felipensp commented 1 year ago

A short reproducible test case:

pub struct Module {
}

pub struct Service {
    callback fn ()
}

pub fn (mut self Module) do_anything[T]() {
}

pub fn (mut self Module) register[T]() {
    _ := Service{
        callback: fn [mut self] [T]() {
            self.do_anything[T]()
        }
    }
}

struct Something {
}

struct SomethingDifferent {
}

fn main(){
    mut mod := Module{}
    mod.register[Something]()
    mod.register[SomethingDifferent]()
}