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::IntPort(p) => match &p.data {
Some(data) => cx.map_to_type(data.ty, env),
None => {
let decl_id = cx.resolve_node(node_id, env)?;
cx.type_of(decl_id, env)
}
},
HirNode::ExtPort(p) => {
if p.exprs.len() == 1 {
let expr = &p.exprs[0];
if !expr.selects.is_empty() {
bug_span!(
p.span,
cx,
"port{} contains a `[...]` selection; not yet supported in typeck",
p.name
.map(|n| format!(" `{}`", n))
.unwrap_or_else(String::new)
);
}
let module = match cx.hir_of(p.module)? {
HirNode::Module(m) => m,
_ => unreachable!(),
};
cx.type_of(module.ports_new.int[expr.port].id, env)
} else {
bug_span!(
p.span,
cx,
"port{} is a concatenation; not yet supported in typeck",
p.name
.map(|n| format!(" `{}`", n))
.unwrap_or_else(String::new)
);
}
}
HirNode::Expr(_) => Ok(cast_type(cx, node_id, env).unwrap().ty),
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.id(), assigned_id.env())
}
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 if let hir::VarKind::Net { .. } = d.kind {
Ok(&ty::LOGIC_TYPE)
} 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::CastSign(..)
| hir::ExprKind::CastSize(..)
| 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)
.and_then(|x| x.ty().get_simple_bit_vector(cx, env, false))
.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(..) => cx.need_type_context(expr.id, env).ty(),
hir::ExprKind::FunctionCall(target, _) => cx
.hir_of(target)
.and_then(|hir| {
let hir = match hir {
HirNode::Subroutine(s) => s,
_ => unreachable!(),
};
match hir.retty {
Some(retty_id) => cx.map_to_type(retty_id, env),
None => Ok(&ty::VOID_TYPE),
}
})
.unwrap_or(&ty::ERROR_TYPE),
}
}
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.id(), assigned_id.env())
}
Some(ParamEnvBinding::Direct(t)) => return Ok(t),
_ => (),
}
if let Some(default) = param.default {
return cx.map_to_type(default, env);
}
let d = DiagBuilder2::error(format!(
"{} not assigned and has no default",
param.desc_full(),
));
let contexts = cx.param_env_contexts(env);
for &context in &contexts {
cx.emit(
d.clone()
.span(cx.span(context))
.add_note("parameter declared here:")
.span(param.human_span()),
);
}
if contexts.is_empty() {
cx.emit(d.span(param.human_span()));
}
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(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(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::TIME_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(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(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),
ValueKind::Error => Err(()),
_ => {
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::RefExpr(expr) => cx.type_of(expr, env),
hir::TypeKind::RefType(ty) => cx.map_to_type(ty, env),
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 cast_type<'gcx>(
cx: &impl Context<'gcx>,
node_id: NodeId,
env: ParamEnv,
) -> Option<CastType<'gcx>> {
let hir = match cx.hir_of(node_id) {
Ok(x) => x,
Err(()) => return Some((&ty::ERROR_TYPE).into()),
};
match hir {
HirNode::Expr(e) => Some(cast_expr_type(cx, e, env)),
_ => None,
}
}
fn cast_expr_type<'gcx>(
cx: &impl Context<'gcx>,
expr: &'gcx hir::Expr,
env: ParamEnv,
) -> CastType<'gcx> {
let cast = cast_expr_type_inner(cx, expr, env);
if cx.sess().has_verbosity(Verbosity::CASTS) && !cast.is_error() && !cast.casts.is_empty() {
let mut d =
DiagBuilder2::note(format!("cast: `{}` to `{}`", cast.init, cast.ty)).span(expr.span);
for (op, ty) in &cast.casts {
let msg = match *op {
CastOp::SimpleBitVector => format!("cast to simple bit vector type `{}`", ty),
CastOp::Bool => format!("cast to boolean `{}`", ty),
CastOp::Sign(sign) => format!("sign cast to {} type `{}`", sign, ty),
CastOp::Range(_, signed) => format!(
"{} size cast to `{}`",
match signed {
true => "sign-extended",
false => "zero-extended",
},
ty
),
CastOp::Domain(domain) => format!(
"cast to {} `{}`",
match domain {
ty::Domain::TwoValued => "two-valued",
ty::Domain::FourValued => "four-valued",
},
ty
),
CastOp::Struct => format!("unpack as struct `{}`", ty),
CastOp::Array => format!("unpack as array `{}`", ty),
CastOp::Transmute => format!("transmute as `{}`", ty),
};
d = d.add_note(msg);
}
cx.emit(d);
}
cast
}
fn cast_expr_type_inner<'gcx>(
cx: &impl Context<'gcx>,
expr: &'gcx hir::Expr,
env: ParamEnv,
) -> CastType<'gcx> {
trace!(
"Computing cast type of `{}` (line {})",
expr.span().extract(),
expr.span().begin().human_line()
);
let inferred = type_of_expr(cx, expr, env);
let context = type_context(cx, expr.id, env);
trace!(" Inferred: {}", inferred);
trace!(
" Context: {}",
context
.map(|c| c.to_string())
.unwrap_or_else(|| "<none>".to_string())
);
if expr_is_lvalue(cx, expr.id, env) {
trace!(" Not casting lvalue");
return inferred.into();
}
let context = match context {
Some(c) => c,
None => return inferred.into(),
};
if inferred.is_error() || context.is_error() {
trace!(" Aborting due to error");
return inferred.into();
}
if let TypeContext::Type(ty) = context {
if ty::identical(ty, inferred) {
return inferred.into();
}
}
let mut cast = CastType {
init: inferred,
ty: inferred,
casts: vec![],
};
if !cast.ty.is_simple_bit_vector() {
trace!(" Casting to SBVT");
match cast.ty.get_simple_bit_vector(cx, env, false) {
Some(ty) => cast.add_cast(CastOp::SimpleBitVector, ty),
None => {
cx.emit(
DiagBuilder2::error(format!(
"cannot cast a value of type `{}` to `{}`",
inferred, context
))
.span(expr.span)
.add_note(format!(
"`{}` has no simple bit-vector type representation",
inferred
)),
);
error!("Cast chain thus far: {}", cast);
return (&ty::ERROR_TYPE).into();
}
}
}
if cast.is_error() {
trace!(" Aborting due to error");
return cast;
}
let context = match context {
TypeContext::Bool => {
trace!(" Casting to bool ({})", context.ty());
cast.add_cast(CastOp::Bool, context.ty());
return cast;
}
TypeContext::Type(ty) => ty,
};
let context_sbvt = if context.is_simple_bit_vector() {
Some(context)
} else {
context.get_simple_bit_vector(cx, env, false)
};
let range = context_sbvt.and_then(|ty| ty.get_range());
if cast.ty.get_range() != range && range.is_some() {
let range = range.unwrap();
let ty = cast.ty.change_range(cx, range);
trace!(
" Casting range from {} to {}",
cast.ty.get_range().unwrap(),
range
);
cast.add_cast(CastOp::Range(range, cast.ty.is_signed()), ty);
}
if cast.is_error() {
trace!(" Aborting due to error");
return cast;
}
match (
cast.ty.resolve_name(),
context_sbvt.map(|ty| ty.resolve_name()),
) {
(
&TypeKind::BitVector {
domain,
sign,
range: ty::Range { size: 1, .. },
dubbed: _,
},
Some(&TypeKind::BitScalar { .. }),
) => {
let ty = cx.intern_type(TypeKind::BitScalar { domain, sign });
cast.add_cast(CastOp::Transmute, ty);
}
(
&TypeKind::BitScalar { domain, sign },
Some(&TypeKind::BitVector {
range: range @ ty::Range { size: 1, .. },
dubbed,
..
}),
) => {
let ty = cx.intern_type(TypeKind::BitVector {
domain,
sign,
range,
dubbed,
});
cast.add_cast(CastOp::Transmute, ty);
}
_ => (),
}
if cast.ty.get_sign() != context.get_sign() && context.get_sign().is_some() {
trace!(
" Casting sign from {:?} to {:?}",
cast.ty.get_sign(),
context.get_sign()
);
let sign = context.get_sign().unwrap();
cast.add_cast(CastOp::Sign(sign), cast.ty.change_sign(cx, sign));
}
if cast.is_error() {
trace!(" Aborting due to error");
return cast;
}
let domain = context_sbvt.and_then(|ty| ty.get_value_domain());
if cast.ty.get_value_domain() != domain && domain.is_some() {
let domain = domain.unwrap();
trace!(
" Casting domain from {:?} to {:?}",
cast.ty.get_value_domain().unwrap(),
domain
);
cast.add_cast(
CastOp::Domain(domain),
cast.ty.change_value_domain(cx, domain),
);
}
if cast.is_error() {
trace!(" Aborting due to error");
return cast;
}
if context.is_struct() {
cast.add_cast(CastOp::Struct, context);
} else if context.is_array() {
cast.add_cast(CastOp::Array, context);
}
if cast.is_error() {
trace!(" Aborting due to error");
return cast;
}
if ty::identical(context, cast.ty) {
trace!(" Cast complete");
return cast;
}
let mut d = DiagBuilder2::error(format!(
"cannot cast a value of type `{}` to `{}`",
inferred, context
))
.span(expr.span);
if !cast.casts.is_empty() {
d = d.add_note(format!(
"`{}` can be cast to an intermediate `{}`, but",
inferred, cast.ty
));
d = d.add_note(format!(
"`{}` cannot be cast to the final `{}`",
cast.ty, context
));
}
cx.emit(d);
error!("Inferred type: {:?}", inferred);
error!("Context type: {:?}", context);
error!("Cast chain thus far: {}", cast);
(&ty::ERROR_TYPE).into()
}
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::CastSign(sign, arg) => cx
.self_determined_type(arg, env)
.map(|x| x.change_sign(cx, sign.value)),
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::CastSize(size, arg) => {
let mut failed = false;
let size = match cx.constant_int_value_of(size, env) {
Ok(r) => r.to_usize().unwrap(),
Err(_) => {
failed = true;
0
}
};
let inner_ty = cx.need_self_determined_type(arg, env);
failed |= inner_ty.is_error();
Some(if failed {
&ty::ERROR_TYPE
} else {
cx.intern_type(TypeKind::BitVector {
domain: inner_ty.get_value_domain().unwrap_or(Domain::TwoValued),
sign: inner_ty.get_sign().unwrap_or(Sign::Unsigned),
range: ty::Range {
size: size,
dir: ty::RangeDir::Down,
offset: 0isize,
},
dubbed: false,
})
})
}
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::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.resolve_name() {
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),
_ => {
cx.emit(
DiagBuilder2::error(format!(
"cannot index into a value of type `{}`",
target_ty
))
.span(expr.span()),
);
error!("Type is {:?}", target_ty);
&ty::ERROR_TYPE
}
}
}),
hir::ExprKind::Unary(op, arg) => match op {
hir::UnaryOp::LogicNot => Some(&ty::LOGIC_TYPE),
hir::UnaryOp::RedAnd
| hir::UnaryOp::RedOr
| hir::UnaryOp::RedXor
| hir::UnaryOp::RedNand
| hir::UnaryOp::RedNor
| hir::UnaryOp::RedXnor => cx.self_determined_type(arg, env).map(|ty| {
ty.get_value_domain()
.map(|dom| dom.bit_type())
.unwrap_or(&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(), ty.is_dubbed(), 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,
};
let dubbed = inner.iter().all(|&(_, _, d, _)| d);
width.map(|w| {
cx.intern_type(TypeKind::BitVector {
sign,
domain,
range: ty::Range {
size: w,
dir: ty::RangeDir::Down,
offset: 0isize,
},
dubbed,
})
})
}
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::Stmt(s) => type_context_imposed_by_stmt(cx, onto, s, 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::IntPort(hir::IntPort { data: Some(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())
}
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())
}
HirNode::Inst(inst) => {
let details = cx.inst_details(inst.id.env(env)).ok()?;
details
.ports
.reverse_find(onto)
.and_then(|id| cx.type_of(id, details.inner_env).ok())
.map(Into::into)
}
HirNode::InstTarget(inst) => {
let details = cx.inst_target_details(inst.id.env(env)).ok()?;
details
.params
.reverse_find_value(onto)
.and_then(|param_id| cx.type_of(param_id, details.inner_env).ok())
.map(Into::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::LOGIC_TYPE,
}
}
pub fn is_error(&self) -> bool {
match self {
TypeContext::Type(t) => t.is_error(),
TypeContext::Bool => false,
}
}
}
impl<'a> From<Type<'a>> for TypeContext<'a> {
fn from(ty: Type<'a>) -> Self {
TypeContext::Type(ty)
}
}
impl std::fmt::Display for TypeContext<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
TypeContext::Type(t) => write!(f, "{}", t),
TypeContext::Bool => write!(f, "<bool>"),
}
}
}
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::RedAnd
| hir::UnaryOp::RedOr
| hir::UnaryOp::RedXor
| hir::UnaryOp::RedNand
| hir::UnaryOp::RedNor
| hir::UnaryOp::RedXnor => None,
hir::UnaryOp::LogicNot => Some(TypeContext::Bool),
hir::UnaryOp::Neg
| hir::UnaryOp::Pos
| hir::UnaryOp::BitNot
| hir::UnaryOp::PreInc
| hir::UnaryOp::PreDec
| hir::UnaryOp::PostInc
| hir::UnaryOp::PostDec => Some(cx.need_operation_type(expr.id, env).into()),
},
hir::ExprKind::Binary(op, lhs, _) => match op {
hir::BinaryOp::LogicAnd | hir::BinaryOp::LogicOr => Some(TypeContext::Bool),
hir::BinaryOp::Pow
| hir::BinaryOp::LogicShL
| hir::BinaryOp::LogicShR
| hir::BinaryOp::ArithShL
| hir::BinaryOp::ArithShR => {
if onto == lhs {
Some(cx.need_operation_type(expr.id, env).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
| hir::BinaryOp::Eq
| hir::BinaryOp::Neq
| hir::BinaryOp::Lt
| hir::BinaryOp::Leq
| hir::BinaryOp::Gt
| hir::BinaryOp::Geq => Some(cx.need_operation_type(expr.id, env).into()),
},
hir::ExprKind::Ternary(_, lhs, rhs) if onto == lhs || onto == rhs => {
Some(cx.need_operation_type(expr.id, env).into())
}
hir::ExprKind::Ternary(cond, _, _) if onto == cond => Some(TypeContext::Bool),
hir::ExprKind::Builtin(hir::BuiltinCall::Signed(arg))
| hir::ExprKind::Builtin(hir::BuiltinCall::Unsigned(arg))
| hir::ExprKind::Cast(_, arg)
| hir::ExprKind::CastSign(_, arg)
| hir::ExprKind::CastSize(_, arg)
if onto == arg =>
{
Some(cx.need_self_determined_type(expr.id, env).into())
}
hir::ExprKind::Concat(..) => {
let ty = cx.need_self_determined_type(onto, env);
let sbvt = match ty.get_simple_bit_vector(cx, env, false) {
Some(ty) => ty,
None => {
cx.emit(
DiagBuilder2::error(
format!("cannot concatenate a value of type `{}`", ty,),
)
.span(expr.span)
.add_note(format!(
"`{}` has no simple bit-vector type representation",
ty
)),
);
&ty::ERROR_TYPE
}
};
Some(sbvt.into())
}
hir::ExprKind::PositionalPattern(ref args) => {
type_context_imposed_by_positional_pattern(cx, onto, expr, args, 1, env)
}
hir::ExprKind::RepeatPattern(count, ref args) if onto != count => {
let const_count = match cx.constant_int_value_of(count, env) {
Ok(c) => c,
Err(()) => return Some((&ty::ERROR_TYPE).into()),
};
let const_count = match const_count.to_usize() {
Some(c) => c,
None => {
cx.emit(
DiagBuilder2::error(format!(
"repetition count {} is outside copable range",
const_count,
))
.span(cx.span(count)),
);
return Some((&ty::ERROR_TYPE).into());
}
};
type_context_imposed_by_positional_pattern(cx, onto, expr, args, const_count, env)
}
_ => None,
}
}
fn type_context_imposed_by_positional_pattern<'gcx>(
cx: &impl Context<'gcx>,
onto: NodeId,
expr: &'gcx hir::Expr,
args: &[NodeId],
repeat: usize,
env: ParamEnv,
) -> Option<TypeContext<'gcx>> {
let index = args.iter().position(|&n| n == onto)?;
let ty = cx.need_type_context(expr.id, env).ty();
if ty.is_error() {
return Some((&ty::ERROR_TYPE).into());
}
match *ty.resolve_name() {
TypeKind::PackedArray(_, elty) => Some(elty.into()),
TypeKind::BitScalar { .. } => Some(ty.into()),
TypeKind::BitVector { sign, domain, .. } => {
Some(cx.intern_type(TypeKind::BitScalar { sign, domain }).into())
}
TypeKind::Struct(_) if repeat != 1 => {
bug_span!(
expr.span,
cx,
"struct patterns with repeat count other than 1 not supported",
);
}
TypeKind::Struct(def_id) => {
let def = match cx.struct_def(def_id) {
Ok(d) => d,
Err(()) => return Some((&ty::ERROR_TYPE).into()),
};
if args.len() > def.fields.len() {
cx.emit(
DiagBuilder2::error(format!(
"pattern has {} fields, but type `{}` requires {}",
args.len(),
ty,
def.fields.len()
))
.span(expr.span),
);
}
Some(
cx.map_to_type(def.fields[index].ty, env)
.unwrap_or(&ty::ERROR_TYPE)
.into(),
)
}
_ => {
bug_span!(
expr.span,
cx,
"type context for field {} of positional pattern with invalid type `{}`",
ty,
index
);
}
}
}
fn type_context_imposed_by_stmt<'gcx>(
cx: &impl Context<'gcx>,
onto: NodeId,
stmt: &'gcx hir::Stmt,
env: ParamEnv,
) -> Option<TypeContext<'gcx>> {
match stmt.kind {
hir::StmtKind::Assign { lhs, rhs, .. } => {
if lhs == onto {
cx.self_determined_type(rhs, env).map(Into::into)
} else if rhs == onto {
cx.self_determined_type(lhs, env).map(Into::into)
} else {
None
}
}
hir::StmtKind::If { cond, .. } if onto == cond => Some(TypeContext::Bool),
hir::StmtKind::Loop { kind, .. } => {
match kind {
hir::LoopKind::Repeat(count) if onto == count => {
None
}
hir::LoopKind::Do(cond)
| hir::LoopKind::While(cond)
| hir::LoopKind::For(_, cond, _)
if onto == cond =>
{
Some(TypeContext::Bool)
}
_ => None,
}
}
hir::StmtKind::Case { expr, ref ways, .. } => {
if ways.iter().flat_map(|(x, _)| x.iter()).any(|&x| x == onto) {
cx.self_determined_type(expr, env).map(Into::into)
} else {
None
}
}
_ => None,
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CastType<'a> {
pub init: Type<'a>,
pub ty: Type<'a>,
pub casts: Vec<(CastOp, Type<'a>)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CastOp {
SimpleBitVector,
Bool,
Sign(ty::Sign),
Range(ty::Range, bool),
Domain(ty::Domain),
Struct,
Array,
Transmute,
}
impl<'a> CastType<'a> {
pub fn is_error(&self) -> bool {
self.ty.is_error()
}
pub fn add_cast(&mut self, op: CastOp, ty: Type<'a>) {
self.casts.push((op, ty));
self.ty = ty;
}
}
impl<'a> From<Type<'a>> for CastType<'a> {
fn from(ty: Type<'a>) -> CastType<'a> {
CastType {
init: ty,
ty,
casts: vec![],
}
}
}
impl std::fmt::Display for CastType<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.init)?;
for (op, ty) in &self.casts {
write!(f, " -> {:?} {}", op, ty)?;
}
Ok(())
}
}
pub(crate) fn expr_is_lvalue<'gcx>(cx: &impl Context<'gcx>, onto: NodeId, _env: ParamEnv) -> bool {
let hir = match cx.hir_of(cx.parent_node_id(onto).unwrap()) {
Ok(x) => x,
Err(()) => return false,
};
match hir {
HirNode::Expr(e) => match e.kind {
hir::ExprKind::Unary(op, _) => match op {
hir::UnaryOp::PreInc
| hir::UnaryOp::PreDec
| hir::UnaryOp::PostInc
| hir::UnaryOp::PostDec => true,
_ => false,
},
_ => false,
},
HirNode::Stmt(s) => match s.kind {
hir::StmtKind::Assign { lhs, .. } => lhs == onto,
_ => false,
},
HirNode::Assign(a) => a.lhs == onto,
_ => false,
}
}