modularml / mojo

The Mojo Programming Language
https://docs.modular.com/mojo/manual/
Other
22.81k stars 2.57k forks source link

[BUG] Cannot bind to trait with missing parameters for unbound types to check trait conformance #3290

Closed martinvuyk closed 1 month ago

martinvuyk commented 1 month ago

Bug description

main problem is this part:

    @staticmethod
    fn getservbyname(
        name: String, proto: SockProtocol = SockProtocol.TCP
    ) -> Optional[SockAddr[sock_family, *_]]:
        ...

error output:

parametric type 'SockAddr[sock_family, ?, ?, ?, ?, ?, ?, ?, ?]' cannot bind to trait with missing parametersmojo
'Optional' parameter #0 has 'CollectionElement' type, but value has type 'AnyStruct[SockAddr[sock_family, ?, ?, ?, ?, ?, ?, ?, ?]]'

Repro:

struct SockPlatform:
    alias LINUX = "LINUX"
    """LINUX."""
    ...
    var _selected: StringLiteral

    fn __init__(inout self, selected: StringLiteral):
        """Construct an instance.

        Args:
            selected: The selected value.
        """
        self._selected = selected

struct SockType:
    """Socket Type."""

    alias SOCK_STREAM = "SOCK_STREAM"
    """SOCK_STREAM."""
    ...
    var _selected: StringLiteral

    fn __init__(inout self, selected: StringLiteral):
        """Construct an instance.

        Args:
            selected: The selected value.
        """
        self._selected = selected

struct SockProtocol:
    """Socket Transmission Protocol."""

    alias TCP = "TCP"
    """Transmission Control Protocol."""
    ...
    var _selected: StringLiteral

    fn __init__(inout self, selected: StringLiteral):
        """Construct an instance.

        Args:
            selected: The selected value.
        """
        self._selected = selected

struct SockFamily:
    """Socket Address Family."""

    alias AF_INET = "AF_INET"
    """AF_INET."""
    ...
    var _selected: StringLiteral

    fn __init__(inout self, selected: StringLiteral):
        """Construct an instance.

        Args:
            selected: The selected value.
        """
        self._selected = selected
    ...

@value
struct SockAddr[
    sock_family: SockFamily,
    T0: CollectionElement,
    T1: CollectionElement,
    T2: CollectionElement = NoneType,
    T3: CollectionElement = NoneType,
    T4: CollectionElement = NoneType,
    T5: CollectionElement = NoneType,
    T6: CollectionElement = NoneType,
    T7: CollectionElement = NoneType,
](CollectionElement):
    ...

@value
struct Socket[
    sock_family: SockFamily = SockFamily.AF_INET,
    sock_type: SockType = SockType.SOCK_STREAM,
    sock_protocol: SockProtocol = SockProtocol.TCP,
    sock_platform: SockPlatform = SockPlatform.LINUX,
](CollectionElement):
    """Struct for using Sockets. In the future this struct should be able to
    use any implementation that conforms to the SocketInterface trait, once
    traits can have attributes and have parameters defined. This will allow the
    user to implement the interface for whatever functionality is missing and
    inject the type.

    Parameters:
        sock_family: The socket family e.g. `SockFamily.AF_INET`.
        sock_type: The socket type e.g. `SockType.SOCK_STREAM`.
        sock_protocol: The socket protocol e.g. `SockProtocol.TCP`.
        sock_platform: The socket platform e.g. `SockPlatform.LINUX`.
    """

    ...

    @staticmethod
    fn getservbyname(
        name: String, proto: SockProtocol = SockProtocol.TCP
    ) -> Optional[SockAddr[sock_family, *_]]:
        ...

Steps to reproduce

System information

- What OS did you do install Mojo on ?
- Provide version information for Mojo by pasting the output of `mojo -v`

`mojo 2024.7.2205`

- Provide Modular CLI version by pasting the output of `modular -v`
soraros commented 1 month ago

I don't think it's related to trait bound check: it won't compile even if you remove the Optional. I think it's really that explicit unbinding syntax doesn't work in return type.

Smaller repro.

@value
struct S[T: CollectionElement]:
    ...

fn g() -> S:
    ...

I'm not sure it should work though. The compiler can't infer the return type, and I think you what want was existential which we currently don't support.

martinvuyk commented 1 month ago

This works though which to me expresses the same thing but it's horribly verbose

    @staticmethod
    fn getservbyname[
        T0: CollectionElement,
        T1: CollectionElement,
        T2: CollectionElement,
        T3: CollectionElement,
        T4: CollectionElement,
        T5: CollectionElement,
        T6: CollectionElement,
        T7: CollectionElement, //,
    ](name: String, proto: SockProtocol = SockProtocol.TCP) -> Optional[
        SockAddr[sock_family, T0, T1, T2, T3, T4, T5, T6, T7]
    ]:
        ...
suahelen commented 1 month ago

I think the error is that T1 and T2 do not have a default and you do not specify them explicitly in the function declaration. If you give it a default it compiles for me.

@value
struct S[sock_family: Int](CollectionElement):
    fn __init__(inout self):
        pass

    fn __moveinit__(inout self, owned other: Self) -> None:
        pass

    fn __copyinit__(inout self, other: Self) -> None:
        pass

    @staticmethod
    fn g(inout self) -> Optional[SockAddr[Self.sock_family]]:
        return Optional[SockAddr[Self.sock_family]](None)

@value
struct SockAddr[
    sock_family: Int,
    T0: CollectionElement = Nonetype, # does not compile if one of them does not have a default
    T1: CollectionElement = NoneType,
](CollectionElement):
    fn __init__(inout self):
        pass

    fn __moveinit__(inout self, owned other: Self) -> None:
        pass

    fn __copyinit__(inout self, other: Self) -> None:
        pass

fn main():
    var s = S[1]()
    pass
suahelen commented 1 month ago

According to the error message I assume that a check is done on the acutal object passed at compile time. And if nothing is supplied it cannot be evaluated properly.

Also according to the error message. I assume that actually none T0-T9 had a default type when you got the error message ;) Because i you will get a '?' for every argument where the type cannot be evaluated. Like so : error: parametric type 'SockAddr[sock_family, ?, NoneType]' cannot bind to trait with missing parameters

martinvuyk commented 1 month ago

Hi, thank you for the example it made me realize what the underlying problem is.

My logic itself is wrong

@staticmethod
fn g0[
    T0: CollectionElement,
    T1: CollectionElement, //,
](address: SockAddr[sock_family, T0, T1]) -> Optional[String]:
    ...

@staticmethod
fn g1[
    T0: CollectionElement,
    T1: CollectionElement, //,
](address: SockAddr[sock_family, T0, T1]) -> Optional[
    SockAddr[sock_family, T0, T1]
]:
    ...

@staticmethod
fn g2(address: SockAddr[sock_family, *_]) -> Optional[String]:
    ...

@staticmethod
fn g3(
    address: SockAddr[sock_family, *_]
) -> Optional[SockAddr[sock_family, *_]]:
    ...

@staticmethod
fn g4(address: SockAddr[sock_family, *_]) -> Optional[__type_of(address)]:
    ...

g0, g1, g2, and g4 work fine. The problem is that g3 has no way of inferring the types that will be going out by what is coming in, since it could be pretty much any type combination imaginable.

I think it's really that explicit unbinding syntax doesn't work in return type. ... The compiler can't infer the return type

so @soraros you were right here :+1: , it has to either be bound to an incoming value of the same type or explicitly declared as function parameters that are not infer only (//)