From e1e798ef8e53152d35620e6c00623e2cfa2d3b43 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 30 Mar 2024 21:17:11 -0700 Subject: [PATCH] Get to the point of needing to construct/reference fields. --- src/backend.rs | 5 +- src/backend/error.rs | 7 +- src/backend/into_crane.rs | 258 ++++++++++++++++++-------------------- src/ir/ast.rs | 10 ++ src/ir/fields.rs | 40 +++++- src/syntax/tokens.rs | 8 -- 6 files changed, 176 insertions(+), 152 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index b2fee29..6047134 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -35,6 +35,7 @@ pub use self::error::BackendError; pub use self::runtime::{RuntimeFunctionError, RuntimeFunctions}; use crate::syntax::ConstantType; use cranelift_codegen::entity::EntityRef; +use cranelift_codegen::ir::types; use cranelift_codegen::settings::Configurable; use cranelift_codegen::{isa, settings}; use cranelift_jit::{JITBuilder, JITModule}; @@ -61,7 +62,7 @@ pub struct Backend { runtime_functions: RuntimeFunctions, defined_strings: HashMap, defined_functions: HashMap, FuncId>, - defined_symbols: HashMap, (DataId, ConstantType)>, + defined_symbols: HashMap, (DataId, types::Type)>, output_buffer: Option, platform: Triple, next_variable: usize, @@ -190,7 +191,7 @@ impl Backend { self.module.define_data(id, &self.data_ctx)?; self.data_ctx.clear(); self.defined_symbols - .insert(ArcIntern::new(name), (id, ctype)); + .insert(ArcIntern::new(name), (id, ctype.into())); Ok(id) } diff --git a/src/backend/error.rs b/src/backend/error.rs index c6dc995..0fd0e7e 100644 --- a/src/backend/error.rs +++ b/src/backend/error.rs @@ -1,4 +1,4 @@ -use crate::{backend::runtime::RuntimeFunctionError, eval::PrimitiveType, ir::Type}; +use crate::{backend::runtime::RuntimeFunctionError, ir::Type}; use codespan_reporting::diagnostic::Diagnostic; use cranelift_codegen::{isa::LookupError, settings::SetError, CodegenError}; use cranelift_module::ModuleError; @@ -40,7 +40,10 @@ pub enum BackendError { #[error(transparent)] Write(#[from] cranelift_object::object::write::Error), #[error("Invalid type cast from {from} to {to}")] - InvalidTypeCast { from: PrimitiveType, to: Type }, + InvalidTypeCast { + from: cranelift_codegen::ir::types::Type, + to: Type, + }, #[error("Unknown string constant '{0}")] UnknownString(ArcIntern), #[error("Compiler doesn't currently support function arguments")] diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index fe7e1c0..644af63 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -14,14 +14,16 @@ use cranelift_module::{DataDescription, FuncId, Linkage, Module}; use internment::ArcIntern; use std::collections::{hash_map, HashMap}; +const VOID_REPR_TYPE: types::Type = types::I64; + /// When we're talking about variables, it's handy to just have a table that points /// from a variable to "what to do if you want to reference this variable", which is /// agnostic about whether the variable is local, global, an argument, etc. Since /// the type of that function is a little bit annoying, we summarize it here. pub enum ReferenceBuilder { - Global(ConstantType, GlobalValue), - Local(ConstantType, cranelift_frontend::Variable), - Argument(ConstantType, entities::Value), + Global(types::Type, GlobalValue), + Local(types::Type, cranelift_frontend::Variable), + Argument(types::Type, entities::Value), } impl Backend { @@ -29,13 +31,10 @@ impl Backend { /// best as possible. fn translate_type(&self, t: &Type) -> AbiParam { let (value_type, extension) = match t { - Type::Function(_, _) => ( + Type::Function(_, _) | Type::Structure(_) => ( types::Type::triple_pointer_type(&self.platform), ir::ArgumentExtension::None, ), - Type::Structure(_) => { - unimplemented!() - } Type::Primitive(PrimitiveType::Void) => (types::I8, ir::ArgumentExtension::None), // FIXME? Type::Primitive(PrimitiveType::I8) => (types::I8, ir::ArgumentExtension::Sext), Type::Primitive(PrimitiveType::I16) => (types::I16, ir::ArgumentExtension::Sext), @@ -88,12 +87,23 @@ impl Backend { )?; tracing::info!(name = %top_level_name, data_type = %pt, "defining top-level data"); self.module.define_data(data_id, &pt.blank_data())?; + let constant_type = ConstantType::from(pt); self.defined_symbols - .insert(top_level_name, (data_id, pt.into())); + .insert(top_level_name, (data_id, constant_type.into())); } - Type::Structure(_) => { - unimplemented!() + Type::Structure(mut fields) => { + let data_id = self.module.declare_data( + top_level_name.as_str(), + Linkage::Export, + true, + false, + )?; + tracing::info!(name = %top_level_name, "defining top-level data structure"); + self.module.define_data(data_id, fields.blank_data())?; + let pointer = self.module.target_config().pointer_type(); + self.defined_symbols + .insert(top_level_name, (data_id, pointer)); } } } @@ -226,13 +236,8 @@ impl Backend { let main_block = builder.create_block(); // add the block parameters, which should be the function parameters for (name, ty) in arguments.iter() { - let constant_type = ty - .try_into() - .map_err(|_| BackendError::NoFunctionArguments { - function_name: function_name.to_string(), - argument_name: name.to_string(), - })?; - let value = builder.append_block_param(main_block, ir::Type::from(constant_type)); + let constant_type = self.translate_type(ty).value_type; + let value = builder.append_block_param(main_block, constant_type); variables.insert( name.clone(), ReferenceBuilder::Argument(constant_type, value), @@ -273,96 +278,98 @@ impl Backend { expr: Expression, variables: &mut HashMap, builder: &mut FunctionBuilder, - ) -> Result<(entities::Value, ConstantType), BackendError> { + ) -> Result<(entities::Value, types::Type), BackendError> { match expr { Expression::Atomic(x) => self.compile_value_or_ref(x, variables, builder), Expression::Cast(_, target_type, valref) => { + let val_is_signed = valref.type_of().is_signed(); let (val, val_type) = self.compile_value_or_ref(valref, variables, builder)?; match (val_type, &target_type) { - (ConstantType::I8, Type::Primitive(PrimitiveType::I8)) => Ok((val, val_type)), - (ConstantType::I8, Type::Primitive(PrimitiveType::I16)) => { - Ok((builder.ins().sextend(types::I16, val), ConstantType::I16)) + (types::I8, Type::Primitive(PrimitiveType::I8)) => Ok((val, val_type)), + (types::I8, Type::Primitive(PrimitiveType::I16)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I16, val), types::I16)) + } else { + Ok((builder.ins().uextend(types::I16, val), types::I16)) + } } - (ConstantType::I8, Type::Primitive(PrimitiveType::I32)) => { - Ok((builder.ins().sextend(types::I32, val), ConstantType::I32)) + (types::I8, Type::Primitive(PrimitiveType::I32)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I32, val), types::I32)) + } else { + Ok((builder.ins().uextend(types::I32, val), types::I32)) + } } - (ConstantType::I8, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().sextend(types::I64, val), ConstantType::I64)) + (types::I8, Type::Primitive(PrimitiveType::I64)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I64, val), types::I64)) + } else { + Ok((builder.ins().uextend(types::I64, val), types::I64)) + } } - (ConstantType::I16, Type::Primitive(PrimitiveType::I16)) => Ok((val, val_type)), - (ConstantType::I16, Type::Primitive(PrimitiveType::I32)) => { - Ok((builder.ins().sextend(types::I32, val), ConstantType::I32)) + (types::I16, Type::Primitive(PrimitiveType::I16)) => Ok((val, val_type)), + (types::I16, Type::Primitive(PrimitiveType::I32)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I32, val), types::I32)) + } else { + Ok((builder.ins().uextend(types::I32, val), types::I32)) + } } - (ConstantType::I16, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().sextend(types::I64, val), ConstantType::I64)) + (types::I16, Type::Primitive(PrimitiveType::I64)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I64, val), types::I64)) + } else { + Ok((builder.ins().uextend(types::I64, val), types::I64)) + } } - (ConstantType::I32, Type::Primitive(PrimitiveType::I32)) => Ok((val, val_type)), - (ConstantType::I32, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().sextend(types::I64, val), ConstantType::I64)) + (types::I32, Type::Primitive(PrimitiveType::I32)) => Ok((val, val_type)), + (types::I32, Type::Primitive(PrimitiveType::I64)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I64, val), types::I64)) + } else { + Ok((builder.ins().uextend(types::I64, val), types::I64)) + } } - (ConstantType::I64, Type::Primitive(PrimitiveType::I64)) => Ok((val, val_type)), + (types::I64, Type::Primitive(PrimitiveType::I64)) => Ok((val, val_type)), - (ConstantType::U8, Type::Primitive(PrimitiveType::U8)) => Ok((val, val_type)), - (ConstantType::U8, Type::Primitive(PrimitiveType::U16)) => { - Ok((builder.ins().uextend(types::I16, val), ConstantType::U16)) + (types::I8, Type::Primitive(PrimitiveType::U8)) => Ok((val, val_type)), + (types::I8, Type::Primitive(PrimitiveType::U16)) => { + Ok((builder.ins().uextend(types::I16, val), types::I16)) } - (ConstantType::U8, Type::Primitive(PrimitiveType::U32)) => { - Ok((builder.ins().uextend(types::I32, val), ConstantType::U32)) + (types::I8, Type::Primitive(PrimitiveType::U32)) => { + Ok((builder.ins().uextend(types::I32, val), types::I32)) } - (ConstantType::U8, Type::Primitive(PrimitiveType::U64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::U64)) + (types::I8, Type::Primitive(PrimitiveType::U64)) => { + Ok((builder.ins().uextend(types::I64, val), types::I64)) } - (ConstantType::U16, Type::Primitive(PrimitiveType::U16)) => Ok((val, val_type)), - (ConstantType::U16, Type::Primitive(PrimitiveType::U32)) => { - Ok((builder.ins().uextend(types::I32, val), ConstantType::U32)) + (types::I16, Type::Primitive(PrimitiveType::U16)) => Ok((val, val_type)), + (types::I16, Type::Primitive(PrimitiveType::U32)) => { + Ok((builder.ins().uextend(types::I32, val), types::I32)) } - (ConstantType::U16, Type::Primitive(PrimitiveType::U64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::U64)) + (types::I16, Type::Primitive(PrimitiveType::U64)) => { + Ok((builder.ins().uextend(types::I64, val), types::I64)) } - (ConstantType::U32, Type::Primitive(PrimitiveType::U32)) => Ok((val, val_type)), - (ConstantType::U32, Type::Primitive(PrimitiveType::U64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::U64)) + (types::I32, Type::Primitive(PrimitiveType::U32)) => Ok((val, val_type)), + (types::I32, Type::Primitive(PrimitiveType::U64)) => { + Ok((builder.ins().uextend(types::I64, val), types::I64)) } - (ConstantType::U64, Type::Primitive(PrimitiveType::U64)) => Ok((val, val_type)), - - (ConstantType::Void, Type::Primitive(PrimitiveType::Void)) => { - Ok((val, val_type)) - } - - (ConstantType::U8, Type::Primitive(PrimitiveType::I16)) => { - Ok((builder.ins().uextend(types::I16, val), ConstantType::I16)) - } - (ConstantType::U8, Type::Primitive(PrimitiveType::I32)) => { - Ok((builder.ins().uextend(types::I32, val), ConstantType::I32)) - } - (ConstantType::U8, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::I64)) - } - (ConstantType::U16, Type::Primitive(PrimitiveType::I32)) => { - Ok((builder.ins().uextend(types::I32, val), ConstantType::I32)) - } - (ConstantType::U16, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::I64)) - } - (ConstantType::U32, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::I64)) - } + (types::I64, Type::Primitive(PrimitiveType::U64)) => Ok((val, val_type)), _ => Err(BackendError::InvalidTypeCast { - from: val_type.into(), + from: val_type, to: target_type, }), } } - Expression::Primitive(_, _, prim, mut vals) => { + Expression::Primitive(_, ret_type, prim, mut vals) => { let mut values = vec![]; let mut first_type = None; @@ -392,7 +399,7 @@ impl Backend { } Primitive::Minus => Ok((builder.ins().ineg(values[0]), first_type)), Primitive::Times => Ok((builder.ins().imul(values[0], values[1]), first_type)), - Primitive::Divide if first_type.is_signed() => { + Primitive::Divide if ret_type.is_signed() => { Ok((builder.ins().sdiv(values[0], values[1]), first_type)) } Primitive::Divide => Ok((builder.ins().udiv(values[0], values[1]), first_type)), @@ -403,7 +410,7 @@ impl Backend { Expression::FieldRef(_, _, _, _) => unimplemented!(), Expression::Block(_, _, mut exprs) => match exprs.pop() { - None => Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)), + None => Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)), Some(last) => { for inner in exprs { // we can ignore all of these return values and such, because we @@ -431,20 +438,31 @@ impl Backend { .declare_data_in_func(string_data_id, builder.func); let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref); - let (val, vtype) = self.compile_value_or_ref(var, variables, builder)?; + let var_type = var.type_of(); + let (val, _) = self.compile_value_or_ref(var, variables, builder)?; - let vtype_repr = builder.ins().iconst(types::I64, vtype as i64); + let (repr_val, casted_val) = match var_type { + Type::Structure(_) => (ConstantType::I64 as i64, val), + Type::Function(_, _) => (ConstantType::I64 as i64, val), + Type::Primitive(pt) => { + let constant_type = pt.into(); - let casted_val = match vtype { - ConstantType::U64 | ConstantType::I64 | ConstantType::Void => val, - ConstantType::I8 | ConstantType::I16 | ConstantType::I32 => { - builder.ins().sextend(types::I64, val) - } - ConstantType::U8 | ConstantType::U16 | ConstantType::U32 => { - builder.ins().uextend(types::I64, val) + let new_val = match constant_type { + ConstantType::U64 | ConstantType::I64 | ConstantType::Void => val, + ConstantType::I8 | ConstantType::I16 | ConstantType::I32 => { + builder.ins().sextend(types::I64, val) + } + ConstantType::U8 | ConstantType::U16 | ConstantType::U32 => { + builder.ins().uextend(types::I64, val) + } + }; + + (constant_type as i64, new_val) } }; + let vtype_repr = builder.ins().iconst(types::I64, repr_val); + // Finally, we can generate the call to print. let print_func_ref = self.runtime_functions.include_runtime_function( "print", @@ -455,12 +473,11 @@ impl Backend { print_func_ref, &[buffer_ptr, name_ptr, vtype_repr, casted_val], ); - Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) + Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) } Expression::Bind(_, name, _, expr) => { let (value, value_type) = self.compile_expression(*expr, variables, builder)?; - let ir_type = ir::Type::from(value_type); let variable = self.generate_local(); match variables.get(&name) { @@ -468,7 +485,7 @@ impl Backend { let pointer = self.module.target_config().pointer_type(); let pointer_to = builder.ins().symbol_value(pointer, *global_value); builder.ins().store(MemFlags::new(), value, pointer_to, 0); - Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) + Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) } Some(ReferenceBuilder::Argument(_, _)) => { @@ -480,10 +497,10 @@ impl Backend { } None => { - builder.declare_var(variable, ir_type); + builder.declare_var(variable, value_type); builder.def_var(variable, value); variables.insert(name, ReferenceBuilder::Local(value_type, variable)); - Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) + Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) } } } @@ -511,21 +528,10 @@ impl Backend { let results = builder.inst_results(call); match results { - [] => Ok(( - builder.ins().iconst(types::I64, 0), - ConstantType::Void, - )), - [result] => match result_type { - Type::Primitive(ct) => Ok((*result, ct.into())), - Type::Function(_, rt) => match *rt { - Type::Function(_, _) => { - panic!("function returns a function?") - } - Type::Structure(_) => unimplemented!(), - Type::Primitive(ct) => Ok((*result, ct.into())), - }, - Type::Structure(_) => unimplemented!(), - }, + [] => Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)), + [result] => { + Ok((*result, self.translate_type(&result_type).value_type)) + } _ => panic!("don't support multi-value returns yet"), } } @@ -544,7 +550,7 @@ impl Backend { value_or_ref: ValueOrRef, variables: &HashMap, builder: &mut FunctionBuilder, - ) -> Result<(entities::Value, ConstantType), BackendError> { + ) -> Result<(entities::Value, types::Type), BackendError> { match value_or_ref { ValueOrRef::Value(_, _, val) => match val { Value::I8(_, v) => { @@ -555,49 +561,31 @@ impl Backend { // negative number for us. Which sets the high bits, which makes Cranelift unhappy. // So first we cast the i8 as u8, to get rid of the whole concept of sign extension, // and *then* we cast to i64. - Ok(( - builder.ins().iconst(types::I8, v as u8 as i64), - ConstantType::I8, - )) + Ok((builder.ins().iconst(types::I8, v as u8 as i64), types::I8)) } Value::I16(_, v) => Ok(( // see above note for the "... as ... as" builder.ins().iconst(types::I16, v as u16 as i64), - ConstantType::I16, + types::I16, )), Value::I32(_, v) => Ok(( // see above note for the "... as ... as" builder.ins().iconst(types::I32, v as u32 as i64), - ConstantType::I32, + types::I32, )), - Value::I64(_, v) => Ok((builder.ins().iconst(types::I64, v), ConstantType::I64)), - Value::U8(_, v) => { - Ok((builder.ins().iconst(types::I8, v as i64), ConstantType::U8)) - } - Value::U16(_, v) => Ok(( - builder.ins().iconst(types::I16, v as i64), - ConstantType::U16, - )), - Value::U32(_, v) => Ok(( - builder.ins().iconst(types::I32, v as i64), - ConstantType::U32, - )), - Value::U64(_, v) => Ok(( - builder.ins().iconst(types::I64, v as i64), - ConstantType::U64, - )), - Value::Void => Ok((builder.ins().iconst(types::I64, 0i64), ConstantType::Void)), + Value::I64(_, v) => Ok((builder.ins().iconst(types::I64, v), types::I64)), + Value::U8(_, v) => Ok((builder.ins().iconst(types::I8, v as i64), types::I8)), + Value::U16(_, v) => Ok((builder.ins().iconst(types::I16, v as i64), types::I16)), + Value::U32(_, v) => Ok((builder.ins().iconst(types::I32, v as i64), types::I32)), + Value::U64(_, v) => Ok((builder.ins().iconst(types::I64, v as i64), types::I64)), + Value::Void => Ok((builder.ins().iconst(types::I64, 0i64), VOID_REPR_TYPE)), }, ValueOrRef::Ref(_, _, name) => match variables.get(&name) { None => Err(BackendError::VariableLookupFailure(name)), Some(ReferenceBuilder::Global(ty, gv)) => { let pointer_to = self.module.target_config().pointer_type(); let pointer_value = builder.ins().symbol_value(pointer_to, *gv); - let cranelift_type = ir::Type::from(*ty); - let value = - builder - .ins() - .load(cranelift_type, MemFlags::new(), pointer_value, 0); + let value = builder.ins().load(*ty, MemFlags::new(), pointer_value, 0); Ok((value, *ty)) } diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 8d86fdf..456aa91 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -257,6 +257,16 @@ impl Type { pub fn is_printable(&self) -> bool { matches!(self, Type::Primitive(_)) } + + /// Returns true if the variable is signed. + pub fn is_signed(&self) -> bool { + matches!( + self, + Type::Primitive( + PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 + ) + ) + } } impl From for Type { diff --git a/src/ir/fields.rs b/src/ir/fields.rs index 71a541c..0c1b689 100644 --- a/src/ir/fields.rs +++ b/src/ir/fields.rs @@ -1,12 +1,23 @@ +use cranelift_module::DataDescription; use internment::ArcIntern; use std::fmt; -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone)] pub struct Fields { ordering: FieldOrdering, + total_size: usize, + cranelift_description: Option, fields: Vec<(ArcIntern, T)>, } +impl PartialEq for Fields { + fn eq(&self, other: &Self) -> bool { + self.ordering == other.ordering && self.fields == other.fields + } +} + +impl Eq for Fields {} + impl fmt::Debug for Fields { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Fields:")?; @@ -29,6 +40,8 @@ impl Fields { pub fn new(ordering: FieldOrdering) -> Fields { Fields { ordering, + total_size: 0, + cranelift_description: None, fields: vec![], } } @@ -38,6 +51,7 @@ impl Fields { } pub fn insert(&mut self, name: ArcIntern, t: T) { + self.total_size += 8; self.fields.push((name, t)); } @@ -54,6 +68,8 @@ impl Fields { pub fn map T2>(self, f: F) -> Fields { Fields { ordering: self.ordering, + total_size: self.total_size, + cranelift_description: self.cranelift_description, fields: self.fields.into_iter().map(|(n, t)| (n, f(t))).collect(), } } @@ -83,10 +99,6 @@ impl Fields { self.fields.iter().map(|(x, y)| (x, y)) } - pub fn into_iter(self) -> impl Iterator, T)> { - self.fields.into_iter() - } - pub fn field_names(&self) -> impl Iterator> { self.fields.iter().map(|(n, _)| n) } @@ -98,4 +110,22 @@ impl Fields { pub fn types_mut(&mut self) -> impl Iterator { self.fields.iter_mut().map(|(_, x)| x) } + + pub fn blank_data(&mut self) -> &DataDescription { + self.cranelift_description.get_or_insert_with(|| { + let mut cranelift_description = DataDescription::new(); + cranelift_description.set_align(8); + cranelift_description.define_zeroinit(self.total_size); + cranelift_description + }) + } +} + +impl IntoIterator for Fields { + type Item = (ArcIntern, T); + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.fields.into_iter() + } } diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index fb139d9..9ad3e09 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -206,14 +206,6 @@ impl From for cranelift_codegen::ir::Type { } impl ConstantType { - /// Returns true if the given type is (a) numeric and (b) signed; - pub fn is_signed(&self) -> bool { - matches!( - self, - ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64 - ) - } - /// Return the set of types that can be safely casted into this type. pub fn safe_casts_to(self) -> Vec { match self {