use super::*;
use crate::{
engine_threading::*,
language::{ty, CallPath},
Ident,
};
use sway_error::error::CompileError;
use sway_types::{integer_bits::IntegerBits, span::Span};
use std::{
collections::HashSet,
fmt,
hash::{Hash, Hasher},
};
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum AbiName {
Deferred,
Known(CallPath),
}
impl fmt::Display for AbiName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(
&(match self {
AbiName::Deferred => "for unspecified ABI".to_string(),
AbiName::Known(cp) => cp.to_string(),
}),
)
}
}
#[derive(Clone)]
pub struct VecSet<T>(pub Vec<T>);
impl<T: fmt::Debug> fmt::Debug for VecSet<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T> core::ops::Deref for VecSet<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: PartialEqWithEngines> VecSet<T> {
pub fn eq(&self, other: &Self, engines: Engines<'_>) -> bool {
self.0.len() <= other.0.len()
&& self
.0
.iter()
.all(|x| other.0.iter().any(|y| x.eq(y, engines)))
}
}
impl<T: PartialEqWithEngines> PartialEqWithEngines for VecSet<T> {
fn eq(&self, other: &Self, engines: Engines<'_>) -> bool {
self.eq(other, engines) && other.eq(self, engines)
}
}
#[derive(Debug, Clone)]
pub enum TypeInfo {
Unknown,
UnknownGeneric {
name: Ident,
trait_constraints: VecSet<TraitConstraint>,
},
Placeholder(TypeParameter),
Str(Length),
UnsignedInteger(IntegerBits),
Enum {
name: Ident,
type_parameters: Vec<TypeParameter>,
variant_types: Vec<ty::TyEnumVariant>,
},
Struct {
name: Ident,
type_parameters: Vec<TypeParameter>,
fields: Vec<ty::TyStructField>,
},
Boolean,
Tuple(Vec<TypeArgument>),
ContractCaller {
abi_name: AbiName,
address: Option<Box<ty::TyExpression>>,
},
Custom {
name: Ident,
type_arguments: Option<Vec<TypeArgument>>,
},
SelfType,
B256,
Numeric,
Contract,
ErrorRecovery,
Array(TypeArgument, Length),
Storage {
fields: Vec<ty::TyStructField>,
},
RawUntypedPtr,
RawUntypedSlice,
}
impl HashWithEngines for TypeInfo {
fn hash<H: Hasher>(&self, state: &mut H, type_engine: &TypeEngine) {
match self {
TypeInfo::Str(len) => {
state.write_u8(1);
len.hash(state);
}
TypeInfo::UnsignedInteger(bits) => {
state.write_u8(2);
bits.hash(state);
}
TypeInfo::Numeric => {
state.write_u8(3);
}
TypeInfo::Boolean => {
state.write_u8(4);
}
TypeInfo::Tuple(fields) => {
state.write_u8(5);
fields.hash(state, type_engine);
}
TypeInfo::B256 => {
state.write_u8(6);
}
TypeInfo::Enum {
name,
variant_types,
type_parameters,
} => {
state.write_u8(7);
name.hash(state);
variant_types.hash(state, type_engine);
type_parameters.hash(state, type_engine);
}
TypeInfo::Struct {
name,
fields,
type_parameters,
} => {
state.write_u8(8);
name.hash(state);
fields.hash(state, type_engine);
type_parameters.hash(state, type_engine);
}
TypeInfo::ContractCaller { abi_name, address } => {
state.write_u8(9);
abi_name.hash(state);
let address = address
.as_ref()
.map(|x| x.span.as_str().to_string())
.unwrap_or_default();
address.hash(state);
}
TypeInfo::Contract => {
state.write_u8(10);
}
TypeInfo::ErrorRecovery => {
state.write_u8(11);
}
TypeInfo::Unknown => {
state.write_u8(12);
}
TypeInfo::SelfType => {
state.write_u8(13);
}
TypeInfo::UnknownGeneric {
name,
trait_constraints,
} => {
state.write_u8(14);
name.hash(state);
trait_constraints.hash(state, type_engine);
}
TypeInfo::Custom {
name,
type_arguments,
} => {
state.write_u8(15);
name.hash(state);
type_arguments.as_deref().hash(state, type_engine);
}
TypeInfo::Storage { fields } => {
state.write_u8(16);
fields.hash(state, type_engine);
}
TypeInfo::Array(elem_ty, count) => {
state.write_u8(17);
elem_ty.hash(state, type_engine);
count.hash(state);
}
TypeInfo::RawUntypedPtr => {
state.write_u8(18);
}
TypeInfo::RawUntypedSlice => {
state.write_u8(19);
}
TypeInfo::Placeholder(ty) => {
state.write_u8(20);
ty.hash(state, type_engine);
}
}
}
}
impl EqWithEngines for TypeInfo {}
impl PartialEqWithEngines for TypeInfo {
fn eq(&self, other: &Self, engines: Engines<'_>) -> bool {
let type_engine = engines.te();
match (self, other) {
(Self::Unknown, Self::Unknown)
| (Self::Boolean, Self::Boolean)
| (Self::SelfType, Self::SelfType)
| (Self::B256, Self::B256)
| (Self::Numeric, Self::Numeric)
| (Self::Contract, Self::Contract)
| (Self::ErrorRecovery, Self::ErrorRecovery) => true,
(
Self::UnknownGeneric {
name: l,
trait_constraints: ltc,
},
Self::UnknownGeneric {
name: r,
trait_constraints: rtc,
},
) => l == r && ltc.eq(rtc, engines),
(Self::Placeholder(l), Self::Placeholder(r)) => l.eq(r, engines),
(
Self::Custom {
name: l_name,
type_arguments: l_type_args,
},
Self::Custom {
name: r_name,
type_arguments: r_type_args,
},
) => l_name == r_name && l_type_args.as_deref().eq(&r_type_args.as_deref(), engines),
(Self::Str(l), Self::Str(r)) => l.val() == r.val(),
(Self::UnsignedInteger(l), Self::UnsignedInteger(r)) => l == r,
(
Self::Enum {
name: l_name,
variant_types: l_variant_types,
type_parameters: l_type_parameters,
},
Self::Enum {
name: r_name,
variant_types: r_variant_types,
type_parameters: r_type_parameters,
},
) => {
l_name == r_name
&& l_variant_types.eq(r_variant_types, engines)
&& l_type_parameters.eq(r_type_parameters, engines)
}
(
Self::Struct {
name: l_name,
fields: l_fields,
type_parameters: l_type_parameters,
},
Self::Struct {
name: r_name,
fields: r_fields,
type_parameters: r_type_parameters,
},
) => {
l_name == r_name
&& l_fields.eq(r_fields, engines)
&& l_type_parameters.eq(r_type_parameters, engines)
}
(Self::Tuple(l), Self::Tuple(r)) => l
.iter()
.zip(r.iter())
.map(|(l, r)| {
type_engine
.get(l.type_id)
.eq(&type_engine.get(r.type_id), engines)
})
.all(|x| x),
(
Self::ContractCaller {
abi_name: l_abi_name,
address: l_address,
},
Self::ContractCaller {
abi_name: r_abi_name,
address: r_address,
},
) => {
l_abi_name == r_abi_name && l_address.as_deref().eq(&r_address.as_deref(), engines)
}
(Self::Array(l0, l1), Self::Array(r0, r1)) => {
type_engine
.get(l0.type_id)
.eq(&type_engine.get(r0.type_id), engines)
&& l1.val() == r1.val()
}
(TypeInfo::Storage { fields: l_fields }, TypeInfo::Storage { fields: r_fields }) => {
l_fields.eq(r_fields, engines)
}
(TypeInfo::RawUntypedPtr, TypeInfo::RawUntypedPtr) => true,
(TypeInfo::RawUntypedSlice, TypeInfo::RawUntypedSlice) => true,
_ => false,
}
}
}
impl Default for TypeInfo {
fn default() -> Self {
TypeInfo::Unknown
}
}
impl DisplayWithEngines for TypeInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: Engines<'_>) -> fmt::Result {
use TypeInfo::*;
let s = match self {
Unknown => "unknown".into(),
UnknownGeneric { name, .. } => name.to_string(),
Placeholder(_) => "_".to_string(),
Str(x) => format!("str[{}]", x.val()),
UnsignedInteger(x) => match x {
IntegerBits::Eight => "u8",
IntegerBits::Sixteen => "u16",
IntegerBits::ThirtyTwo => "u32",
IntegerBits::SixtyFour => "u64",
}
.into(),
Boolean => "bool".into(),
Custom { name, .. } => format!("unresolved {}", name.as_str()),
Tuple(fields) => {
let field_strs = fields
.iter()
.map(|field| engines.help_out(field).to_string())
.collect::<Vec<String>>();
format!("({})", field_strs.join(", "))
}
SelfType => "Self".into(),
B256 => "b256".into(),
Numeric => "numeric".into(),
Contract => "contract".into(),
ErrorRecovery => "unknown due to error".into(),
Enum {
name,
type_parameters,
..
} => print_inner_types(
engines,
name.as_str().to_string(),
type_parameters.iter().map(|x| x.type_id),
),
Struct {
name,
type_parameters,
..
} => print_inner_types(
engines,
name.as_str().to_string(),
type_parameters.iter().map(|x| x.type_id),
),
ContractCaller { abi_name, address } => {
format!(
"contract caller {} ( {} )",
abi_name,
address
.as_ref()
.map(|address| address.span.as_str().to_string())
.unwrap_or_else(|| "None".into())
)
}
Array(elem_ty, count) => {
format!("[{}; {}]", engines.help_out(elem_ty), count.val())
}
Storage { .. } => "contract storage".into(),
RawUntypedPtr => "raw untyped ptr".into(),
RawUntypedSlice => "raw untyped slice".into(),
};
write!(f, "{}", s)
}
}
impl UnconstrainedTypeParameters for TypeInfo {
fn type_parameter_is_unconstrained(
&self,
engines: Engines<'_>,
type_parameter: &TypeParameter,
) -> bool {
let type_engine = engines.te();
let type_parameter_info = type_engine.get(type_parameter.type_id);
match self {
TypeInfo::UnknownGeneric {
trait_constraints, ..
} => {
self.eq(&type_parameter_info, engines)
|| trait_constraints
.iter()
.flat_map(|trait_constraint| {
trait_constraint.type_arguments.iter().map(|type_arg| {
type_arg
.type_id
.type_parameter_is_unconstrained(engines, type_parameter)
})
})
.any(|x| x)
}
TypeInfo::Enum {
type_parameters,
variant_types,
..
} => {
let unconstrained_in_type_parameters = type_parameters
.iter()
.map(|type_param| {
type_param
.type_id
.type_parameter_is_unconstrained(engines, type_parameter)
})
.any(|x| x);
let unconstrained_in_variants = variant_types
.iter()
.map(|variant| {
variant
.type_id
.type_parameter_is_unconstrained(engines, type_parameter)
})
.any(|x| x);
unconstrained_in_type_parameters || unconstrained_in_variants
}
TypeInfo::Struct {
type_parameters,
fields,
..
} => {
let unconstrained_in_type_parameters = type_parameters
.iter()
.map(|type_param| {
type_param
.type_id
.type_parameter_is_unconstrained(engines, type_parameter)
})
.any(|x| x);
let unconstrained_in_fields = fields
.iter()
.map(|field| {
field
.type_id
.type_parameter_is_unconstrained(engines, type_parameter)
})
.any(|x| x);
unconstrained_in_type_parameters || unconstrained_in_fields
}
TypeInfo::Tuple(elems) => elems
.iter()
.map(|elem| {
elem.type_id
.type_parameter_is_unconstrained(engines, type_parameter)
})
.any(|x| x),
TypeInfo::Custom { type_arguments, .. } => type_arguments
.clone()
.unwrap_or_default()
.iter()
.map(|type_arg| {
type_arg
.type_id
.type_parameter_is_unconstrained(engines, type_parameter)
})
.any(|x| x),
TypeInfo::Array(elem, _) => elem
.type_id
.type_parameter_is_unconstrained(engines, type_parameter),
TypeInfo::Unknown
| TypeInfo::Str(_)
| TypeInfo::UnsignedInteger(_)
| TypeInfo::Boolean
| TypeInfo::ContractCaller { .. }
| TypeInfo::SelfType
| TypeInfo::B256
| TypeInfo::Numeric
| TypeInfo::Contract
| TypeInfo::ErrorRecovery
| TypeInfo::RawUntypedPtr
| TypeInfo::RawUntypedSlice
| TypeInfo::Storage { .. }
| TypeInfo::Placeholder(_) => false,
}
}
}
impl TypeInfo {
pub fn json_abi_str(&self, type_engine: &TypeEngine) -> String {
use TypeInfo::*;
match self {
Unknown => "unknown".into(),
UnknownGeneric { name, .. } => name.to_string(),
TypeInfo::Placeholder(_) => "_".to_string(),
Str(x) => format!("str[{}]", x.val()),
UnsignedInteger(x) => match x {
IntegerBits::Eight => "u8",
IntegerBits::Sixteen => "u16",
IntegerBits::ThirtyTwo => "u32",
IntegerBits::SixtyFour => "u64",
}
.into(),
Boolean => "bool".into(),
Custom { name, .. } => name.to_string(),
Tuple(fields) => {
let field_strs = fields
.iter()
.map(|field| field.json_abi_str(type_engine))
.collect::<Vec<String>>();
format!("({})", field_strs.join(", "))
}
SelfType => "Self".into(),
B256 => "b256".into(),
Numeric => "u64".into(), Contract => "contract".into(),
ErrorRecovery => "unknown due to error".into(),
Enum { name, .. } => {
format!("enum {}", name)
}
Struct { name, .. } => {
format!("struct {}", name)
}
ContractCaller { abi_name, .. } => {
format!("contract caller {}", abi_name)
}
Array(elem_ty, length) => {
format!("[{}; {}]", elem_ty.json_abi_str(type_engine), length.val())
}
Storage { .. } => "contract storage".into(),
RawUntypedPtr => "raw untyped ptr".into(),
RawUntypedSlice => "raw untyped slice".into(),
}
}
pub(crate) fn to_selector_name(
&self,
type_engine: &TypeEngine,
error_msg_span: &Span,
) -> CompileResult<String> {
use TypeInfo::*;
let name = match self {
Str(len) => format!("str[{}]", len.val()),
UnsignedInteger(bits) => {
use IntegerBits::*;
match bits {
Eight => "u8",
Sixteen => "u16",
ThirtyTwo => "u32",
SixtyFour => "u64",
}
.into()
}
Boolean => "bool".into(),
Tuple(fields) => {
let field_names = {
let names = fields
.iter()
.map(|field_type| {
type_engine
.to_typeinfo(field_type.type_id, error_msg_span)
.expect("unreachable?")
.to_selector_name(type_engine, error_msg_span)
})
.collect::<Vec<CompileResult<String>>>();
let mut buf = vec![];
for name in names {
match name.value {
Some(value) => buf.push(value),
None => return name,
}
}
buf
};
format!("({})", field_names.join(","))
}
B256 => "b256".into(),
Struct {
fields,
type_parameters,
..
} => {
let field_names = {
let names = fields
.iter()
.map(|ty| {
let ty = match type_engine.to_typeinfo(ty.type_id, error_msg_span) {
Err(e) => return err(vec![], vec![e.into()]),
Ok(ty) => ty,
};
ty.to_selector_name(type_engine, error_msg_span)
})
.collect::<Vec<CompileResult<String>>>();
let mut buf = vec![];
for name in names {
match name.value {
Some(value) => buf.push(value),
None => return name,
}
}
buf
};
let type_arguments = {
let type_arguments = type_parameters
.iter()
.map(|ty| {
let ty = match type_engine.to_typeinfo(ty.type_id, error_msg_span) {
Err(e) => return err(vec![], vec![e.into()]),
Ok(ty) => ty,
};
ty.to_selector_name(type_engine, error_msg_span)
})
.collect::<Vec<CompileResult<String>>>();
let mut buf = vec![];
for arg in type_arguments {
match arg.value {
Some(value) => buf.push(value),
None => return arg,
}
}
buf
};
if type_arguments.is_empty() {
format!("s({})", field_names.join(","))
} else {
format!("s<{}>({})", type_arguments.join(","), field_names.join(","))
}
}
Enum {
variant_types,
type_parameters,
..
} => {
let variant_names = {
let names = variant_types
.iter()
.map(|ty| {
let ty = match type_engine.to_typeinfo(ty.type_id, error_msg_span) {
Err(e) => return err(vec![], vec![e.into()]),
Ok(ty) => ty,
};
ty.to_selector_name(type_engine, error_msg_span)
})
.collect::<Vec<CompileResult<String>>>();
let mut buf = vec![];
for name in names {
match name.value {
Some(value) => buf.push(value),
None => return name,
}
}
buf
};
let type_arguments = {
let type_arguments = type_parameters
.iter()
.map(|ty| {
let ty = match type_engine.to_typeinfo(ty.type_id, error_msg_span) {
Err(e) => return err(vec![], vec![e.into()]),
Ok(ty) => ty,
};
ty.to_selector_name(type_engine, error_msg_span)
})
.collect::<Vec<CompileResult<String>>>();
let mut buf = vec![];
for arg in type_arguments {
match arg.value {
Some(value) => buf.push(value),
None => return arg,
}
}
buf
};
if type_arguments.is_empty() {
format!("e({})", variant_names.join(","))
} else {
format!(
"e<{}>({})",
type_arguments.join(","),
variant_names.join(",")
)
}
}
Array(elem_ty, length) => {
let name = type_engine
.get(elem_ty.type_id)
.to_selector_name(type_engine, error_msg_span);
let name = match name.value {
Some(name) => name,
None => return name,
};
format!("a[{};{}]", name, length.val())
}
RawUntypedPtr => "rawptr".to_string(),
RawUntypedSlice => "rawslice".to_string(),
_ => {
return err(
vec![],
vec![CompileError::InvalidAbiType {
span: error_msg_span.clone(),
}],
)
}
};
ok(name, vec![], vec![])
}
pub fn is_uninhabited(&self, type_engine: &TypeEngine) -> bool {
let id_uninhabited = |id| type_engine.get(id).is_uninhabited(type_engine);
match self {
TypeInfo::Enum { variant_types, .. } => variant_types
.iter()
.all(|variant_type| id_uninhabited(variant_type.type_id)),
TypeInfo::Struct { fields, .. } => {
fields.iter().any(|field| id_uninhabited(field.type_id))
}
TypeInfo::Tuple(fields) => fields
.iter()
.any(|field_type| id_uninhabited(field_type.type_id)),
TypeInfo::Array(elem_ty, length) => length.val() > 0 && id_uninhabited(elem_ty.type_id),
_ => false,
}
}
pub fn is_zero_sized(&self, type_engine: &TypeEngine) -> bool {
match self {
TypeInfo::Enum { variant_types, .. } => {
let mut found_unit_variant = false;
for variant_type in variant_types {
let type_info = type_engine.get(variant_type.type_id);
if type_info.is_uninhabited(type_engine) {
continue;
}
if type_info.is_zero_sized(type_engine) && !found_unit_variant {
found_unit_variant = true;
continue;
}
return false;
}
true
}
TypeInfo::Struct { fields, .. } => {
let mut all_zero_sized = true;
for field in fields {
let type_info = type_engine.get(field.type_id);
if type_info.is_uninhabited(type_engine) {
return true;
}
if !type_info.is_zero_sized(type_engine) {
all_zero_sized = false;
}
}
all_zero_sized
}
TypeInfo::Tuple(fields) => {
let mut all_zero_sized = true;
for field in fields {
let field_type = type_engine.get(field.type_id);
if field_type.is_uninhabited(type_engine) {
return true;
}
if !field_type.is_zero_sized(type_engine) {
all_zero_sized = false;
}
}
all_zero_sized
}
TypeInfo::Array(elem_ty, length) => {
length.val() == 0 || type_engine.get(elem_ty.type_id).is_zero_sized(type_engine)
}
_ => false,
}
}
pub fn can_safely_ignore(&self, type_engine: &TypeEngine) -> bool {
if self.is_zero_sized(type_engine) {
return true;
}
match self {
TypeInfo::Tuple(fields) => fields.iter().all(|type_argument| {
type_engine
.get(type_argument.type_id)
.can_safely_ignore(type_engine)
}),
TypeInfo::Array(elem_ty, length) => {
length.val() == 0
|| type_engine
.get(elem_ty.type_id)
.can_safely_ignore(type_engine)
}
TypeInfo::ErrorRecovery => true,
TypeInfo::Unknown => true,
_ => false,
}
}
pub fn is_unit(&self) -> bool {
match self {
TypeInfo::Tuple(fields) => fields.is_empty(),
_ => false,
}
}
pub fn is_copy_type(&self) -> bool {
matches!(
self,
TypeInfo::Boolean | TypeInfo::UnsignedInteger(_) | TypeInfo::RawUntypedPtr
) || self.is_unit()
}
pub(crate) fn apply_type_arguments(
self,
type_arguments: Vec<TypeArgument>,
span: &Span,
) -> CompileResult<TypeInfo> {
let warnings = vec![];
let mut errors = vec![];
if type_arguments.is_empty() {
return ok(self, warnings, errors);
}
match self {
TypeInfo::Enum { .. } | TypeInfo::Struct { .. } => {
errors.push(CompileError::Internal(
"did not expect to apply type arguments to this type",
span.clone(),
));
err(warnings, errors)
}
TypeInfo::Custom {
name,
type_arguments: other_type_arguments,
} => {
if other_type_arguments.is_some() {
errors.push(CompileError::TypeArgumentsNotAllowed { span: span.clone() });
err(warnings, errors)
} else {
let type_info = TypeInfo::Custom {
name,
type_arguments: Some(type_arguments),
};
ok(type_info, warnings, errors)
}
}
TypeInfo::Unknown
| TypeInfo::UnknownGeneric { .. }
| TypeInfo::Str(_)
| TypeInfo::UnsignedInteger(_)
| TypeInfo::Boolean
| TypeInfo::Tuple(_)
| TypeInfo::ContractCaller { .. }
| TypeInfo::SelfType
| TypeInfo::B256
| TypeInfo::Numeric
| TypeInfo::RawUntypedPtr
| TypeInfo::RawUntypedSlice
| TypeInfo::Contract
| TypeInfo::ErrorRecovery
| TypeInfo::Array(_, _)
| TypeInfo::Storage { .. }
| TypeInfo::Placeholder(_) => {
errors.push(CompileError::TypeArgumentsNotAllowed { span: span.clone() });
err(warnings, errors)
}
}
}
pub(crate) fn extract_inner_types(&self, type_engine: &TypeEngine) -> HashSet<TypeId> {
let helper = |type_id: TypeId| {
let mut inner_types = HashSet::new();
match type_engine.get(type_id) {
TypeInfo::Enum {
type_parameters,
variant_types,
..
} => {
inner_types.insert(type_id);
for type_param in type_parameters.iter() {
inner_types.extend(
type_engine
.get(type_param.type_id)
.extract_inner_types(type_engine),
);
}
for variant in variant_types.iter() {
inner_types.extend(
type_engine
.get(variant.type_id)
.extract_inner_types(type_engine),
);
}
}
TypeInfo::Struct {
type_parameters,
fields,
..
} => {
inner_types.insert(type_id);
for type_param in type_parameters.iter() {
inner_types.extend(
type_engine
.get(type_param.type_id)
.extract_inner_types(type_engine),
);
}
for field in fields.iter() {
inner_types.extend(
type_engine
.get(field.type_id)
.extract_inner_types(type_engine),
);
}
}
TypeInfo::Custom { type_arguments, .. } => {
inner_types.insert(type_id);
if let Some(type_arguments) = type_arguments {
for type_arg in type_arguments.iter() {
inner_types.extend(
type_engine
.get(type_arg.type_id)
.extract_inner_types(type_engine),
);
}
}
}
TypeInfo::Array(elem_ty, _) => {
inner_types.insert(elem_ty.type_id);
inner_types.extend(type_engine.get(type_id).extract_inner_types(type_engine));
}
TypeInfo::Tuple(elems) => {
inner_types.insert(type_id);
for elem in elems.iter() {
inner_types.extend(
type_engine
.get(elem.type_id)
.extract_inner_types(type_engine),
);
}
}
TypeInfo::Storage { fields } => {
inner_types.insert(type_id);
for field in fields.iter() {
inner_types.extend(
type_engine
.get(field.type_id)
.extract_inner_types(type_engine),
);
}
}
TypeInfo::Unknown
| TypeInfo::UnknownGeneric { .. }
| TypeInfo::Str(_)
| TypeInfo::UnsignedInteger(_)
| TypeInfo::Boolean
| TypeInfo::ContractCaller { .. }
| TypeInfo::SelfType
| TypeInfo::B256
| TypeInfo::Numeric
| TypeInfo::RawUntypedPtr
| TypeInfo::RawUntypedSlice
| TypeInfo::Contract
| TypeInfo::Placeholder(_) => {
inner_types.insert(type_id);
}
TypeInfo::ErrorRecovery => {}
}
inner_types
};
let mut inner_types = HashSet::new();
match self {
TypeInfo::Enum {
type_parameters,
variant_types,
..
} => {
for type_param in type_parameters.iter() {
inner_types.extend(helper(type_param.type_id));
}
for variant in variant_types.iter() {
inner_types.extend(helper(variant.type_id));
}
}
TypeInfo::Struct {
type_parameters,
fields,
..
} => {
for type_param in type_parameters.iter() {
inner_types.extend(helper(type_param.type_id));
}
for field in fields.iter() {
inner_types.extend(helper(field.type_id));
}
}
TypeInfo::Custom { type_arguments, .. } => {
if let Some(type_arguments) = type_arguments {
for type_arg in type_arguments.iter() {
inner_types.extend(helper(type_arg.type_id));
}
}
}
TypeInfo::Array(elem_ty, _) => {
inner_types.extend(helper(elem_ty.type_id));
}
TypeInfo::Tuple(elems) => {
for elem in elems.iter() {
inner_types.extend(helper(elem.type_id));
}
}
TypeInfo::Storage { fields } => {
for field in fields.iter() {
inner_types.extend(helper(field.type_id));
}
}
TypeInfo::Unknown
| TypeInfo::UnknownGeneric { .. }
| TypeInfo::Str(_)
| TypeInfo::UnsignedInteger(_)
| TypeInfo::Boolean
| TypeInfo::ContractCaller { .. }
| TypeInfo::SelfType
| TypeInfo::B256
| TypeInfo::Numeric
| TypeInfo::Contract
| TypeInfo::RawUntypedPtr
| TypeInfo::RawUntypedSlice
| TypeInfo::ErrorRecovery
| TypeInfo::Placeholder(_) => {}
}
inner_types
}
pub(crate) fn expect_is_supported_in_match_expressions(
&self,
span: &Span,
) -> CompileResult<()> {
let warnings = vec![];
let mut errors = vec![];
match self {
TypeInfo::UnsignedInteger(_)
| TypeInfo::Enum { .. }
| TypeInfo::Struct { .. }
| TypeInfo::Boolean
| TypeInfo::Tuple(_)
| TypeInfo::B256
| TypeInfo::UnknownGeneric { .. }
| TypeInfo::Numeric => ok((), warnings, errors),
TypeInfo::Unknown
| TypeInfo::RawUntypedPtr
| TypeInfo::RawUntypedSlice
| TypeInfo::ContractCaller { .. }
| TypeInfo::Custom { .. }
| TypeInfo::SelfType
| TypeInfo::Str(_)
| TypeInfo::Contract
| TypeInfo::Array(_, _)
| TypeInfo::Storage { .. }
| TypeInfo::Placeholder(_) => {
errors.push(CompileError::Unimplemented(
"matching on this type is unsupported right now",
span.clone(),
));
err(warnings, errors)
}
TypeInfo::ErrorRecovery => {
err(warnings, errors)
}
}
}
pub(crate) fn expect_is_supported_in_impl_blocks_self(&self, span: &Span) -> CompileResult<()> {
let warnings = vec![];
let mut errors = vec![];
match self {
TypeInfo::UnsignedInteger(_)
| TypeInfo::Enum { .. }
| TypeInfo::Struct { .. }
| TypeInfo::Boolean
| TypeInfo::Tuple(_)
| TypeInfo::B256
| TypeInfo::RawUntypedPtr
| TypeInfo::RawUntypedSlice
| TypeInfo::Custom { .. }
| TypeInfo::Str(_)
| TypeInfo::Array(_, _)
| TypeInfo::Contract
| TypeInfo::Numeric => ok((), warnings, errors),
TypeInfo::Unknown
| TypeInfo::UnknownGeneric { .. }
| TypeInfo::ContractCaller { .. }
| TypeInfo::SelfType
| TypeInfo::Storage { .. }
| TypeInfo::Placeholder(_) => {
errors.push(CompileError::Unimplemented(
"implementing traits on this type is unsupported right now",
span.clone(),
));
err(warnings, errors)
}
TypeInfo::ErrorRecovery => {
err(warnings, errors)
}
}
}
pub(crate) fn extract_nested_types(
self,
type_engine: &TypeEngine,
span: &Span,
) -> CompileResult<Vec<TypeInfo>> {
let mut warnings = vec![];
let mut errors = vec![];
let mut all_nested_types = vec![self.clone()];
match self {
TypeInfo::Enum {
variant_types,
type_parameters,
..
} => {
for type_parameter in type_parameters.iter() {
let mut nested_types = check!(
type_engine
.get(type_parameter.type_id)
.extract_nested_types(type_engine, span),
return err(warnings, errors),
warnings,
errors
);
all_nested_types.append(&mut nested_types);
}
for variant_type in variant_types.iter() {
let mut nested_types = check!(
type_engine
.get(variant_type.type_id)
.extract_nested_types(type_engine, span),
return err(warnings, errors),
warnings,
errors
);
all_nested_types.append(&mut nested_types);
}
}
TypeInfo::Struct {
fields,
type_parameters,
..
} => {
for type_parameter in type_parameters.iter() {
let mut nested_types = check!(
type_engine
.get(type_parameter.type_id)
.extract_nested_types(type_engine, span),
return err(warnings, errors),
warnings,
errors
);
all_nested_types.append(&mut nested_types);
}
for field in fields.iter() {
let mut nested_types = check!(
type_engine
.get(field.type_id)
.extract_nested_types(type_engine, span),
return err(warnings, errors),
warnings,
errors
);
all_nested_types.append(&mut nested_types);
}
}
TypeInfo::Tuple(type_arguments) => {
for type_argument in type_arguments.iter() {
let mut nested_types = check!(
type_engine
.get(type_argument.type_id)
.extract_nested_types(type_engine, span),
return err(warnings, errors),
warnings,
errors
);
all_nested_types.append(&mut nested_types);
}
}
TypeInfo::Array(elem_ty, _) => {
let mut nested_types = check!(
type_engine
.get(elem_ty.type_id)
.extract_nested_types(type_engine, span),
return err(warnings, errors),
warnings,
errors
);
all_nested_types.append(&mut nested_types);
}
TypeInfo::Storage { fields } => {
for field in fields.iter() {
let mut nested_types = check!(
type_engine
.get(field.type_id)
.extract_nested_types(type_engine, span),
return err(warnings, errors),
warnings,
errors
);
all_nested_types.append(&mut nested_types);
}
}
TypeInfo::UnknownGeneric {
trait_constraints, ..
} => {
for trait_constraint in trait_constraints.iter() {
for type_arg in trait_constraint.type_arguments.iter() {
let mut nested_types = check!(
type_engine
.get(type_arg.type_id)
.extract_nested_types(type_engine, span),
return err(warnings, errors),
warnings,
errors
);
all_nested_types.append(&mut nested_types);
}
}
}
TypeInfo::Unknown
| TypeInfo::Str(_)
| TypeInfo::UnsignedInteger(_)
| TypeInfo::Boolean
| TypeInfo::ContractCaller { .. }
| TypeInfo::B256
| TypeInfo::Numeric
| TypeInfo::RawUntypedPtr
| TypeInfo::RawUntypedSlice
| TypeInfo::Contract
| TypeInfo::Placeholder(_) => {}
TypeInfo::Custom { .. } | TypeInfo::SelfType => {
errors.push(CompileError::Internal(
"did not expect to find this type here",
span.clone(),
));
return err(warnings, errors);
}
TypeInfo::ErrorRecovery => {
return err(warnings, errors);
}
}
ok(all_nested_types, warnings, errors)
}
pub(crate) fn extract_nested_generics<'a>(
&self,
engines: Engines<'a>,
span: &Span,
) -> CompileResult<HashSet<WithEngines<'a, TypeInfo>>> {
let mut warnings = vec![];
let mut errors = vec![];
let nested_types = check!(
self.clone().extract_nested_types(engines.te(), span),
return err(warnings, errors),
warnings,
errors
);
let generics = HashSet::from_iter(
nested_types
.into_iter()
.filter(|x| matches!(x, TypeInfo::UnknownGeneric { .. }))
.map(|thing| WithEngines::new(thing, engines)),
);
ok(generics, warnings, errors)
}
pub(crate) fn is_subset_of(&self, other: &TypeInfo, engines: Engines<'_>) -> bool {
match (self, other) {
(
Self::UnknownGeneric {
trait_constraints: ltc,
..
},
Self::UnknownGeneric {
trait_constraints: rtc,
..
},
) => {
return rtc.eq(ltc, engines);
}
(_, Self::UnknownGeneric { .. }) => {
return true;
}
_ => {}
}
self.is_subset_inner(other, engines)
}
pub(crate) fn is_subset_of_for_item_import(
&self,
other: &TypeInfo,
engines: Engines<'_>,
) -> bool {
self.is_subset_inner(other, engines)
}
fn is_subset_inner(&self, other: &TypeInfo, engines: Engines<'_>) -> bool {
let type_engine = engines.te();
match (self, other) {
(Self::Array(l0, l1), Self::Array(r0, r1)) => {
type_engine
.get(l0.type_id)
.is_subset_of(&type_engine.get(r0.type_id), engines)
&& l1.val() == r1.val()
}
(
Self::Custom {
name: l_name,
type_arguments: l_type_args,
},
Self::Custom {
name: r_name,
type_arguments: r_type_args,
},
) => {
let l_types = l_type_args
.as_ref()
.unwrap_or(&vec![])
.iter()
.map(|x| type_engine.get(x.type_id))
.collect::<Vec<_>>();
let r_types = r_type_args
.as_ref()
.unwrap_or(&vec![])
.iter()
.map(|x| type_engine.get(x.type_id))
.collect::<Vec<_>>();
l_name == r_name && types_are_subset_of(engines, &l_types, &r_types)
}
(
Self::Enum {
name: l_name,
variant_types: l_variant_types,
type_parameters: l_type_parameters,
},
Self::Enum {
name: r_name,
variant_types: r_variant_types,
type_parameters: r_type_parameters,
},
) => {
let l_names = l_variant_types
.iter()
.map(|x| x.name.clone())
.collect::<Vec<_>>();
let r_names = r_variant_types
.iter()
.map(|x| x.name.clone())
.collect::<Vec<_>>();
let l_types = l_type_parameters
.iter()
.map(|x| type_engine.get(x.type_id))
.collect::<Vec<_>>();
let r_types = r_type_parameters
.iter()
.map(|x| type_engine.get(x.type_id))
.collect::<Vec<_>>();
l_name == r_name
&& l_names == r_names
&& types_are_subset_of(engines, &l_types, &r_types)
}
(
Self::Struct {
name: l_name,
fields: l_fields,
type_parameters: l_type_parameters,
},
Self::Struct {
name: r_name,
fields: r_fields,
type_parameters: r_type_parameters,
},
) => {
let l_names = l_fields.iter().map(|x| x.name.clone()).collect::<Vec<_>>();
let r_names = r_fields.iter().map(|x| x.name.clone()).collect::<Vec<_>>();
let l_types = l_type_parameters
.iter()
.map(|x| type_engine.get(x.type_id))
.collect::<Vec<_>>();
let r_types = r_type_parameters
.iter()
.map(|x| type_engine.get(x.type_id))
.collect::<Vec<_>>();
l_name == r_name
&& l_names == r_names
&& types_are_subset_of(engines, &l_types, &r_types)
}
(Self::Tuple(l_types), Self::Tuple(r_types)) => {
let l_types = l_types
.iter()
.map(|x| type_engine.get(x.type_id))
.collect::<Vec<_>>();
let r_types = r_types
.iter()
.map(|x| type_engine.get(x.type_id))
.collect::<Vec<_>>();
types_are_subset_of(engines, &l_types, &r_types)
}
(a, b) => a.eq(b, engines),
}
}
pub(crate) fn apply_subfields(
&self,
engines: Engines<'_>,
subfields: &[Ident],
span: &Span,
) -> CompileResult<ty::TyStructField> {
let mut warnings = vec![];
let mut errors = vec![];
let type_engine = engines.te();
match (self, subfields.split_first()) {
(TypeInfo::Struct { .. }, None) => err(warnings, errors),
(TypeInfo::Struct { name, fields, .. }, Some((first, rest))) => {
let field = match fields
.iter()
.find(|field| field.name.as_str() == first.as_str())
{
Some(field) => field.clone(),
None => {
let available_fields =
fields.iter().map(|x| x.name.as_str()).collect::<Vec<_>>();
errors.push(CompileError::FieldNotFound {
field_name: first.clone(),
struct_name: name.clone(),
available_fields: available_fields.join(", "),
});
return err(warnings, errors);
}
};
let field = if rest.is_empty() {
field
} else {
check!(
type_engine
.get(field.type_id)
.apply_subfields(engines, rest, span),
return err(warnings, errors),
warnings,
errors
)
};
ok(field, warnings, errors)
}
(TypeInfo::ErrorRecovery, _) => {
err(warnings, errors)
}
(type_info, _) => {
errors.push(CompileError::FieldAccessOnNonStruct {
actually: engines.help_out(type_info).to_string(),
span: span.clone(),
});
err(warnings, errors)
}
}
}
pub(crate) fn can_change(&self) -> bool {
match self {
TypeInfo::Enum {
type_parameters, ..
} => !type_parameters.is_empty(),
TypeInfo::Struct {
type_parameters, ..
} => !type_parameters.is_empty(),
TypeInfo::Str(_)
| TypeInfo::UnsignedInteger(_)
| TypeInfo::Boolean
| TypeInfo::B256
| TypeInfo::RawUntypedPtr
| TypeInfo::RawUntypedSlice
| TypeInfo::ErrorRecovery => false,
TypeInfo::Unknown
| TypeInfo::UnknownGeneric { .. }
| TypeInfo::ContractCaller { .. }
| TypeInfo::Custom { .. }
| TypeInfo::SelfType
| TypeInfo::Tuple(_)
| TypeInfo::Array(_, _)
| TypeInfo::Contract
| TypeInfo::Storage { .. }
| TypeInfo::Numeric
| TypeInfo::Placeholder(_) => true,
}
}
pub(crate) fn has_valid_constructor(&self) -> bool {
match self {
TypeInfo::Unknown => false,
TypeInfo::Enum { variant_types, .. } => !variant_types.is_empty(),
_ => true,
}
}
pub(crate) fn expect_tuple(
&self,
engines: Engines<'_>,
debug_string: impl Into<String>,
debug_span: &Span,
) -> CompileResult<&Vec<TypeArgument>> {
let warnings = vec![];
let errors = vec![];
match self {
TypeInfo::Tuple(elems) => ok(elems, warnings, errors),
TypeInfo::ErrorRecovery => err(warnings, errors),
a => err(
vec![],
vec![CompileError::NotATuple {
name: debug_string.into(),
span: debug_span.clone(),
actually: engines.help_out(a).to_string(),
}],
),
}
}
pub(crate) fn expect_enum(
&self,
engines: Engines<'_>,
debug_string: impl Into<String>,
debug_span: &Span,
) -> CompileResult<(&Ident, &Vec<ty::TyEnumVariant>)> {
let warnings = vec![];
let errors = vec![];
match self {
TypeInfo::Enum {
name,
variant_types,
..
} => ok((name, variant_types), warnings, errors),
TypeInfo::ErrorRecovery => err(warnings, errors),
a => err(
vec![],
vec![CompileError::NotAnEnum {
name: debug_string.into(),
span: debug_span.clone(),
actually: engines.help_out(a).to_string(),
}],
),
}
}
#[allow(dead_code)]
pub(crate) fn expect_struct(
&self,
engines: Engines<'_>,
debug_span: &Span,
) -> CompileResult<(&Ident, &Vec<ty::TyStructField>)> {
let warnings = vec![];
let errors = vec![];
match self {
TypeInfo::Struct { name, fields, .. } => ok((name, fields), warnings, errors),
TypeInfo::ErrorRecovery => err(warnings, errors),
a => err(
vec![],
vec![CompileError::NotAStruct {
span: debug_span.clone(),
actually: engines.help_out(a).to_string(),
}],
),
}
}
}
fn types_are_subset_of(engines: Engines<'_>, left: &[TypeInfo], right: &[TypeInfo]) -> bool {
if left.len() != right.len() {
return false;
}
if left.is_empty() && right.is_empty() {
return true;
}
for (l, r) in left.iter().zip(right.iter()) {
if !l.is_subset_of(r, engines) {
return false;
}
}
let mut constraints = vec![];
for i in 0..(right.len() - 1) {
for j in (i + 1)..right.len() {
let a = right.get(i).unwrap();
let b = right.get(j).unwrap();
if a.eq(b, engines) {
constraints.push((i, j));
}
}
}
for (i, j) in constraints.into_iter() {
let a = left.get(i).unwrap();
let b = left.get(j).unwrap();
if !a.eq(b, engines) {
return false;
}
}
true
}
fn print_inner_types(
engines: Engines<'_>,
name: String,
inner_types: impl Iterator<Item = TypeId>,
) -> String {
let inner_types = inner_types
.map(|x| engines.help_out(x).to_string())
.collect::<Vec<_>>();
format!(
"{}{}",
name,
if inner_types.is_empty() {
"".into()
} else {
format!("<{}>", inner_types.join(", "))
}
)
}