Skip to content

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

Closed

Description

@zhuah

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?

Activity

  1. randall77 commented on Dec 16, 2021

    @randall77
    Contributor
  2. mdempsky commented on Dec 16, 2021

    @mdempsky
    Contributor

    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.

  3. zhuah commented on Dec 17, 2021

    @zhuah
    Author

    @mdempsky thank you, i got it.

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

  4. typesanitizer commented on Jan 11, 2022

    @typesanitizer

    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());
    }
  5. mdempsky commented on Jan 11, 2022

    @mdempsky
    Contributor

    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.

  6. locked and limited conversation to collaborators on Jan 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      ⌂ 换网址