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

Cannot use std::hash_map and std::hash_set with recursive types #127

Open
ashn-dot-dev opened this issue Dec 8, 2023 · 1 comment
Open

Comments

@ashn-dot-dev
Copy link
Owner

ashn-dot-dev commented Dec 8, 2023

std::hash_map

import "std";

struct foo {
    var map: std::hash_map[[ssize, foo]];
}

func main() void {
    var x: foo = uninit;
}
$ sunder-compile test.sunder
[std/std.sunder:3100] error: struct `struct { var state: usize; var index: usize; var key: ssize; var value: foo; }` contains a member variable of incomplete struct type `foo`
        var value: V;
        ^
[test.sunder:4] info: ...encountered during template instantiation of `std::hash_map[[ssize, foo]]`
    var map: std::hash_map[[ssize, foo]];
                  ^

std::hash_set

import "std";

struct foo {
    var set: std::set[[foo]];
}

func main() void {
    var x: foo = uninit;
}
$ sunder-compile test.sunder
[std/std.sunder:3096] error: struct `struct { var state: usize; var index: usize; var key: foo; var value: void; }` contains a member variable of incomplete struct type `foo`
        var key: K;
        ^
[std/std.sunder:3408] info: ...encountered during template instantiation of `std::hash_map[[foo, void]]`
    var _hash_map: std::hash_map[[T, void]];
                        ^
[test.sunder:4] info: ...encountered during template instantiation of `std::hash_set[[foo]]`
    var set: std::hash_set[[foo]];
                  ^

Additional Notes

Attempting to box the templated type using a pointer also does not work:

import "std";

struct foo {
    var map: *std::hash_map[[ssize, foo]];
}

func main() void {
    var x: foo = uninit;
}
$ sunder-compile test.sunder
[std/std.sunder:3100] error: struct `struct { var state: usize; var index: usize; var key: ssize; var value: foo; }` contains a member variable of incomplete struct type `foo`
        var value: V;
        ^
[test.sunder:4] info: ...encountered during template instantiation of `std::hash_map[[ssize, foo]]`
    var map: *std::hash_map[[ssize, foo]];
                   ^

In order to hold the data, a boxed any must be used to "smuggle" the heap allocated struct:

import "std";
import "sys";

struct foo {
    var _map: *any; # std::hash_map[[ssize, foo]];

    func map(self: *foo) *std::hash_map[[ssize, foo]] {
        return (:*std::hash_map[[ssize, foo]])self.*._map;
    }
}

func main() void {
    var x: foo = uninit;
    sys::dump[[typeof(x.map())]](x.map());
}
$ sunder-run test.sunder
00 00 00 00 00 00 00 00

The discovery of this issue, and an example of this smuggling in action, comes from an initial draft of a data interchange format library called bubby: https://github.com/ashn-dot-dev/bubby/blob/4f1f6f0f605b8ba7f6d18fa1c0945460576fabab/bubby.sunder#L22C1-L23C50

@ashn-dot-dev
Copy link
Owner Author

A better workaround was used in ashn-dot-dev/bubby@afc18d9. The size of both std::hash_map and std::hash_set is fixed regardless of the template types used, so std::hash_map[[void, void]] and std::hash_set[[void]] can be used as placeholder types, with type punning being used to later extract the desired type from the hash map and hash set member variables.

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

No branches or pull requests

1 participant