use crate::{
crate_prelude::*,
hir::HirNode,
ty::{bit_size_of_type, Domain, Sign, Type, TypeKind},
value::ValueKind,
ParamEnv, ParamEnvBinding,
};
use num::{cast::ToPrimitive, BigInt, One, Signed};
pub(crate) fn type_of<'gcx>(
cx: &impl Context<'gcx>,
node_id: NodeId,
env: ParamEnv,
) -> Result<Type<'gcx>> {
let hir = cx.hir_of(node_id)?;
#[allow(unreachable_patterns)]
match hir {
HirNode::Port(p) => cx.map_to_type(p.ty, env),
HirNode::Expr(e) => Ok(type_of_expr(cx, e, env)),
HirNode::ValueParam(p) => {
if is_explicit_type(cx, p.ty)? {
return cx.map_to_type(p.ty, env);
}
let env_data = cx.param_env_data(env);
match env_data.find_value(node_id) {
Some(ParamEnvBinding::Indirect(assigned_id)) => {
return cx.type_of(assigned_id.0, assigned_id.1)
}
Some(ParamEnvBinding::Direct(t)) => return Ok(t.ty),
_ => (),
}
if let Some(default) = p.default {
return cx.type_of(default, env);
}
cx.emit(
DiagBuilder2::error(format!(
"{} has implicit type but was not assigned and has no default",
p.desc_full()
))
.span(p.human_span())
.add_note("specify a type for the parameter; or")
.add_note("add a default value for the parameter; or")
.add_note("override the parameter from outside"),
);
Err(())
}
HirNode::VarDecl(d) => {
if is_explicit_type(cx, d.ty)? {
cx.map_to_type(d.ty, env)
} else if let Some(init) = d.init {
cx.type_of(init, env)
} else {
cx.emit(
DiagBuilder2::error(format!(
"{} has implicit type but is not initialized",
d.desc_full()
))
.span(d.human_span())
.add_note("specify a type for the variable; or")
.add_note("add an initial value"),
);
Err(())
}
}
HirNode::GenvarDecl(_) => Ok(&ty::INT_TYPE),
HirNode::EnumVariant(v) => {
let hir = match cx.hir_of(v.enum_id)? {
HirNode::Type(hir) => hir,
_ => unreachable!(),
};
let mut kind = &hir.kind;
loop {
kind = match kind {
hir::TypeKind::PackedArray(ref inner, ..) => inner.as_ref(),
_ => break,
}
}
map_type_kind(cx, v.enum_id, env, hir, kind)
}
HirNode::Package(_) => Ok(&ty::VOID_TYPE),
HirNode::Assign(_) => unreachable!("has no type: {:?}", hir),
_ => {
error!("{:#?}", hir);
panic!(
"{}",
DiagBuilder2::bug(format!(
"type analysis of {} not implemented",
hir.desc_full()
))
.span(hir.span())
)
}
}
}
fn type_of_expr<'gcx>(cx: &impl Context<'gcx>, expr: &'gcx hir::Expr, env: ParamEnv) -> Type<'gcx> {
match expr.kind {
hir::ExprKind::IntConst { .. }
| hir::ExprKind::TimeConst(..)
| hir::ExprKind::StringConst(..)
| hir::ExprKind::Ident(..)
| hir::ExprKind::Scope(..)
| hir::ExprKind::Concat(..)
| hir::ExprKind::Cast(..)
| hir::ExprKind::Inside(..)
| hir::ExprKind::Builtin(hir::BuiltinCall::Unsupported)
| hir::ExprKind::Builtin(hir::BuiltinCall::Clog2(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::Bits(_))
| hir::ExprKind::Field(..)
| hir::ExprKind::Index(..) => cx.need_self_determined_type(expr.id, env),
hir::ExprKind::UnsizedConst(..) => cx
.type_context(expr.id, env)
.map(|x| x.ty())
.unwrap_or_else(|| cx.need_self_determined_type(expr.id, env)),
hir::ExprKind::Unary(op, _) => {
match op {
hir::UnaryOp::Neg
| hir::UnaryOp::Pos
| hir::UnaryOp::BitNot
| hir::UnaryOp::PreInc
| hir::UnaryOp::PreDec
| hir::UnaryOp::PostInc
| hir::UnaryOp::PostDec => cx.need_operation_type(expr.id, env),
hir::UnaryOp::LogicNot
| hir::UnaryOp::RedAnd
| hir::UnaryOp::RedOr
| hir::UnaryOp::RedXor
| hir::UnaryOp::RedNand
| hir::UnaryOp::RedNor
| hir::UnaryOp::RedXnor => cx.need_self_determined_type(expr.id, env),
}
}
hir::ExprKind::Binary(op, _, _) => {
match op {
hir::BinaryOp::Add
| hir::BinaryOp::Sub
| hir::BinaryOp::Mul
| hir::BinaryOp::Div
| hir::BinaryOp::Mod
| hir::BinaryOp::Pow
| hir::BinaryOp::LogicShL
| hir::BinaryOp::LogicShR
| hir::BinaryOp::ArithShL
| hir::BinaryOp::ArithShR
| hir::BinaryOp::BitAnd
| hir::BinaryOp::BitNand
| hir::BinaryOp::BitOr
| hir::BinaryOp::BitNor
| hir::BinaryOp::BitXor
| hir::BinaryOp::BitXnor => cx.need_operation_type(expr.id, env),
hir::BinaryOp::Eq
| hir::BinaryOp::Neq
| hir::BinaryOp::Lt
| hir::BinaryOp::Leq
| hir::BinaryOp::Gt
| hir::BinaryOp::Geq
| hir::BinaryOp::LogicAnd
| hir::BinaryOp::LogicOr => cx.need_self_determined_type(expr.id, env),
}
}
hir::ExprKind::Ternary(..) => cx.need_operation_type(expr.id, env),
hir::ExprKind::Builtin(hir::BuiltinCall::Signed(arg)) => cx
.type_of(arg, env)
.unwrap_or(&ty::ERROR_TYPE)
.change_sign(cx, Sign::Signed),
hir::ExprKind::Builtin(hir::BuiltinCall::Unsigned(arg)) => cx
.type_of(arg, env)
.unwrap_or(&ty::ERROR_TYPE)
.change_sign(cx, Sign::Unsigned),
hir::ExprKind::PositionalPattern(..)
| hir::ExprKind::NamedPattern(..)
| hir::ExprKind::RepeatPattern(..)
| hir::ExprKind::EmptyPattern => cx.need_type_context(expr.id, env).ty(),
}
}
pub(crate) fn map_to_type<'gcx>(
cx: &impl Context<'gcx>,
node_id: NodeId,
env: ParamEnv,
) -> Result<Type<'gcx>> {
let hir = cx.hir_of(node_id)?;
#[allow(unreachable_patterns)]
match hir {
HirNode::Type(hir) => map_type_kind(cx, node_id, env, hir, &hir.kind),
HirNode::TypeParam(param) => {
let env_data = cx.param_env_data(env);
match env_data.find_type(node_id) {
Some(ParamEnvBinding::Indirect(assigned_id)) => {
return cx.map_to_type(assigned_id.0, assigned_id.1)
}
Some(ParamEnvBinding::Direct(t)) => return Ok(t),
_ => (),
}
if let Some(default) = param.default {
return cx.map_to_type(default, env);
}
let mut d = DiagBuilder2::error(format!(
"{} not assigned and has no default",
param.desc_full(),
));
let contexts = cx.param_env_contexts(env);
for &context in &contexts {
d = d.span(cx.span(context));
}
if contexts.is_empty() {
d = d.span(param.human_span());
}
cx.emit(d);
Err(())
}
HirNode::Typedef(def) => cx.map_to_type(def.ty, env),
HirNode::Expr(hir) => match hir.kind {
hir::ExprKind::Ident(name) => {
let binding = cx.resolve_upwards_or_error(name, node_id)?;
Ok(cx.mkty_named(name, (binding, env)))
}
hir::ExprKind::Scope(scope_id, name) => {
let within = cx.resolve_node(scope_id, env)?;
let binding = cx.resolve_downwards_or_error(name, within)?;
Ok(cx.mkty_named(name, (binding, env)))
}
_ => {
error!("{:#?}", hir);
cx.emit(
DiagBuilder2::error(format!("{} is not a type", hir.desc_full()))
.span(hir.span()),
);
Err(())
}
},
_ => cx.unimp_msg("conversion to type of", &hir),
}
}
fn is_explicit_type<'gcx>(cx: &impl Context<'gcx>, node_id: NodeId) -> Result<bool> {
Ok(match cx.hir_of(node_id)? {
HirNode::Type(x) => x.is_explicit(),
_ => false,
})
}
fn map_type_kind<'gcx>(
cx: &impl Context<'gcx>,
node_id: NodeId,
env: ParamEnv,
root: &hir::Type,
kind: &hir::TypeKind,
) -> Result<Type<'gcx>> {
#[allow(unreachable_patterns)]
match *kind {
hir::TypeKind::Builtin(hir::BuiltinType::Void) => Ok(cx.mkty_void()),
hir::TypeKind::Builtin(hir::BuiltinType::Bit) => Ok(&ty::BIT_TYPE),
hir::TypeKind::Builtin(hir::BuiltinType::Logic) => Ok(&ty::LOGIC_TYPE),
hir::TypeKind::Builtin(hir::BuiltinType::Byte) => Ok(&ty::BYTE_TYPE),
hir::TypeKind::Builtin(hir::BuiltinType::ShortInt) => Ok(&ty::SHORTINT_TYPE),
hir::TypeKind::Builtin(hir::BuiltinType::Int) => Ok(&ty::INT_TYPE),
hir::TypeKind::Builtin(hir::BuiltinType::Integer) => Ok(&ty::INTEGER_TYPE),
hir::TypeKind::Builtin(hir::BuiltinType::LongInt) => Ok(&ty::LONGINT_TYPE),
hir::TypeKind::Builtin(hir::BuiltinType::Time) => Ok(&ty::LONGINT_TYPE),
hir::TypeKind::Builtin(hir::BuiltinType::String) => {
Ok(cx.mkty_packed_array(1, &ty::BYTE_TYPE))
}
hir::TypeKind::Named(name) => {
let binding = cx.resolve_upwards_or_error(name, node_id)?;
Ok(cx.mkty_named(name, (binding, env)))
}
hir::TypeKind::Scope(scope_id, name) => {
let within = cx.resolve_node(scope_id, env)?;
let binding = cx.resolve_downwards_or_error(name, within)?;
Ok(cx.mkty_named(name, (binding, env)))
}
hir::TypeKind::Struct(..) => Ok(cx.mkty_struct(node_id)),
hir::TypeKind::PackedArray(ref inner, lhs, rhs) => {
let map_bound = |bound: NodeId| -> Result<&num::BigInt> {
match cx.constant_value_of(bound, env)?.kind {
ValueKind::Int(ref int, ..) => Ok(int),
_ => {
let span = cx.span(bound);
cx.emit(
DiagBuilder2::error(format!(
"array bound `{}` is not an integer",
span.extract()
))
.span(span),
);
return Err(());
}
}
};
let lhs = map_bound(lhs)?;
let rhs = map_bound(rhs)?;
let (dir, lo, hi) = if lhs < rhs {
(ty::RangeDir::Up, lhs, rhs)
} else {
(ty::RangeDir::Down, rhs, lhs)
};
let size = (hi - lo) + num::BigInt::one();
let size = match size.to_usize() {
Some(i) => i,
None => {
cx.emit(
DiagBuilder2::error(format!("{} is too large", kind.desc_full()))
.span(root.human_span())
.add_note(format!("array would contain {} elements", size)),
);
return Err(());
}
};
let offset = lo.to_isize().unwrap();
match **inner {
hir::TypeKind::Implicit | hir::TypeKind::Builtin(hir::BuiltinType::Bit) => Ok(cx
.intern_type(TypeKind::BitVector {
domain: ty::Domain::TwoValued,
sign: ty::Sign::Unsigned,
range: ty::Range { size, dir, offset },
dubbed: false,
})),
hir::TypeKind::Builtin(hir::BuiltinType::Logic) => {
Ok(cx.intern_type(TypeKind::BitVector {
domain: ty::Domain::FourValued,
sign: ty::Sign::Unsigned,
range: ty::Range { size, dir, offset },
dubbed: false,
}))
}
_ => {
let inner_ty = map_type_kind(cx, node_id, env, root, inner)?;
Ok(cx.mkty_packed_array(size, inner_ty))
}
}
}
hir::TypeKind::Enum(ref variants, repr) => match repr {
Some(repr) => cx.map_to_type(repr, env),
None => Ok(cx.mkty_int(variants.len().next_power_of_two().trailing_zeros() as usize)),
},
hir::TypeKind::Implicit => {
error!("{:#?}", root);
unreachable!(
"{}",
DiagBuilder2::bug("implicit type not resolved").span(root.span)
)
}
_ => {
error!("{:#?}", root);
cx.unimp_msg("type analysis of", root)
}
}
}
pub(crate) fn self_determined_type<'gcx>(
cx: &impl Context<'gcx>,
node_id: NodeId,
env: ParamEnv,
) -> Option<Type<'gcx>> {
let hir = match cx.hir_of(node_id) {
Ok(x) => x,
Err(()) => return Some(&ty::ERROR_TYPE),
};
match hir {
HirNode::Expr(e) => self_determined_expr_type(cx, e, env),
_ => None,
}
}
pub(crate) fn need_self_determined_type<'gcx>(
cx: &impl Context<'gcx>,
node_id: NodeId,
env: ParamEnv,
) -> Type<'gcx> {
match cx.self_determined_type(node_id, env) {
Some(ty) => ty,
None => {
let extract = cx.span(node_id).extract();
let desc = cx
.ast_of(node_id)
.map(|x| x.desc_full())
.unwrap_or_else(|_| format!("`{}`", extract));
cx.emit(
DiagBuilder2::error(format!("{} has no self-determined type", desc))
.span(cx.span(node_id))
.add_note(format!(
"The type of {} must be inferred from \
context, but the location where you used it does not \
provide such information.",
desc
))
.add_note(format!("Try a cast: `T'({})`", extract)),
);
&ty::ERROR_TYPE
}
}
}
fn self_determined_expr_type<'gcx>(
cx: &impl Context<'gcx>,
expr: &'gcx hir::Expr,
env: ParamEnv,
) -> Option<Type<'gcx>> {
match expr.kind {
hir::ExprKind::UnsizedConst(_) => Some(&ty::LOGIC_TYPE),
hir::ExprKind::IntConst { width, signed, .. } => {
Some(cx.intern_type(TypeKind::BitVector {
domain: ty::Domain::TwoValued,
sign: if signed {
ty::Sign::Signed
} else {
ty::Sign::Unsigned
},
range: ty::Range {
size: width,
dir: ty::RangeDir::Down,
offset: 0isize,
},
dubbed: true,
}))
}
hir::ExprKind::TimeConst(_) => Some(cx.mkty_time()),
hir::ExprKind::StringConst(_) => Some(cx.mkty_packed_array(1, &ty::BYTE_TYPE)),
hir::ExprKind::Ident(_) | hir::ExprKind::Scope(..) => Some(
cx.resolve_node(expr.id, env)
.and_then(|x| cx.type_of(x, env))
.unwrap_or(&ty::ERROR_TYPE),
),
hir::ExprKind::Concat(repeat, ref exprs) => {
let mut failed = false;
let mut bit_width = 0;
for &expr in exprs {
match cx
.type_of(expr, env)
.and_then(|ty| bit_size_of_type(cx, ty, env))
{
Ok(w) => bit_width += w,
Err(()) => failed = true,
}
}
let repeat = match repeat.map(|r| cx.constant_int_value_of(r, env)) {
Some(Ok(r)) => r.to_usize().unwrap(),
Some(Err(_)) => {
failed = true;
0
}
None => 1,
};
Some(if failed {
&ty::ERROR_TYPE
} else {
cx.intern_type(TypeKind::BitVector {
sign: Sign::Unsigned,
domain: Domain::FourValued,
range: ty::Range {
size: repeat * bit_width,
dir: ty::RangeDir::Down,
offset: 0isize,
},
dubbed: false,
})
})
}
hir::ExprKind::Cast(ty, _) => Some(cx.map_to_type(ty, env).unwrap_or(&ty::ERROR_TYPE)),
hir::ExprKind::Inside(..) => Some(&ty::LOGIC_TYPE),
hir::ExprKind::Builtin(hir::BuiltinCall::Unsupported)
| hir::ExprKind::Builtin(hir::BuiltinCall::Clog2(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::Bits(_)) => Some(&ty::INT_TYPE),
hir::ExprKind::Builtin(hir::BuiltinCall::Signed(arg)) => cx
.self_determined_type(arg, env)
.map(|x| x.change_sign(cx, Sign::Signed)),
hir::ExprKind::Builtin(hir::BuiltinCall::Unsigned(arg)) => cx
.self_determined_type(arg, env)
.map(|x| x.change_sign(cx, Sign::Unsigned)),
hir::ExprKind::Field(..) => Some(
cx.resolve_field_access(expr.id, env)
.and_then(|(_, _, field_id)| cx.type_of(field_id, env))
.unwrap_or(&ty::ERROR_TYPE),
),
hir::ExprKind::Index(target, mode) => Some({
let width = || -> Result<_> {
Ok(match mode {
hir::IndexMode::One(..) => None,
hir::IndexMode::Many(ast::RangeMode::RelativeUp, _, delta)
| hir::IndexMode::Many(ast::RangeMode::RelativeDown, _, delta) => {
Some(cx.constant_int_value_of(delta, env)?.to_usize().unwrap())
}
hir::IndexMode::Many(ast::RangeMode::Absolute, lhs, rhs) => {
let lhs_int = cx.constant_int_value_of(lhs, env)?;
let rhs_int = cx.constant_int_value_of(rhs, env)?;
let length = (lhs_int - rhs_int).abs() + BigInt::one();
Some(length.to_usize().unwrap())
}
})
}();
let width = match width {
Ok(w) => w,
Err(_) => return Some(&ty::ERROR_TYPE),
};
let target_ty = cx.type_of(target, env).unwrap_or(&ty::ERROR_TYPE);
match *target_ty {
TypeKind::PackedArray(_, ty) => {
if let Some(width) = width {
cx.intern_type(TypeKind::PackedArray(width, ty))
} else {
ty
}
}
TypeKind::Bit(domain) | TypeKind::Int(_, domain) => {
cx.intern_type(TypeKind::BitVector {
domain,
sign: Sign::Signed,
range: ty::Range {
size: width.unwrap_or(1),
dir: ty::RangeDir::Down,
offset: 0isize,
},
dubbed: false,
})
}
TypeKind::BitScalar { domain, sign } => cx.intern_type(TypeKind::BitVector {
domain,
sign,
range: ty::Range {
size: width.unwrap_or(1),
dir: ty::RangeDir::Down,
offset: 0isize,
},
dubbed: false,
}),
TypeKind::BitVector {
domain,
sign,
range,
..
} => cx.intern_type(TypeKind::BitVector {
domain,
sign,
range: ty::Range {
size: width.unwrap_or(1),
dir: range.dir,
offset: 0isize,
},
dubbed: false,
}),
TypeKind::Error => (target_ty),
_ => {
let desc = cx
.hir_of(target)
.map(|x| x.desc_full())
.unwrap_or_else(|_| cx.span(target).extract());
cx.emit(
DiagBuilder2::error(format!("{} cannot be indexed into", desc))
.span(expr.span()),
);
&ty::ERROR_TYPE
}
}
}),
hir::ExprKind::Unary(op, arg) => match op {
hir::UnaryOp::LogicNot
| hir::UnaryOp::RedAnd
| hir::UnaryOp::RedOr
| hir::UnaryOp::RedXor
| hir::UnaryOp::RedNand
| hir::UnaryOp::RedNor
| hir::UnaryOp::RedXnor => Some(&ty::LOGIC_TYPE),
hir::UnaryOp::Neg
| hir::UnaryOp::Pos
| hir::UnaryOp::BitNot
| hir::UnaryOp::PreInc
| hir::UnaryOp::PreDec
| hir::UnaryOp::PostInc
| hir::UnaryOp::PostDec => {
unify_operator_types(cx, env, cx.self_determined_type(arg, env).into_iter())
}
},
hir::ExprKind::Binary(op, lhs, rhs) => match op {
hir::BinaryOp::Eq
| hir::BinaryOp::Neq
| hir::BinaryOp::Lt
| hir::BinaryOp::Leq
| hir::BinaryOp::Gt
| hir::BinaryOp::Geq
| hir::BinaryOp::LogicAnd
| hir::BinaryOp::LogicOr => Some(&ty::LOGIC_TYPE),
hir::BinaryOp::Add
| hir::BinaryOp::Sub
| hir::BinaryOp::Mul
| hir::BinaryOp::Div
| hir::BinaryOp::Mod
| hir::BinaryOp::BitAnd
| hir::BinaryOp::BitNand
| hir::BinaryOp::BitOr
| hir::BinaryOp::BitNor
| hir::BinaryOp::BitXor
| hir::BinaryOp::BitXnor => {
let tlhs = cx.self_determined_type(lhs, env);
let trhs = cx.self_determined_type(rhs, env);
unify_operator_types(cx, env, tlhs.into_iter().chain(trhs.into_iter()))
}
hir::BinaryOp::Pow
| hir::BinaryOp::LogicShL
| hir::BinaryOp::LogicShR
| hir::BinaryOp::ArithShL
| hir::BinaryOp::ArithShR => cx.self_determined_type(lhs, env),
},
hir::ExprKind::Ternary(_, lhs, rhs) => {
let tlhs = cx.self_determined_type(lhs, env);
let trhs = cx.self_determined_type(rhs, env);
unify_operator_types(cx, env, tlhs.into_iter().chain(trhs.into_iter()))
}
_ => None,
}
}
pub(crate) fn operation_type<'gcx>(
cx: &impl Context<'gcx>,
node_id: NodeId,
env: ParamEnv,
) -> Option<Type<'gcx>> {
let hir = match cx.hir_of(node_id) {
Ok(x) => x,
Err(_) => return Some(&ty::ERROR_TYPE),
};
let expr = match hir {
HirNode::Expr(x) => x,
_ => return None,
};
match expr.kind {
hir::ExprKind::Unary(op, arg) => {
let ty = match op {
hir::UnaryOp::RedAnd
| hir::UnaryOp::RedOr
| hir::UnaryOp::RedXor
| hir::UnaryOp::RedNand
| hir::UnaryOp::RedNor
| hir::UnaryOp::RedXnor
| hir::UnaryOp::Neg
| hir::UnaryOp::Pos
| hir::UnaryOp::BitNot
| hir::UnaryOp::PreInc
| hir::UnaryOp::PreDec
| hir::UnaryOp::PostInc
| hir::UnaryOp::PostDec => {
let tc = cx.type_context(node_id, env).map(|x| x.ty());
let targ = cx.self_determined_type(arg, env);
unify_operator_types(cx, env, tc.into_iter().chain(targ.into_iter()))
}
hir::UnaryOp::LogicNot => Some(&ty::LOGIC_TYPE),
};
if ty.is_none() {
cx.emit(
DiagBuilder2::error(format!("type of {} cannot be inferred", expr.desc_full()))
.span(expr.human_span())
.add_note(
"The operand does not have a self-determined type, \
and the type cannot be inferred from the context.",
)
.add_note(format!("Try a cast: `T'({})`", expr.span().extract())),
);
Some(&ty::ERROR_TYPE)
} else {
ty
}
}
hir::ExprKind::Binary(op, lhs, rhs) => {
let ty = match op {
hir::BinaryOp::Add
| hir::BinaryOp::Sub
| hir::BinaryOp::Mul
| hir::BinaryOp::Div
| hir::BinaryOp::Mod
| hir::BinaryOp::BitAnd
| hir::BinaryOp::BitNand
| hir::BinaryOp::BitOr
| hir::BinaryOp::BitNor
| hir::BinaryOp::BitXor
| hir::BinaryOp::BitXnor => {
let tc = cx.type_context(node_id, env).map(|x| x.ty());
let tlhs = cx.self_determined_type(lhs, env);
let trhs = cx.self_determined_type(rhs, env);
unify_operator_types(
cx,
env,
tc.into_iter()
.chain(tlhs.into_iter())
.chain(trhs.into_iter()),
)
}
hir::BinaryOp::Eq
| hir::BinaryOp::Neq
| hir::BinaryOp::Lt
| hir::BinaryOp::Leq
| hir::BinaryOp::Gt
| hir::BinaryOp::Geq => {
let tlhs = cx.self_determined_type(lhs, env);
let trhs = cx.self_determined_type(rhs, env);
unify_operator_types(cx, env, tlhs.into_iter().chain(trhs.into_iter()))
}
hir::BinaryOp::LogicAnd | hir::BinaryOp::LogicOr => Some(&ty::LOGIC_TYPE),
hir::BinaryOp::Pow
| hir::BinaryOp::LogicShL
| hir::BinaryOp::LogicShR
| hir::BinaryOp::ArithShL
| hir::BinaryOp::ArithShR => {
let tc = cx.type_context(node_id, env).map(|x| x.ty());
let sdt = cx.self_determined_type(lhs, env);
unify_operator_types(cx, env, tc.into_iter().chain(sdt.into_iter()))
}
};
if ty.is_none() {
cx.emit(
DiagBuilder2::error(format!("type of {} cannot be inferred", expr.desc_full()))
.span(expr.human_span())
.add_note(
"Neither of the operands has a self-determined type, \
and the type cannot be inferred from the context.",
)
.add_note(format!("Try a cast: `T'({})`", expr.span().extract())),
);
Some(&ty::ERROR_TYPE)
} else {
ty
}
}
hir::ExprKind::Ternary(_, lhs, rhs) => {
let tc = cx.type_context(node_id, env).map(|x| x.ty());
let tlhs = cx.self_determined_type(lhs, env);
let trhs = cx.self_determined_type(rhs, env);
unify_operator_types(
cx,
env,
tc.into_iter()
.chain(tlhs.into_iter())
.chain(trhs.into_iter()),
)
}
hir::ExprKind::Inside(lhs, ref ranges) => {
let tlhs = cx.self_determined_type(lhs, env);
let tranges = ranges.iter().flat_map(|r| {
let (a, b) = match r.value {
hir::InsideRange::Single(rhs) => (cx.self_determined_type(rhs, env), None),
hir::InsideRange::Range(lo, hi) => (
cx.self_determined_type(lo, env),
cx.self_determined_type(hi, env),
),
};
a.into_iter().chain(b.into_iter())
});
unify_operator_types(cx, env, tlhs.into_iter().chain(tranges))
}
_ => None,
}
}
fn unify_operator_types<'gcx>(
cx: &impl Context<'gcx>,
env: ParamEnv,
types: impl Iterator<Item = Type<'gcx>>,
) -> Option<Type<'gcx>> {
let inner: Vec<_> = types
.flat_map(|ty| {
bit_size_of_type(cx, ty, env).map(|w| ((ty.get_sign(), ty.get_value_domain(), w)))
})
.collect();
let width: Option<usize> = inner.iter().map(|&(_, _, w)| w).max();
let sign = match inner.iter().all(|&(s, _, _)| s == Some(Sign::Signed)) {
true => Sign::Signed,
false => Sign::Unsigned,
};
let domain = match inner.iter().all(|&(_, d, _)| d == Some(Domain::TwoValued)) {
true => Domain::TwoValued,
false => Domain::FourValued,
};
width.map(|w| {
cx.intern_type(TypeKind::BitVector {
sign,
domain,
range: ty::Range {
size: w,
dir: ty::RangeDir::Down,
offset: 0isize,
},
dubbed: false,
})
})
}
pub(crate) fn need_operation_type<'gcx>(
cx: &impl Context<'gcx>,
node_id: NodeId,
env: ParamEnv,
) -> Type<'gcx> {
match cx.operation_type(node_id, env) {
Some(x) => x,
None => {
let span = cx.span(node_id);
cx.emit(
DiagBuilder2::bug(format!("`{}` has no operation type", span.extract())).span(span),
);
&ty::ERROR_TYPE
}
}
}
pub(crate) fn type_context<'gcx>(
cx: &impl Context<'gcx>,
onto: NodeId,
env: ParamEnv,
) -> Option<TypeContext<'gcx>> {
let hir = match cx.hir_of(cx.parent_node_id(onto).unwrap()) {
Ok(x) => x,
Err(()) => return None,
};
match hir {
HirNode::Expr(e) => type_context_imposed_by_expr(cx, onto, e, env),
HirNode::Assign(a) => {
if a.lhs == onto {
cx.self_determined_type(a.rhs, env).map(Into::into)
} else if a.rhs == onto {
cx.self_determined_type(a.lhs, env).map(Into::into)
} else {
None
}
}
HirNode::VarDecl(v)
if v.init == Some(onto) && is_explicit_type(cx, v.ty).unwrap_or(false) =>
{
Some(cx.map_to_type(v.ty, env).unwrap_or(&ty::ERROR_TYPE).into())
}
HirNode::ValueParam(v)
if v.default == Some(onto) && is_explicit_type(cx, v.ty).unwrap_or(false) =>
{
Some(cx.map_to_type(v.ty, env).unwrap_or(&ty::ERROR_TYPE).into())
}
_ => None,
}
}
pub(crate) fn need_type_context<'gcx>(
cx: &impl Context<'gcx>,
node_id: NodeId,
env: ParamEnv,
) -> TypeContext<'gcx> {
match cx.type_context(node_id, env) {
Some(ty) => ty,
None => {
let extract = cx.span(node_id).extract();
let desc = cx
.ast_of(node_id)
.map(|x| x.desc_full())
.unwrap_or_else(|_| format!("`{}`", extract));
cx.emit(
DiagBuilder2::error(format!("type of {} cannot be inferred from context", desc))
.span(cx.span(node_id))
.add_note(format!(
"The type of {} must be inferred from \
context, but the location where you used it does not \
provide such information.",
desc
))
.add_note(format!("Try a cast: `T'({})`", extract)),
);
TypeContext::Type(&ty::ERROR_TYPE)
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum TypeContext<'a> {
Type(Type<'a>),
Bool,
}
impl<'a> TypeContext<'a> {
pub fn ty(&self) -> Type<'a> {
match *self {
TypeContext::Type(t) => t,
TypeContext::Bool => &ty::BIT_TYPE,
}
}
}
impl<'a> From<Type<'a>> for TypeContext<'a> {
fn from(ty: Type<'a>) -> Self {
TypeContext::Type(ty)
}
}
fn type_context_imposed_by_expr<'gcx>(
cx: &impl Context<'gcx>,
onto: NodeId,
expr: &'gcx hir::Expr,
env: ParamEnv,
) -> Option<TypeContext<'gcx>> {
match expr.kind {
hir::ExprKind::Unary(op, _) => match op {
hir::UnaryOp::LogicNot
| hir::UnaryOp::RedAnd
| hir::UnaryOp::RedOr
| hir::UnaryOp::RedXor
| hir::UnaryOp::RedNand
| hir::UnaryOp::RedNor
| hir::UnaryOp::RedXnor => None,
hir::UnaryOp::Neg
| hir::UnaryOp::Pos
| hir::UnaryOp::BitNot
| hir::UnaryOp::PreInc
| hir::UnaryOp::PreDec
| hir::UnaryOp::PostInc
| hir::UnaryOp::PostDec => cx.self_determined_type(expr.id, env).map(Into::into),
},
hir::ExprKind::Binary(op, lhs, _) => match op {
hir::BinaryOp::Eq
| hir::BinaryOp::Neq
| hir::BinaryOp::Lt
| hir::BinaryOp::Leq
| hir::BinaryOp::Gt
| hir::BinaryOp::Geq
| hir::BinaryOp::LogicAnd
| hir::BinaryOp::LogicOr => None,
hir::BinaryOp::Pow
| hir::BinaryOp::LogicShL
| hir::BinaryOp::LogicShR
| hir::BinaryOp::ArithShL
| hir::BinaryOp::ArithShR => {
if onto == lhs {
cx.self_determined_type(expr.id, env).map(Into::into)
} else {
None
}
}
hir::BinaryOp::Add
| hir::BinaryOp::Sub
| hir::BinaryOp::Mul
| hir::BinaryOp::Div
| hir::BinaryOp::Mod
| hir::BinaryOp::BitAnd
| hir::BinaryOp::BitNand
| hir::BinaryOp::BitOr
| hir::BinaryOp::BitNor
| hir::BinaryOp::BitXor
| hir::BinaryOp::BitXnor => cx.self_determined_type(expr.id, env).map(Into::into),
},
hir::ExprKind::Ternary(_, lhs, rhs) if onto == lhs || onto == rhs => {
cx.self_determined_type(expr.id, env).map(Into::into)
}
hir::ExprKind::Builtin(hir::BuiltinCall::Signed(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::Unsigned(_)) => {
cx.self_determined_type(expr.id, env).map(Into::into)
}
_ => None,
}
}