// Copyright (c) 2016-2021 Fabian Schuiki //! This module implements the type calculation of the scoreboard. use std::cell::Cell; use std::collections::HashMap; use std::fmt::Debug; use crate::common::errors::*; use crate::common::score::{NodeMaker, NodeStorage, Result}; use crate::common::source::{Span, Spanned, INVALID_SPAN}; use crate::common::{NodeId, Verbosity}; use crate::hir; use crate::konst::*; use crate::lazy::LazyNode; use crate::score::*; use crate::ty::*; /// A context to typecheck things in. /// /// This context helps checking the types of things and keeping track of errors. pub struct TypeckContext<'sbc, 'lazy: 'sbc, 'sb: 'lazy, 'ast: 'sb, 'ctx: 'sb> { /// The parent context. pub ctx: &'sbc ScoreContext<'lazy, 'sb, 'ast, 'ctx>, /// Whether any of the type checking failed. failed: Cell, } impl<'sbc, 'lazy, 'sb, 'ast, 'ctx> TypeckContext<'sbc, 'lazy, 'sb, 'ast, 'ctx> { /// Create a new type checking context. pub fn new( ctx: &'sbc ScoreContext<'lazy, 'sb, 'ast, 'ctx>, ) -> TypeckContext<'sbc, 'lazy, 'sb, 'ast, 'ctx> { TypeckContext { ctx: ctx, failed: Cell::new(false), } } /// Consume the context and return the result of the typeck. pub fn finish(self) -> bool { !self.failed.get() } /// Emit a diagnostic message. pub fn emit(&self, diag: DiagBuilder2) { if diag.severity >= Severity::Error { self.failed.set(true); } self.ctx.sess.emit(diag) } /// Check the type of a node. /// /// If the node already had its type checked, immediately returns the result /// of that operation. Otherwise runs the task scheduled in the lazy table. pub fn lazy_typeck(&self, id: I) where I: Into, { let id = id.into(); // If the typeck has already been performed, return its result. if let Some(&node) = self.ctx.sb.typeck_table.borrow().get(&id) { if node.is_err() { self.failed.set(true); } return; } // Otherwise run the task scheduled in the lazy typeck table, then store // the result. let task = self.ctx.lazy.typeck.borrow_mut().set(id, LazyNode::Running); let result = match task { Some(LazyNode::Pending(f)) => f(self), Some(LazyNode::Running) => { self.ctx.bug(id, format!("recursion on typeck of {:?}", id)); Err(()) } None => { self.ctx .bug(id, format!("no typeck scheduled for {:?}", id)); Err(()) } }; if result.is_err() { self.failed.set(true); } self.ctx.sb.typeck_table.borrow_mut().insert(id, result); } /// Determine the type of a node. /// /// If the node already had its type determined, immediately returns the /// result of that operation. Otherwise runs the task scheduled in the lazy /// table. pub fn lazy_typeval(&self, id: I) -> Result<&'ctx Ty> where I: Into, { let id = id.into(); // If the typeval has already been performed, return its result. if let Some(&node) = self.ctx.sb.typeval_table.borrow().get(&id) { return node; } // Otherwise run the task scheduled in the lazy typeval table, then store // the result. let task = self .ctx .lazy .typeval .borrow_mut() .set(id, LazyNode::Running); let result = match task { Some(LazyNode::Pending(f)) => f(self), Some(LazyNode::Running) => { self.ctx .bug(id, format!("recursion on typeval of {:?}", id)); Err(()) } None => { self.ctx .bug(id, format!("no typeval scheduled for {:?}", id)); Err(()) } }; if result.is_err() { self.failed.set(true); } // Emit a diagnostic for the determined type if the corresponding flag // is active. if self.ctx.sess.opts.verbosity.contains(Verbosity::TYPES) { match (result, self.ctx.span(id)) { (Ok(ty), Some(span)) => { self.emit( DiagBuilder2::note(format!("type of `{}` is {}", span.extract(), ty)) .span(span), ); } _ => (), } } self.ctx.sb.typeval_table.borrow_mut().insert(id, result); result } /// Ensure that two types are compatible. pub fn must_match(&self, exp: &'ctx Ty, act: &'ctx Ty, span: Span) -> bool { assert!(span != INVALID_SPAN); if self.ctx.sess.opts.verbosity.contains(Verbosity::TYPECK) { self.emit( DiagBuilder2::note(format!("typeck expected {} and actual {}", exp, act)) .span(span), ); } if exp == act { return true; } let (exp_flat, act_flat) = match ( self.ctx.deref_named_type(exp), self.ctx.deref_named_type(act), ) { (Ok(e), Ok(a)) => (e, a), _ => return false, }; match (exp_flat, act_flat) { (e, a) if e == a => return true, // (e,a) if a.is_subtype_of(e) => return true, (&Ty::Int(..), &Ty::UniversalInt) => return true, _ => (), } self.emit( DiagBuilder2::error(format!( "expected type {}, but `{}` has type {}", exp, span.extract(), act )) .span(span) .add_note(format!("expected type: {}", exp_flat)) .add_note(format!(" actual type: {}", act_flat)), ); false } /// Ensure that one type can be cast into the other. pub fn must_cast(&self, into: &'ctx Ty, from: &'ctx Ty, span: Span) -> bool { self.must_match(into, from, span) } /// Type check the time expression in a delay mechanism. pub fn typeck_delay_mechanism(&self, _node: &'ctx hir::DelayMechanism) { // TODO: implement this } /// Type check a waveform. pub fn typeck_waveform(&self, node: &'ctx hir::Waveform, exp: &'ctx Ty) { for elem in node { self.typeck_wave_elem(elem, exp); } } /// Type check a waveform element. pub fn typeck_wave_elem(&self, node: &'ctx hir::WaveElem, _exp: &'ctx Ty) { if let Some(_value) = node.value { // TODO: type check value expression // self.typeck_node(value, exp); // let ty = self.lazy_typeval(value); // self.must_match(exp, ty, node.span); } if let Some(_after) = node.after { // TODO: type check time expression // self.typeck_node(after, /* time type */); } } /// Type check a subprogram specification. pub fn typeck_subprog_spec(&self, node: &'ctx hir::SubprogSpec) { self.typeck_slice(&node.generics); // self.typeck_slice(&node.generic_map); self.typeck_slice(&node.params); if let Some(ref ty) = node.return_type { self.typeck(ty.value); } } /// Type check any node that can have its type calculated. pub fn typeck_node(&self, id: I, exp: &'ctx Ty) where I: 'ctx + Copy + Debug + Into, ScoreContext<'lazy, 'sb, 'ast, 'ctx>: NodeMaker, { if let Ok(act) = self.ctx.ty(id) { if act != exp { // TODO: We need some span information here! self.emit(DiagBuilder2::error(format!( "typecheck failed, expected {:?}, got {:?}", exp, act ))); } } else { self.failed.set(true); } } /// Type check a slice of nodes. pub fn typeck_slice(&self, ids: T) where T: AsRef<[I]>, I: Copy, TypeckContext<'sbc, 'lazy, 'sb, 'ast, 'ctx>: Typeck, { for &id in ids.as_ref() { self.typeck(id); } } /// Apply a range constraint to a type. pub fn apply_range_constraint(&self, ty: &Ty, con: Spanned<&hir::Range>) -> Result<&'ctx Ty> { // Determine the applied range. let (dir, lb, rb) = match *con.value { hir::Range::Immediate(dir, lb, rb) => (dir, lb, rb), }; let lb = self.ctx.const_value(lb)?; let rb = self.ctx.const_value(rb)?; // Determine the inner type to which the constraint shall be applied. let ty = self.ctx.deref_named_type(ty)?; match *ty { Ty::Int(ref ty) => { // Make sure we have an integer range. let (lb, rb) = match (lb, rb) { (&Const::Int(ref lb), &Const::Int(ref rb)) => (lb, rb), _ => { self.emit( DiagBuilder2::error(format!( "non-integer range `{} {} {}` cannot constrain an integer type", lb, dir, rb )) .span(con.span), ); return Err(()); } }; // Make sure that this is actually a subtype. if ty.dir != dir || ty.left_bound > lb.value || ty.right_bound < rb.value { self.emit( DiagBuilder2::error(format!( "`{} {} {}` is not a subrange of `{}`", lb, dir, rb, ty )) .span(con.span), ); return Err(()); } // Create the new type. Ok(self .ctx .intern_ty(IntTy::new(ty.dir, lb.value.clone(), rb.value.clone()).maybe_null())) } // All other types we simply cannot constrain by range. _ => { self.emit( DiagBuilder2::error(format!( "{} cannot be constrained by range", ty.kind_desc() )) .span(con.span), ); return Err(()); } } } /// Apply an array constraint to a type. pub fn apply_array_constraint( &self, ty: &'ctx Ty, con: Spanned<&hir::ArrayConstraint>, ) -> Result<&'ctx Ty> { // Determine the inner type to which the constraint shall be applied. let ty = self.ctx.deref_named_type(ty)?; match *ty { Ty::Array(ref ty) => { let indices = if !con.value.index.is_empty() { if con.value.index.len() != ty.indices.len() { self.emit( DiagBuilder2::error(format!( "constrained {} indices, but array has {}", con.value.index.len(), ty.indices.len() )) .span(con.span) .add_note(format!( "`{}` constrained with `{}`", ty, con.span.extract() )), ); return Err(()); } ty.indices .iter() .zip(con.value.index.iter()) .map(|(ty, con)| self.apply_index_constraint(ty, con.as_ref())) .collect::>>()? } else { ty.indices.clone() }; let element = if let Some(ref elem_con) = con.value.elem { match elem_con.value { hir::ElementConstraint::Array(ref ac) => self.apply_array_constraint( &*ty.element, Spanned::new(ac, elem_con.span), )?, hir::ElementConstraint::Record(ref rc) => self.apply_record_constraint( &*ty.element, Spanned::new(rc, elem_con.span), )?, } } else { &*ty.element }; Ok(self .ctx .intern_ty(ArrayTy::new(indices, Box::new(element.clone())))) } _ => { self.emit( DiagBuilder2::error(format!( "array constraint `{}` does not apply to {}", con.span.extract(), ty.kind_desc() )) .span(con.span), ); return Err(()); } } } /// Apply a record constraint to a type. pub fn apply_record_constraint( &self, ty: &'ctx Ty, con: Spanned<&hir::RecordConstraint>, ) -> Result<&'ctx Ty> { use moore_common::name::Name; // Determine the inner type to which the constraint shall be applied. let ty = self.ctx.deref_named_type(ty)?; match *ty { Ty::Record(ref ty) => { let mut fields: Vec<(Name, &Ty)> = ty .fields .iter() .map(|&(name, ref ty)| (name, ty.as_ref())) .collect(); let mut had_fails = false; for &(name, ref con) in &con.value.elems { // Find the field that we're supposed to constrain. let idx = match ty.lookup.get(&name.value) { Some(&idx) => idx, None => { self.emit( DiagBuilder2::error(format!( "record has no element `{}`", name.value )) .span(name.span) .add_note(format!("{}", ty)), ); had_fails = true; continue; } }; // Constrain the field. fields[idx].1 = match con.value { hir::ElementConstraint::Array(ref ac) => { self.apply_array_constraint(&fields[idx].1, Spanned::new(ac, con.span))? } hir::ElementConstraint::Record(ref rc) => self .apply_record_constraint(&fields[idx].1, Spanned::new(rc, con.span))?, }; } if had_fails { return Err(()); } let fields = fields .into_iter() .map(|(name, ty)| (name, Box::new(ty.clone()))) .collect(); Ok(self.ctx.intern_ty(RecordTy::new(fields))) } _ => { self.emit( DiagBuilder2::error(format!( "array constraint `{}` does not apply to {}", con.span.extract(), ty.kind_desc() )) .span(con.span), ); return Err(()); } } } /// Apply an index constraint to an array index. pub fn apply_index_constraint( &self, index: &'ctx ArrayIndex, con: Spanned<&hir::DiscreteRange>, ) -> Result { // Convert the discrete range applied as constraint into a type. let con_ty = Spanned::new( self.ctx .deref_named_type(self.type_from_discrete_range(con)?)?, con.span, ); // Impose the type as a subtype on the index. let index_ty = match *index { ArrayIndex::Unbounded(ref ty) | ArrayIndex::Constrained(ref ty) => { self.apply_subtype(&*ty, con_ty)? } }; Ok(ArrayIndex::Constrained(Box::new(index_ty.clone()))) } /// Impose a subtype on a type. pub fn apply_subtype(&self, orig_ty: &'ctx Ty, subty: Spanned<&Ty>) -> Result<&'ctx Ty> { let deref = self.ctx.deref_named_type(orig_ty)?; let span = subty.span; match (deref, self.ctx.deref_named_type(subty.value)?) { (&Ty::Int(ref ty), &Ty::Int(ref subty)) => { use std::cmp::{max, min}; if ty.dir != subty.dir { self.emit( DiagBuilder2::error(format!( "directions disagree; `{}` and `{}`", subty, ty )) .span(span), ); return Err(()); } let (ty_lo, ty_hi, subty_lo, subty_hi) = match ty.dir { Dir::To => ( &ty.left_bound, &ty.right_bound, &subty.left_bound, &subty.right_bound, ), Dir::Downto => ( &ty.right_bound, &ty.left_bound, &subty.right_bound, &subty.left_bound, ), }; if ty_lo > subty_lo || ty_hi < subty_hi { self.emit( DiagBuilder2::error(format!("`{}` is not a subrange of `{}`", subty, ty)) .span(span) .add_note( "The range of a subtype must be entirely contained within the \ range of the target type.", ), // TODO: Add reference to standard. ); } let lo = max(ty_lo, subty_lo); let hi = min(ty_hi, subty_hi); let (lb, rb) = match ty.dir { Dir::To => (lo, hi), Dir::Downto => (hi, lo), }; let new_ty: Ty = IntTy::new(ty.dir, lb.clone(), rb.clone()).into(); if &new_ty == deref { Ok(orig_ty) } else { Ok(self.ctx.intern_ty(new_ty)) } } _ => { self.emit( DiagBuilder2::error(format!( "`{}` is not a subtype of `{}`", subty.span.extract(), orig_ty )) .span(span), ); return Err(()); } } } /// Evaluate a discrete range as a type. pub fn type_from_discrete_range( &self, range: Spanned<&hir::DiscreteRange>, ) -> Result<&'ctx Ty> { match *range.value { hir::DiscreteRange::Subtype(id) => self.ctx.ty(id), hir::DiscreteRange::Range(ref r) => self.type_from_range(Spanned::new(r, range.span)), } } /// Evaluate a range as a type. pub fn type_from_range(&self, range: Spanned<&hir::Range>) -> Result<&'ctx Ty> { match *range.value { hir::Range::Immediate(dir, lb, rb) => { let lb = self.ctx.const_value(lb)?; let rb = self.ctx.const_value(rb)?; match (lb, rb) { (&Const::Int(ref lb), &Const::Int(ref rb)) => Ok(self .ctx .intern_ty(IntTy::new(dir, lb.value.clone(), rb.value.clone()))), _ => { self.emit( DiagBuilder2::error(format!( "`{} {} {}` is not a valid range", lb, dir, rb )) .span(range.span), ); return Err(()); } } } } } } use crate::ty2::RangeDir; impl From for RangeDir { fn from(d: Dir) -> RangeDir { match d { Dir::To => RangeDir::To, Dir::Downto => RangeDir::Downto, } } } /// Performs a type check. pub trait Typeck { fn typeck(&self, id: I); } /// A macro to implement the `Typeck` trait. macro_rules! impl_typeck { ($slf:tt, $id:ident: $id_ty:ty => $blk:block) => { impl<'sbc, 'lazy, 'sb, 'ast, 'ctx> Typeck<$id_ty> for TypeckContext<'sbc, 'lazy, 'sb, 'ast, 'ctx> { fn typeck(&$slf, $id: $id_ty) $blk } } } /// A macro to implement the `Typeck` trait. macro_rules! impl_typeck_err { ($slf:tt, $id:ident: $id_ty:ty => $blk:block) => { impl<'sbc, 'lazy, 'sb, 'ast, 'ctx> Typeck<$id_ty> for TypeckContext<'sbc, 'lazy, 'sb, 'ast, 'ctx> { fn typeck(&$slf, $id: $id_ty) { use std; let res = (move || -> Result<()> { $blk })(); std::mem::forget(res); } } } } // Implement the `Typeck` trait for everything that supports type calculation. impl<'sbc, 'lazy: 'sbc, 'sb: 'lazy, 'ast: 'sb, 'ctx: 'sb, I> Typeck for TypeckContext<'sbc, 'lazy, 'sb, 'ast, 'ctx> where ScoreContext<'lazy, 'sb, 'ast, 'ctx>: NodeMaker, { fn typeck(&self, id: I) { match ScoreContext::make(self.ctx, id) { Ok(_) => (), Err(()) => self.failed.set(true), } } } /// Checks whether a node is of a given type. pub trait TypeckNode<'ctx, I> { fn typeck_node(&self, id: I, expected: &'ctx Ty) -> Result<()>; } // Implement the `TypeckNode` trait for everything that supports type // calculation. impl<'lazy, 'sb, 'ast, 'ctx, I> TypeckNode<'ctx, I> for ScoreContext<'lazy, 'sb, 'ast, 'ctx> where ScoreContext<'lazy, 'sb, 'ast, 'ctx>: NodeMaker, { fn typeck_node(&self, id: I, expected: &'ctx Ty) -> Result<()> { let actual = self.make(id)?; if actual != expected { self.emit(DiagBuilder2::error(format!( "typecheck failed, expected {:?}, got {:?}", expected, actual ))); Err(()) } else { Ok(()) } } } macro_rules! unimp { ($slf:tt, $id:expr) => {{ $slf.emit(DiagBuilder2::bug(format!( "typeck of {:?} not implemented", $id ))); return; }}; } macro_rules! unimp_err { ($slf:tt, $id:expr) => {{ $slf.emit(DiagBuilder2::bug(format!( "typeck of {:?} not implemented", $id ))); return Err(()); }}; } macro_rules! unimpmsg { ($slf:tt, $span:expr, $msg:expr) => {{ $slf.emit(DiagBuilder2::bug(format!("{} not implemented", $msg)).span($span)); return Err(()); }}; } impl_typeck_err!(self, id: LibRef => { let hir = self.ctx.hir(id)?; self.typeck_slice(&hir.pkg_decls); self.typeck_slice(&hir.pkg_insts); self.typeck_slice(&hir.pkg_bodies); self.typeck_slice(&hir.ctxs); self.typeck_slice(&hir.entities); self.typeck_slice(&hir.archs); self.typeck_slice(&hir.cfgs); Ok(()) }); impl_typeck_err!(self, id: PkgDeclRef => { let hir = self.ctx.hir(id)?; self.typeck_slice(&hir.generics); self.typeck_slice(&hir.decls); Ok(()) }); impl_typeck_err!(self, id: PkgBodyRef => { let hir = self.ctx.hir(id)?; self.typeck_slice(&hir.decls); Ok(()) }); impl_typeck_err!(self, id: PkgInstRef => { let _hir = self.ctx.hir(id)?; // self.typeck_slice(&hir.generic_map); Ok(()) }); impl_typeck!(self, id: CtxRef => { unimp!(self, id) }); impl_typeck!(self, id: CfgRef => { unimp!(self, id) }); impl_typeck_err!(self, id: EntityRef => { let hir = self.ctx.hir(id)?; for &generic in &hir.generics { self.typeck(generic); } for &port in &hir.ports { self.typeck(port); } Ok(()) }); impl_typeck_err!(self, id: ArchRef => { let hir = self.ctx.hir(id)?; self.typeck(hir.entity); for &decl in &hir.decls { self.typeck(decl); } for &stmt in &hir.stmts { self.typeck(stmt); } Ok(()) }); impl_typeck!(self, id: GenericRef => { match id { GenericRef::Type(id) => self.typeck(id), GenericRef::Subprog(id) => self.typeck(id), GenericRef::Pkg(id) => self.typeck(id), GenericRef::Const(id) => self.typeck(id), } }); // impl_typeck!(self, id: IntfSignalRef => { // self.typeck(self.hir(id)?.ty) // }); impl_typeck!(self, id: IntfTypeRef => { unimp!(self, id) // self.typeck(self.hir(id)?.ty) }); impl_typeck!(self, id: IntfSubprogRef => { unimp!(self, id) // self.typeck(self.hir(id)?.ty) }); impl_typeck!(self, id: IntfPkgRef => { unimp!(self, id) // self.typeck(self.hir(id)?.ty) }); impl_make!(self, id: IntfConstRef => &Ty { // TODO: Implement this. unimp_err!(self, id) }); impl_make!(self, id: IntfVarRef => &Ty { // TODO: Implement this. unimp_err!(self, id) }); impl_make!(self, id: IntfSignalRef => &Ty { let hir = self.hir(id)?; self.ty(hir.ty) }); impl_make!(self, id: IntfFileRef => &Ty { // TODO: Implement this. unimp_err!(self, id) }); impl_typeck!(self, id: DeclInPkgRef => { match id { DeclInPkgRef::Subprog(id) => self.typeck(id), DeclInPkgRef::SubprogInst(id) => self.typeck(id), DeclInPkgRef::Pkg(id) => self.typeck(id), DeclInPkgRef::PkgInst(id) => self.typeck(id), DeclInPkgRef::Type(id) => self.typeck(id), DeclInPkgRef::Subtype(id) => self.typeck(id), DeclInPkgRef::Const(id) => self.typeck(id), DeclInPkgRef::Signal(id) => self.typeck(id), DeclInPkgRef::Var(id) => self.typeck(id), DeclInPkgRef::File(id) => self.typeck(id), DeclInPkgRef::Alias(id) => self.typeck(id), DeclInPkgRef::Comp(id) => self.typeck(id), DeclInPkgRef::Attr(id) => self.typeck(id), DeclInPkgRef::AttrSpec(id) => self.typeck(id), DeclInPkgRef::Discon(id) => self.typeck(id), DeclInPkgRef::GroupTemp(id) => self.typeck(id), DeclInPkgRef::Group(id) => self.typeck(id), } }); impl_typeck!(self, id: DeclInPkgBodyRef => { match id { DeclInPkgBodyRef::Subprog(id) => self.typeck(id), DeclInPkgBodyRef::SubprogBody(id) => self.typeck(id), DeclInPkgBodyRef::SubprogInst(id) => self.typeck(id), DeclInPkgBodyRef::Pkg(id) => self.typeck(id), DeclInPkgBodyRef::PkgBody(id) => self.typeck(id), DeclInPkgBodyRef::PkgInst(id) => self.typeck(id), DeclInPkgBodyRef::Type(id) => self.typeck(id), DeclInPkgBodyRef::Subtype(id) => self.typeck(id), DeclInPkgBodyRef::Const(id) => self.typeck(id), DeclInPkgBodyRef::Var(id) => self.typeck(id), DeclInPkgBodyRef::File(id) => self.typeck(id), DeclInPkgBodyRef::Alias(id) => self.typeck(id), DeclInPkgBodyRef::Attr(id) => self.typeck(id), DeclInPkgBodyRef::AttrSpec(id) => self.typeck(id), DeclInPkgBodyRef::GroupTemp(id) => self.typeck(id), DeclInPkgBodyRef::Group(id) => self.typeck(id), } }); impl_typeck!(self, id: DeclInSubprogRef => { match id { DeclInSubprogRef::Subprog(id) => self.typeck(id), DeclInSubprogRef::SubprogBody(id) => self.typeck(id), DeclInSubprogRef::SubprogInst(id) => self.typeck(id), DeclInSubprogRef::Pkg(id) => self.typeck(id), DeclInSubprogRef::PkgBody(id) => self.typeck(id), DeclInSubprogRef::PkgInst(id) => self.typeck(id), DeclInSubprogRef::Type(id) => self.typeck(id), DeclInSubprogRef::Subtype(id) => self.typeck(id), DeclInSubprogRef::Const(id) => self.typeck(id), DeclInSubprogRef::Var(id) => self.typeck(id), DeclInSubprogRef::File(id) => self.typeck(id), DeclInSubprogRef::Alias(id) => self.typeck(id), DeclInSubprogRef::Attr(id) => self.typeck(id), DeclInSubprogRef::AttrSpec(id) => self.typeck(id), DeclInSubprogRef::GroupTemp(id) => self.typeck(id), DeclInSubprogRef::Group(id) => self.typeck(id), } }); impl_typeck!(self, id: DeclInBlockRef => { match id { DeclInBlockRef::Subprog(id) => self.typeck(id), DeclInBlockRef::SubprogBody(id) => self.typeck(id), DeclInBlockRef::SubprogInst(id) => self.typeck(id), DeclInBlockRef::Pkg(id) => self.typeck(id), DeclInBlockRef::PkgBody(id) => self.typeck(id), DeclInBlockRef::PkgInst(id) => self.typeck(id), DeclInBlockRef::Type(id) => self.typeck(id), DeclInBlockRef::Subtype(id) => self.typeck(id), DeclInBlockRef::Const(id) => self.typeck(id), DeclInBlockRef::Signal(id) => self.typeck(id), DeclInBlockRef::Var(id) => self.typeck(id), DeclInBlockRef::File(id) => self.typeck(id), DeclInBlockRef::Alias(id) => self.typeck(id), DeclInBlockRef::Comp(id) => self.typeck(id), DeclInBlockRef::Attr(id) => self.typeck(id), DeclInBlockRef::AttrSpec(id) => self.typeck(id), DeclInBlockRef::CfgSpec(id) => self.typeck(id), DeclInBlockRef::Discon(id) => self.typeck(id), DeclInBlockRef::GroupTemp(id) => self.typeck(id), DeclInBlockRef::Group(id) => self.typeck(id), } }); impl_typeck!(self, id: DeclInProcRef => { match id { DeclInProcRef::Subprog(id) => self.typeck(id), DeclInProcRef::SubprogBody(id) => self.typeck(id), DeclInProcRef::SubprogInst(id) => self.typeck(id), DeclInProcRef::Pkg(id) => self.typeck(id), DeclInProcRef::PkgBody(id) => self.typeck(id), DeclInProcRef::PkgInst(id) => self.typeck(id), DeclInProcRef::Type(id) => self.typeck(id), DeclInProcRef::Subtype(id) => self.typeck(id), DeclInProcRef::Const(id) => self.typeck(id), DeclInProcRef::Var(id) => self.typeck(id), DeclInProcRef::File(id) => self.typeck(id), DeclInProcRef::Alias(id) => self.typeck(id), DeclInProcRef::Attr(id) => self.typeck(id), DeclInProcRef::AttrSpec(id) => self.typeck(id), DeclInProcRef::GroupTemp(id) => self.typeck(id), DeclInProcRef::Group(id) => self.typeck(id), } }); impl_typeck!(self, id: ConcStmtRef => { match id { ConcStmtRef::Block(id) => self.typeck(id), ConcStmtRef::Process(id) => self.typeck(id), ConcStmtRef::ConcProcCall(id) => self.typeck(id), ConcStmtRef::ConcAssert(id) => self.typeck(id), ConcStmtRef::ConcSigAssign(id) => self.typeck(id), ConcStmtRef::CompInst(id) => self.typeck(id), ConcStmtRef::ForGen(id) => self.typeck(id), ConcStmtRef::IfGen(id) => self.typeck(id), ConcStmtRef::CaseGen(id) => self.typeck(id), } }); impl_typeck!(self, id: SeqStmtRef => { match id { SeqStmtRef::Wait(id) => self.lazy_typeck(id), SeqStmtRef::Assert(id) => self.lazy_typeck(id), SeqStmtRef::Report(id) => self.lazy_typeck(id), SeqStmtRef::SigAssign(id) => self.lazy_typeck(id), SeqStmtRef::VarAssign(id) => self.lazy_typeck(id), SeqStmtRef::ProcCall(id) => self.lazy_typeck(id), SeqStmtRef::If(id) => self.lazy_typeck(id), SeqStmtRef::Case(id) => self.lazy_typeck(id), SeqStmtRef::Loop(id) => self.lazy_typeck(id), SeqStmtRef::Nexit(id) => self.lazy_typeck(id), SeqStmtRef::Return(id) => self.lazy_typeck(id), SeqStmtRef::Null(id) => self.lazy_typeck(id), } }); impl_typeck_err!(self, id: SubprogDeclRef => { let hir = self.ctx.hir(id)?; self.typeck_subprog_spec(&hir.spec); Ok(()) }); impl_typeck_err!(self, id: SubprogBodyRef => { let hir = self.ctx.hir(id)?; self.typeck_subprog_spec(&hir.spec); self.typeck_slice(&hir.decls); self.typeck_slice(&hir.stmts); Ok(()) }); impl_typeck_err!(self, id: SubprogInstRef => { let _hir = self.ctx.hir(id)?; // self.typeck_slice(&hir.generic_map); Ok(()) }); impl_typeck_err!(self, id: ConstDeclRef => { self.ctx.lazy_typeval(id)?; Ok(()) }); impl_typeck_err!(self, id: SignalDeclRef => { self.ctx.lazy_typeval(id)?; Ok(()) }); impl_typeck_err!(self, id: VarDeclRef => { self.ctx.lazy_typeval(id)?; Ok(()) }); impl_typeck_err!(self, id: FileDeclRef => { self.ctx.lazy_typeval(id)?; Ok(()) }); impl_typeck!(self, id: AliasDeclRef => { unimp!(self, id) }); impl_typeck!(self, id: CompDeclRef => { unimp!(self, id) }); impl_typeck!(self, id: AttrDeclRef => { unimp!(self, id) }); impl_typeck!(self, id: AttrSpecRef => { unimp!(self, id) }); impl_typeck!(self, id: CfgSpecRef => { unimp!(self, id) }); impl_typeck!(self, id: DisconSpecRef => { unimp!(self, id) }); impl_typeck!(self, id: GroupTempRef => { unimp!(self, id) }); impl_typeck!(self, id: GroupDeclRef => { unimp!(self, id) }); impl_typeck!(self, id: BlockStmtRef => { unimp!(self, id) }); impl_typeck_err!(self, id: ProcessStmtRef => { let hir = self.ctx.hir(id)?; for &decl in &hir.decls { self.typeck(decl); } for &stmt in &hir.stmts { self.typeck(stmt); } Ok(()) }); impl_typeck!(self, id: ConcCallStmtRef => { unimp!(self, id) }); impl_typeck!(self, id: ConcAssertStmtRef => { unimp!(self, id) }); impl_typeck!(self, id: ConcSigAssignStmtRef => { unimp!(self, id) }); impl_typeck!(self, id: CompInstStmtRef => { unimp!(self, id) }); impl_typeck!(self, id: ForGenStmtRef => { unimp!(self, id) }); impl_typeck!(self, id: IfGenStmtRef => { unimp!(self, id) }); impl_typeck!(self, id: CaseGenStmtRef => { unimp!(self, id) }); impl_typeck_err!(self, id: SigAssignStmtRef => { let hir = self.ctx.hir(id)?; let lhs_ty = match hir.target { hir::SigAssignTarget::Name(sig) => self.ctx.ty(sig)?, hir::SigAssignTarget::Aggregate => unimpmsg!(self, hir.target_span, "assignment to aggregate signal"), }; // let mut ctx = TypeckContext::new(self); // let typeck_dm = |dm| match dm { // // TODO: typeck time expression // // &hir::DelayMechanism::RejectInertial(expr) => self.typeck_node(expr, self.intern_ty(/* time type */))?, // _ => Ok(()), // }; match hir.kind { hir::SigAssignKind::SimpleWave(ref dm, ref wave) => { self.typeck_delay_mechanism(dm); self.typeck_waveform(wave, lhs_ty); } hir::SigAssignKind::SimpleForce(_, _expr) => { // self.typeck_node(expr, lhs_ty)?; } hir::SigAssignKind::SimpleRelease(_) => (), hir::SigAssignKind::CondWave(ref dm, ref _cond) => { self.typeck_delay_mechanism(dm); // self.typeck_node(cond, lhs_ty)?; } hir::SigAssignKind::CondForce(_, ref _cond) => { // self.typeck_node(cond, lhs_ty)?; } hir::SigAssignKind::SelWave(ref dm, ref _sel) => { self.typeck_delay_mechanism(dm); // self.typeck_node(sel, lhs_ty)?; } hir::SigAssignKind::SelForce(_, ref _sel) => { // self.typeck_node(sel, lhs_ty)?; } } Ok(()) }); impl<'lazy, 'sb, 'ast, 'ctx> ScoreContext<'lazy, 'sb, 'ast, 'ctx> { /// Replace `Ty::Named` by the actual type definition recursively. pub fn deref_named_type<'a>(&self, ty: &'a Ty) -> Result<&'a Ty> where 'ctx: 'a, { match ty { &Ty::Named(_, tmr) => { let inner = self.ty(tmr)?; self.deref_named_type(inner) } other => Ok(other), } } } /// Determine the type of a type mark. impl_make!(self, id: TypeMarkRef => &Ty { match id { TypeMarkRef::Type(id) => self.make(id), TypeMarkRef::Subtype(id) => self.make(id), } }); /// Determine the type of a subtype indication. impl_make!(self, id: SubtypeIndRef => &Ty { self.lazy_typeval(id) // let hir = self.hir(id)?; // let ctx = TypeckContext::new(self); // let inner = self.intern_ty(Ty::Named(hir.type_mark.span, hir.type_mark.value)); // match hir.constraint { // None => Ok(inner), // Some(Spanned{ value: hir::Constraint::Range(ref con), span }) => { // ctx.apply_range_constraint(inner, Spanned::new(con, span)) // } // Some(Spanned{ value: hir::Constraint::Array(ref ac), span }) => { // ctx.apply_array_constraint(inner, Spanned::new(ac, span)) // } // Some(Spanned{ value: hir::Constraint::Record(ref rc), span }) => { // ctx.apply_record_constraint(inner, Spanned::new(rc, span)) // } // } }); /// Determine the type of a type declaration. impl_make!(self, id: TypeDeclRef => &Ty { let hir = self.lazy_hir(id)?; let data = match hir.data { Some(ref d) => d, None => { self.emit( DiagBuilder2::error(format!("declaration of type `{}` is incomplete", hir.name.value)) .span(hir.name.span) ); return Err(()); } }; match data.value { hir::TypeData::Range(dir, lb_id, rb_id) => { self.make_range_ty(dir, lb_id, rb_id, data.span) } hir::TypeData::Physical(dir, lb_id, rb_id, ref units, primary_index) => { let base = self.make_range_ty(dir, lb_id, rb_id, data.span)?; let base = match *base { Ty::Int(ref it) => it.clone(), _ => unreachable!(), }; let units = units.iter().map(|&(name, ref abs, ref rel)| PhysicalUnit::new(name.value, abs.clone(), rel.clone()) ).collect(); Ok(self.intern_ty(PhysicalTy::new(id, base, units, primary_index))) } hir::TypeData::Enum(ref lits) => { use crate::ty2::{EnumBasetype, EnumVariant}; let ty = EnumBasetype::new(lits.iter().map(|l| match *l { hir::EnumLit::Ident(sp) => EnumVariant::from(sp.value), hir::EnumLit::Char(sp) => EnumVariant::from(sp.value), })); debugln!("type from enum `{}` = {}", hir.name, ty); Ok(self.intern_ty(EnumTy::new(id))) } hir::TypeData::Access(subty_id) => { let ty = self.ty(subty_id)?.clone(); Ok(self.intern_ty(Ty::Access(Box::new(ty)))) } hir::TypeData::Array(ref index_ids, elem_ty) => { // To determine the type of an array, we first need to obtain the // HIR of each index. Based on that we can decide whether this is an // unbounded or constrained array type, and proceed accordingly. let mut had_fails = false; let mut indices = Vec::new(); for &index_id in index_ids { let hir = match self.hir(index_id) { Ok(h) => h, Err(()) => { had_fails = true; continue; } }; indices.push(match hir.value { hir::ArrayTypeIndex::Unbounded(tm) => { ArrayIndex::Unbounded(Box::new(self.ty(tm.value)?.clone())) } hir::ArrayTypeIndex::Subtype(subty) => { ArrayIndex::Constrained(Box::new(self.ty(subty)?.clone())) } hir::ArrayTypeIndex::Range(dir, lb_id, rb_id) => { ArrayIndex::Constrained(Box::new( self.make_range_ty(dir, lb_id, rb_id, hir.span)?.clone() )) } }); } if had_fails { return Err(()); } let elem_ty = self.ty(elem_ty)?.clone(); Ok(self.intern_ty(ArrayTy::new(indices, Box::new(elem_ty)))) } hir::TypeData::File(tm) => { let inner = self.ty(tm.value)?.clone(); Ok(self.intern_ty(Ty::File(Box::new(inner)))) } hir::TypeData::Record(ref fields) => { let mut had_fails = false; let mut mapped_fields = Vec::new(); let mut used_names = HashMap::new(); for &(name, subty) in fields { if let Some(&span) = used_names.get(&name.value) { self.emit( DiagBuilder2::error(format!("field `{}` already declared", name.value)) .span(name.span) .add_note("Previous declaration was here:") .span(span) ); had_fails = true; } else { used_names.insert(name.value, name.span); } mapped_fields.push((name.value, Box::new(self.ty(subty)?.clone()))) } if had_fails { return Err(()); } Ok(self.intern_ty(RecordTy::new(mapped_fields))) } } }); impl<'lazy, 'sb, 'ast, 'ctx> ScoreContext<'lazy, 'sb, 'ast, 'ctx> { pub fn make_range_ty( &self, dir: hir::Dir, lb_id: ExprRef, rb_id: ExprRef, span: Span, ) -> Result<&'ctx Ty> { let lb = self.const_value(lb_id)?; let rb = self.const_value(rb_id)?; Ok(match (lb, rb) { (&Const::Int(ref lb), &Const::Int(ref rb)) => { use crate::ty2::{IntegerBasetype, Range}; let ty = IntegerBasetype::new(Range::with_left_right( dir, lb.value.clone(), rb.value.clone(), )); debugln!("type from range `{}` = {}", span.extract(), ty); self.intern_ty(IntTy::new(dir, lb.value.clone(), rb.value.clone()).maybe_null()) } (&Const::Float(ref _lb), &Const::Float(ref _rb)) => { self.emit(DiagBuilder2::error("Float range bounds not yet supported").span(span)); return Err(()); } _ => { self.emit( DiagBuilder2::error("Bounds of range are not of the same type").span(span), ); return Err(()); } }) } } /// Determine the type of a subtype declaration. impl_make!(self, id: SubtypeDeclRef => &Ty { let hir = self.hir(id)?; self.ty(hir.subty) }); /// Determine the type of a signal declaration. // impl_make!(self, id: SignalDeclRef => &Ty { // let hir = self.lazy_hir(id)?; // self.lazy_typeval(hir.decl.ty) // }); // // /// Determine the type of an expression. // impl_make!(self, id: ExprRef => &Ty { // let hir = self.hir(id)?; // match hir.data { // hir::ExprData::IntegerLiteral(ref c) => { // // Integer literals either have a type attached, or they inherit // // their type from the context. // if let Some(ref ty) = c.ty { // return Ok(self.intern_ty(ty.clone())); // } // if let Some(ty) = self.type_context_resolved(id)? { // if let &Ty::Int(_) = self.deref_named_type(ty)? { // return Ok(ty); // } // } // self.emit( // DiagBuilder2::error(format!("cannot infer type of `{}` from context", hir.span.extract())) // .span(hir.span) // ); // Err(()) // } // // hir::ExprData::FloatLiteral(ref _c) => { // unimp_err!(self, id); // // // Float literals either have a type attached, or they inherit their // // // type from the context. // // if let Some(ref ty) = c.ty { // // return Ok(self.intern_ty(ty.clone())); // // } // // if let Some(ty) = self.type_context_resolved(id)? { // // if let &Ty::Float(_) = self.deref_named_type(ty)? { // // return Ok(ty); // // } // // } // // self.emit( // // DiagBuilder2::error("cannot infer type of float literal from context") // // .span(hir.span) // // ); // // Err(()) // } // // _ => unimp_err!(self, id), // } // }); /// Determine the type of a typed node. impl_make!(self, id: TypedNodeRef => &Ty { match id { TypedNodeRef::SubtypeInd(id) => self.make(id), TypedNodeRef::Signal(id) => self.make(id), } }); impl_make!(self, id: SignalRef => &Ty { match id { SignalRef::Intf(id) => self.make(id), SignalRef::Decl(id) => self.lazy_typeval(id), } }); impl_make!(self, id: IntfObjRef => &Ty { match id { IntfObjRef::Const(id) => self.make(id), IntfObjRef::Var(id) => self.make(id), IntfObjRef::Signal(id) => self.make(id), IntfObjRef::File(id) => self.make(id), } }); impl_make!(self, id: LatentTypeMarkRef => &Ty { self.ty(self.hir(id)?.value) }); // impl_make!(self, id: LatentSubprogRef => &Ty { // self.ty(self.hir(id)?.value) // }); // // impl_make!(self, id: SubprogRef => &Ty { // match id { // SubprogRef::Decl(id) => self.make(id), // SubprogRef::Inst(id) => self.make(id), // } // });