// Copyright (c) 2016-2021 Fabian Schuiki //! A collection if instantiation details. #[warn(missing_docs)] use crate::{ crate_prelude::*, hir::{self, HirNode}, port_list::AsPortedNode, resolver::InstTarget, Context, ParamEnv, ParamEnvData, ParamEnvSource, PortMapping, }; use std::sync::Arc; /// Instantiation details /// /// This struct bundles all the information associated with an instantiation, /// most importantly the parameter bindings and port connections. /// /// This corresponds to the `bar(y)` in `foo #(x) bar(y);`. #[derive(Debug)] pub struct InstDetails<'a> { /// The HIR instantiation. pub hir: &'a hir::Inst<'a>, /// The target details. pub target: Arc>, /// The port connections. pub ports: Arc>, /// The parameter environment generated by the instantiation, including any /// implicit parameters due to interface ports. pub inner_env: ParamEnv, } /// Instantiation target details /// /// This struct bundles all the information associated with an instantiation /// target, most importantly the parameter bindings. /// /// This corresponds to the `foo #(x)` in `foo #(x) bar(y);`. #[derive(Debug)] pub struct InstTargetDetails<'a> { /// The HIR instantiation target. pub hir: &'a hir::InstTarget<'a>, /// The instantiated node. pub kind: InstTarget<'a>, /// The parameter environment around the instantiation. pub outer_env: ParamEnv, /// The parameter environment generated by the instantiation. pub inner_env: ParamEnv, /// The parameter bindings. pub params: &'a ParamEnvData<'a>, } /// Compute the details of an instantiation. #[moore_derive::query] pub(crate) fn inst_details<'a>( cx: &impl Context<'a>, Ref(inst): Ref<'a, hir::Inst<'a>>, env: ParamEnv, ) -> Result>> { // Look up the HIR of the instantiation. let inst_target = match cx.hir_of(inst.target)? { HirNode::InstTarget(x) => x, _ => unreachable!(), }; // Determine the details of the instantiation target. let target = cx.inst_target_details(Ref(inst_target), env)?; // Determine the port connections of the instantiations. Connections // are made to the module's external ports, and must later be mapped // to the actual internal ports in a second step. let port_mapping = cx.port_mapping( target.kind.as_any().as_all().get_ported().unwrap(), target.outer_env, target.inner_env, Ref(inst), &inst.pos_ports, &inst.named_ports, inst.has_wildcard_port, )?; // Derive additional parametrization for the interface ports of the // instantiated module, if any. trace!("Deriving additional parametrization due to interfaces"); let mut intf_params = vec![]; for &(ext_port, assigned) in port_mapping.0.iter() { // Check if this is a simple 1:1 mapping from external to internal port, // which is the only possibility for interfaces. let port_list = cx.canonicalize_ports(ext_port.node); let int_port = match ext_port.exprs.as_slice() { &[ref expr] if expr.selects.is_empty() => &port_list.int[expr.port], _ => continue, }; let data = match int_port.data { Some(ref x) => x, None => continue, }; // Check if the port is actually an interface. let ty = cx.packed_type_from_ast(Ref(data.ty), target.inner_env, None); if ty.get_interface().is_none() { continue; } // Add this parametrization. trace!( " - Adding indirect {:?} for interface `{}` port `{}`", assigned, ty, int_port.name ); intf_params.push((int_port.id, assigned)); } // If we have found any additional parametrization, create an extended // parameter environment for this instance. let inner_env = if !intf_params.is_empty() { let mut params = target.params.clone(); params.add_interfaces(intf_params); trace!( "Extended parametrization with implicit interface parameters: {:?}", params ); cx.intern_param_env(params) } else { target.inner_env }; // Wrap everything up. Ok(Arc::new(InstDetails { hir: inst, target: target, ports: port_mapping, inner_env, })) } /// Compute the details of an instantiated module or interface. #[moore_derive::query] pub(crate) fn inst_target_details<'a>( cx: &impl Context<'a>, Ref(inst_target): Ref<'a, hir::InstTarget<'a>>, env: ParamEnv, ) -> Result>> { // Resolve the instantiation target. let target = cx.resolve_inst_target(inst_target.ast)?; // Create a new parameter environment that is generated by the // parametrization of this instance. let inst_env = cx.param_env(match target { resolver::InstTarget::Module(node) => ParamEnvSource::ModuleInst { module: Ref(cx.hir_of_module(node)?), env, pos: &inst_target.pos_params, named: &inst_target.named_params, }, resolver::InstTarget::Interface(node) => ParamEnvSource::InterfaceInst { interface: Ref(cx.hir_of_interface(node)?), env, pos: &inst_target.pos_params, named: &inst_target.named_params, }, })?; let inst_env_data = cx.param_env_data(inst_env); // Wrap everything up. Ok(Arc::new(InstTargetDetails { hir: inst_target, kind: target, outer_env: env, inner_env: inst_env, params: inst_env_data, })) } /// A visitor that emits instantiation details diagnostics. pub struct InstVerbosityVisitor<'a, 'gcx> { cx: &'a GlobalContext<'gcx>, env: ParamEnv, } impl<'a, 'gcx> InstVerbosityVisitor<'a, 'gcx> { /// Create a new visitor that emits instantiation details. pub fn new(cx: &'a GlobalContext<'gcx>) -> Self { Self { cx, env: cx.default_param_env(), } } } impl<'a, 'gcx> hir::Visitor<'gcx> for InstVerbosityVisitor<'a, 'gcx> { type Context = GlobalContext<'gcx>; fn context(&self) -> &Self::Context { self.cx } fn visit_inst(&mut self, hir: &'gcx hir::Inst<'gcx>) { let details = match self.cx.inst_details(Ref(hir), self.env) { Ok(x) => x, Err(()) => return, }; self.cx.emit( DiagBuilder2::note("instantiation details") .span(hir.name.span) .add_note(format!("{:#?}", details)), ); Self { cx: self.cx, env: details.inner_env, } .visit_node_with_id(details.target.kind.as_any().id(), false); } }