use crate::crate_prelude::*;
use crate::{ast_map::AstNode, common::arenas::Alloc};
use std::borrow::Cow;
#[derive(Debug, PartialEq, Eq)]
pub struct FuncArgList<'a> {
pub func: &'a ast::SubroutineDecl<'a>,
pub args: Vec<FuncArg<'a>>,
}
#[derive(Debug, PartialEq, Eq)]
pub struct FuncArg<'a> {
pub ast: &'a dyn ast::AnyNode<'a>,
pub func: &'a ast::SubroutineDecl<'a>,
pub span: Span,
pub name: Option<Spanned<Name>>,
pub dir: ast::SubroutinePortDir,
pub ty: &'a ast::Type<'a>,
pub unpacked_dims: &'a [ast::TypeDim<'a>],
pub var: bool,
pub default: Option<&'a ast::Expr<'a>>,
}
#[moore_derive::query]
pub(crate) fn canonicalize_func_args<'a>(
cx: &impl Context<'a>,
Ref(node): Ref<'a, ast::SubroutineDecl<'a>>,
) -> &'a FuncArgList<'a> {
debug!("Building port list of {:?}", node);
let next_rib = node.id();
let args = if let Some(ref args) = node.prototype.args {
trace!("Uses ANSI style");
gather_from_args(cx, node, args, next_rib)
} else {
trace!("Uses non-ANSI style");
gather_from_items(cx, node, &node.items, next_rib)
};
let list = FuncArgList { func: node, args };
trace!("Argument list of {:?} is: {:#?}", node, list);
cx.gcx().arena.alloc_func_arg_list(list)
}
fn gather_from_args<'a>(
cx: &impl Context<'a>,
node: &'a ast::SubroutineDecl<'a>,
args: &'a [ast::SubroutinePort<'a>],
next_rib: NodeId,
) -> Vec<FuncArg<'a>> {
let mut out_args = vec![];
let first_span = args.get(0).map(|x| x.span()).unwrap_or(node.span());
let mut carry_dir = ast::SubroutinePortDir::Input;
let mut carry_ty = Cow::Owned(ast::TypeKind::new(
first_span.begin().into(),
ast::LogicType,
));
let mut carry_sign = ast::TypeSign::None;
let mut carry_packed_dims: &[ast::TypeDim] = &[];
for arg in args {
let (ty, name) = cx.resolve_subroutine_port_ty_name(Ref(arg));
let dir = arg.dir.unwrap_or(carry_dir);
if arg.dir.is_some() {
carry_ty = Cow::Owned(ast::TypeKind::new(
first_span.begin().into(),
ast::LogicType,
));
carry_sign = ast::TypeSign::None;
carry_packed_dims = &[];
}
let inherit = ty.kind.data == ast::ImplicitType
&& ty.sign == ast::TypeSign::None
&& ty.dims.is_empty();
let (ty, sign, packed_dims, ty_span) = if inherit {
(carry_ty, carry_sign, carry_packed_dims, None)
} else {
let kind = if ty.kind.data == ast::ImplicitType {
carry_ty
} else {
Cow::Borrowed(&ty.kind)
};
(kind, ty.sign, ty.dims.as_slice(), Some(ty.span()))
};
carry_dir = dir;
carry_ty = ty.clone();
carry_sign = sign;
carry_packed_dims = packed_dims;
let ty = cx.arena().alloc(ast::Type::new(
ty_span.unwrap_or(arg.span()),
ast::TypeData {
kind: ty.into_owned(),
sign: sign,
dims: packed_dims.to_vec(),
},
));
ty.link_attach(arg, arg.order());
cx.register_ast(ty);
cx.map_ast_with_parent(AstNode::Type(ty), next_rib);
let (name, unpacked_dims, default): (_, &[_], _) = match name {
Some(pn) => (Some(pn.name), pn.dims.as_slice(), pn.expr.as_ref()),
None => (None, &[], None),
};
out_args.push(FuncArg {
ast: arg,
func: node,
span: arg.span,
name,
dir,
ty,
var: arg.var,
unpacked_dims,
default,
});
}
out_args
}
fn gather_from_items<'a>(
cx: &impl Context<'a>,
node: &'a ast::SubroutineDecl<'a>,
items: &'a [ast::SubroutineItem<'a>],
next_rib: NodeId,
) -> Vec<FuncArg<'a>> {
let mut args = vec![];
let mut non_port = None;
let mut non_port_reported = false;
for item in items {
let port = match item {
ast::SubroutineItem::PortDecl(x) => x,
ast::SubroutineItem::Stmt(x) => {
non_port = Some(non_port.unwrap_or(x));
continue;
}
};
if let Some(non_port) = non_port {
if !non_port_reported {
cx.emit(
DiagBuilder2::warning(format!("port after statement"))
.span(port.span())
.add_note("Port declaration appears after this statement:")
.span(non_port.span())
.add_note(
"This is accepted by moore, but is forbidden according to IEEE \
1800-2017 where all ports must appear before regular statements.",
),
);
non_port_reported = true;
}
}
let ty: &_ = if port.ty.kind.data == ast::ImplicitType {
let ty = cx.arena().alloc(ast::Type::new(
port.ty.span(),
ast::TypeData {
kind: ast::TypeKind::new(port.ty.span(), ast::LogicType),
sign: port.ty.sign,
dims: port.ty.dims.clone(),
},
));
ty.link_attach(port, port.order());
cx.register_ast(ty);
cx.map_ast_with_parent(AstNode::Type(ty), next_rib);
ty
} else {
&port.ty
};
for name in &port.names {
args.push(FuncArg {
ast: port,
func: node,
span: port.span,
name: Some(Spanned::new(name.name, name.span)),
dir: port.dir,
ty,
var: port.var,
unpacked_dims: &name.dims,
default: name.init.as_ref(),
});
}
}
args
}
pub struct FuncArgsVerbosityVisitor<'cx, C> {
cx: &'cx C,
}
impl<'cx, C> FuncArgsVerbosityVisitor<'cx, C> {
pub fn new(cx: &'cx C) -> Self {
FuncArgsVerbosityVisitor { cx }
}
}
impl<'a, 'cx, C> ast::Visitor<'a> for FuncArgsVerbosityVisitor<'cx, C>
where
C: Context<'a>,
'a: 'cx,
{
fn pre_visit_subroutine_decl(&mut self, node: &'a ast::SubroutineDecl<'a>) -> bool {
let list = self.cx.canonicalize_func_args(Ref(node));
let mut d = DiagBuilder2::note("canonicalized subroutine arguments")
.span(node.span())
.add_note(format!(
"Subroutine `{}` has the following arguments:",
node.prototype.name
));
for arg in &list.args {
let name = match arg.name {
Some(name) => format!(" {}", name),
None => format!(""),
};
let ty = self.cx.unpacked_type_from_ast(
Ref(arg.ty),
Ref(arg.unpacked_dims),
self.cx.default_param_env(),
None,
);
d = d.add_note(format!("{} {}{}", arg.dir, ty, name));
}
self.cx.emit(d);
true
}
}