Closed alexferl closed 11 months ago
simplify:
module main
struct Root[T] {
edges []Edge[T]
}
struct Edge[T] {}
fn new[T]() Root[T] {
return Root[T]{}
}
fn main() {
t2 := new[int]()
dump(t2)
}
$ v run a.v
cgen error: could not generate string method `main__Edge[int]_str` for type `main__Edge[int]`
@spytheman I need your assistance 😄
I have found a relatively serious problem, which may be just my cognitive error. However, based on the results of my analysis over the past two days, the problem should exist.
The problem occurs in the concretization of structures with generics:
TypeSymbols
of generic structures such as ast.GenericInst
, kind: .generic_inst
are generated by the parser
, but only for specific types that appear on the code's literally. For element types of array elements and maps, if no specific types appear on the code's literally, the parser
will not generate TypeSymbols(ast.GenericInst
, kind: .generic_inst
)
The builder
will concretize the GenericInsts
in all TypeSymbols before starting the checker
work, and only after completion will the checker
start work
So when generic structures are used as array elements or map elements, their TypeSymbols
will be lost unless specific types are used literally in the code.
Solving this problem can be complex:
There is a functional boundary between parsers and checkers, and we definitely cannot let parsers analyze specific types that have not appeared in the code literal
Before the checker starts working, thoroughly analyze all missing ast.GenericInst
, kind: .generic_inst
of the TypeSymbol, and needs to be completed, which requires a lot of work, equivalent to the checker working twice, involving modifications to the overall architecture of the compiler.
There is also a compromise and temporary solution: during the checker's
work, the TypeSymbol
of the generic structure is reset to ast.GenericInst
, kind: .generic_inst
and re concretized. However, this solution is clumsy and has a huge impact on performance.
This is a portion of the code that follows the third method, which has not been completed yet, but the following code can run successfully:
https://github.com/shove70/v/commit/b134065d738ded7899424bb630fdf327472c56ca (This is just my attempt and I am not planning to submit a PR on this issue at the moment)
module main
struct Root[T] {
edges []Edge[T]
}
struct Edge[T] {}
fn new[T]() Root[T] {
return Root[T]{}
}
fn main() {
// mut t := Root[int]{}
// dump(t)
t2 := new[int]()
dump(t2)
}
The builder will concretize the GenericInsts in all TypeSymbols before starting the checker work, and only after completion will the checker start work
That is wrong, the checker does everything, the builder has no business knowing about generics. The builder just orchestrates stages.
pub fn (mut b Builder) middle_stages() ! {
util.timing_start('CHECK')
util.timing_start('Checker.generic_insts_to_concrete')
b.table.generic_insts_to_concrete()
util.timing_measure('Checker.generic_insts_to_concrete')
b.checker.check_files(b.parsed_files)
util.timing_measure('CHECK')
b.print_warnings_and_errors()
if b.checker.should_abort {
return error('too many errors/warnings/notices')
}
if b.pref.check_only {
return error_with_code('stop_after_checker', 8001)
}
util.timing_start('TRANSFORM')
b.transformer.transform_files(b.parsed_files)
util.timing_measure('TRANSFORM')
//
b.table.complete_interface_check()
if b.pref.skip_unused {
markused.mark_used(mut b.table, b.pref, b.parsed_files)
}
if b.pref.show_callgraph {
callgraph.show(mut b.table, b.pref, b.parsed_files)
}
}
Perhaps the translation software cannot accurately express my intention. The key point is not how to expand, but that the parser has missed some TypeSymbols, resulting in the inability to fully expand. I also know that the work being carried out is done by the checker.
// generic struct instantiations to concrete types
pub fn (mut t Table) generic_insts_to_concrete() {
// --->
t.find_sym('main.Root[int]') or {
sym_, idx_ := t.find_sym_and_type_idx('main.Root')
if idx_ > 0 {
new_idx := t.register_sym(ast.TypeSymbol{
kind: .generic_inst
name: '${sym_.name}[int]'
cname: '${sym_.cname}_T_int'
mod: sym_.mod
info: ast.GenericInst{
parent_idx: idx_
concrete_types: [new_type(7)]
}
})
println(new_idx)
}
}
// ---> Here: Manually completed the TypeSymbol 'generic_inst' that was missed by the parser
// The code reported by this issue can run normally
for mut sym in t.type_symbols {
if sym.kind == .generic_inst {
info := sym.info as GenericInst
parent := t.type_symbols[info.parent_idx]
if parent.kind == .placeholder {
sym.kind = .placeholder
continue
}
match parent.info {
...
Manually completed the TypeSymbol kind: .generic_inst
that was missed by the parser
The code reported by this issue can run normally
module main
struct Root[T] {
edges map[int]Edge[T] // Here: Edge[T] is element of a map!
}
struct Edge[T] {
}
fn new[T]() Root[T] {
return Root[T]{}
}
fn main() {
t2 := new[int]()
dump(t2)
}
The conclusion of my tested yesterday was incorrect, so I redid it. It does present a problem when used as an element of maps. It seems that it will take some time to solve this problem
Describe the bug
Getting a
cgen error
when trying to run the included code.Expected Behavior
No cgen error, maybe a relevant error if the code is incorrect?
Current Behavior
Reproduction Steps
Possible Solution
No response
Additional Information/Context
No response
V version
V 0.4.0 efcb15d
Environment details (OS name and version, etc.)