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

working with gc types #317

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft

working with gc types #317

wants to merge 6 commits into from

Conversation

oovm
Copy link

@oovm oovm commented Mar 13, 2024

Add Canonical ABI options to support passing parameters by reference type

design/mvp/Explainer.md Outdated Show resolved Hide resolved
design/mvp/Explainer.md Outdated Show resolved Hide resolved
design/mvp/Explainer.md Outdated Show resolved Hide resolved
@oovm
Copy link
Author

oovm commented Mar 16, 2024

I realized that the discriminant actually is required for variants that have multiple arms of the same type, like:

variant either-list {
    left(list<u8>),
    right(list<u8>),
}

In this case, both left and right would have the same (ref eq) subtype.

@fitzgen It occurred to me that the method compiled to (ref eq) still exists, that is, each variant is actually a new type.

If a language went the (enum, ref null eq) route, then the binding might look like (rust pseudocode):

enum Variants {
    Null,                 // (0, 0, 0)
    Bool { value: i32 },  // (1, i32, 0)
    U64 { value: i64 },   // (2, i64) , flatten i64 to i32 + i32
    I64 { value: i64 },   // (3, i64) , flatten i64 to i32 + i32
}

impl Variants {
    fn is_bool(&self) -> bool {
        match self {
            Variants::Bool { value } => true,
            _ => false,
        }
    }
}

If a language took the (ref eq) route, then the binding might look like (C# pseudocode):

// (struct $variants)
abstract class Variants
{
    // (struct $None)
    // (ref eq $variants)
    class Null : Variants
    {
        public override bool isBool()
        {
            return false;
        }
    }
    // (struct $Bool (field value i32))
    // (ref eq $variants)
    class Bool : Variants
    {
        int value;

        public override bool isBool()
        {
            return true;
        }
    }

    // (struct $U64 (field value u64))
    // (ref eq $variants)
    class U64 : Variants
    {
        long value;

        public override bool isBool()
        {
            return false;
        }
    }
    // (struct $U64 (field value i64))
    // (ref eq $variants)
    class I64 : Variants
    {
        int value;

        public override bool isBool()
        {
            return false;
        }
    }

    // virtual table pointer
    public abstract bool isBool();

    // static method
    public static bool isBool(Variants v)
    {
        switch (v)
        {
            case Null _:
                return false;
            case Bool _:
                return true;
            case U64 _:
                return false;
            case I64 _:
                return false;
            default:
                throw new Exception("unreachable");
        }
    }
}

These languages are more likely to implement variant determination through virtual tables

  • X is Variants
  • X is Variants::None
  • X is Variants::Bool
  • X is Variants::U64
  • X is Variants::I64

I think it would also help to achieve: #136

type, such as `(ref array (mut u8))`, instead of type-erased
`(ref eq)`.

📡 When `reference-type` is enabled, the parameter type will change as follows:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the right place to put this spec information is in CanonicalABI.md (along-side the current spec information for how non-gc works) and in canonical-abi/definitions.py (which has the benefit that you can write tests for it in run_tests.py)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how to rewrite definitions.py. The binary needs to reference an index of an already defined type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since definitions.py is not a complete Python reference implementation but, rather, just a "suggestive" subset that describes just the lifting/lowering/built-in rules, what we do is, for canon definitions that take a typeidx in the binary format, we have the corresponding canon_* Python function take a Python object that directly represents the type (e.g., see how canon_lift takes a FuncType directly). That being said, I don't think there are any cases where you'll need to take a core wasm gc type as an immediate -- core wasm types are programmatically derived from component-level types by, e.g., flatten_functype which is called by canon_lift and canon_lower. Thus, I think you just need to add CoreArrayType and CoreStructType Python classes (analogous to CoreFuncType) so they can be created by flatten_functype in the appropriate cases when cx.opts.gc is true (which you'd also add to CanonicalOptions.

design/mvp/Binary.md Outdated Show resolved Hide resolved
Copy link
Member

@lukewagner lukewagner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for starting this! I left a few comments and requests above.

@oovm
Copy link
Author

oovm commented Mar 20, 2024

reference-type now focuses on the mapping of three heap types: string, array, and struct.

The reference types corresponding to option, result, variants have been moved to #325

@kevmoo
Copy link

kevmoo commented Jun 25, 2024

Super excited about the possibilities here. What's the status?

@oovm
Copy link
Author

oovm commented Jun 25, 2024

  • The mapping of struct <-> record, struct <-> tuple, list <-> array has been agreed upon.
  • For enum and flags, no mapping is required.
  • For variant and string, there are objections and they have been removed from the proposal.

The unprepared part is mainly the implementation of the python validator (definitions.py), which has recently changed.

I have not been able to spend too much time on implementing this part recently due to my employer, so I am very sorry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Working with GC types without copy
5 participants