GithubHelp home page GithubHelp logo

Question about the sub-typing rules about gc HOT 15 CLOSED

birbe avatar birbe commented on June 30, 2024
Question about the sub-typing rules

from gc.

Comments (15)

birbe avatar birbe commented on June 30, 2024

specifically, I encountered this issue when I tried to add headers to each struct I allocate into the heap. Java objects have metadata (such as to which class they belong to) and I was trying to emulate this in WASM by having every object begin with a (field i32) (which would be initialized as a pointer to a data structure in memory). Every struct began with said i32 field; they also specified that they had a super-type which was nothing but (struct (field i32)). When I tried to pass arbitrary references to structs (which began with the i32 field but contained other fields) into a function which expected a reference to just a (struct (field i32)), I got a cast error in the debugger. The cast error went away when I manually added the definition for the sub-type into the module inside of a recursive type definition. This isn't feasible to do automatically as I am not aware when generating a class which classes will extend it or what their field layouts would be, and things are therefore put into separate modules

from gc.

kripken avatar kripken commented on June 30, 2024

In the example should public class ClassB { be public class ClassB extends ClassA {?

from gc.

tlively avatar tlively commented on June 30, 2024

Can you share the text format of the modules/types you are generating? That will make it easier to spot the problem.

from gc.

birbe avatar birbe commented on June 30, 2024

In the example should public class ClassB { be public class ClassB extends ClassA {?

good catch, i've just updated it

from gc.

birbe avatar birbe commented on June 30, 2024

Can you share the text format of the modules/types you are generating? That will make it easier to spot the problem.

sure, i'll grab them

from gc.

rossberg avatar rossberg commented on June 30, 2024

When defining the representation type for A you don't need to know what types extend it later. But when you define the representation type for B you of course need to know which types it extends. This is no different from Java itself.

If I understand your question correctly, all representations share a common ancestor, which is your struct with just i32. So what is the problem with declaring all types subtypes of this one?

Note that type definitions do not have to be within the same recursion group to be subtypes. You can do this:

(rec
  (type $obj (sub (struct (field i32))))
)

(rec
  (type $A (sub $obj (struct (field i32 i32))))
)

(rec
  (type $B (sub $A (struct (field i32 i32 i32))))
)

Furthermore, if these definitions occur in multiple modules they will still be compatible, even when some of these modules do not contain $A or $B.

from gc.

birbe avatar birbe commented on June 30, 2024

When defining the representation type for A you don't need to know what types extend it later. But when you define the representation type for B you of course need to know which types it extends. This is no different from Java itself.

If I understand your question correctly, all representations share a common ancestor, which is your struct with just i32. So what is the problem with declaring all types subtypes of this one?

Note that type definitions do not have to be within the same recursion group to be subtypes. You can do this:

(rec
  (type $obj (sub (struct (field i32))))
)

(rec
  (type $A (sub $obj (struct (field i32 i32))))
)

(rec
  (type $B (sub $A (struct (field i32 i32 i32))))
)

Furthermore, if these definitions occur in multiple modules they will still be compatible, even when some of these modules do not contain $A or $B.

I already am defining all the classes as ancestors of the (struct (field i32)), or, at least, I'm pretty sure I am; here's the helper module I generate when the JVM first loads (the array types can be ignored, I've disabled their usage atm):

(module
  (type $type0 (struct (field $field0 (mut i32))))
  (type $type1 (array (field (mut structref))))
  (type $type2 (array (field (mut i8))))
  (type $type3 (array (field (mut i16))))
  (type $type4 (array (field (mut i32))))
  (type $type5 (array (field (mut i64))))
  (type $type6 (array (field (mut f32))))
  (type $type7 (array (field (mut f64))))
  (table $jvm.objects (;0;) (import "jvm" "objects") 0 anyref)
  (func $jvm.alloc_ref (;0;) (import "jvm" "alloc_ref") (result i32))
  (func $obj_class (;1;) (export "obj_class") (param $var0 i32) (result i32)
    local.get $var0
    table.get $jvm.objects
    ref.cast null $type0
    struct.get $type0 $field0
  )
)

Here's the shortened version of the WASM module that created the struct that's passed in. I've attached the full version as a file

(module
  (type $type0 (struct (field $field0 (mut i32))))
  (type $type1 (struct_subtype (field $field0 (mut i32)) $type0))
  (type $type2 (struct_subtype (field $field0 (mut i32)) (field $field1 (mut i32)) $type1))
  (type $type3 (struct_subtype
    (field $field0 (mut i32))
    (field $field1 (mut i32))
    (field $field2 (mut i32))
    $type2))
)

Main$Test wat.txt

Here's the error I get in Edge's debugger
image

from gc.

kripken avatar kripken commented on June 30, 2024

You may be declaring your base type as final, which does not allow subtyping. Try to use struct_subtype on the base type and not just struct. Or, in the final text format, this:

(module
  (type $type0 (sub (struct (field $field0 (mut i32)))))
  (type $type1 (sub $type0 (struct (field $field0 (mut i32)))))
  (type $type2 (sub $type1 (struct (field $field0 (mut i32)) (field $field1 (mut i32)))))
  (type $type3 (sub $type2 (struct
    (field $field0 (mut i32))
    (field $field1 (mut i32))
    (field $field2 (mut i32))
  )))
)

Note the use of (sub ..) for $type0 even though it has no declared supertype, without which it will error on attempts to subtype it.

from gc.

birbe avatar birbe commented on June 30, 2024

I'm using the wasm_encoder crate for generating the WASM modules. after checking my code, I can confirm that nowhere do I have it set to final for any type. The base type is also already declared as a sub-type, specifically I use this https://docs.rs/wasm-encoder/latest/wasm_encoder/struct.TypeSection.html#method.rec and all of the types in the hierarchy are in the provided iterator

from gc.

tlively avatar tlively commented on June 30, 2024

What tool are you using to generate the text here? I ask because parts of this text use older pre-standard syntax (e.g. struct_subtype and ref.cast null) and using an out-of-date disassembler may be hiding the real issue.

(FWIW it looks like Edge's disassembler is also out-of-date)

Even with the older text format, it looks like none of your structs are actually declaring a supertype. Could that be the issue?

from gc.

birbe avatar birbe commented on June 30, 2024

What tool are you using to generate the text here? I ask because parts of this text use older pre-standard syntax (e.g. struct_subtype and ref.cast null) and using an out-of-date disassembler may be hiding the real issue.

(FWIW it looks like Edge's disassembler is also out-of-date)

Even with the older text format, it looks like none of your structs are actually declaring a supertype. Could that be the issue?

yeah it looks like Edge is printing it out improperly. here's it debugged with wasmprinter

(module
  (rec
    (type (;0;) (sub (struct (field (mut i32)))))
    (type (;1;) (sub 0 (struct (field (mut i32)))))
    (type (;2;) (sub 1 (struct (field (mut i32)) (field (mut i32)))))
    (type (;3;) (sub 2 (struct (field (mut i32)) (field (mut i32)) (field (mut i32)))))
  )
)

from gc.

birbe avatar birbe commented on June 30, 2024

and the helper class:

(module
  (rec
    (type (;0;) (sub (struct (field (mut i32)))))
  )
  (type (;1;) (array (mut structref)))
  (type (;2;) (array (mut i8)))
  (type (;3;) (array (mut i16)))
  (type (;4;) (array (mut i32)))
  (type (;5;) (array (mut i64)))
  (type (;6;) (array (mut f32)))
  (type (;7;) (array (mut f64)))
  (type (;8;) (func (result i32)))
  (type (;9;) (func (param i32) (result i32)))
  (type (;10;) (func (param i32 i32) (result i32)))
  (type (;11;) (func (param i32 i32 i32)))
  (import "jvm" "objects" (table (;0;) 0 anyref))
  (import "jvm" "alloc_ref" (func (;0;) (type 8)))
  (func (;1;) (type 9) (param i32) (result i32)
    local.get 0
    table.get 0
    ref.cast (ref null 0)
    struct.get 0 0
  )
  (export "obj_class" (func 1))
)

from gc.

tlively avatar tlively commented on June 30, 2024

Alright, great, I think I know what the issue is now.

In the one module, your top Object type ((type (;0;) (sub (struct (field (mut i32)))))) is declared in the same rec group as a bunch of other things and in the other module it is in its own rec group.

Since type identity is determined by index into a rec group and these Object types are defined in rec groups with different structures, you're actually defining two different Object types rather than the same Object type in two places. That's why the cast is failing; the Object types are incompatible with each other.

The fix is to make sure the rec groups are the same in every module, probably by making them as small as possible, as @rossberg suggested.

from gc.

birbe avatar birbe commented on June 30, 2024

I think I understand, I'll try it out and report back. tysm!

from gc.

birbe avatar birbe commented on June 30, 2024

making all of the struct types be in their own recursive group fixed my issue. thank you for being so helpful!

from gc.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.