Merge pull request #3542 from rtfeldman/bindgen-duplicates

Don't bindgen duplicate types
This commit is contained in:
Brian Carroll 2022-07-18 08:09:45 +01:00 committed by GitHub
commit 2c953671ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 329 additions and 44 deletions

1
Cargo.lock generated
View File

@ -3323,6 +3323,7 @@ dependencies = [
"cli_utils",
"ctor",
"dircpy",
"fnv",
"indexmap",
"indoc",
"pretty_assertions",

View File

@ -31,6 +31,7 @@ clap = { version = "3.1.15", default-features = false, features = ["std", "color
strum = "0.24.0"
strum_macros = "0.24"
indexmap = "1.8.1"
fnv = "1.0.7"
[dev-dependencies]
pretty_assertions = "1.0.0"

View File

@ -269,7 +269,7 @@ fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impl
// This is recursively pointing to a type that should already have been added,
// so no extra work needs to happen.
}
RocType::Function(_, _) => {
RocType::Function { .. } => {
// TODO actually bindgen functions!
}
}
@ -657,7 +657,7 @@ pub struct {name} {{
payload_args = answer.payload_args;
args_to_payload = answer.args_to_payload;
}
RocType::Function(_, _) => todo!(),
RocType::Function { .. } => todo!(),
};
{
@ -1133,7 +1133,7 @@ pub struct {name} {{
buf.join("\n")
}
RocType::Function(_, _) => todo!(),
RocType::Function { .. } => todo!(),
};
format!(
@ -1309,7 +1309,7 @@ fn type_name(id: TypeId, types: &Types) -> String {
| RocType::TagUnion(RocTagUnion::NullableUnwrapped { name, .. })
| RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { name, .. }) => name.clone(),
RocType::RecursivePointer(content) => type_name(*content, types),
RocType::Function(_, _) => todo!(),
RocType::Function { name, .. } => name.clone(),
}
}
@ -1457,7 +1457,7 @@ pub struct {name} {{
owned_ret_type = answer.owned_ret_type;
borrowed_ret_type = answer.borrowed_ret_type;
}
RocType::Function(_, _) => todo!(),
RocType::Function { .. } => todo!(),
};
// Add a convenience constructor function for the tag with the payload, e.g.
@ -1690,7 +1690,7 @@ pub struct {name} {{
buf.join(&format!("\n{INDENT}{INDENT}{INDENT}{INDENT}{INDENT}"))
}
RocType::Function(_, _) => todo!(),
RocType::Function { .. } => todo!(),
};
let body = format!(
@ -1892,7 +1892,7 @@ fn cannot_derive_default(roc_type: &RocType, types: &Types) -> bool {
| RocType::TagUnion { .. }
| RocType::RocResult(_, _)
| RocType::RecursivePointer { .. }
| RocType::Function(_, _) => true,
| RocType::Function { .. } => true,
RocType::RocStr | RocType::Bool | RocType::Num(_) => false,
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
cannot_derive_default(types.get_type(*id), types)
@ -1918,7 +1918,7 @@ fn cannot_derive_copy(roc_type: &RocType, types: &Types) -> bool {
| RocType::Bool
| RocType::Num(_)
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
| RocType::Function(_, _) => false,
| RocType::Function { .. } => false,
RocType::RocStr
| RocType::RocList(_)
| RocType::RocDict(_, _)
@ -1969,7 +1969,7 @@ fn has_float_help(roc_type: &RocType, types: &Types, do_not_recurse: &[TypeId])
| RocType::RocStr
| RocType::Bool
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
| RocType::Function(_, _) => false,
| RocType::Function { .. } => false,
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
has_float_help(types.get_type(*id), types, do_not_recurse)
}

View File

@ -1,6 +1,7 @@
use crate::enums::Enums;
use crate::structs::Structs;
use bumpalo::Bump;
use fnv::FnvHashMap;
use roc_builtins::bitcode::{
FloatWidth::*,
IntWidth::{self, *},
@ -38,6 +39,9 @@ pub struct Types {
sizes: Vec<u32>,
aligns: Vec<u32>,
// Needed to check for duplicates
types_by_name: FnvHashMap<String, TypeId>,
/// Dependencies - that is, which type depends on which other type.
/// This is important for declaration order in C; we need to output a
/// type declaration earlier in the file than where it gets referenced by another type.
@ -50,13 +54,277 @@ impl Types {
Self {
target: target_info,
types: Vec::with_capacity(cap),
types_by_name: FnvHashMap::with_capacity_and_hasher(10, Default::default()),
sizes: Vec::new(),
aligns: Vec::new(),
deps: VecMap::with_capacity(cap),
}
}
pub fn add(&mut self, typ: RocType, layout: Layout<'_>) -> TypeId {
pub fn is_equivalent(&self, a: &RocType, b: &RocType) -> bool {
use RocType::*;
match (a, b) {
(RocStr, RocStr) | (Bool, Bool) | (EmptyTagUnion, EmptyTagUnion) | (Unit, Unit) => true,
(RocResult(ok_a, err_a), RocResult(ok_b, err_b)) => {
self.is_equivalent(self.get_type(*ok_a), self.get_type(*ok_b))
&& self.is_equivalent(self.get_type(*err_a), self.get_type(*err_b))
}
(Num(num_a), Num(num_b)) => num_a == num_b,
(RocList(elem_a), RocList(elem_b))
| (RocSet(elem_a), RocSet(elem_b))
| (RocBox(elem_a), RocBox(elem_b))
| (RecursivePointer(elem_a), RecursivePointer(elem_b)) => {
self.is_equivalent(self.get_type(*elem_a), self.get_type(*elem_b))
}
(RocDict(key_a, val_a), RocDict(key_b, val_b)) => {
self.is_equivalent(self.get_type(*key_a), self.get_type(*key_b))
&& self.is_equivalent(self.get_type(*val_a), self.get_type(*val_b))
}
(TagUnion(union_a), TagUnion(union_b)) => {
use RocTagUnion::*;
match (union_a, union_b) {
(Enumeration { tags: tags_a, .. }, Enumeration { tags: tags_b, .. }) => {
tags_a == tags_b
}
(
NonRecursive {
tags: tags_a,
discriminant_type: disc_a,
..
},
NonRecursive {
tags: tags_b,
discriminant_type: disc_b,
..
},
)
| (
Recursive {
tags: tags_a,
discriminant_type: disc_a,
..
},
Recursive {
tags: tags_b,
discriminant_type: disc_b,
..
},
) => {
if disc_a != disc_b || tags_a.len() != tags_b.len() {
false
} else {
tags_a.iter().zip(tags_b.iter()).all(
|((name_a, opt_id_a), (name_b, opt_id_b))| {
name_a == name_b
&& match (opt_id_a, opt_id_b) {
(Some(id_a), Some(id_b)) => self.is_equivalent(
self.get_type(*id_a),
self.get_type(*id_b),
),
(None, None) => true,
(None, Some(_)) | (Some(_), None) => false,
}
},
)
}
}
(
NonNullableUnwrapped {
content: content_a, ..
},
NonNullableUnwrapped {
content: content_b, ..
},
) => content_a == content_b,
(
NullableWrapped {
null_tag: null_a,
non_null_tags: non_null_a,
..
},
NullableWrapped {
null_tag: null_b,
non_null_tags: non_null_b,
..
},
) => {
if null_a != null_b || non_null_a.len() != non_null_b.len() {
false
} else {
non_null_a.iter().zip(non_null_b.iter()).all(
|((disc_a, name_a, opt_id_a), (disc_b, name_b, opt_id_b))| {
disc_a == disc_b
&& name_a == name_b
&& match (opt_id_a, opt_id_b) {
(Some(id_a), Some(id_b)) => self.is_equivalent(
self.get_type(*id_a),
self.get_type(*id_b),
),
(None, None) => true,
(None, Some(_)) | (Some(_), None) => false,
}
},
)
}
}
(
NullableUnwrapped {
null_tag: null_tag_a,
non_null_tag: non_null_tag_a,
non_null_payload: non_null_payload_a,
null_represents_first_tag: null_represents_first_tag_a,
..
},
NullableUnwrapped {
null_tag: null_tag_b,
non_null_tag: non_null_tag_b,
non_null_payload: non_null_payload_b,
null_represents_first_tag: null_represents_first_tag_b,
..
},
) => {
null_tag_a == null_tag_b
&& non_null_tag_a == non_null_tag_b
&& non_null_payload_a == non_null_payload_b
&& null_represents_first_tag_a == null_represents_first_tag_b
}
// These are all listed explicitly so that if we ever add a new variant,
// we'll get an exhaustiveness error here.
(Enumeration { .. }, _)
| (_, Enumeration { .. })
| (NonRecursive { .. }, _)
| (_, NonRecursive { .. })
| (Recursive { .. }, _)
| (_, Recursive { .. })
| (NonNullableUnwrapped { .. }, _)
| (_, NonNullableUnwrapped { .. })
| (NullableUnwrapped { .. }, _)
| (_, NullableUnwrapped { .. }) => false,
}
}
(
Struct {
fields: fields_a, ..
},
Struct {
fields: fields_b, ..
},
) => {
if fields_a.len() == fields_b.len() {
fields_a
.iter()
.zip(fields_b.iter())
.all(|((name_a, id_a), (name_b, id_b))| {
name_a == name_b
&& self.is_equivalent(self.get_type(*id_a), self.get_type(*id_b))
})
} else {
false
}
}
(
TagUnionPayload {
fields: fields_a, ..
},
TagUnionPayload {
fields: fields_b, ..
},
) => {
if fields_a.len() == fields_b.len() {
fields_a
.iter()
.zip(fields_b.iter())
.all(|((name_a, id_a), (name_b, id_b))| {
name_a == name_b
&& self.is_equivalent(self.get_type(*id_a), self.get_type(*id_b))
})
} else {
false
}
}
(
Function {
name: name_a,
args: args_a,
ret: ret_a,
},
Function {
name: name_b,
args: args_b,
ret: ret_b,
},
) => {
// for functions, the name is actually important because two functions
// with the same type could have completely different implementations!
if name_a == name_b
&& args_a.len() == args_b.len()
&& self.is_equivalent(self.get_type(*ret_a), self.get_type(*ret_b))
{
args_a.iter().zip(args_b.iter()).all(|(id_a, id_b)| {
self.is_equivalent(self.get_type(*id_a), self.get_type(*id_b))
})
} else {
false
}
}
// These are all listed explicitly so that if we ever add a new variant,
// we'll get an exhaustiveness error here.
(RocStr, _)
| (_, RocStr)
| (Bool, _)
| (_, Bool)
| (RocResult(_, _), _)
| (_, RocResult(_, _))
| (Num(_), _)
| (_, Num(_))
| (RocList(_), _)
| (_, RocList(_))
| (RocDict(_, _), _)
| (_, RocDict(_, _))
| (RocSet(_), _)
| (_, RocSet(_))
| (RocBox(_), _)
| (_, RocBox(_))
| (TagUnion(_), _)
| (_, TagUnion(_))
| (EmptyTagUnion, _)
| (_, EmptyTagUnion)
| (Struct { .. }, _)
| (_, Struct { .. })
| (TagUnionPayload { .. }, _)
| (_, TagUnionPayload { .. })
| (RecursivePointer(_), _)
| (_, RecursivePointer(_))
| (Function { .. }, _)
| (_, Function { .. }) => false,
}
}
pub fn add_named(&mut self, name: String, typ: RocType, layout: Layout<'_>) -> TypeId {
if let Some(existing_type_id) = self.types_by_name.get(&name) {
let existing_type = self.get_type(*existing_type_id);
if self.is_equivalent(existing_type, &typ) {
*existing_type_id
} else {
// TODO report this gracefully!
panic!(
"Duplicate name detected - {:?} could refer to either {:?} or {:?}",
name, existing_type, typ
);
}
} else {
let id = self.add_anonymous(typ, layout);
self.types_by_name.insert(name, id);
id
}
}
pub fn add_anonymous(&mut self, typ: RocType, layout: Layout<'_>) -> TypeId {
let id = TypeId(self.types.len());
assert!(id.0 <= TypeId::MAX.0);
@ -163,7 +431,11 @@ pub enum RocType {
/// this would be the field of Cons containing the (recursive) StrConsList type,
/// and the TypeId is the TypeId of StrConsList itself.
RecursivePointer(TypeId),
Function(Vec<TypeId>, TypeId),
Function {
name: String,
args: Vec<TypeId>,
ret: TypeId,
},
/// A zero-sized type, such as an empty record or a single-tag union with no payload
Unit,
}
@ -438,7 +710,7 @@ fn add_type_help<'a>(
}
}
},
Content::Structure(FlatType::Func(args, _closure_var, ret_var)) => {
Content::Structure(FlatType::Func(args, closure_var, ret_var)) => {
let args = env.subs.get_subs_slice(*args);
let mut arg_type_ids = Vec::with_capacity(args.len());
@ -460,8 +732,16 @@ fn add_type_help<'a>(
add_type_help(env, ret_layout, *ret_var, None, types)
};
let fn_type_id =
types.add(RocType::Function(arg_type_ids.clone(), ret_type_id), layout);
let name = format!("TODO_roc_function_{:?}", closure_var);
let fn_type_id = types.add_named(
name.clone(),
RocType::Function {
name,
args: arg_type_ids.clone(),
ret: ret_type_id,
},
layout,
);
types.depends(fn_type_id, ret_type_id);
@ -475,8 +755,10 @@ fn add_type_help<'a>(
todo!()
}
Content::Structure(FlatType::Erroneous(_)) => todo!(),
Content::Structure(FlatType::EmptyRecord) => types.add(RocType::Unit, layout),
Content::Structure(FlatType::EmptyTagUnion) => types.add(RocType::EmptyTagUnion, layout),
Content::Structure(FlatType::EmptyRecord) => types.add_anonymous(RocType::Unit, layout),
Content::Structure(FlatType::EmptyTagUnion) => {
types.add_anonymous(RocType::EmptyTagUnion, layout)
}
Content::Alias(name, alias_vars, real_var, _) => {
if name.is_builtin() {
match layout {
@ -498,7 +780,7 @@ fn add_type_help<'a>(
}
}
types.add(RocType::Bool, layout)
types.add_anonymous(RocType::Bool, layout)
}
Layout::Union(union_layout) if *name == Symbol::RESULT_RESULT => {
match union_layout {
@ -519,7 +801,8 @@ fn add_type_help<'a>(
env.layout_cache.from_var(env.arena, err_var, subs).unwrap();
let err_id = add_type_help(env, err_layout, err_var, None, types);
let type_id = types.add(RocType::RocResult(ok_id, err_id), layout);
let type_id =
types.add_anonymous(RocType::RocResult(ok_id, err_id), layout);
types.depends(type_id, ok_id);
types.depends(type_id, err_id);
@ -547,7 +830,7 @@ fn add_type_help<'a>(
Content::RangedNumber(_) => todo!(),
Content::Error => todo!(),
Content::RecursionVar { structure, .. } => {
let type_id = types.add(RocType::RecursivePointer(TypeId::PENDING), layout);
let type_id = types.add_anonymous(RocType::RecursivePointer(TypeId::PENDING), layout);
let structure_layout = env
.layout_cache
.from_var(env.arena, *structure, subs)
@ -577,31 +860,31 @@ fn add_builtin_type<'a>(
match (builtin, builtin_type) {
(Builtin::Int(width), _) => match width {
U8 => types.add(RocType::Num(RocNum::U8), layout),
U16 => types.add(RocType::Num(RocNum::U16), layout),
U32 => types.add(RocType::Num(RocNum::U32), layout),
U64 => types.add(RocType::Num(RocNum::U64), layout),
U128 => types.add(RocType::Num(RocNum::U128), layout),
I8 => types.add(RocType::Num(RocNum::I8), layout),
I16 => types.add(RocType::Num(RocNum::I16), layout),
I32 => types.add(RocType::Num(RocNum::I32), layout),
I64 => types.add(RocType::Num(RocNum::I64), layout),
I128 => types.add(RocType::Num(RocNum::I128), layout),
U8 => types.add_anonymous(RocType::Num(RocNum::U8), layout),
U16 => types.add_anonymous(RocType::Num(RocNum::U16), layout),
U32 => types.add_anonymous(RocType::Num(RocNum::U32), layout),
U64 => types.add_anonymous(RocType::Num(RocNum::U64), layout),
U128 => types.add_anonymous(RocType::Num(RocNum::U128), layout),
I8 => types.add_anonymous(RocType::Num(RocNum::I8), layout),
I16 => types.add_anonymous(RocType::Num(RocNum::I16), layout),
I32 => types.add_anonymous(RocType::Num(RocNum::I32), layout),
I64 => types.add_anonymous(RocType::Num(RocNum::I64), layout),
I128 => types.add_anonymous(RocType::Num(RocNum::I128), layout),
},
(Builtin::Float(width), _) => match width {
F32 => types.add(RocType::Num(RocNum::F32), layout),
F64 => types.add(RocType::Num(RocNum::F64), layout),
F128 => types.add(RocType::Num(RocNum::F128), layout),
F32 => types.add_anonymous(RocType::Num(RocNum::F32), layout),
F64 => types.add_anonymous(RocType::Num(RocNum::F64), layout),
F128 => types.add_anonymous(RocType::Num(RocNum::F128), layout),
},
(Builtin::Decimal, _) => types.add(RocType::Num(RocNum::Dec), layout),
(Builtin::Bool, _) => types.add(RocType::Bool, layout),
(Builtin::Str, _) => types.add(RocType::RocStr, layout),
(Builtin::Decimal, _) => types.add_anonymous(RocType::Num(RocNum::Dec), layout),
(Builtin::Bool, _) => types.add_anonymous(RocType::Bool, layout),
(Builtin::Str, _) => types.add_anonymous(RocType::RocStr, layout),
(Builtin::List(elem_layout), Structure(Apply(Symbol::LIST_LIST, args))) => {
let args = env.subs.get_subs_slice(*args);
debug_assert_eq!(args.len(), 1);
let elem_id = add_type_help(env, *elem_layout, args[0], opt_name, types);
let list_id = types.add(RocType::RocList(elem_id), layout);
let list_id = types.add_anonymous(RocType::RocList(elem_id), layout);
types.depends(list_id, elem_id);
@ -658,7 +941,7 @@ where
})
.collect::<Vec<(L, TypeId)>>();
types.add(to_type(name, fields), layout)
types.add_named(name.clone(), to_type(name, fields), layout)
}
fn add_tag_union<'a>(
@ -712,7 +995,7 @@ fn add_tag_union<'a>(
}
_ => {
// create a RocType for the payload and save it
let struct_name = format!("{}_{}", name, tag_name); // e.g. "MyUnion_MyVariant"
let struct_name = format!("{}_{}", &name, tag_name); // e.g. "MyUnion_MyVariant"
let fields = payload_vars.iter().copied().enumerate();
let struct_id =
add_struct(env, struct_name, fields, types, layout, |name, fields| {
@ -736,7 +1019,7 @@ fn add_tag_union<'a>(
let discriminant_type = UnionLayout::discriminant_size(tags.len()).into();
RocType::TagUnion(RocTagUnion::NonRecursive {
name,
name: name.clone(),
tags,
discriminant_type,
})
@ -747,7 +1030,7 @@ fn add_tag_union<'a>(
let discriminant_type = UnionLayout::discriminant_size(tags.len()).into();
RocType::TagUnion(RocTagUnion::Recursive {
name,
name: name.clone(),
tags,
discriminant_type,
})
@ -793,7 +1076,7 @@ fn add_tag_union<'a>(
let (non_null_tag, non_null_payload) = non_null;
RocType::TagUnion(RocTagUnion::NullableUnwrapped {
name,
name: name.clone(),
null_tag,
non_null_tag,
non_null_payload: non_null_payload.unwrap(),
@ -803,7 +1086,7 @@ fn add_tag_union<'a>(
}
}
Layout::Builtin(Builtin::Int(_)) => RocType::TagUnion(RocTagUnion::Enumeration {
name,
name: name.clone(),
tags: tags.into_iter().map(|(tag_name, _)| tag_name).collect(),
}),
Layout::Builtin(_)
@ -817,14 +1100,14 @@ fn add_tag_union<'a>(
// This should be a very rare use case, and it's not worth overcomplicating
// the rest of bindgen to make it do something different.
RocType::TagUnion(RocTagUnion::NonRecursive {
name,
name: name.clone(),
tags,
discriminant_type: RocNum::U8,
})
}
};
let type_id = types.add(typ, layout);
let type_id = types.add_named(name, typ, layout);
if is_recursive {
env.known_recursive_types.insert(layout, type_id);