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

Sub-Types (Type Safety v1.1) #8

Open
TheDan64 opened this issue Jul 18, 2017 · 7 comments
Open

Sub-Types (Type Safety v1.1) #8

TheDan64 opened this issue Jul 18, 2017 · 7 comments

Comments

@TheDan64
Copy link
Owner

TheDan64 commented Jul 18, 2017

WARNING: Brain dump ahead!

Today, build_int_add looks approximately like build_int_add(&self, left: &IntValue, right: &IntValue) -> IntValue. This is great, because it'll stop you from trying to add a FloatValue and an IntValue, for instance which would not work in LLVM. But I wonder if we can take type checking a step further! What happens when left is a u8 and right is a u16? Needs to be verified, but I believe this is also a LLVM error because it doesn't make much sense. How would adding two values of different sizes work (without casting)?

Therefore, I wonder if we can add sub-type annotations using generics (here-on known as sub-types). For example, IntType<u32> and IntValue<u32> so that you could express something like: build_int_add<T>(&self, left: &IntValue<T>, right: &IntValue<T>) -> IntValue<T> which would ensure only the same sub-types are added together when needed. So, IntValue<u8> and IntValue<u8> would be able to be added together but it'd prevent IntValue<u8> and IntValue<u16> from being added together, requiring a cast first. And, in case you do want different sub-types to be valid input you would just specify separate type variables: do_foo<L, R>(&self, left: &IntType<L>, right: &IntType<R>)

In terms of implementation details, the sub-type should basically just be a marker and not take up any additional space (as PhantomData if needed) (type parameters are cool!)

Outstanding questions:

  • How would custom & 80 bit width types be handled given that they don't map to rust types (yet?)? It may be permissible to disallow custom & 80 bit types since they are far less common. Or just find some workaround. i128 is stable; we can use custom type for the f128 and f80, etc I think.
  • usize should probably be a valid sub-type for ints, but I wonder if there's any issue with it's variable width nature? LLVM should allow us to map it to the pointer size at runtime LLVM has a pointer-width type
  • Signatures will quickly get verbose. Just imagine a simple String type: StructType<(PointerType<IntType<u8>>, IntType<usize>, IntType<usize>)> Will the compiler be able to hide most of this from the user so that they don't have to specify annotations themselves?
  • Could the num crate's traits help with float and int sub-types?
  • Should signed types be explicit(IE IntType<i8>) or just an irrelevant fact about an IntType<u8>? I'm guessing LLVM doesn't let you mix signed and unsigned addition without casting first, so I'm leaning towards keeping them explicit.
@TheDan64 TheDan64 added this to the 0.2.0 milestone Jul 18, 2017
@TheDan64 TheDan64 changed the title Type Safety v2 Sub-Types (Type Safety v1.1) Jul 25, 2017
@TheDan64
Copy link
Owner Author

num crate doesn't look like it could help but typenum defines types for a lot of int sizes. So we could probably use those for custom width types: IntValue<U9>, IntType<I30> along with the builtins: IntType<u32>, etc. Also, I don't think LLVM supports custom width floats (at least not in 3.7) so those would only use builtins, though there's no f16 or f128 yet.

@TheDan64
Copy link
Owner Author

TheDan64 commented Jul 25, 2017

Also, it seems like signed types should be explicit, though LLVM doesn't make this distinction.

@TheDan64
Copy link
Owner Author

TheDan64 commented Aug 1, 2017

It's worth noting StructTypes (and probably StructValues) have two interesting properties:

  • Can be opaque, that is, no body yet set, which is useful for recursive types. So we need something like StructType<Opaque>. This should allow us to implement a set_body for only for StructType<Opaque>
  • StructTypes need a variable number of types. Not sure what kind of support rust has for variable sized tuples, but knowing variable sized arrays, I don't expect it to be great. Need to be able to express something like StructType<(A, B, C, D, ..., Z)>

@TheDan64
Copy link
Owner Author

TheDan64 commented Feb 25, 2018

After looking at #32, it seems that build_global_string and related build_global_string_ptr methods segfault when called without an active function being built on for some reason (even though the string is global 👀). One possible solution would be to have a subtype for a builder in the context of a function, ie Builder<Global> & Builder<Function> similarly to how librustc_trans has two methods for creating builders (global and function scoped): https://github.com/rust-lang/rust/blob/master/src/librustc_trans/builder.rs#L54-L77

Those two methods could be implemented for only the latter Builder Function subtype.

@Michael-F-Bryan
Copy link
Contributor

I know gtk-rs encountered a similar problem when emulating inheritance in Rust. They came up with an IsA trait which lets you use a child class as its parent.

So you might write a function that takes anything which IsA<IntType>, allowing you to accept a u32, i64, etc.

@TheDan64
Copy link
Owner Author

Inkwell use to have something like that, however the issue was primarily that the trait would require you to assume the global context which may not always be desired and wasn't super intuitive (why does u64 suddenly have the global context even though my other types have been working with my non global context?)

@TheDan64
Copy link
Owner Author

We're going to want Builder subtypes, as an unpositioned builder can cause segfaults in many scenarios.

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

2 participants