Open TheDan64 opened 7 years ago
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.
Also, it seems like signed types should be explicit, though LLVM doesn't make this distinction.
It's worth noting StructTypes (and probably StructValues) have two interesting properties:
StructType<Opaque>
. This should allow us to implement a set_body
for only for StructType<Opaque>
StructType<(A, B, C, D, ..., Z)>
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.
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.
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?)
We're going to want Builder
subtypes, as an unpositioned builder can cause segfaults in many scenarios.
WARNING: Brain dump ahead!
Today,
build_int_add
looks approximately likebuild_int_add(&self, left: &IntValue, right: &IntValue) -> IntValue
. This is great, because it'll stop you from trying to add aFloatValue
and anIntValue
, 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 au8
and right is au16
? 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>
andIntValue<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>
andIntValue<u8>
would be able to be added together but it'd preventIntValue<u8>
andIntValue<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.LLVM has a pointer-width typeusize
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 runtimeString
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 thenum
crate's traits help with float and int sub-types?Should signed types be explicit(IEIntType<i8>
) or just an irrelevant fact about anIntType<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.