Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/compile: instantiation cycle in closure function created by method of generic type #50215

Closed
zhuah opened this issue Dec 16, 2021 · 5 comments

Comments

@zhuah
Copy link

zhuah commented Dec 16, 2021

What version of Go are you using (go version)?

$ go version
go version devel go1.18-9d0ca262bb Wed Dec 15 00:33:55 2021 +0000 darwin/amd6

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env

What did you do?

https://go.dev/play/p/l9gzFUOBT5W?v=gotip

package main

import (
	"fmt"
	"strconv"
	"strings"
)

type StringParser[T any] func(s string) (T, error)

func ParseList[T any](p StringParser[T], s string) ([]T, error) {
	if s == "" {
		return nil, nil
	}
	parts := strings.Split(s, ",")
	vals := make([]T, len(parts))
	for i, part := range parts {
		v, err := p(part)
		if err != nil {
			return nil, err
		}
		vals[i] = v
	}
	return vals, nil
}

func (p StringParser[T]) ToListParser() StringParser[[]T] {
	return func(s string) ([]T, error) {
		return ParseList(p, s)
	}
}

func ToListParser[T any](p StringParser[T]) StringParser[[]T] {
	return func(s string) ([]T, error) {
		return ParseList(p, s)
	}
}

func main() {
	p := StringParser[int](strconv.Atoi).ToListParser()
	fmt.Println(p("1,2,3"))
}

ToListParser is fine to compile,, but the compiler reports errors on StringParser.ToListParser, :

./prog.go:9:19: instantiation cycle:
	prog.go:27:54: T instantiated as []T

as i can see, []T is depends on T, rather than StringParser[T], there shouldn't be a cycle.

I don't know whether this is a bug, or go doesn't allow this usage ?

What did you expect to see?

What did you see instead?

@randall77
Copy link
Contributor

@mdempsky

@mdempsky
Copy link
Member

I think the error is correct. StringParser[T]'s ToListParser method has a return type of StringParser[[]T]. This means if we want to instantiate StringParser[int], then we'll also have to instantiate StringParser[[]int] too; which then means we'll also have to instantiate StringParser[[][]int], etc etc.

@zhuah
Copy link
Author

zhuah commented Dec 17, 2021

@mdempsky thank you, i got it.

it' may better be reported as infinite instantiation instead of cycle ?

@typesanitizer
Copy link

typesanitizer commented Jan 11, 2022

which then means we'll also have to instantiate StringParser[[][]int],

Could you explain how this follows from the previous part? There are no method calls for StringParser[[]int], so it looks like there should never be any need to consider StringParser[[][]int].

Similar code in (say) Rust compiles just fine:

struct StringParser<T> {
    f: Box<dyn Fn(String) -> Option<T>>
}

impl<T> StringParser<T> {
    fn to_list_parser(self) -> StringParser<Vec<T>> {
        todo!()
    }
}

fn parse_list<T>(p: StringParser<T>, s: String) -> Option<Vec<T>> {
    todo!()
}

fn main() {
    let p = StringParser { f: Box::new(|s| str::parse::<i64>(&s).ok()) };
    (p.to_list_parser().f)("1,2,3".to_string());
}

@mdempsky
Copy link
Member

Could you explain how this follows from the previous part?

Go allows method calls to happen dynamically through interface assertions or reflection. As a result, if a type is instantiated, all of its methods must be instantiated too, even if not statically called.

There are certainly cases where we could determine this isn't actually needed, but that would complicate the language specification.

@golang golang locked and limited conversation to collaborators Jan 11, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants