use crate::crate_prelude::*;
use crate::{
call_mapping::CallArgSource,
common::arenas::Alloc,
hir::HirNode,
port_list,
resolver::{DefNode, InstTarget},
syntax::ast::BasicNode,
ty::{
Domain, IntAtomType, IntVecType, PackedCore, PackedType, RealType, SbvType, Sign,
UnpackedCore, UnpackedType,
},
value::ValueKind,
ParamEnv, ParamEnvBinding,
};
use num::{cast::ToPrimitive, BigInt, One, Signed};
use std::collections::HashSet;
#[moore_derive::query]
pub(crate) fn type_of<'a>(
cx: &impl Context<'a>,
node_id: NodeId,
env: ParamEnv,
) -> Result<&'a UnpackedType<'a>> {
let ast = cx.ast_for_id(node_id);
match ast.as_all() {
ast::AllNode::VarDeclName(name) => match name.get_parent().unwrap().as_all() {
ast::AllNode::VarDecl(..) => return Ok(cx.type_of_var_decl(Ref(name), env)),
ast::AllNode::NetDecl(..) => return Ok(cx.type_of_net_decl(Ref(name), env)),
ast::AllNode::StructMember(..) => return Ok(cx.type_of_struct_member(Ref(name), env)),
ast::AllNode::PortDecl(..) => return Ok(cx.type_of_port_decl(Ref(name), env)),
x => bug_span!(ast.span(), cx, "VarDeclName with weird parent {:?}", x),
},
ast::AllNode::ParamValueDecl(x) => return Ok(cx.type_of_value_param(Ref(x), env)),
ast::AllNode::SubroutinePort(x) => return Ok(cx.type_of_subroutine_port(x, env)),
_ => (),
};
let hir = cx.hir_of(node_id)?;
#[allow(unreachable_patterns)]
match hir {
HirNode::IntPort(p) => Ok(cx.type_of_int_port(Ref(p), env)),
HirNode::ExtPort(p) => Ok(cx.type_of_ext_port(Ref(p), env)),
HirNode::Expr(_) => Ok(cx.cast_type(node_id, env).unwrap().ty),
HirNode::GenvarDecl(_) => {
Ok(SbvType::nice(ty::Domain::TwoValued, ty::Sign::Signed, 32).to_unpacked(cx))
}
HirNode::EnumVariant(v) => {
let ty = cx.packed_type_from_ast(
Ref(cx
.ast_for_id(v.enum_id)
.as_all()
.get_type()
.expect("enum_id should resolve to a type")),
env,
None,
);
if ty.is_error() {
return Ok(ty);
}
let packed = match ty.resolve_full().core {
ty::UnpackedCore::Packed(p) => p,
_ => panic!("enum type should have a packed core; got `{}`", ty),
};
let enm = match packed.resolve_full().core {
ty::PackedCore::Enum(ref e) => e,
_ => panic!("enum type should actually be an enum; got `{}`", packed),
};
Ok(enm.base.to_unpacked(cx))
}
HirNode::Package(_) => Ok(UnpackedType::make_void()),
HirNode::Assign(_) => unreachable!("has no type: {:?}", hir),
HirNode::Inst(hir) => Ok(cx.type_of_inst(Ref(hir), env)),
_ => {
error!("{:#?}", hir);
bug_span!(
hir.span(),
cx,
"type analysis of {} not implemented",
hir.desc_full()
)
}
}
}
#[moore_derive::query]
pub(crate) fn type_of_int_port<'a>(
cx: &impl Context<'a>,
Ref(port): Ref<'a, port_list::IntPort<'a>>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
match port.data {
Some(ref data) => {
let ty = cx.unpacked_type_from_ast(Ref(data.ty), Ref(data.unpacked_dims), env, None);
let intf = match ty.resolve_full().core.get_interface() {
Some(x) => x,
None => return ty,
};
let assigned = match cx.param_env_data(env).find_interface(port.id) {
Some(x) => x,
None => return ty,
};
let assigned_ty = cx.need_self_determined_type(assigned.id(), assigned.env());
let assigned_intf = assigned_ty
.resolve_full()
.core
.get_interface()
.expect("must be an intf here");
assert_eq!(intf.ast, assigned_intf.ast);
let mut intf = intf.clone();
intf.env = assigned_intf.env;
let mut ty = ty.clone();
ty.core = intf.into();
ty.intern(cx)
}
None => {
cx.resolve_node(port.id, env)
.and_then(|decl_id| cx.type_of(decl_id, env))
.unwrap_or(UnpackedType::make_error())
}
}
}
#[moore_derive::query]
pub(crate) fn type_of_ext_port<'a>(
cx: &impl Context<'a>,
Ref(port): Ref<'a, port_list::ExtPort<'a>>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
if port.exprs.len() == 1 {
let expr = &port.exprs[0];
if !expr.selects.is_empty() {
bug_span!(
port.span,
cx,
"port{} contains a `[...]` selection; not yet supported in typeck",
port.name
.map(|n| format!(" `{}`", n))
.unwrap_or_else(String::new)
);
}
let port_list = cx.canonicalize_ports(port.node);
cx.type_of_int_port(Ref(&port_list.int[expr.port]), env)
} else {
bug_span!(
port.span,
cx,
"port{} is a concatenation; not yet supported in typeck",
port.name
.map(|n| format!(" `{}`", n))
.unwrap_or_else(String::new)
);
}
}
#[moore_derive::query]
pub(crate) fn type_of_port_decl<'a>(
cx: &impl Context<'a>,
Ref(ast): Ref<'a, ast::VarDeclName<'a>>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
let ast_decl = ast
.get_parent()
.unwrap()
.as_all()
.get_port_decl()
.expect("parent not a PortDecl");
type_of_varlike(cx, ast_decl, &ast_decl.ty, ast, &ast.dims, env)
}
#[moore_derive::query]
pub(crate) fn type_of_var_decl<'a>(
cx: &impl Context<'a>,
Ref(ast): Ref<'a, ast::VarDeclName<'a>>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
let ast_decl = ast
.get_parent()
.unwrap()
.as_all()
.get_var_decl()
.expect("parent not a VarDecl");
type_of_varlike(cx, ast_decl, &ast_decl.ty, ast, &ast.dims, env)
}
#[moore_derive::query]
pub(crate) fn type_of_net_decl<'a>(
cx: &impl Context<'a>,
Ref(ast): Ref<'a, ast::VarDeclName<'a>>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
let ast_decl = ast
.get_parent()
.unwrap()
.as_all()
.get_net_decl()
.expect("parent not a NetDecl");
type_of_varlike(cx, ast_decl, &ast_decl.ty, ast, &ast.dims, env)
}
#[moore_derive::query]
pub(crate) fn type_of_struct_member<'a>(
cx: &impl Context<'a>,
Ref(ast): Ref<'a, ast::VarDeclName<'a>>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
let ast_member = ast
.get_parent()
.unwrap()
.as_all()
.get_struct_member()
.expect("parent not a StructMember");
type_of_varlike(cx, ast_member, &ast_member.ty, ast, &ast.dims, env)
}
fn type_of_varlike<'a>(
cx: &impl Context<'a>,
ast_decl: &'a dyn ast::AnyNode<'a>,
ast_ty: &'a ast::Type<'a>,
ast_name: &'a ast::VarDeclName<'a>,
ast_dims: &'a [ast::TypeDim<'a>],
env: ParamEnv,
) -> &'a UnpackedType<'a> {
let ast_implicit_default = if ast_decl.as_all().is_net_decl() {
Some(ty::PackedCore::IntVec(ty::IntVecType::Logic))
} else {
None
};
if !ast_ty.is_implicit() || ast_implicit_default.is_some() {
return cx.unpacked_type_from_ast(Ref(ast_ty), Ref(ast_dims), env, ast_implicit_default);
}
if let Some(init) = &ast_name.init {
let hir = match cx.hir_of(init.id()) {
Ok(HirNode::Expr(e)) => e,
Err(()) => return UnpackedType::make_error(),
_ => unreachable!(),
};
return cx.type_of_expr(Ref(hir), env);
}
cx.emit(
DiagBuilder2::error(format!(
"`{}` has implicit type but is not initialized",
ast_name.name
))
.span(ast_name.name_span)
.add_note("specify a type or add an initial value"),
);
UnpackedType::make_error()
}
#[moore_derive::query]
pub(crate) fn type_of_value_param<'a>(
cx: &impl Context<'a>,
Ref(ast): Ref<'a, ast::ParamValueDecl<'a>>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
if !ast.ty.is_implicit() {
return cx.unpacked_type_from_ast(Ref(&ast.ty), Ref(&ast.dims), env, None);
}
let env_data = cx.param_env_data(env);
match env_data.find_value(ast.id()) {
Some(ParamEnvBinding::Indirect(assigned_id)) => {
let hir = match cx.hir_of(assigned_id.id()) {
Ok(HirNode::Expr(e)) => e,
Err(()) => return UnpackedType::make_error(),
_ => unreachable!(),
};
return cx.type_of_expr(Ref(hir), assigned_id.env());
}
Some(ParamEnvBinding::Direct(t)) => return t.ty,
_ => (),
}
if let Some(ref expr) = ast.expr {
let hir = match cx.hir_of(expr.id()) {
Ok(HirNode::Expr(e)) => e,
Err(()) => return UnpackedType::make_error(),
_ => unreachable!(),
};
return cx.type_of_expr(Ref(hir), env);
}
cx.emit(
DiagBuilder2::error(format!(
"{} has implicit type but was not assigned and has no default",
ast
))
.span(ast.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"),
);
UnpackedType::make_error()
}
#[moore_derive::query]
pub(crate) fn type_of_inst<'a>(
cx: &impl Context<'a>,
Ref(hir): Ref<'a, hir::Inst<'a>>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
let details = match cx.inst_details(Ref(hir), env) {
Ok(x) => x,
_ => return UnpackedType::make_error(),
};
let ty = UnpackedType::make(
cx,
match details.target.kind {
InstTarget::Module(ast) => ty::UnpackedCore::Module(ty::ModuleType {
ast,
env: details.inner_env,
}),
InstTarget::Interface(ast) => ty::UnpackedCore::Interface(ty::InterfaceType {
ast,
env: details.inner_env,
modport: None,
}),
},
);
apply_unpacked_dims(cx, ty, &details.hir.ast.dims, env, details.hir.ast.span())
}
#[moore_derive::query]
pub(crate) fn type_of_subroutine_port<'a>(
cx: &impl Context<'a>,
ast: &'a ast::SubroutinePort<'a>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
let prototype = ast
.get_parent()
.unwrap()
.as_all()
.get_subroutine_prototype()
.expect("parent not a SubroutinePrototype");
let decl = prototype
.get_parent()
.unwrap()
.as_all()
.get_subroutine_decl()
.expect("parent not a SubroutineDecl");
let func_args = cx.canonicalize_func_args(Ref(decl));
let arg = func_args
.args
.iter()
.find(|a| a.ast.as_ptr() == ast.as_ptr())
.expect("no corresponding canonicalized arg");
cx.type_of_func_arg(Ref(arg), env)
}
#[moore_derive::query]
pub(crate) fn type_of_func_arg<'a>(
cx: &impl Context<'a>,
arg: Ref<'a, func_args::FuncArg<'a>>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
cx.unpacked_type_from_ast(Ref(arg.ty), Ref(arg.unpacked_dims), env, None)
}
#[moore_derive::query]
pub(crate) fn map_to_type<'a>(
cx: &impl Context<'a>,
Ref(ast): Ref<'a, dyn ast::AnyNode<'a>>,
env: ParamEnv,
) -> Option<&'a UnpackedType<'a>> {
match ast.as_all() {
ast::AllNode::Type(ast) => Some(cx.packed_type_from_ast(Ref(ast), env, None)),
ast::AllNode::Interface(ast) => {
let hir = match cx.hir_of_interface(ast) {
Ok(x) => x,
_ => return Some(UnpackedType::make_error()),
};
let env = match cx.param_env(ParamEnvSource::InterfaceInst {
interface: Ref(hir),
env,
pos: &[],
named: &[],
}) {
Ok(x) => x,
_ => return Some(UnpackedType::make_error()),
};
Some(UnpackedType::make(
cx,
UnpackedCore::Interface(ty::InterfaceType {
ast,
env,
modport: None,
}),
))
}
ast::AllNode::Expr(ast) => {
let ast = cx.arena().alloc(ast::TypeOrExpr::Expr(ast));
debug!("Disambiguating {:?}", ast);
let rst = match cx.disamb_type_or_expr(Ref(ast)) {
Ok(rst) => rst,
Err(()) => return Some(UnpackedType::make_error()),
};
match rst {
ast::TypeOrExpr::Type(ast) => Some(cx.packed_type_from_ast(Ref(ast), env, None)),
ast::TypeOrExpr::Expr(_) => None,
}
}
ast::AllNode::Typedef(ast) => {
Some(cx.unpacked_type_from_ast(Ref(&ast.ty), Ref(&ast.dims), env, None))
}
ast::AllNode::ParamTypeDecl(ast) => {
let env_data = cx.param_env_data(env);
match env_data.find_type(ast.id()) {
Some(ParamEnvBinding::Indirect(assigned_id)) => {
let assigned_ast = cx.ast_for_id(assigned_id.id());
match cx.map_to_type(Ref(assigned_ast), assigned_id.env()) {
Some(x) => return Some(x),
None => {
cx.emit(
DiagBuilder2::error(format!("{} is not a type", assigned_ast))
.span(assigned_ast.human_span())
.add_note(format!(
"Assigned to type parameter `{}` here:",
ast.name
))
.span(ast.human_span()),
);
error!("Offending node: {:#2?}", assigned_ast);
return Some(UnpackedType::make_error());
}
}
}
Some(ParamEnvBinding::Direct(t)) => return Some(t),
_ => (),
}
if let Some(ref ty) = ast.ty {
return Some(cx.packed_type_from_ast(Ref(ty), env, None));
}
let d = DiagBuilder2::error(format!("{} not assigned and has no default", ast,));
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(ast.human_span()),
);
}
if contexts.is_empty() {
cx.emit(d.span(ast.human_span()));
}
Some(UnpackedType::make_error())
}
_ => {
debug!("{:#1?} does not map to a type", ast);
None
}
}
}
#[moore_derive::query]
pub(crate) fn map_to_type_or_error<'a>(
cx: &impl Context<'a>,
ast: Ref<'a, dyn ast::AnyNode<'a>>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
match cx.map_to_type(ast, env) {
Some(ty) => ty,
None => {
cx.emit(
DiagBuilder2::error(format!("`{}` is not a type", ast.span().extract()))
.span(ast.span()),
);
error!("Offending node: {:#2?}", ast);
UnpackedType::make_error()
}
}
}
#[moore_derive::query]
pub(crate) fn packed_type_from_ast<'a>(
cx: &impl Context<'a>,
ast: Ref<'a, ast::Type<'a>>,
env: ParamEnv,
implicit_default: Option<ty::PackedCore<'a>>,
) -> &'a UnpackedType<'a> {
let Ref(ast) = ast;
let mut failed = false;
use PackedOrUnpacked::*;
let mut ast_sign = ast.sign;
let core = match ast.kind.data {
ast::ImplicitType | ast::ImplicitSignedType | ast::ImplicitUnsignedType => {
match implicit_default {
Some(core) => Packed(core),
None => {
cx.emit(
DiagBuilder2::error(format!("cannot infer implicit type"))
.span(ast.span())
.add_note(
"This usually indicates that a declaration is missing a default \
value.",
),
);
error!("offending implicit type is {:#1?}", ast.kind);
return UnpackedType::make_error();
}
}
}
ast::VoidType => Packed(PackedCore::Void),
ast::BitType => Packed(PackedCore::IntVec(IntVecType::Bit)),
ast::LogicType => Packed(PackedCore::IntVec(IntVecType::Logic)),
ast::RegType => Packed(PackedCore::IntVec(IntVecType::Reg)),
ast::ByteType => Packed(PackedCore::IntAtom(IntAtomType::Byte)),
ast::ShortIntType => Packed(PackedCore::IntAtom(IntAtomType::ShortInt)),
ast::IntType => Packed(PackedCore::IntAtom(IntAtomType::Int)),
ast::IntegerType => Packed(PackedCore::IntAtom(IntAtomType::Integer)),
ast::LongIntType => Packed(PackedCore::IntAtom(IntAtomType::LongInt)),
ast::TimeType => Packed(PackedCore::IntAtom(IntAtomType::Time)),
ast::ShortRealType => Unpacked(UnpackedCore::Real(RealType::ShortReal)),
ast::RealType => Unpacked(UnpackedCore::Real(RealType::Real)),
ast::RealtimeType => Unpacked(UnpackedCore::Real(RealType::RealTime)),
ast::StringType => Unpacked(UnpackedCore::String),
ast::ChandleType => Unpacked(UnpackedCore::Chandle),
ast::EventType => Unpacked(UnpackedCore::Event),
ast::StructType(ref strukt) => {
let mut def = ty::StructType {
ast: strukt,
ast_type: ast,
kind: strukt.kind,
members: Default::default(),
};
for member in &strukt.members {
for name in &member.names {
let ty = if strukt.packed {
cx.packed_type_from_ast(Ref(&member.ty), env, None)
} else {
cx.unpacked_type_from_ast(Ref(&member.ty), Ref(&name.dims), env, None)
};
def.members.push(ty::StructMember {
name: Spanned::new(name.name, name.span),
ty,
ast_member: member,
ast_name: name,
});
}
}
if ast_sign != ast::TypeSign::None {
cx.emit(
DiagBuilder2::error(format!(
"`signed` or `unsigned` goes before the struct contents"
))
.span(ast.span()),
);
failed = true;
}
ast_sign = strukt.signing;
if strukt.packed {
Packed(PackedCore::Struct(def))
} else {
Unpacked(UnpackedCore::Struct(def))
}
}
ast::EnumType(ref enm) => {
let base = match enm.base_type {
Some(ref ty) => {
let bty = cx.packed_type_from_ast(Ref(ty), env, None);
if bty.is_error() {
return UnpackedType::make_error();
}
if let Some(packed) = bty.get_packed() {
packed
} else {
cx.emit(
DiagBuilder2::error(format!(
"enum base type must be packed, but `{}` is unpacked",
bty
))
.span(ty.span()),
);
return UnpackedType::make_error();
}
}
None => SbvType::nice(ty::Domain::TwoValued, ty::Sign::Signed, 32).to_packed(cx),
};
let base_explicit = enm.base_type.is_some();
let def = ty::EnumType {
ast: enm,
base,
base_explicit,
variants: enm.variants.iter().map(|v| (v.name, v)).collect(),
};
Packed(PackedCore::Enum(def))
}
ast::NamedType(name) => {
let loc = cx.scope_location(ast);
let def = match cx.resolve_local_or_error(name, loc, false) {
Ok(def) => def,
Err(()) => return UnpackedType::make_error(),
};
packed_type_from_def(cx, def, name.span, env)
}
ast::ForwardType { kind: _ } => {
bug_span!(ast.span(), cx, "type {:#1?} not implemented", ast.kind)
}
ast::ScopedType {
ref ty,
member: false,
name,
} => match ty.kind.data {
ast::NamedType(pkg_name) => {
let loc = cx.scope_location(ty.as_ref());
let def = match cx.resolve_local_or_error(pkg_name, loc, false) {
Ok(def) => def,
_ => return UnpackedType::make_error(),
};
let pkg = match def.node {
DefNode::Ast(node) => node.as_all().get_package(),
_ => None,
};
let pkg = match pkg {
Some(x) => x,
None => {
cx.emit(
DiagBuilder2::error(format!("`{}` is not a package", pkg_name))
.span(pkg_name.span)
.add_note(format!("`{}` was declared here:", pkg_name))
.span(def.node.span()),
);
return UnpackedType::make_error();
}
};
let def = match cx.resolve_hierarchical_or_error(name, pkg) {
Ok(def) => def,
_ => return UnpackedType::make_error(),
};
packed_type_from_def(cx, def, ast.span(), env)
}
_ => {
cx.emit(
DiagBuilder2::error(format!("`{}` is not a package", ty.span().extract()))
.span(ty.span()),
);
return UnpackedType::make_error();
}
},
ast::ScopedType {
ref ty,
member: true,
name,
} => {
let inner_ty = cx.packed_type_from_ast(Ref(ty), env, None);
if let Some(intf) = inner_ty.get_interface() {
let def = match cx.resolve_hierarchical_or_error(name, intf.ast) {
Ok(def) => def,
_ => return UnpackedType::make_error(),
};
let modport = match def.node {
DefNode::Ast(node) => node.as_all().get_modport_name(),
_ => None,
};
let modport = match modport {
Some(x) => x,
None => {
cx.emit(
DiagBuilder2::error(format!(
"`{}` is not a modport of {}",
name, intf.ast
))
.span(name.span)
.add_note(format!("`{}` was defined here:", name))
.span(def.name.span),
);
return UnpackedType::make_error();
}
};
let new_intf = ty::InterfaceType {
modport: Some(modport),
..*intf
};
Unpacked(UnpackedCore::Interface(new_intf))
} else {
cx.emit(
DiagBuilder2::error(format!("`{}` is not an interface", ty.span().extract()))
.span(ty.span()),
);
error!("Offending AST: {:#2?}", ast);
return UnpackedType::make_error();
}
}
ast::TypeRef(ref arg) => {
let arg = match cx.disamb_type_or_expr(Ref(arg)) {
Ok(arg) => arg,
Err(()) => return UnpackedType::make_error(),
};
let ty = match arg {
ast::TypeOrExpr::Expr(expr) => cx.need_self_determined_type(expr.id(), env),
ast::TypeOrExpr::Type(ty) => cx.packed_type_from_ast(Ref(ty), env, None),
};
if let Some(ty) = ty.get_packed() {
Packed(PackedCore::Ref {
span: ast.kind.span(),
ty,
})
} else {
Unpacked(UnpackedCore::Ref {
span: ast.kind.span(),
ty,
})
}
}
ast::VirtIntfType { .. } | ast::MailboxType | ast::SpecializedType(..) => {
bug_span!(ast.span(), cx, "type {:#1?} not implemented", ast.kind)
}
};
let ty = match core {
Packed(core) => {
let sign = match ast_sign {
ast::TypeSign::None => core.default_sign(),
ast::TypeSign::Unsigned => ty::Sign::Unsigned,
ast::TypeSign::Signed => ty::Sign::Signed,
};
let sign_explicit = ast_sign != ast::TypeSign::None;
let mut dims = vec![];
for dim in &ast.dims {
match dim {
ast::TypeDim::Unsized => dims.push(ty::PackedDim::Unsized),
ast::TypeDim::Range(lhs, rhs) => {
match range_from_bounds_exprs(cx, lhs.id(), rhs.id(), env, ast.span()) {
Ok(r) => dims.push(ty::PackedDim::Range(r)),
Err(()) => {
failed = true;
continue;
}
}
}
_ => {
cx.emit(
DiagBuilder2::error(format!(
"unpacked dimension in the position of a packed dimension",
))
.span(ast.span()),
);
failed = true;
continue;
}
}
}
PackedType::make_sign_and_dims(cx, core, sign, sign_explicit, dims).to_unpacked(cx)
}
Unpacked(core) => {
if ast_sign != ast::TypeSign::None {
cx.emit(
DiagBuilder2::error(format!(
"unpacked type `{}` cannot be signed or unsigned",
core
))
.span(ast.span()),
);
failed = true;
}
if !ast.dims.is_empty() {
cx.emit(
DiagBuilder2::error(format!(
"unpacked type `{}` cannot have packed dimensions",
core
))
.span(ast.span()),
);
failed = true;
}
UnpackedType::make(cx, core)
}
};
if failed {
UnpackedType::make_error()
} else {
ty
}
}
fn packed_type_from_def<'a>(
cx: &impl Context<'a>,
def: &'a resolver::Def<'a>,
span: Span,
env: ParamEnv,
) -> PackedOrUnpacked<'a> {
use PackedOrUnpacked::*;
let ty = match def.node {
DefNode::Ast(node) => cx.map_to_type(Ref(node), env),
_ => None,
};
let ty = match ty {
Some(x) => x,
None => {
cx.emit(
DiagBuilder2::error(format!("`{}` is not a type", def.name))
.span(span)
.add_note(format!("`{}` was declared here:", def.name))
.span(def.node.span()),
);
error!("Offending node: {:#2?}", def.node);
return Packed(PackedCore::Error);
}
};
if let Some(ty) = ty.get_packed() {
Packed(PackedCore::Named { name: def.name, ty })
} else {
Unpacked(UnpackedCore::Named { name: def.name, ty })
}
}
enum PackedOrUnpacked<'a> {
Packed(PackedCore<'a>),
Unpacked(UnpackedCore<'a>),
}
#[moore_derive::query]
pub(crate) fn unpacked_type_from_ast<'a>(
cx: &impl Context<'a>,
ast_type: Ref<'a, ast::Type<'a>>,
Ref(ast_dims): Ref<'a, [ast::TypeDim<'a>]>,
env: ParamEnv,
implicit_default: Option<ty::PackedCore<'a>>,
) -> &'a UnpackedType<'a> {
let ty = cx.packed_type_from_ast(ast_type, env, implicit_default);
apply_unpacked_dims(cx, ty, ast_dims, env, ast_type.span())
}
fn apply_unpacked_dims<'a>(
cx: &impl Context<'a>,
ty: &'a UnpackedType<'a>,
ast_dims: &'a [ast::TypeDim<'a>],
env: ParamEnv,
span: Span,
) -> &'a UnpackedType<'a> {
let mut failed = false;
let mut dims = vec![];
for dim in ast_dims {
match dim {
ast::TypeDim::Unsized => dims.push(ty::UnpackedDim::Unsized),
ast::TypeDim::Expr(size) => match size_from_bounds_expr(cx, size.id(), env, span) {
Ok(s) => dims.push(ty::UnpackedDim::Array(s)),
Err(()) => {
failed = true;
continue;
}
},
ast::TypeDim::Range(lhs, rhs) => {
match range_from_bounds_exprs(cx, lhs.id(), rhs.id(), env, span) {
Ok(r) => dims.push(ty::UnpackedDim::Range(r)),
Err(()) => {
failed = true;
continue;
}
}
}
ast::TypeDim::Queue(Some(init_size)) => {
match size_from_bounds_expr(cx, init_size.id(), env, span) {
Ok(s) => dims.push(ty::UnpackedDim::Queue(Some(s))),
Err(()) => {
failed = true;
continue;
}
}
}
ast::TypeDim::Queue(None) => dims.push(ty::UnpackedDim::Queue(None)),
ast::TypeDim::Associative(Some(ty)) => {
let ty = cx.packed_type_from_ast(Ref(ty), env, None);
if ty.is_error() {
failed = true;
}
dims.push(ty::UnpackedDim::Assoc(Some(ty)));
}
ast::TypeDim::Associative(None) => dims.push(ty::UnpackedDim::Assoc(None)),
}
}
if failed {
UnpackedType::make_error()
} else if dims.is_empty() {
ty
} else {
let mut ty = ty.clone();
ty.dims.extend(dims);
ty.intern(cx)
}
}
#[moore_derive::query]
pub(crate) fn type_of_expr<'a>(
cx: &impl Context<'a>,
Ref(expr): Ref<'a, hir::Expr<'a>>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
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::Builtin(hir::BuiltinCall::CountOnes(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::OneHot(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::OneHot0(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::IsUnknown(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::ArrayDim(..))
| hir::ExprKind::Field(..)
| hir::ExprKind::Index(..)
| hir::ExprKind::Assign { .. } => cx.need_self_determined_type(expr.id, env),
hir::ExprKind::UnsizedConst(..) => cx
.type_context(Ref(expr), env)
.and_then(|x| x.ty().get_simple_bit_vector())
.map(|x| x.to_unpacked(cx))
.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(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::Unsigned(_))
| hir::ExprKind::FunctionCall(..) => cx.need_self_determined_type(expr.id, env),
hir::ExprKind::PositionalPattern(..)
| hir::ExprKind::NamedPattern(..)
| hir::ExprKind::RepeatPattern(..) => cx.need_type_context(Ref(expr), env).ty(),
hir::ExprKind::Ast(expr) => bug_span!(expr.span(), cx, "unsupported raw AST expr in HIR"),
}
}
#[moore_derive::query]
pub(crate) fn cast_type<'a>(
cx: &impl Context<'a>,
node_id: NodeId,
env: ParamEnv,
) -> Option<CastType<'a>> {
let hir = match cx.hir_of(node_id) {
Ok(x) => x,
Err(()) => return Some(ty::UnpackedType::make_error().into()),
};
match hir {
HirNode::Expr(e) => Some(cx.cast_expr_type(Ref(e), env)),
_ => None,
}
}
#[moore_derive::query]
pub(crate) fn cast_expr_type<'a>(
cx: &impl Context<'a>,
Ref(expr): Ref<'a, hir::Expr<'a>>,
env: ParamEnv,
) -> CastType<'a> {
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::PackSBVT => format!("pack as simple bit vector type `{}`", ty),
CastOp::UnpackSBVT => format!("unpack simple bit vector type as `{}`", 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::PickModport => format!("implicitly picking modport `{}`", ty),
CastOp::PackString => format!("pack as string `{}`", ty),
CastOp::UnpackString => format!("unpack string 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<'gcx>,
env: ParamEnv,
) -> CastType<'gcx> {
trace!(
"Computing cast type of `{}` (line {}) {:?}",
expr.span().extract(),
expr.span().begin().human_line(),
cx.ast_for_id(expr.id),
);
let inferred = cx.type_of_expr(Ref(expr), env);
let context = cx.type_context(Ref(expr), 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.is_strictly_identical(inferred) {
trace!(" Already identical");
return inferred.into();
} else if ty.to_string() == inferred.to_string() {
trace!(" Types look identical, but they are distinct:");
trace!(" Inferred: {:?}", inferred.resolve_full());
trace!(" Context: {:?}", ty.resolve_full());
}
}
let mut cast = CastType {
init: inferred,
ty: inferred,
casts: vec![],
};
if let TypeContext::Type(context) = context {
if let (Some(from), Some(to)) = (
inferred.resolve_full().core.get_interface(),
context.resolve_full().core.get_interface(),
) {
trace!(" Both are interfaces: {:?}", from);
trace!(" and: {:?}", to);
if from.ast == to.ast && from.env == to.env {
trace!(" Casting `{}` to its modport `{}`", inferred, context);
cast.add_cast(CastOp::PickModport, context);
return cast;
}
}
}
let inferred = match context.ty().get_simple_bit_vector() {
Some(context_sbvt) if inferred.is_string() => {
let ty = context_sbvt.forget().to_unpacked(cx);
trace!(" Packing string as SBVT ({})", ty);
cast.add_cast(CastOp::PackString, ty);
ty
}
_ => inferred,
};
let inferred_sbvt = match inferred.get_simple_bit_vector() {
Some(ty) => {
let ty = ty.forget();
if !inferred.is_simple_bit_vector() {
trace!(" Packing SBVT");
cast.add_cast(CastOp::PackSBVT, ty.to_unpacked(cx));
}
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::UnpackedType::make_error().into();
}
};
if cast.is_error() {
trace!(" Aborting due to error");
return cast;
}
trace!(
" Mapped `{}` (`{}`) to SBVT `{}`",
inferred,
inferred.resolve_full(),
inferred_sbvt
);
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,
};
if context.is_string() {
trace!(" Unpacking string from SBVT ({})", context);
cast.add_cast(CastOp::UnpackString, context);
return cast;
}
let context_sbvt = match context.get_simple_bit_vector() {
Some(ty) => ty.forget(),
None => {
cx.emit(
DiagBuilder2::error(format!("cannot cast to a value of type `{}`", context))
.span(expr.span)
.add_note(format!(
"`{}` has no simple bit-vector type representation",
context
)),
);
error!("Cast chain thus far: {}", cast);
return ty::UnpackedType::make_error().into();
}
};
trace!(
" Mapped `{}` (`{}`) to SBVT `{}`",
context,
context.resolve_full(),
context_sbvt
);
let inferred_sbvt = if inferred_sbvt.size != context_sbvt.size {
trace!(
" Casting size from {} to {}",
inferred_sbvt.range(),
context_sbvt.range()
);
let ty = inferred_sbvt.change_size(context_sbvt.size);
cast.add_cast(
CastOp::Range(context_sbvt.range(), inferred_sbvt.is_signed()),
ty.to_unpacked(cx),
);
ty
} else {
inferred_sbvt
};
if cast.is_error() {
trace!(" Aborting due to error");
return cast;
}
let inferred_sbvt = if inferred_sbvt.sign != context_sbvt.sign {
trace!(
" Casting sign from {:?} to {:?}",
inferred_sbvt.sign,
context_sbvt.sign
);
let ty = inferred_sbvt.change_sign(context_sbvt.sign);
cast.add_cast(CastOp::Sign(context_sbvt.sign), ty.to_unpacked(cx));
ty
} else {
inferred_sbvt
};
if cast.is_error() {
trace!(" Aborting due to error");
return cast;
}
let inferred_sbvt = if inferred_sbvt.domain != context_sbvt.domain {
trace!(
" Casting domain from {:?} to {:?}",
inferred_sbvt.domain,
context_sbvt.domain
);
let ty = inferred_sbvt.change_domain(context_sbvt.domain);
cast.add_cast(CastOp::Domain(context_sbvt.domain), ty.to_unpacked(cx));
ty
} else {
inferred_sbvt
};
if cast.is_error() {
trace!(" Aborting due to error");
return cast;
}
assert!(
inferred_sbvt.is_identical(&context_sbvt),
"SBVTs `{}` and `{}` must match at this point",
inferred_sbvt,
context_sbvt
);
if !context.is_simple_bit_vector() {
trace!(" Unpacking SBVT");
cast.add_cast(CastOp::UnpackSBVT, context);
}
if cast.is_error() {
trace!(" Aborting due to error");
return cast;
}
if context.is_strictly_identical(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::UnpackedType::make_error().into()
}
#[moore_derive::query]
pub(crate) fn self_determined_type<'a>(
cx: &impl Context<'a>,
node_id: NodeId,
env: ParamEnv,
) -> Option<&'a UnpackedType<'a>> {
let hir = match cx.hir_of(node_id) {
Ok(x) => x,
Err(()) => return Some(UnpackedType::make_error()),
};
match hir {
HirNode::Expr(e) => self_determined_expr_type(cx, e, env),
_ => None,
}
}
#[moore_derive::query]
pub(crate) fn need_self_determined_type<'a>(
cx: &impl Context<'a>,
node_id: NodeId,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
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)),
);
UnpackedType::make_error()
}
}
}
fn self_determined_expr_type<'gcx>(
cx: &impl Context<'gcx>,
expr: &'gcx hir::Expr<'gcx>,
env: ParamEnv,
) -> Option<&'gcx UnpackedType<'gcx>> {
match expr.kind {
hir::ExprKind::UnsizedConst(_) => Some(UnpackedType::make_logic()),
hir::ExprKind::IntConst {
width,
signed,
ref special_bits,
..
} => Some(
SbvType::nice(
match special_bits.any() {
true => ty::Domain::FourValued,
false => ty::Domain::TwoValued,
},
if signed {
ty::Sign::Signed
} else {
ty::Sign::Unsigned
},
width,
)
.to_unpacked(cx),
),
hir::ExprKind::TimeConst(_) => Some(UnpackedType::make_time()),
hir::ExprKind::StringConst(string) => Some(
ty::PackedType::make_dims(
cx,
ty::IntVecType::Bit,
vec![ty::PackedDim::Range(ty::Range {
size: string.value.as_str().len() * 8,
dir: ty::RangeDir::Down,
offset: 0,
})],
)
.to_unpacked(cx),
),
hir::ExprKind::Ident(_) | hir::ExprKind::Scope(..) => Some(
cx.resolve_node(expr.id, env)
.and_then(|x| cx.type_of(x, env))
.unwrap_or(UnpackedType::make_error()),
),
hir::ExprKind::Concat(repeat, ref exprs) => {
let mut failed = false;
let mut bit_width = 0;
let mut domain = ty::Domain::TwoValued;
for &expr in exprs {
let ty = cx.need_self_determined_type(expr, env);
if ty.is_error() {
failed = true;
continue;
}
if ty.domain() == ty::Domain::FourValued {
domain = ty::Domain::FourValued;
}
match ty.get_simple_bit_vector() {
Some(sbv) => bit_width += sbv.size,
None => {
cx.emit(
DiagBuilder2::error(format!(
"cannot concatenate a value of type `{}`",
ty
))
.span(cx.span(expr))
.add_note(format!(
"`{}` has no simple bit-vector type representation",
ty
)),
);
failed = true;
continue;
}
}
}
let repeat = match repeat {
Some(repeat) => match cx.constant_int_value_of(repeat, env) {
Ok(r) => r.to_usize().unwrap(),
Err(()) => {
failed = true;
0
}
},
None => 1,
};
if failed {
Some(UnpackedType::make_error())
} else {
Some(SbvType::new(domain, Sign::Unsigned, repeat * bit_width).to_unpacked(cx))
}
}
hir::ExprKind::Cast(ty, _) => Some(cx.packed_type_from_ast(
Ref(cx.ast_for_id(ty).as_all().get_type().unwrap()),
env,
None,
)),
hir::ExprKind::CastSign(sign, arg) => {
Some(self_determined_sign_cast_type(cx, sign.value, arg, env))
}
hir::ExprKind::Builtin(hir::BuiltinCall::Signed(arg)) => {
Some(self_determined_sign_cast_type(cx, Sign::Signed, arg, env))
}
hir::ExprKind::Builtin(hir::BuiltinCall::Unsigned(arg)) => {
Some(self_determined_sign_cast_type(cx, Sign::Unsigned, arg, env))
}
hir::ExprKind::CastSize(size, arg) => {
let size = match cx.constant_int_value_of(size, env) {
Ok(r) => r.to_usize().unwrap(),
Err(_) => {
return Some(UnpackedType::make_error());
}
};
let inner_ty = cx.need_self_determined_type(arg, env);
if inner_ty.is_error() {
return Some(UnpackedType::make_error());
}
let sbv = match inner_ty.get_simple_bit_vector() {
Some(sbv) => sbv,
None => {
cx.emit(
DiagBuilder2::error(format!(
"cannot size-cast a value of type `{}`",
inner_ty
))
.span(cx.span(arg))
.add_note(format!(
"`{}` has no simple bit-vector type representation",
inner_ty
)),
);
return Some(UnpackedType::make_error());
}
};
Some(sbv.change_size(size).to_unpacked(cx))
}
hir::ExprKind::Inside(..) => Some(UnpackedType::make_logic()),
hir::ExprKind::Builtin(hir::BuiltinCall::Unsupported)
| hir::ExprKind::Builtin(hir::BuiltinCall::Clog2(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::Bits(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::CountOnes(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::ArrayDim(..)) => {
Some(PackedType::make(cx, ty::IntAtomType::Int).to_unpacked(cx))
}
hir::ExprKind::Builtin(hir::BuiltinCall::OneHot(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::OneHot0(_))
| hir::ExprKind::Builtin(hir::BuiltinCall::IsUnknown(_)) => {
Some(PackedType::make(cx, ty::IntVecType::Bit).to_unpacked(cx))
}
hir::ExprKind::Field(target, name) => {
let target_ty = cx.self_determined_type(target, env)?;
if let Some(intf) = target_ty.get_interface() {
let def = cx.resolve_hierarchical_or_error(name, intf.ast).ok()?;
if let Some(modport) = def.node.as_all().get_modport_name() {
debug!("Interface access selected modport {:?}", modport);
if intf.modport.is_some() {
cx.emit(
DiagBuilder2::error(format!(
"value of type `{}` already has a modport; cannot change to {}",
target_ty, modport
))
.span(cx.span(target))
.add_note(format!(
"`{}` refers to a modport in interface `{}` defined here:",
name, intf.ast.name,
))
.span(modport.span()),
);
Some(UnpackedType::make_error())
} else {
let new_intf = ty::InterfaceType {
modport: Some(modport),
..*intf
};
Some(UnpackedType::make(cx, UnpackedCore::Interface(new_intf)))
}
} else {
Some(
cx.type_of(def.node.id(), intf.env)
.unwrap_or(UnpackedType::make_error()),
)
}
} else {
Some(
cx.resolve_field_access(expr.id, env)
.map(|(_, member)| member.ty)
.unwrap_or(UnpackedType::make_error()),
)
}
}
hir::ExprKind::Index(target, mode) => {
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(UnpackedType::make_error()),
};
let target_ty = cx.need_operation_type(expr.id, env);
if target_ty.is_error() {
return Some(target_ty);
}
let result = if let Some(dim) = target_ty.outermost_dim() {
match width {
Some(width) => {
let range = ty::Range::with_size(width);
target_ty.replace_dim(
cx,
match dim {
ty::Dim::Packed(..) => ty::Dim::Packed(range.into()),
ty::Dim::Unpacked(..) => ty::Dim::Unpacked(range.into()),
},
)
}
None => {
target_ty.pop_dim(cx).unwrap()
}
}
} else {
let sbvt = target_ty.simple_bit_vector(cx, cx.span(target));
match width {
Some(width) => {
sbvt.change_size(width).to_unpacked(cx)
}
None => {
let mut sbvt = sbvt.change_size(1);
sbvt.size_explicit = false;
sbvt.to_unpacked(cx)
}
}
};
Some(result)
}
hir::ExprKind::Unary(op, arg) => match op {
hir::UnaryOp::LogicNot => Some(UnpackedType::make_logic()),
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| {
PackedType::make(
cx,
match ty.domain() {
Domain::TwoValued => ty::IntVecType::Bit,
Domain::FourValued => ty::IntVecType::Logic,
},
)
.to_unpacked(cx)
}),
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(UnpackedType::make_logic()),
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()))
}
hir::ExprKind::FunctionCall(target, _) => {
Some(return_type_of_function(cx, &target.prototype, env))
}
hir::ExprKind::Assign { lhs, rhs, .. } => cx
.self_determined_type(lhs.id, env)
.or_else(|| cx.self_determined_type(rhs.id, env)),
_ => None,
}
}
fn self_determined_sign_cast_type<'gcx>(
cx: &impl Context<'gcx>,
sign: Sign,
arg: NodeId,
env: ParamEnv,
) -> &'gcx UnpackedType<'gcx> {
let ty = cx.need_self_determined_type(arg, env);
if ty.is_error() {
return UnpackedType::make_error();
}
let sbv = match ty.get_simple_bit_vector() {
Some(sbv) => sbv,
None => {
cx.emit(
DiagBuilder2::error(format!("cannot sign-cast a value of type `{}`", ty))
.span(cx.span(arg))
.add_note(format!(
"`{}` has no simple bit-vector type representation",
ty
)),
);
return UnpackedType::make_error();
}
};
sbv.change_sign(sign).to_unpacked(cx)
}
#[moore_derive::query]
pub(crate) fn operation_type<'a>(
cx: &impl Context<'a>,
node_id: NodeId,
env: ParamEnv,
) -> Option<&'a UnpackedType<'a>> {
let hir = match cx.hir_of(node_id) {
Ok(x) => x,
Err(_) => return Some(UnpackedType::make_error()),
};
let expr = match hir {
HirNode::Expr(x) => x,
_ => return None,
};
let node = expr.ast;
match expr.kind {
hir::ExprKind::Unary(op, arg) => {
let ty = match op {
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(Ref(node), 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::RedAnd
| hir::UnaryOp::RedOr
| hir::UnaryOp::RedXor
| hir::UnaryOp::RedNand
| hir::UnaryOp::RedNor
| hir::UnaryOp::RedXnor => cx.self_determined_type(arg, env),
hir::UnaryOp::LogicNot => Some(UnpackedType::make_logic()),
};
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(UnpackedType::make_error())
} 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(Ref(node), 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);
if tlhs.map(|t| t.is_string()).unwrap_or(false)
&& trhs.map(|t| t.is_string()).unwrap_or(false)
{
tlhs
} else {
unify_operator_types(cx, env, tlhs.into_iter().chain(trhs.into_iter()))
}
}
hir::BinaryOp::LogicAnd | hir::BinaryOp::LogicOr => {
Some(UnpackedType::make_logic())
}
hir::BinaryOp::Pow
| hir::BinaryOp::LogicShL
| hir::BinaryOp::LogicShR
| hir::BinaryOp::ArithShL
| hir::BinaryOp::ArithShR => {
let tc = cx.type_context(Ref(node), 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!(
"operation 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(UnpackedType::make_error())
} else {
ty
}
}
hir::ExprKind::Ternary(_, lhs, rhs) => {
let tc = cx.type_context(Ref(node), 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))
}
hir::ExprKind::Index(target, _mode) => {
let target_ty = cx.need_self_determined_type(target, env);
if target_ty.is_error() {
return Some(target_ty);
}
if let Some(_dim) = target_ty.outermost_dim() {
Some(target_ty)
} else {
match target_ty.get_simple_bit_vector() {
Some(sbvt) => Some(sbvt.forget().to_unpacked(cx)),
None => {
cx.emit(
DiagBuilder2::error(format!(
"cannot index into a value of type `{}`",
target_ty
))
.span(expr.span)
.add_note(format!(
"`{}` must be an array or have a simple bit-vector type \
representation",
target_ty
)),
);
Some(UnpackedType::make_error())
}
}
}
}
hir::ExprKind::Assign { lhs, rhs, .. } => cx
.self_determined_type(lhs.id, env)
.or_else(|| cx.self_determined_type(rhs.id, env)),
_ => None,
}
}
fn unify_operator_types<'gcx>(
cx: &impl Context<'gcx>,
_env: ParamEnv,
types: impl Iterator<Item = &'gcx UnpackedType<'gcx>>,
) -> Option<&'gcx UnpackedType<'gcx>> {
let inner: Vec<_> = types.flat_map(|ty| ty.get_simple_bit_vector()).collect();
let width: Option<usize> = inner.iter().map(|&sbv| sbv.size).max();
let sign = match inner.iter().all(|&sbv| sbv.is_signed()) {
true => Sign::Signed,
false => Sign::Unsigned,
};
let domain = match inner.iter().all(|&sbv| sbv.domain == Domain::TwoValued) {
true => Domain::TwoValued,
false => Domain::FourValued,
};
width.map(|w| SbvType::nice(domain, sign, w).to_unpacked(cx))
}
#[moore_derive::query]
pub(crate) fn need_operation_type<'a>(
cx: &impl Context<'a>,
node_id: NodeId,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
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),
);
UnpackedType::make_error()
}
}
}
#[moore_derive::query]
pub(crate) fn type_context<'a>(
cx: &impl Context<'a>,
Ref(onto): Ref<'a, ast::Expr<'a>>,
env: ParamEnv,
) -> Option<TypeContext<'a>> {
let parent = onto.get_parent().unwrap();
match parent.as_all() {
ast::AllNode::Expr(parent) => {
return type_context_imposed_by_expr(
cx,
onto.id(),
cx.hir_of_expr(Ref(parent)).ok()?,
env,
)
}
ast::AllNode::Stmt(parent) => {
return type_context_imposed_by_stmt(
cx,
onto.id(),
cx.hir_of_stmt(Ref(parent)).ok()?,
env,
)
}
ast::AllNode::SubroutinePort(port) => {
return Some(cx.type_of_subroutine_port(port, env).into());
}
ast::AllNode::CallArg(arg) => {
let call = arg.get_parent().unwrap().as_all().get_expr().unwrap();
let call = cx.hir_of_expr(Ref(call)).ok()?;
let (target, call_args) = match &call.kind {
hir::ExprKind::FunctionCall(target, args) => (target, args),
_ => return type_context_imposed_by_expr(cx, onto.id(), call, env),
};
let decl_args = cx.canonicalize_func_args(Ref(target));
let mapping = cx.call_mapping(Ref(decl_args), Ref(call_args), call.span);
let mapped_arg = mapping
.args
.iter()
.find(|x| x.call == CallArgSource::Call(arg))?;
return Some(cx.type_of_func_arg(Ref(mapped_arg.decl), env).into());
}
ast::AllNode::PortConn(port) => {
let inst = cx.hir_of(port.get_parent().unwrap().id()).ok()?;
let inst = match &inst {
HirNode::Inst(inst) => inst,
_ => bug_span!(
port.span(),
cx,
"port conn parent bust be inst, got {:#?}",
inst
),
};
let details = cx.inst_details(Ref(inst), env).ok()?;
let port = details.ports.reverse_find(onto.id())?;
return Some(cx.type_of_ext_port(Ref(port), details.inner_env).into());
}
ast::AllNode::PatternField(field) => {
let pattern = field.get_parent().unwrap().as_all().get_expr().unwrap();
let pattern = cx.hir_of_expr(Ref(pattern)).ok()?;
return type_context_imposed_by_expr(cx, onto.id(), pattern, env);
}
_ => (),
}
let onto = onto.id();
let parent_hir = match cx.hir_of(parent.id()) {
Ok(x) => x,
Err(()) => return None,
};
match parent_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(p) => match p.data {
Some(ref v) if v.default == Some(onto) && !v.ty.is_implicit() => {
Some(cx.type_of_int_port(Ref(p), env).into())
}
_ => None,
},
HirNode::VarDecl(v) if v.init == Some(onto) => {
let ty = cx.ast_for_id(v.ty).as_all().get_type().unwrap();
if !ty.is_implicit() || v.kind != ast::VarKind::Var {
Some(
cx.type_of(parent.id(), env)
.unwrap_or(UnpackedType::make_error())
.into(),
)
} else {
None
}
}
HirNode::ValueParam(v) if v.default == Some(onto) => {
let ty = cx.ast_for_id(v.ty).as_all().get_type().unwrap();
if !ty.is_implicit() {
Some(
cx.type_of(parent.id(), env)
.unwrap_or(UnpackedType::make_error())
.into(),
)
} else {
None
}
}
HirNode::InstTarget(inst) => {
let details = cx.inst_target_details(Ref(inst), 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,
}
}
#[moore_derive::query]
pub(crate) fn need_type_context<'a>(
cx: &impl Context<'a>,
Ref(node): Ref<'a, ast::Expr<'a>>,
env: ParamEnv,
) -> TypeContext<'a> {
match cx.type_context(Ref(node), env) {
Some(ty) => ty,
None => {
let extract = node.span().extract();
cx.emit(
DiagBuilder2::error(format!("cannot infer type from context: {}", node))
.span(node.span())
.add_note(format!(
"The type of {} must be inferred from context, but the location where you \
used it does not provide such information.",
node
))
.add_note(format!("Try a cast: `T'({})`", extract)),
);
UnpackedType::make_error().into()
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum TypeContext<'a> {
Type(&'a ty::UnpackedType<'a>),
Bool,
}
impl<'a> TypeContext<'a> {
pub fn ty(&self) -> &'a ty::UnpackedType<'a> {
match *self {
TypeContext::Type(t) => t,
TypeContext::Bool => ty::UnpackedType::make_logic(),
}
}
pub fn is_error(&self) -> bool {
match self {
TypeContext::Type(t) => t.is_error(),
TypeContext::Bool => false,
}
}
}
impl<'a> From<&'a ty::UnpackedType<'a>> for TypeContext<'a> {
fn from(ty: &'a ty::UnpackedType<'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<'gcx>,
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);
if ty.is_error() {
return Some(ty.into());
}
let sbvt = match ty.get_simple_bit_vector() {
Some(ty) => ty.to_unpacked(cx),
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
)),
);
UnpackedType::make_error()
}
};
Some(sbvt.into())
}
hir::ExprKind::PositionalPattern(..) => {
type_context_imposed_by_pattern(cx, onto, expr, env)
}
hir::ExprKind::RepeatPattern(rep, ..) if onto != rep => {
type_context_imposed_by_pattern(cx, onto, expr, env)
}
hir::ExprKind::NamedPattern(ref nodes) => {
if nodes.iter().all(|&(n, _)| match n {
hir::PatternMapping::Type(n) => n != onto,
hir::PatternMapping::Member(n) => n != onto,
_ => true,
}) {
type_context_imposed_by_pattern(cx, onto, expr, env)
} else {
None
}
}
hir::ExprKind::Inside(..) => Some(cx.need_operation_type(expr.id, env).into()),
hir::ExprKind::Index(target, _) if onto == target => {
let opty = cx.need_operation_type(expr.id, env);
debug!(
"Imposing operation type `{}` on `{}`",
opty,
cx.span(target).extract()
);
Some(opty.into())
}
hir::ExprKind::Assign { .. } => Some(cx.need_operation_type(expr.id, env).into()),
_ => None,
}
}
fn type_context_imposed_by_pattern<'gcx>(
cx: &impl Context<'gcx>,
onto: NodeId,
expr: &'gcx hir::Expr<'gcx>,
env: ParamEnv,
) -> Option<TypeContext<'gcx>> {
let map = match cx.map_pattern(Ref(expr), env) {
Ok(x) => x,
_ => return Some(UnpackedType::make_error().into()),
};
let tys: HashSet<_> = map
.fields
.iter()
.flat_map(|(field, expr)| {
if expr.id == onto {
Some(field.ty(cx))
} else {
None
}
})
.collect();
if tys.len() == 1 {
Some(tys.into_iter().next().unwrap().into())
} else {
warn!(
"Expression `{}` assigned to multiple fields with distinct types; no context imposed",
cx.span(onto).extract()
);
None
}
}
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
}
}
hir::StmtKind::Timed {
control: hir::TimingControl::Delay(expr),
..
} if expr == onto => Some(UnpackedType::make_time().into()),
_ => None,
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CastType<'a> {
pub init: &'a ty::UnpackedType<'a>,
pub ty: &'a ty::UnpackedType<'a>,
pub casts: Vec<(CastOp, &'a ty::UnpackedType<'a>)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CastOp {
PackSBVT,
UnpackSBVT,
Bool,
Sign(ty::Sign),
Range(ty::Range, bool),
Domain(ty::Domain),
PickModport,
PackString,
UnpackString,
}
impl<'a> CastType<'a> {
pub fn is_error(&self) -> bool {
self.ty.is_error()
}
pub fn add_cast(&mut self, op: CastOp, ty: &'a ty::UnpackedType<'a>) {
self.casts.push((op, ty));
self.ty = ty;
}
}
impl<'a> From<&'a ty::UnpackedType<'a>> for CastType<'a> {
fn from(ty: &'a ty::UnpackedType<'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,
}
}
fn size_from_bounds_expr<'a>(
cx: &impl Context<'a>,
expr: NodeId,
env: ParamEnv,
span: Span,
) -> Result<usize> {
let size = match cx.constant_value_of(expr, env).kind {
ValueKind::Int(ref int, ..) => int,
ValueKind::Error => return Err(()),
_ => {
let span = cx.span(expr);
cx.emit(
DiagBuilder2::error(format!(
"array bound `{}` is not an integer",
span.extract()
))
.span(span),
);
return Err(());
}
};
let size = match size.to_usize() {
Some(i) => i,
None => {
cx.emit(
DiagBuilder2::error(format!("{} is too large", span.extract()))
.span(span)
.add_note(format!("array would contain {} elements", size)),
);
return Err(());
}
};
Ok(size)
}
fn range_from_bounds_exprs<'a>(
cx: &impl Context<'a>,
lhs: NodeId,
rhs: NodeId,
env: ParamEnv,
span: Span,
) -> Result<ty::Range> {
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", span.extract()))
.span(span)
.add_note(format!("array would contain {} elements", size)),
);
return Err(());
}
};
let offset = lo.to_isize().unwrap();
Ok(ty::Range { size, dir, offset })
}
pub fn return_type_of_function<'a>(
cx: &impl Context<'a>,
node: &'a ast::SubroutinePrototype<'a>,
env: ParamEnv,
) -> &'a UnpackedType<'a> {
match &node.retty {
Some(ty) => cx.packed_type_from_ast(Ref(ty), env, Some(IntVecType::Logic.into())),
None => {
cx.emit(
DiagBuilder2::error(format!("no return type: {}", node)).span(node.name.span()),
);
UnpackedType::make_error()
}
}
}