spade_ast_lowering/
lib.rs

1mod attributes;
2pub mod builtins;
3pub mod error;
4pub mod global_symbols;
5mod impls;
6mod lambda;
7pub mod pipelines;
8pub mod testutil;
9mod type_alias;
10mod type_level_if;
11pub mod types;
12
13use std::sync::Arc;
14
15use attributes::LocAttributeExt;
16use global_symbols::visit_meta_type;
17use impls::visit_impl;
18use itertools::Itertools;
19use lambda::visit_lambda;
20use num::{BigInt, FromPrimitive, Zero};
21use pipelines::PipelineContext;
22use recursive::recursive;
23use spade_diagnostics::codespan::Span;
24use spade_diagnostics::diag_list::{DiagList, ResultExt};
25use spade_diagnostics::diagnostic::SuggestionParts;
26use spade_diagnostics::{diag_anyhow, diag_bail, Diagnostic};
27use spade_hir::expression::Safety;
28use spade_types::meta_types::MetaType;
29use tracing::{event, Level};
30use type_level_if::expand_type_level_if;
31
32use crate::attributes::AttributeListExt;
33pub use crate::impls::ensure_unique_anonymous_traits;
34use crate::pipelines::maybe_perform_pipelining_tasks;
35use crate::types::{IsInOut, IsPort, IsSelf};
36use ast::{Binding, CallKind, ParameterList};
37use hir::expression::{BinaryOperator, IntLiteralKind};
38use hir::param_util::ArgumentError;
39use hir::symbol_table::DeclarationState;
40use hir::symbol_table::{LookupError, SymbolTable, Thing, TypeSymbol};
41use hir::{ConstGeneric, ExecutableItem, PatternKind, TraitName, WalTrace};
42use rustc_hash::FxHashSet as HashSet;
43use spade_ast::{self as ast, Attribute, Expression, TypeParam, WhereClause};
44pub use spade_common::id_tracker;
45use spade_common::id_tracker::{ExprIdTracker, ImplIdTracker};
46use spade_common::location_info::{FullSpan, Loc, WithLocation};
47use spade_common::name::{Identifier, Path, PathSegment, Visibility};
48use spade_hir::{self as hir, ExprKind, ItemList, Module, TraitSpec, TypeExpression, TypeSpec};
49
50use error::Result;
51
52#[derive(Debug)]
53pub struct Context {
54    pub symtab: SymbolTable,
55    pub item_list: ItemList,
56    pub idtracker: Arc<ExprIdTracker>,
57    pub impl_idtracker: ImplIdTracker,
58    pub pipeline_ctx: Option<PipelineContext>,
59    pub self_ctx: SelfContext,
60    pub current_unit: Option<hir::UnitHead>,
61    pub diags: DiagList,
62    pub safety: Safety,
63}
64
65impl Context {
66    fn in_fresh_unit<T>(&mut self, transform: impl FnOnce(&mut Context) -> T) -> T {
67        let mut tmp_pipeline_ctx = None;
68        let mut tmp_self_ctx = SelfContext::FreeStanding;
69        let mut tmp_current_unit = None;
70        {
71            let Context {
72                symtab: _,
73                item_list: _,
74                idtracker: _,
75                impl_idtracker: _,
76                pipeline_ctx,
77                self_ctx,
78                current_unit,
79                diags: _,
80                safety: _,
81            } = self;
82            std::mem::swap(pipeline_ctx, &mut tmp_pipeline_ctx);
83            std::mem::swap(self_ctx, &mut tmp_self_ctx);
84            std::mem::swap(current_unit, &mut tmp_current_unit);
85        }
86        let result = transform(self);
87        {
88            let Context {
89                symtab: _,
90                item_list: _,
91                idtracker: _,
92                impl_idtracker: _,
93                pipeline_ctx,
94                self_ctx,
95                current_unit,
96                diags: _,
97                safety: _,
98            } = self;
99            std::mem::swap(pipeline_ctx, &mut tmp_pipeline_ctx);
100            std::mem::swap(self_ctx, &mut tmp_self_ctx);
101            std::mem::swap(current_unit, &mut tmp_current_unit);
102        }
103        result
104    }
105
106    pub fn in_named_namespace<T>(
107        &mut self,
108        new_ident: Loc<Identifier>,
109        f: impl FnOnce(&mut Self) -> T,
110    ) -> T {
111        self.symtab.push_namespace(PathSegment::Named(new_ident));
112        let result = f(self);
113        self.symtab.pop_namespace();
114        result
115    }
116
117    pub fn in_new_scope<T>(&mut self, f: impl Fn(&mut Self) -> T) -> T {
118        self.symtab.new_scope();
119        let result = f(self);
120        self.symtab.close_scope();
121        result
122    }
123}
124
125trait LocExt<T> {
126    fn try_visit<V, U>(&self, visitor: V, context: &mut Context) -> Result<Loc<U>>
127    where
128        V: Fn(&T, &mut Context) -> Result<U>;
129
130    fn visit<V, U>(&self, visitor: V, context: &mut Context) -> Loc<U>
131    where
132        V: Fn(&T, &mut Context) -> U;
133}
134
135impl<T> LocExt<T> for Loc<T> {
136    fn try_visit<V, U>(&self, visitor: V, context: &mut Context) -> Result<Loc<U>>
137    where
138        V: Fn(&T, &mut Context) -> Result<U>,
139    {
140        self.map_ref(|t| visitor(t, context)).map_err(|e, _| e)
141    }
142
143    fn visit<V, U>(&self, visitor: V, context: &mut Context) -> Loc<U>
144    where
145        V: Fn(&T, &mut Context) -> U,
146    {
147        self.map_ref(|t| visitor(t, context))
148    }
149}
150
151/// Visit an AST type parameter, converting it to a HIR type parameter and adding it
152/// to the symbol table
153#[tracing::instrument(skip_all, fields(name=%param.name()))]
154pub fn visit_type_param(param: &ast::TypeParam, ctx: &mut Context) -> Result<hir::TypeParam> {
155    match &param {
156        ast::TypeParam::TypeName {
157            name: ident,
158            traits,
159        } => {
160            let trait_bounds: Vec<Loc<TraitSpec>> = traits
161                .iter()
162                .map(|t| visit_trait_spec(t, &TypeSpecKind::TraitBound, ctx))
163                .collect::<Result<_>>()?;
164
165            let name_id = ctx.symtab.add_type(
166                ident.clone(),
167                TypeSymbol::GenericArg {
168                    traits: trait_bounds.clone(),
169                }
170                .at_loc(ident),
171                Visibility::Implicit.nowhere(),
172            );
173
174            Ok(hir::TypeParam {
175                ident: ident.clone(),
176                name_id,
177                trait_bounds,
178                meta: MetaType::Type,
179            })
180        }
181        ast::TypeParam::TypeWithMeta { meta, name } => {
182            let meta = visit_meta_type(meta)?;
183            let name_id = ctx.symtab.add_type(
184                name.clone(),
185                TypeSymbol::GenericMeta(meta.clone()).at_loc(name),
186                Visibility::Implicit.nowhere(),
187            );
188
189            Ok(hir::TypeParam {
190                ident: name.clone(),
191                name_id,
192                trait_bounds: vec![],
193                meta,
194            })
195        }
196    }
197}
198
199/// Visit an AST type parameter, converting it to a HIR type parameter. The name is not
200/// added to the symbol table as this function is re-used for both global symbol collection
201/// and normal HIR lowering.
202#[tracing::instrument(skip_all, fields(name=%param.name()))]
203pub fn re_visit_type_param(param: &ast::TypeParam, ctx: &Context) -> Result<hir::TypeParam> {
204    match &param {
205        ast::TypeParam::TypeName {
206            name: ident,
207            traits: _,
208        } => {
209            let path = Path::ident(ident.clone()).at_loc(ident);
210            let (name_id, tsym) = ctx.symtab.lookup_type_symbol(&path)?;
211
212            let trait_bounds = match &tsym.inner {
213                TypeSymbol::GenericArg { traits } => traits.clone(),
214                _ => return Err(Diagnostic::bug(
215                    ident,
216                    format!(
217                        "Trait bound on {ident} on non-generic argument, which should've been caught by the first pass"
218                    ),
219                ))
220            };
221
222            Ok(hir::TypeParam {
223                ident: ident.clone(),
224                name_id,
225                trait_bounds,
226                meta: MetaType::Type,
227            })
228        }
229        ast::TypeParam::TypeWithMeta { meta, name } => {
230            let path = Path::ident(name.clone()).at_loc(name);
231            let name_id = ctx.symtab.lookup_type_symbol(&path)?.0;
232            Ok(hir::TypeParam {
233                ident: name.clone(),
234                name_id,
235                trait_bounds: vec![],
236                meta: visit_meta_type(meta)?,
237            })
238        }
239    }
240}
241
242/// The context in which a type expression occurs. This controls what hir::TypeExpressions an
243/// ast::TypeExpression can be lowered to
244pub enum TypeSpecKind {
245    Argument,
246    OutputType,
247    EnumMember,
248    StructMember,
249    ImplTrait,
250    ImplTarget,
251    BindingType,
252    Turbofish,
253    PipelineHeadDepth,
254    PipelineRegCount,
255    PipelineInstDepth,
256    TraitBound,
257    TypeLevelIf,
258}
259
260#[recursive]
261pub fn visit_type_expression(
262    expr: &ast::TypeExpression,
263    kind: &TypeSpecKind,
264    ctx: &mut Context,
265) -> Result<hir::TypeExpression> {
266    match expr {
267        ast::TypeExpression::TypeSpec(spec) => {
268            let inner = visit_type_spec(spec, kind, ctx)?;
269            // Look up the type. For now, we'll panic if we don't find a concrete type
270            Ok(hir::TypeExpression::TypeSpec(inner.inner))
271        }
272        ast::TypeExpression::Integer(val) => Ok(hir::TypeExpression::Integer(val.clone())),
273        ast::TypeExpression::String(val) => Ok(hir::TypeExpression::String(val.clone())),
274        ast::TypeExpression::ConstGeneric(expr) => {
275            let default_error = |message, primary| {
276                Err(Diagnostic::error(
277                    expr.as_ref(),
278                    format!("{message} cannot have const generics in their type"),
279                )
280                .primary_label(format!("Const generic in {primary}")))
281            };
282            match kind {
283                TypeSpecKind::ImplTrait => default_error("Implemented traits", "implemented trait"),
284                TypeSpecKind::ImplTarget => default_error("Impl targets", "impl target"),
285                TypeSpecKind::EnumMember => default_error("Enum members", "enum member"),
286                TypeSpecKind::StructMember => default_error("Struct members", "struct member"),
287                TypeSpecKind::TraitBound => {
288                    default_error("Traits used in trait bounds", "trait bound")
289                }
290                TypeSpecKind::Argument
291                | TypeSpecKind::OutputType
292                | TypeSpecKind::Turbofish
293                | TypeSpecKind::TypeLevelIf
294                | TypeSpecKind::BindingType
295                | TypeSpecKind::PipelineInstDepth
296                | TypeSpecKind::PipelineRegCount
297                | TypeSpecKind::PipelineHeadDepth => {
298                    visit_const_generic(expr.as_ref(), ctx).map(hir::TypeExpression::ConstGeneric)
299                }
300            }
301        }
302    }
303}
304
305pub fn visit_type_spec(
306    t: &Loc<ast::TypeSpec>,
307    kind: &TypeSpecKind,
308    ctx: &mut Context,
309) -> Result<Loc<hir::TypeSpec>> {
310    let trait_loc = if let SelfContext::TraitDefinition(TraitName::Named(name)) = &ctx.self_ctx {
311        name.loc()
312    } else {
313        ().nowhere()
314    };
315
316    if matches!(ctx.self_ctx, SelfContext::TraitDefinition(_)) && t.is_self()? {
317        return Ok(hir::TypeSpec::TraitSelf(().at_loc(&trait_loc)).at_loc(t));
318    };
319
320    let result = match &t.inner {
321        ast::TypeSpec::Named(path, params) => {
322            // Lookup the referenced type
323            let (base_id, base_t) = ctx.symtab.lookup_type_symbol(path)?;
324
325            // Check if the type is a declared type or a generic argument.
326            match &base_t.inner {
327                TypeSymbol::Declared(generic_args, _) => {
328                    // We'll defer checking the validity of generic args to the type checker,
329                    // but we still have to visit them now
330                    let visited_params = params
331                        // We can't flatten "through" Option<Loc<Vec<...>>>, so map it away.
332                        .as_ref()
333                        .map(|o| &o.inner)
334                        .into_iter()
335                        .flatten()
336                        .map(|p| p.try_map_ref(|p| visit_type_expression(p, kind, ctx)))
337                        .collect::<Result<Vec<_>>>()?;
338
339                    if generic_args.len() != visited_params.len() {
340                        Err(Diagnostic::error(
341                            params
342                                .as_ref()
343                                .map(|p| ().at_loc(p))
344                                .unwrap_or(().at_loc(t)),
345                            "Wrong number of generic type parameters",
346                        )
347                        .primary_label(format!(
348                            "Expected {} type parameter{}",
349                            generic_args.len(),
350                            if generic_args.len() != 1 { "s" } else { "" }
351                        ))
352                        .secondary_label(
353                            if !generic_args.is_empty() {
354                                ().between_locs(
355                                    &generic_args[0],
356                                    &generic_args[generic_args.len() - 1],
357                                )
358                            } else {
359                                ().at_loc(&base_t)
360                            },
361                            format!(
362                                "Because this has {} type parameter{}",
363                                generic_args.len(),
364                                if generic_args.len() != 1 { "s" } else { "" }
365                            ),
366                        ))
367                    } else {
368                        Ok(hir::TypeSpec::Declared(
369                            base_id.at_loc(path),
370                            visited_params,
371                        ))
372                    }
373                }
374                TypeSymbol::GenericArg { traits: _ } | TypeSymbol::GenericMeta(_) => {
375                    // If this typename refers to a generic argument we need to make
376                    // sure that no generic arguments are passed, as generic names
377                    // can't have generic parameters
378
379                    if let Some(params) = params {
380                        Err(
381                            Diagnostic::error(params, "Generic arguments given for a generic type")
382                                .primary_label("Generic arguments not allowed here")
383                                .secondary_label(base_t, format!("{path} is a generic type")),
384                        )
385                    } else {
386                        Ok(hir::TypeSpec::Generic(base_id.at_loc(path)))
387                    }
388                }
389                TypeSymbol::Alias(expr) => Ok(expr.inner.clone()),
390            }
391        }
392        ast::TypeSpec::Array { inner, size } => {
393            let inner = match visit_type_expression(inner, kind, ctx)? {
394                hir::TypeExpression::TypeSpec(t) => Box::new(t.at_loc(inner)),
395                _ => {
396                    return Err(Diagnostic::error(
397                        inner.as_ref(),
398                        "Arrays elements must be types, not type level integers",
399                    )
400                    .primary_label("Non-type array element"))
401                }
402            };
403            let size = Box::new(visit_type_expression(size, kind, ctx)?.at_loc(size));
404
405            Ok(hir::TypeSpec::Array { inner, size })
406        }
407        ast::TypeSpec::Tuple(inner) => {
408            let inner = inner
409                .iter()
410                .map(|p| match visit_type_expression(p, kind, ctx)? {
411                    hir::TypeExpression::TypeSpec(t) => match &t {
412                        TypeSpec::Tuple(_)
413                        | TypeSpec::Array { inner: _, size: _ }
414                        | TypeSpec::Inverted(_)
415                        | TypeSpec::Wire(_)
416                        | TypeSpec::TraitSelf(_)
417                        | TypeSpec::Wildcard(_)
418                        | TypeSpec::Declared(_, _) => Ok(t.at_loc(p)),
419                        TypeSpec::Generic(name) => {
420                            let inner = ctx.symtab.type_symbol_by_id(&name.inner);
421                            match &inner.inner {
422                                TypeSymbol::Declared(_, _)
423                                | TypeSymbol::GenericArg { traits: _ }
424                                | TypeSymbol::GenericMeta(MetaType::Type) => Ok(t.at_loc(p)),
425                                | TypeSymbol::GenericMeta(other_meta) => {
426                                    return Err(Diagnostic::error(name, format!("Tuple members can only be types, found {other_meta}"))
427                                    .primary_label(format!("Expected type, found {other_meta}"))
428                                    .secondary_label(&inner, format!("{name} is defined as {other_meta} here")))
429                                }
430                                TypeSymbol::Alias(_) => {
431                                    return Err(Diagnostic::bug(p, "Aliases in tuple types are currently unsupported"));
432                                },
433                            }
434                        }
435                    },
436                    _ => {
437                        return Err(Diagnostic::error(
438                            p,
439                            "Tuple elements must be types, not type level integers",
440                        )
441                        .primary_label("Tuples cannot contain non-types"))
442                    }
443                })
444                .collect::<Result<Vec<_>>>()?;
445
446            // Check if this tuple is a port by checking if any of the contained types
447            // are ports. If they are, retain the first one to use as a witness for this fact
448            // for error reporting
449            let transitive_port_witness = inner
450                .iter()
451                .map(|p| {
452                    if p.is_port(ctx)? {
453                        Ok(Some(p))
454                    } else {
455                        Ok(None)
456                    }
457                })
458                .collect::<Result<Vec<_>>>()?
459                .into_iter()
460                .find_map(|x| x);
461
462            if let Some(witness) = transitive_port_witness {
463                // Since this type has 1 port, all members must be ports
464                for ty in &inner {
465                    if !ty.is_port(ctx)? {
466                        return Err(Diagnostic::error(
467                            ty,
468                            "Cannot mix ports and non-ports in a tuple",
469                        )
470                        .primary_label("This is not a port")
471                        .secondary_label(witness, "This is a port")
472                        .note("A tuple must either contain only ports or no ports"));
473                    }
474                }
475            }
476
477            Ok(hir::TypeSpec::Tuple(inner))
478        }
479        ast::TypeSpec::Wire(inner) => {
480            let inner = match visit_type_expression(inner, kind, ctx)? {
481                hir::TypeExpression::TypeSpec(t) => t.at_loc(inner),
482                _ => {
483                    return Err(Diagnostic::error(
484                        inner.as_ref(),
485                        "Wire inner types must be types, not type level integers",
486                    )
487                    .primary_label("Wires cannot contain non-types"))
488                }
489            };
490
491            if inner.is_port(&ctx)? {
492                return Err(Diagnostic::from(error::WireOfPort {
493                    full_type: t.loc(),
494                    inner_type: inner.loc(),
495                }));
496            }
497
498            if inner.is_inout(&ctx)? {
499                return Err(Diagnostic::from(error::WireOfInOut {
500                    full_type: t.loc(),
501                    inner_type: inner.loc(),
502                }));
503            }
504
505            Ok(hir::TypeSpec::Wire(Box::new(inner)))
506        }
507        ast::TypeSpec::Inverted(inner) => {
508            let inner = match visit_type_expression(inner, kind, ctx)? {
509                hir::TypeExpression::TypeSpec(t) => t.at_loc(inner),
510                _ => {
511                    return Err(Diagnostic::error(
512                        inner.as_ref(),
513                        "Inverted inner types must be types, not type level integers",
514                    )
515                    .primary_label("Non-type cannot be inverted"))
516                }
517            };
518
519            if !inner.is_port(ctx)? {
520                Err(Diagnostic::error(t, "A non-port type can not be inverted")
521                    .primary_label("Inverting non-port")
522                    .secondary_label(inner, "This is not a port"))
523            } else {
524                Ok(hir::TypeSpec::Inverted(Box::new(inner)))
525            }
526        }
527        ast::TypeSpec::Wildcard => {
528            let default_error = |message, primary| {
529                Err(
530                    Diagnostic::error(t, format!("{message} cannot have wildcards in their type"))
531                        .primary_label(format!("Wildcard in {primary}")),
532                )
533            };
534            match kind {
535                TypeSpecKind::Argument => default_error("Argument types", "argument type"),
536                TypeSpecKind::OutputType => default_error("Return types", "return type"),
537                TypeSpecKind::ImplTrait => default_error("Implemented traits", "implemented trait"),
538                TypeSpecKind::ImplTarget => default_error("Impl targets", "impl target"),
539                TypeSpecKind::EnumMember => default_error("Enum members", "enum member"),
540                TypeSpecKind::StructMember => default_error("Struct members", "struct member"),
541                TypeSpecKind::PipelineHeadDepth => {
542                    default_error("Pipeline depths", "pipeline depth")
543                }
544                TypeSpecKind::PipelineRegCount => {
545                    default_error("Register counts", "register count")
546                }
547                TypeSpecKind::TraitBound => {
548                    default_error("Traits used in trait bound", "trait bound")
549                }
550                TypeSpecKind::PipelineInstDepth
551                | TypeSpecKind::TypeLevelIf
552                | TypeSpecKind::Turbofish
553                | TypeSpecKind::BindingType => Ok(hir::TypeSpec::Wildcard(t.loc())),
554            }
555        }
556    };
557
558    Ok(result?.at_loc(t))
559}
560
561#[derive(Debug, Clone, PartialEq)]
562pub enum SelfContext {
563    /// `self` currently does not refer to anything
564    FreeStanding,
565    /// `self` refers to `TypeSpec` in an impl block for that type
566    ImplBlock(Loc<hir::TypeSpec>),
567    /// `self` refers to a trait implementor
568    TraitDefinition(TraitName),
569}
570
571fn visit_parameter_list(
572    l: &Loc<ParameterList>,
573    ctx: &mut Context,
574    no_mangle_all: Option<Loc<()>>,
575) -> Result<Loc<hir::ParameterList>> {
576    let mut arg_names: HashSet<Loc<Identifier>> = HashSet::default();
577    let mut result = vec![];
578
579    if let SelfContext::ImplBlock(_) = ctx.self_ctx {
580        if l.self_.is_none() {
581            // Suggest insertion after the first paren
582            let mut diag = Diagnostic::error(l, "Method must take 'self' as the first parameter")
583                .primary_label("Missing self");
584
585            let suggest_msg = "Consider adding self";
586            diag = if l.args.is_empty() {
587                diag.span_suggest_replace(suggest_msg, l, "(self)")
588            } else {
589                diag.span_suggest_insert_before(suggest_msg, &l.args[0].1, "self, ")
590            };
591            return Err(diag);
592        }
593    }
594
595    if let Some(ref self_) = l.self_ {
596        let mut attrs = self_.inner.clone();
597        let no_mangle = attrs
598            .consume_no_mangle()
599            .map(|ident| ident.loc())
600            .or(no_mangle_all);
601        attrs.report_unused("`self` parameter")?;
602
603        match &ctx.self_ctx {
604            SelfContext::FreeStanding => {
605                return Err(Diagnostic::error(
606                    self_,
607                    "'self' cannot be used in free standing units",
608                )
609                .primary_label("not allowed here"))
610            }
611            SelfContext::ImplBlock(spec) => result.push(hir::Parameter {
612                no_mangle,
613                name: Identifier::intern("self").at_loc(self_),
614                ty: spec.clone(),
615                field_translator: None,
616            }),
617            // When visiting trait definitions, we don't need to add self to the
618            // symtab at all since we won't be visiting unit bodies here.
619            // NOTE: This will be incorrect if we add default impls for traits
620            SelfContext::TraitDefinition(_) => result.push(hir::Parameter {
621                no_mangle,
622                name: Identifier::intern("self").at_loc(self_),
623                ty: hir::TypeSpec::TraitSelf(self_.loc()).at_loc(self_),
624                field_translator: None,
625            }),
626        }
627    }
628
629    for (attrs, name, input_type) in &l.args {
630        if let Some(prev) = arg_names.get(name) {
631            return Err(
632                Diagnostic::error(name, "Multiple arguments with the same name")
633                    .primary_label(format!("{name} later declared here"))
634                    .secondary_label(prev, format!("{name} previously declared here")),
635            );
636        }
637        arg_names.insert(name.clone());
638        let t = visit_type_spec(input_type, &TypeSpecKind::Argument, ctx)?;
639
640        let mut attrs = attrs.clone();
641        let no_mangle = attrs
642            .consume_no_mangle()
643            .map(|ident| ident.loc())
644            .or(no_mangle_all);
645        let field_translator = attrs.consume_translator();
646        attrs.report_unused("a parameter")?;
647
648        result.push(hir::Parameter {
649            name: name.clone(),
650            ty: t,
651            no_mangle,
652            field_translator,
653        });
654    }
655    Ok(hir::ParameterList(result).at_loc(l))
656}
657
658/// Builds a diagnostic for a `#[no_mangle(all)]`-marked unit with a non-unit output type.
659fn build_no_mangle_all_output_diagnostic(
660    head: &ast::UnitHead,
661    output_type: &Loc<TypeSpec>,
662    body_for_diagnostics: Option<&Loc<Expression>>,
663) -> Diagnostic {
664    let suffix_length = head
665        .inputs
666        .args
667        .iter()
668        .filter_map(|(_, name, _)| {
669            if name.inner.as_str().contains("out") {
670                Some(name.inner.as_str().len())
671            } else {
672                None
673            }
674        })
675        .max()
676        .unwrap_or(2)
677        - 2;
678    let suggested_name = format!("out{}", "_".repeat(suffix_length));
679    let output_is_wire = output_type.to_string().starts_with("&");
680    let suggested_type = format!(
681        "inv {}{}",
682        if output_is_wire {
683            // ports shouldn't have duplicate & (thanks Astrid)
684            ""
685        } else {
686            "&"
687        },
688        output_type
689    );
690
691    let mut diagnostic = Diagnostic::error(output_type, "Cannot apply `#[no_mangle(all)]`")
692        .primary_label("Output types are always mangled");
693
694    let output_arrow_span = &head.output_type.as_ref().unwrap().0.span;
695    let output_type_full_span: FullSpan = output_type.into();
696    let mut first_suggestion = SuggestionParts::new().part(
697        (
698            Span::new(output_arrow_span.start(), output_type_full_span.0.end()),
699            output_type_full_span.1,
700        ),
701        "",
702    );
703    if head.inputs.args.is_empty() {
704        let (span, file) = (head.inputs.span, head.inputs.file_id);
705        first_suggestion = first_suggestion.part(
706            (span, file),
707            format!("({}: {})", suggested_name, suggested_type),
708        );
709    } else {
710        let last_parameter = &head.inputs.args.last().unwrap().2;
711        let (span, file) = (last_parameter.span, last_parameter.file_id);
712        first_suggestion.push_part(
713            (Span::new(span.end(), span.end()), file),
714            format!(", {}: {}", suggested_name, suggested_type),
715        );
716    }
717
718    diagnostic.push_span_suggest_multipart(
719        "Consider replacing the output with an inverted input",
720        first_suggestion,
721    );
722
723    // add the set suggestion for non-externs
724    if let Some(block) = body_for_diagnostics {
725        let block = block.assume_block();
726        if let Some(result) = block.result.as_ref() {
727            if output_is_wire {
728                diagnostic = diagnostic.span_suggest_remove("Remember to `set` the inverted input instead of ending the block with an output", result);
729            } else {
730                let (span, file) = (result.span, result.file_id);
731                diagnostic.push_span_suggest_multipart(
732                    "...and `set` the inverted input to the return value",
733                    SuggestionParts::new()
734                        .part(
735                            (Span::new(span.start(), span.start()), file),
736                            format!("set {} = &", suggested_name),
737                        )
738                        .part((Span::new(span.end(), span.end()), file), ";"),
739                );
740            }
741        }
742    }
743
744    diagnostic
745}
746
747pub fn visit_unit_kind(kind: &ast::UnitKind, ctx: &mut Context) -> Result<hir::UnitKind> {
748    let inner = match kind {
749        ast::UnitKind::Function => hir::UnitKind::Function(hir::FunctionKind::Fn),
750        ast::UnitKind::Entity => hir::UnitKind::Entity,
751        ast::UnitKind::Pipeline(depth) => hir::UnitKind::Pipeline {
752            depth: depth
753                .try_map_ref(|t| visit_type_expression(t, &TypeSpecKind::PipelineHeadDepth, ctx))?,
754            depth_typeexpr_id: ctx.idtracker.next(),
755        },
756    };
757    Ok(inner)
758}
759
760/// Visit the head of an entity to generate an entity head
761#[tracing::instrument(skip_all, fields(name=%head.name))]
762pub fn unit_head(
763    head: &ast::UnitHead,
764    scope_type_params: &Option<Loc<Vec<Loc<TypeParam>>>>,
765    scope_where_clauses: &[Loc<hir::WhereClause>],
766    ctx: &mut Context,
767    body_for_diagnostics: Option<&Loc<Expression>>,
768) -> Result<hir::UnitHead> {
769    ctx.symtab.new_scope();
770
771    let scope_type_params = scope_type_params
772        .as_ref()
773        .map(Loc::strip_ref)
774        .into_iter()
775        .flatten()
776        .map(|loc| loc.try_map_ref(|p| re_visit_type_param(p, ctx)))
777        .collect::<Result<Vec<Loc<hir::TypeParam>>>>()?;
778
779    let unit_type_params = head
780        .type_params
781        .as_ref()
782        .map(Loc::strip_ref)
783        .into_iter()
784        .flatten()
785        .map(|loc| loc.try_map_ref(|p| visit_type_param(p, ctx)))
786        .collect::<Result<Vec<Loc<hir::TypeParam>>>>()?;
787
788    let unit_where_clauses = visit_where_clauses(&head.where_clauses, ctx);
789
790    let output_type = if let Some(output_type) = &head.output_type {
791        Some(visit_type_spec(
792            &output_type.1,
793            &TypeSpecKind::OutputType,
794            ctx,
795        )?)
796    } else {
797        None
798    };
799
800    let no_mangle_all = head
801        .attributes
802        .0
803        .iter()
804        .find(|attribute| matches!(attribute.inner, Attribute::NoMangle { all: true }))
805        .map(|attribute| ().at_loc(attribute));
806
807    // we only care if we're trying to no_mangle_all a type with a non-unit output type
808    if no_mangle_all.is_some()
809        && output_type
810            .as_ref()
811            .map(|output_type| {
812                !(matches!(&**output_type, TypeSpec::Tuple(inner) if inner.is_empty()))
813            })
814            .unwrap_or(false)
815    {
816        return Err(build_no_mangle_all_output_diagnostic(
817            head,
818            output_type.as_ref().unwrap(),
819            body_for_diagnostics,
820        ));
821    }
822
823    let inputs = visit_parameter_list(&head.inputs, ctx, no_mangle_all)?;
824
825    // Check for ports in functions
826    // We need to have the scope open to check this, but we also need to close
827    // the scope if we fail here, so we'll store port_error in a variable
828
829    let unit_kind: Result<_> = head.unit_kind.try_map_ref(|k| visit_unit_kind(k, ctx));
830
831    ctx.symtab.close_scope();
832    let where_clauses = unit_where_clauses?
833        .iter()
834        .chain(scope_where_clauses.iter())
835        .cloned()
836        .collect();
837
838    Ok(hir::UnitHead {
839        name: head.name.clone(),
840        is_nonstatic_method: head.inputs.self_.is_some(),
841        inputs,
842        output_type,
843        unit_type_params,
844        scope_type_params,
845        unit_kind: unit_kind?,
846        where_clauses,
847        unsafe_marker: head.unsafe_token,
848        documentation: head.attributes.merge_docs(),
849    })
850}
851
852pub fn visit_const_generic(
853    t: &Loc<ast::Expression>,
854    ctx: &mut Context,
855) -> Result<Loc<ConstGeneric>> {
856    let kind = match &t.inner {
857        ast::Expression::Identifier(name) => {
858            let (name, sym) = ctx.symtab.lookup_type_symbol(name)?;
859            match &sym.inner {
860                TypeSymbol::Declared(_, _) => {
861                    return Err(Diagnostic::error(t, format!(
862                            "{name} is not a type level integer but is used in a const generic expression."
863                        ),
864                    )
865                        .primary_label(format!("Expected type level integer"))
866                    .secondary_label(&sym, format!("{name} is defined here")))
867                }
868                TypeSymbol::GenericArg { traits: _ }=> {
869                    return Err(Diagnostic::error(
870                        t,
871                        format!(
872                            "{name} is not a type level integer but is used in a const generic expression."
873                        ))
874                        .primary_label("Expected type level integer")
875                        .secondary_label(&sym, format!("{name} is defined here"))
876                        .span_suggest_insert_before(
877                            "Try making the generic an integer",
878                        &sym,
879                        "#int ",
880                    )
881                    .span_suggest_insert_before(
882                        "or an unsigned integer",
883                            &sym,
884                            "#uint ",
885                        ))
886                }
887                TypeSymbol::GenericMeta(_) => {
888                    ConstGeneric::Name(name.at_loc(t))
889                },
890                TypeSymbol::Alias(a) => {
891                    return Err(Diagnostic::error(t, "Type aliases are not supported in const generics").primary_label("Type alias in const generic")
892                    .secondary_label(a, "Alias defined here"))
893                }
894            }
895        }
896        ast::Expression::IntLiteral(val) => ConstGeneric::Int(val.inner.clone().as_signed()),
897        ast::Expression::StrLiteral(val) => ConstGeneric::Str(val.inner.clone()),
898        ast::Expression::BinaryOperator(lhs, op, rhs) => {
899            let lhs = visit_const_generic(lhs, ctx)?;
900            let rhs = visit_const_generic(rhs, ctx)?;
901
902            match &op.inner {
903                ast::BinaryOperator::Add => ConstGeneric::Add(Box::new(lhs), Box::new(rhs)),
904                ast::BinaryOperator::Sub => ConstGeneric::Sub(Box::new(lhs), Box::new(rhs)),
905                ast::BinaryOperator::Mul => ConstGeneric::Mul(Box::new(lhs), Box::new(rhs)),
906                ast::BinaryOperator::Equals => ConstGeneric::Eq(Box::new(lhs), Box::new(rhs)),
907                ast::BinaryOperator::NotEquals => ConstGeneric::NotEq(Box::new(lhs), Box::new(rhs)),
908                ast::BinaryOperator::Div => ConstGeneric::Div(Box::new(lhs), Box::new(rhs)),
909                ast::BinaryOperator::Mod => ConstGeneric::Mod(Box::new(lhs), Box::new(rhs)),
910                other => {
911                    return Err(Diagnostic::error(
912                        op,
913                        format!("Operator `{other}` is not supported in a type expression"),
914                    )
915                    .primary_label("Not supported in a type expression"))
916                }
917            }
918        }
919        ast::Expression::UnaryOperator(op, operand) => {
920            let operand = visit_const_generic(operand, ctx)?;
921
922            match &op.inner {
923                ast::UnaryOperator::Sub => ConstGeneric::Sub(
924                    Box::new(ConstGeneric::Int(BigInt::zero()).at_loc(&operand)),
925                    Box::new(operand),
926                ),
927                other => {
928                    return Err(Diagnostic::error(
929                        t,
930                        format!("Operator `{other}` is not supported in a type expression"),
931                    )
932                    .primary_label("Not supported in a type expression"))
933                }
934            }
935        }
936        ast::Expression::Call {
937            kind: CallKind::Function,
938            callee,
939            args,
940            turbofish: None,
941        } => match callee.to_named_strs().as_slice() {
942            [Some("uint_bits_to_fit")] => match &args.inner {
943                ast::ArgumentList::Positional(a) => {
944                    if a.len() != 1 {
945                        return Err(Diagnostic::error(
946                            args,
947                            format!("This function takes one argument, {} provided", a.len()),
948                        )
949                        .primary_label("Expected 1 argument"));
950                    } else {
951                        let arg = visit_const_generic(&a[0], ctx)?;
952
953                        ConstGeneric::UintBitsToFit(Box::new(arg))
954                    }
955                }
956                ast::ArgumentList::Named(_) => {
957                    return Err(Diagnostic::error(
958                        t,
959                        "Passing arguments by name is unsupported in type expressions",
960                    )
961                    .primary_label("Arguments passed by name in type expression"))
962                }
963            },
964            _ => {
965                return Err(Diagnostic::error(
966                    callee,
967                    format!("{callee} cannot be evaluated in a type expression"),
968                )
969                .primary_label("Not supported in a type expression"))
970            }
971        },
972        ast::Expression::Parenthesized(inner) => visit_const_generic(inner, ctx)?.inner,
973        _ => {
974            return Err(Diagnostic::error(
975                t,
976                format!("This expression is not supported in a type expression"),
977            )
978            .primary_label("Not supported in a type expression"))
979        }
980    };
981
982    Ok(kind.at_loc(t))
983}
984
985pub fn visit_where_clauses(
986    where_clauses: &[WhereClause],
987    ctx: &mut Context,
988) -> Result<Vec<Loc<hir::WhereClause>>> {
989    let mut visited_where_clauses: Vec<Loc<hir::WhereClause>> = vec![];
990    for where_clause in where_clauses {
991        match where_clause {
992            WhereClause::GenericInt {
993                target,
994                kind,
995                expression,
996                if_unsatisfied,
997            } => {
998                let make_diag = |primary| {
999                    Diagnostic::error(
1000                        target,
1001                        format!("Expected `{}` to be a type level integer", target),
1002                    )
1003                    .primary_label(primary)
1004                    .secondary_label(expression, "This is an integer constraint")
1005                };
1006                let (name_id, sym) = match ctx.symtab.lookup_type_symbol(target) {
1007                    Ok(res) => res,
1008                    Err(LookupError::NotATypeSymbol(_, thing)) => {
1009                        return Err(make_diag(format!("`{target}` is not a type level integer"))
1010                            .secondary_label(
1011                                thing.loc(),
1012                                format!("`{}` is a {} declared here", target, thing.kind_string()),
1013                            ))
1014                    }
1015                    Err(e) => return Err(e.into()),
1016                };
1017                match &sym.inner {
1018                    TypeSymbol::GenericMeta(_) => {
1019                        let clause = hir::WhereClause::Int {
1020                            target: name_id.at_loc(target),
1021                            kind: match kind {
1022                                ast::Inequality::Eq => hir::WhereClauseKind::Eq,
1023                                ast::Inequality::Neq => hir::WhereClauseKind::Neq,
1024                                ast::Inequality::Lt => hir::WhereClauseKind::Lt,
1025                                ast::Inequality::Leq => hir::WhereClauseKind::Leq,
1026                                ast::Inequality::Gt => hir::WhereClauseKind::Gt,
1027                                ast::Inequality::Geq => hir::WhereClauseKind::Geq,
1028                            },
1029                            constraint: visit_const_generic(expression, ctx)?,
1030                            if_unsatisfied: if_unsatisfied.clone()
1031                        }
1032                        .between_locs(target, expression);
1033
1034                        visited_where_clauses.push(clause);
1035                    }
1036                    TypeSymbol::GenericArg { .. } => {
1037                        return Err(
1038                            make_diag("Generic type in generic integer constraint".into())
1039                                .secondary_label(
1040                                    sym.clone(),
1041                                    format!("`{target}` is a generic type"),
1042                                )
1043                                .span_suggest_insert_before(
1044                                    "Try making the generic an integer",
1045                                    &sym,
1046                                    "#int ",
1047                                )
1048                                .span_suggest_insert_before(
1049                                    "or an unsigned integer",
1050                                    &sym,
1051                                    "#uint ",
1052                                ),
1053                        );
1054                    }
1055                    TypeSymbol::Declared { .. } => {
1056                        return Err(make_diag(
1057                            "Declared type in generic integer constraint".into(),
1058                        )
1059                        .secondary_label(sym, format!("`{target}` is a type declared here")));
1060                    }
1061                    TypeSymbol::Alias(a) => {
1062                        return Err(Diagnostic::error(
1063                                &sym, "Type aliases are not supported in where clauses"
1064                            )
1065                            .primary_label("Type alias in where clause")
1066                            .secondary_label(a, "Alias defined here")
1067                            .note(
1068                                "This is a soft limitation in the compiler. If you need this feature, open an issue at https://gitlab.com/spade-lang/spade/"
1069                            )
1070                        )
1071                    }
1072                }
1073            }
1074            WhereClause::TraitBounds { target, traits } => {
1075                let make_diag = |primary| {
1076                    Diagnostic::error(
1077                        target,
1078                        format!("Expected `{}` to be a generic type", target),
1079                    )
1080                    .primary_label(primary)
1081                };
1082                let (name_id, sym) = match ctx.symtab.lookup_type_symbol(where_clause.target()) {
1083                    Ok(res) => res,
1084                    Err(LookupError::NotATypeSymbol(path, thing)) => {
1085                        return Err(make_diag(format!("`{target}` is not a type symbol"))
1086                            .secondary_label(
1087                                path,
1088                                format!("`{}` is {} declared here", target, thing.kind_string()),
1089                            ));
1090                    }
1091                    Err(e) => return Err(e.into()),
1092                };
1093                match &sym.inner {
1094                    TypeSymbol::GenericArg { .. } | TypeSymbol::GenericMeta(MetaType::Type) => {
1095                        let traits = traits
1096                            .iter()
1097                            .map(|t| visit_trait_spec(t, &TypeSpecKind::TraitBound, ctx))
1098                            .collect::<Result<Vec<_>>>()?;
1099
1100                        ctx.symtab.add_traits_to_generic(&name_id, traits.clone())?;
1101
1102                        visited_where_clauses.push(
1103                            hir::WhereClause::Type {
1104                                target: name_id.at_loc(target),
1105                                traits,
1106                            }
1107                            .at_loc(target),
1108                        );
1109                    }
1110                    TypeSymbol::GenericMeta(_) => {
1111                        return Err(make_diag("Generic int in trait bound".into())
1112                            .secondary_label(sym, format!("{target} is a generic int")));
1113                    }
1114                    TypeSymbol::Declared { .. } => {
1115                        return Err(make_diag("Declared type in trait bound".into())
1116                            .secondary_label(sym, format!("{target} is a type declared here")));
1117                    }
1118                    TypeSymbol::Alias(a) => {
1119                        return Err(Diagnostic::error(
1120                                &sym, "Type aliases are not supported in where clauses"
1121                            )
1122                            .primary_label("Type alias in where clause")
1123                            .secondary_label(a, "Alias defined here")
1124                            .note(
1125                                "This is a soft limitation in the compiler. If you need this feature, open an issue at https://gitlab.com/spade-lang/spade/"
1126                            )
1127                        )
1128                    }
1129                };
1130            }
1131        }
1132    }
1133    Ok(visited_where_clauses)
1134}
1135
1136/// The `extra_path` parameter allows specifying an extra path prepended to
1137/// the name of the entity. This is used by impl blocks to append a unique namespace
1138#[tracing::instrument(skip_all, fields(%unit.head.name, %unit.head.unit_kind))]
1139pub fn visit_unit(
1140    extra_path: Option<Path>,
1141    unit: &Loc<ast::Unit>,
1142    scope_type_params: &Option<Loc<Vec<Loc<ast::TypeParam>>>>,
1143    ctx: &mut Context,
1144) -> Result<hir::Item> {
1145    let ast::Unit {
1146        head:
1147            ast::UnitHead {
1148                visibility: _,
1149                unsafe_token: _,
1150                extern_token: _,
1151                name,
1152                attributes,
1153                inputs: _,
1154                output_type: _,
1155                unit_kind: _,
1156                type_params,
1157                where_clauses: _,
1158            },
1159        body,
1160    } = &unit.inner;
1161
1162    ctx.symtab.new_scope();
1163
1164    let path = extra_path
1165        .unwrap_or(Path(vec![]))
1166        .join(Path::ident(name.clone()))
1167        .at_loc(&name.loc());
1168
1169    let (id, head) = ctx
1170        .symtab
1171        .lookup_unit_ignore_visibility(&path)
1172        .map_err(|_| {
1173            diag_anyhow!(
1174                path,
1175                "Attempting to lower an entity that has not been added to the symtab previously"
1176            )
1177        })?;
1178
1179    ctx.current_unit = Some(head.inner.clone());
1180
1181    let mut unit_name = if type_params.is_some() || scope_type_params.is_some() {
1182        hir::UnitName::WithID(id.clone().at_loc(name))
1183    } else {
1184        hir::UnitName::FullPath(id.clone().at_loc(name))
1185    };
1186
1187    let mut wal_suffix = None;
1188
1189    let attributes = attributes.lower(&mut |attr: &Loc<ast::Attribute>| match &attr.inner {
1190        ast::Attribute::Optimize { passes } => Ok(Some(hir::Attribute::Optimize {
1191            passes: passes.clone(),
1192        })),
1193        ast::Attribute::NoMangle { .. } => {
1194            if let Some(generic_list) = type_params {
1195                // if it's a verilog extern (so `body.is_none()`), then we allow generics insofar
1196                // as they are numbers or strings (checked later on)
1197                if body.is_some() {
1198                    Err(
1199                        Diagnostic::error(attr, "no_mangle is not allowed on generic units")
1200                            .primary_label("no_mangle not allowed here")
1201                            .secondary_label(generic_list, "Because this unit is generic"),
1202                    )
1203                } else {
1204                    // yucky code duplication
1205                    unit_name = hir::UnitName::Unmangled(
1206                        name.inner.as_str().to_owned(),
1207                        id.clone().at_loc(name),
1208                    );
1209                    Ok(None)
1210                }
1211            } else if let Some(generic_list) = scope_type_params {
1212                Err(Diagnostic::error(
1213                    attr,
1214                    "no_mangle is not allowed on units inside generic impls",
1215                )
1216                .primary_label("no_mangle not allowed here")
1217                .secondary_label(generic_list, "Because this impl is generic"))
1218            } else {
1219                unit_name = hir::UnitName::Unmangled(
1220                    name.inner.as_str().to_owned(),
1221                    id.clone().at_loc(name),
1222                );
1223                Ok(None)
1224            }
1225        }
1226        ast::Attribute::WalSuffix { suffix } => {
1227            if body.is_none() {
1228                return Err(
1229                    Diagnostic::error(attr, "wal_suffix is not allowed on `extern` units")
1230                        .primary_label("Not allowed on `extern` units")
1231                        .secondary_label(unit.head.extern_token.unwrap(), "This unit is `extern`"),
1232                );
1233            }
1234
1235            wal_suffix = Some(suffix.clone());
1236            Ok(None)
1237        }
1238        ast::Attribute::VerilogAttrs { entries } => Ok(Some(hir::Attribute::VerilogAttrs {
1239            entries: entries.clone(),
1240        })),
1241        ast::Attribute::Documentation { .. } => Ok(None),
1242        _ => Err(attr.report_unused("a unit")),
1243    })?;
1244
1245    // If this is a extern entity
1246    if body.is_none() {
1247        ctx.symtab.close_scope();
1248        return Ok(hir::Item::ExternUnit(unit_name, head));
1249    }
1250
1251    // Add the inputs to the symtab
1252    let inputs = head
1253        .inputs
1254        .0
1255        .iter()
1256        .map(
1257            |hir::Parameter {
1258                 name: ident,
1259                 ty,
1260                 no_mangle: _,
1261                 field_translator: _,
1262             }| {
1263                (
1264                    ctx.symtab.add_local_variable(ident.clone()).at_loc(ident),
1265                    ty.clone(),
1266                )
1267            },
1268        )
1269        .collect::<Vec<_>>();
1270
1271    // Add the type params to the symtab to make them visible in the body
1272    for param in head.get_type_params() {
1273        let hir::TypeParam {
1274            ident,
1275            name_id,
1276            trait_bounds: _,
1277            meta: _,
1278        } = param.inner;
1279        ctx.symtab.re_add_type(ident, name_id)
1280    }
1281
1282    ctx.pipeline_ctx = maybe_perform_pipelining_tasks(unit, &head, ctx)?;
1283
1284    let mut body = body
1285        .as_ref()
1286        .unwrap()
1287        .map_ref(|body| visit_expression(&body, ctx));
1288
1289    // Add wal_suffixes for the signals if requested. This creates new statements
1290    // which we'll add to the end of the body
1291    if let Some(suffix) = wal_suffix {
1292        match &mut body.kind {
1293            hir::ExprKind::Block(block) => {
1294                block.statements.append(
1295                    &mut inputs
1296                        .iter()
1297                        .map(|(name, _)| {
1298                            hir::Statement::WalSuffixed {
1299                                suffix: suffix.inner.clone(),
1300                                target: name.clone(),
1301                            }
1302                            .at_loc(&suffix)
1303                        })
1304                        .collect(),
1305                );
1306            }
1307            _ => diag_bail!(body, "Unit body was not block"),
1308        }
1309    }
1310
1311    ctx.symtab.close_scope();
1312    ctx.current_unit = None;
1313
1314    Ok(hir::Item::Unit(expand_type_level_if(
1315        hir::Unit {
1316            name: unit_name,
1317            head: head.clone().inner,
1318            attributes,
1319            inputs,
1320            body,
1321        }
1322        .at_loc(unit),
1323        ctx,
1324    )?))
1325}
1326
1327#[tracing::instrument(skip_all, fields(name=?trait_spec.path))]
1328pub fn visit_trait_spec(
1329    trait_spec: &Loc<ast::TraitSpec>,
1330    type_spec_kind: &TypeSpecKind,
1331    ctx: &mut Context,
1332) -> Result<Loc<hir::TraitSpec>> {
1333    let (name_id, loc) = match ctx.symtab.lookup_trait(&trait_spec.inner.path) {
1334        Ok(res) => res,
1335        Err(LookupError::IsAType(path, loc)) => {
1336            return Err(Diagnostic::error(
1337                path.clone(),
1338                format!("Unexpected type {}, expected a trait", path.inner),
1339            )
1340            .primary_label("Unexpected type")
1341            .secondary_label(loc, format!("Type {} defined here", path.inner)));
1342        }
1343        Err(err) => return Err(err.into()),
1344    };
1345    // This must always succeed because `lookup_trait` succeeded right before
1346    let Some(Thing::Trait(marker)) = ctx.symtab.thing_by_id(&name_id) else {
1347        unreachable!();
1348    };
1349    if trait_spec.paren_syntax && !marker.paren_sugar {
1350        // Paren syntax enforces at least two type parameters to be present
1351        let Some(type_params) = trait_spec.type_params.as_ref() else {
1352            unreachable!();
1353        };
1354
1355        return Err(
1356            Diagnostic::error(trait_spec, "Trait does not support function-like notation")
1357                .primary_label("Trait does not support function-like notation")
1358                .span_suggest_replace(
1359                    "replace it with a regular type parameter list",
1360                    type_params,
1361                    format!(
1362                        "<{}>",
1363                        type_params.inner.iter().map(|tp| tp.to_string()).join(", ")
1364                    ),
1365                ),
1366        );
1367    } else if !trait_spec.paren_syntax && marker.paren_sugar {
1368        let mut diag = Diagnostic::warning(trait_spec, "Trait supports function-like notation")
1369            .primary_label("Trait supports function-like notation");
1370
1371        let whole_loc = trait_spec.type_params.as_ref().map(Loc::loc);
1372        let type_params = trait_spec
1373            .type_params
1374            .as_ref()
1375            .map_or(&[][..], |tp| &tp.inner[..]);
1376
1377        let (rest_tys, param_ty, return_ty) = match type_params {
1378            [] => (&[][..], None, None),
1379            [ref arg] => (&[][..], Some(&arg.inner), None),
1380            [ref rest @ .., ref arg, ref ret] => (rest, Some(&arg.inner), Some(&ret.inner)),
1381        };
1382
1383        let rest_str = match rest_tys {
1384            [] => String::new(),
1385            tys => format!("<{}>", tys.iter().map(|tp| tp.to_string()).join(", ")),
1386        };
1387
1388        let param_str = match param_ty {
1389            None => String::from("()"),
1390            Some(ty @ ast::TypeExpression::TypeSpec(ts)) if ts.is_tuple() => ty.to_string(),
1391            Some(ty) => format!("({})", ty.to_string()),
1392        };
1393
1394        let return_str = match return_ty {
1395            None => String::new(),
1396            Some(ast::TypeExpression::TypeSpec(ts)) if ts.is_empty_tuple() => String::new(),
1397            Some(ty) => format!(" -> {ty}"),
1398        };
1399
1400        let message = "consider using function-like syntax";
1401        let suggestion = format!("{rest_str}{param_str}{return_str}");
1402
1403        if let Some(loc) = whole_loc {
1404            diag = diag.span_suggest_replace(message, loc, suggestion);
1405        } else {
1406            diag = diag.span_suggest_insert_after(message, &trait_spec.path, suggestion);
1407        }
1408        ctx.diags.errors.push(diag);
1409    }
1410    let name = TraitName::Named(name_id.at_loc(&loc));
1411    let type_params = match &trait_spec.inner.type_params {
1412        Some(params) => Some(params.try_map_ref(|params| {
1413            params
1414                .iter()
1415                .map(|param| param.try_map_ref(|te| visit_type_expression(te, type_spec_kind, ctx)))
1416                .collect::<Result<_>>()
1417        })?),
1418        None => None,
1419    };
1420    Ok(hir::TraitSpec {
1421        name,
1422        type_params,
1423        paren_syntax: trait_spec.paren_syntax,
1424    }
1425    .at_loc(trait_spec))
1426}
1427
1428#[tracing::instrument(skip_all, fields(name=?item.name()))]
1429pub fn visit_item(item: &ast::Item, ctx: &mut Context) -> Result<Vec<hir::Item>> {
1430    match item {
1431        ast::Item::Unit(u) => Ok(vec![visit_unit(None, u, &None, ctx)?]),
1432        ast::Item::TraitDef(_) => {
1433            // Global symbol lowering already visits traits
1434            event!(Level::INFO, "Trait definition");
1435            Ok(vec![])
1436        }
1437        ast::Item::Type(_) => {
1438            // Global symbol lowering already visits type declarations
1439            event!(Level::INFO, "Type definition");
1440            Ok(vec![])
1441        }
1442        ast::Item::ImplBlock(block) => visit_impl(block, ctx),
1443        ast::Item::ExternalMod(_) => Ok(vec![]),
1444        ast::Item::Module(m) => visit_module(m, ctx).map(|_| vec![]),
1445        ast::Item::Use(ss) => {
1446            for s in &ss.inner {
1447                ctx.symtab
1448                    .lookup_id(&s.path, true)
1449                    .map_err(Diagnostic::from)?;
1450            }
1451
1452            Ok(vec![])
1453        }
1454    }
1455}
1456
1457#[tracing::instrument(skip_all, fields(module.name = %module.name.inner))]
1458pub fn visit_module(module: &ast::Module, ctx: &mut Context) -> Result<()> {
1459    let path = &ctx
1460        .symtab
1461        .current_namespace()
1462        .clone()
1463        .at_loc(&module.name.loc());
1464
1465    let (id, thing) = ctx
1466        .symtab
1467        .lookup_thing(path)
1468        .map_err(|_| diag_anyhow!(module.name, "Failed to find {path} in symtab"))?;
1469
1470    if !matches!(thing, Thing::Module(_, _)) {
1471        diag_bail!(
1472            module.name,
1473            "Found {} to be a {}",
1474            module.name,
1475            thing.kind_string()
1476        );
1477    }
1478
1479    let documentation = module.body.documentation.join("\n");
1480
1481    ctx.item_list.modules.insert(
1482        id.clone(),
1483        Module {
1484            name: id.at_loc(&module.name),
1485            documentation,
1486        },
1487    );
1488
1489    ctx.in_named_namespace(module.name, |ctx| visit_module_body(&module.body, ctx))
1490}
1491
1492#[tracing::instrument(skip_all)]
1493pub fn visit_module_body(body: &ast::ModuleBody, ctx: &mut Context) -> Result<()> {
1494    for i in &body.members {
1495        for item in visit_item(i, ctx)? {
1496            match item {
1497                hir::Item::Unit(u) => ctx
1498                    .item_list
1499                    .add_executable(u.name.name_id().clone(), ExecutableItem::Unit(u))?,
1500
1501                hir::Item::ExternUnit(name, head) => ctx.item_list.add_executable(
1502                    name.name_id().clone(),
1503                    ExecutableItem::ExternUnit(name, head),
1504                )?,
1505            }
1506        }
1507    }
1508
1509    Ok(())
1510}
1511
1512fn try_lookup_enum_variant(path: &Loc<Path>, ctx: &mut Context) -> Result<hir::PatternKind> {
1513    let (name_id, variant) = ctx.symtab.lookup_enum_variant(path)?;
1514    if variant.inner.params.argument_num() == 0 {
1515        Ok(hir::PatternKind::Type(name_id.at_loc(path), vec![]))
1516    } else {
1517        let expected = variant.inner.params.argument_num();
1518        Err(Diagnostic::from(error::PatternListLengthMismatch {
1519            expected,
1520            got: 0,
1521            at: path.loc(),
1522            for_what: Some("enum variant".to_string()),
1523        })
1524        // FIXME: actual names of variant arguments?
1525        .span_suggest_insert_after(
1526            "help: Add arguments here",
1527            path.loc(),
1528            format!(
1529                "({})",
1530                std::iter::repeat("_")
1531                    .take(expected)
1532                    .collect::<Vec<_>>()
1533                    .join(", ")
1534            ),
1535        ))
1536    }
1537}
1538
1539pub fn visit_pattern(p: &ast::Pattern, ctx: &mut Context) -> Result<hir::Pattern> {
1540    let kind = match &p {
1541        ast::Pattern::Integer(val) => hir::PatternKind::Integer(val.clone().as_signed()),
1542        ast::Pattern::Bool(val) => hir::PatternKind::Bool(*val),
1543        ast::Pattern::Path(path) => {
1544            match (try_lookup_enum_variant(path, ctx), path.inner.0.as_slice()) {
1545                (Ok(kind), _) => kind,
1546                (_, [segment]) => {
1547                    let ident = segment.unwrap_named();
1548                    // Check if this is declaring a variable
1549                    let (name_id, pre_declared) =
1550                        if let Some(state) = ctx.symtab.get_declaration(ident) {
1551                            match state.inner {
1552                                DeclarationState::Undefined(id) => {
1553                                    ctx.symtab
1554                                        .mark_declaration_defined(ident.clone(), ident.loc());
1555                                    (id, true)
1556                                }
1557                                DeclarationState::Undecleared(id) => {
1558                                    ctx.symtab.add_thing_with_name_id(
1559                                        id.clone(),
1560                                        Thing::Variable(ident.clone()),
1561                                        None,
1562                                    );
1563                                    ctx.symtab
1564                                        .mark_declaration_defined(ident.clone(), ident.loc());
1565                                    (id, true)
1566                                }
1567                                DeclarationState::Defined(previous) => {
1568                                    return Err(Diagnostic::error(
1569                                        ident,
1570                                        format!("{ident} was already defined"),
1571                                    )
1572                                    .secondary_label(previous, "First defined here")
1573                                    .primary_label("Later defined here")
1574                                    .secondary_label(state.loc(), format!("{ident} declared here"))
1575                                    .note("Declared variables can only be defined once"));
1576                                }
1577                            }
1578                        } else {
1579                            (
1580                                ctx.symtab.add_thing(
1581                                    Path::ident(ident.clone()),
1582                                    Thing::Variable(ident.clone()),
1583                                    None,
1584                                ),
1585                                false,
1586                            )
1587                        };
1588
1589                    hir::PatternKind::Name {
1590                        name: name_id.at_loc(ident),
1591                        pre_declared,
1592                    }
1593                }
1594                (_, []) => unreachable!(),
1595                (Err(e), _) => return Err(e),
1596            }
1597        }
1598        ast::Pattern::Tuple(pattern) => {
1599            let inner = pattern
1600                .iter()
1601                .map(|p| p.try_map_ref(|p| visit_pattern(p, ctx)))
1602                .collect::<Result<_>>()?;
1603            hir::PatternKind::Tuple(inner)
1604        }
1605        ast::Pattern::Array(patterns) => {
1606            let inner = patterns
1607                .iter()
1608                .map(|p| p.try_map_ref(|p| visit_pattern(p, ctx)))
1609                .collect::<Result<_>>()?;
1610            hir::PatternKind::Array(inner)
1611        }
1612        ast::Pattern::Type(path, args) => {
1613            // Look up the name to see if it's an enum variant.
1614            let (name_id, p) = ctx.symtab.lookup_patternable_type(path)?;
1615            match &args.inner {
1616                ast::ArgumentPattern::Named(patterns) => {
1617                    let mut bound = HashSet::<Loc<Identifier>>::default();
1618                    let mut unbound = p
1619                        .params
1620                        .0
1621                        .iter()
1622                        .map(
1623                            |hir::Parameter {
1624                                 name: ident,
1625                                 ty: _,
1626                                 no_mangle: _,
1627                                 field_translator: _,
1628                             }| ident.inner.clone(),
1629                        )
1630                        .collect::<HashSet<_>>();
1631
1632                    let mut patterns = patterns
1633                        .iter()
1634                        .map(|(target, pattern)| {
1635                            let ast_pattern = pattern.as_ref().cloned().unwrap_or_else(|| {
1636                                ast::Pattern::Path(Path::ident_with_loc(target.clone()))
1637                                    .at_loc(target)
1638                            });
1639                            let new_pattern = visit_pattern(&ast_pattern, ctx)?;
1640                            // Check if this is a new binding
1641                            if let Some(prev) = bound.get(target) {
1642                                return Err(Diagnostic::from(
1643                                    ArgumentError::DuplicateNamedBindings {
1644                                        new: target.clone(),
1645                                        prev_loc: prev.loc(),
1646                                    },
1647                                ));
1648                            }
1649                            bound.insert(target.clone());
1650                            if unbound.take(target).is_none() {
1651                                return Err(Diagnostic::from(ArgumentError::NoSuchArgument {
1652                                    name: target.clone(),
1653                                }));
1654                            }
1655
1656                            let kind = match pattern {
1657                                Some(_) => hir::ArgumentKind::Named,
1658                                None => hir::ArgumentKind::ShortNamed,
1659                            };
1660
1661                            Ok(hir::PatternArgument {
1662                                target: target.clone(),
1663                                value: new_pattern.at_loc(&ast_pattern),
1664                                kind,
1665                            })
1666                        })
1667                        .collect::<Result<Vec<_>>>()?;
1668
1669                    if !unbound.is_empty() {
1670                        return Err(Diagnostic::from(ArgumentError::MissingArguments {
1671                            missing: unbound.into_iter().collect(),
1672                            at: args.loc(),
1673                        }));
1674                    }
1675
1676                    patterns.sort_by_cached_key(|arg| p.params.arg_index(&arg.target).unwrap());
1677
1678                    hir::PatternKind::Type(name_id.at_loc(path), patterns)
1679                }
1680                ast::ArgumentPattern::Positional(patterns) => {
1681                    // Ensure we have the correct amount of arguments
1682                    if p.params.argument_num() != patterns.len() {
1683                        return Err(Diagnostic::from(error::PatternListLengthMismatch {
1684                            expected: p.params.argument_num(),
1685                            got: patterns.len(),
1686                            at: args.loc(),
1687                            for_what: None,
1688                        }));
1689                    }
1690
1691                    let patterns = patterns
1692                        .iter()
1693                        .zip(p.params.0.iter())
1694                        .map(|(p, arg)| {
1695                            let pat = p.try_map_ref(|p| visit_pattern(p, ctx))?;
1696                            Ok(hir::PatternArgument {
1697                                target: arg.name.clone(),
1698                                value: pat,
1699                                kind: hir::ArgumentKind::Positional,
1700                            })
1701                        })
1702                        .collect::<Result<Vec<_>>>()?;
1703
1704                    hir::PatternKind::Type(name_id.at_loc(path), patterns)
1705                }
1706            }
1707        }
1708    };
1709    Ok(kind.with_id(ctx.idtracker.next()))
1710}
1711
1712fn try_visit_statement(
1713    s: &Loc<ast::Statement>,
1714    ctx: &mut Context,
1715) -> Result<Vec<Loc<hir::Statement>>> {
1716    match &s.inner {
1717        ast::Statement::Declaration(names) => {
1718            let names = names
1719                .iter()
1720                .map(|name| {
1721                    ctx.symtab
1722                        .add_declaration(name.clone())
1723                        .map(|decl| decl.at_loc(name))
1724                })
1725                .filter_map(|name| name.handle_in(&mut ctx.diags))
1726                .collect::<Vec<_>>();
1727
1728            Ok(vec![hir::Statement::Declaration(names).at_loc(s)])
1729        }
1730        ast::Statement::Binding(Binding {
1731            pattern,
1732            ty,
1733            value,
1734            attrs,
1735        }) => {
1736            let mut stmts = vec![];
1737
1738            let hir_type = if let Some(t) = ty {
1739                Some(visit_type_spec(t, &TypeSpecKind::BindingType, ctx)?)
1740            } else {
1741                None
1742            };
1743
1744            let value = value.visit(visit_expression, ctx);
1745
1746            let pattern = pattern.try_visit(visit_pattern, ctx)?;
1747
1748            let mut wal_trace = None;
1749            attrs.lower(&mut |attr| match &attr.inner {
1750                ast::Attribute::WalTrace { clk, rst } => {
1751                    wal_trace = Some(
1752                        WalTrace {
1753                            clk: clk.as_ref().map(|x| x.visit(visit_expression, ctx)),
1754                            rst: rst.as_ref().map(|x| x.visit(visit_expression, ctx)),
1755                        }
1756                        .at_loc(attr),
1757                    );
1758                    Ok(None)
1759                }
1760                ast::Attribute::WalSuffix { suffix } => {
1761                    // All names in the pattern should have the suffix applied to them
1762                    for name in pattern.get_names() {
1763                        stmts.push(
1764                            hir::Statement::WalSuffixed {
1765                                suffix: suffix.inner.clone(),
1766                                target: name.clone(),
1767                            }
1768                            .at_loc(suffix),
1769                        );
1770                    }
1771                    Ok(None)
1772                }
1773                ast::Attribute::VerilogAttrs { .. }
1774                | ast::Attribute::NoMangle { .. }
1775                | ast::Attribute::Fsm { .. }
1776                | ast::Attribute::Optimize { .. }
1777                | ast::Attribute::Documentation { .. }
1778                | ast::Attribute::SurferTranslator(_)
1779                | ast::Attribute::SpadecParenSugar
1780                | ast::Attribute::WalTraceable { .. } => Err(attr.report_unused("let binding")),
1781            })?;
1782
1783            stmts.push(
1784                hir::Statement::Binding(hir::Binding {
1785                    pattern,
1786                    ty: hir_type,
1787                    value,
1788                    wal_trace,
1789                })
1790                .at_loc(s),
1791            );
1792            Ok(stmts)
1793        }
1794        ast::Statement::Expression(expr) => {
1795            let value = expr.visit(visit_expression, ctx);
1796            Ok(vec![hir::Statement::Expression(value).at_loc(expr)])
1797        }
1798        ast::Statement::Register(inner) => visit_register(inner, ctx),
1799        ast::Statement::PipelineRegMarker(count, cond) => {
1800            let cond = match cond {
1801                Some(cond) => Some(cond.visit(visit_expression, ctx)),
1802                None => None,
1803            };
1804
1805            let extra = match (count, cond) {
1806                (None, None) => None,
1807                (Some(count), None) => Some(hir::PipelineRegMarkerExtra::Count {
1808                    count: count.try_map_ref(|c| {
1809                        visit_type_expression(c, &TypeSpecKind::PipelineRegCount, ctx)
1810                    })?,
1811                    count_typeexpr_id: ctx.idtracker.next(),
1812                }),
1813                (None, Some(cond)) => Some(hir::PipelineRegMarkerExtra::Condition(cond)),
1814                (Some(count), Some(cond)) => {
1815                    return Err(Diagnostic::error(
1816                        count,
1817                        "Multiple registers with conditions can not be defined",
1818                    )
1819                    .primary_label("Multiple registers not allowed")
1820                    .secondary_label(cond, "Condition specified here")
1821                    .help("Consider splitting into two reg statements"));
1822                }
1823            };
1824
1825            Ok(vec![hir::Statement::PipelineRegMarker(extra).at_loc(s)])
1826        }
1827        ast::Statement::Label(name) => {
1828            // NOTE: pipeline labels are lowered in visit_pipeline
1829            let (name, sym) = ctx
1830                .symtab
1831                .lookup_type_symbol(&Path::ident(name.clone()).at_loc(name))?;
1832            Ok(vec![hir::Statement::Label(name.at_loc(&sym)).at_loc(s)])
1833        }
1834        ast::Statement::Assert(expr) => {
1835            let expr = expr.visit(visit_expression, ctx);
1836
1837            Ok(vec![hir::Statement::Assert(expr).at_loc(s)])
1838        }
1839        ast::Statement::Set { target, value } => {
1840            let target = target.visit(visit_expression, ctx);
1841            let value = value.visit(visit_expression, ctx);
1842
1843            Ok(vec![hir::Statement::Set { target, value }.at_loc(s)])
1844        }
1845    }
1846}
1847
1848fn visit_statement(s: &Loc<ast::Statement>, ctx: &mut Context) -> Vec<Loc<hir::Statement>> {
1849    match try_visit_statement(s, ctx) {
1850        Ok(result) => result,
1851        Err(e) => {
1852            ctx.diags.errors.push(e);
1853            vec![hir::Statement::Error.at_loc(s)]
1854        }
1855    }
1856}
1857
1858#[tracing::instrument(skip_all)]
1859fn visit_argument_list(
1860    arguments: &ast::ArgumentList,
1861    ctx: &mut Context,
1862) -> Result<hir::ArgumentList<hir::Expression>> {
1863    match arguments {
1864        ast::ArgumentList::Positional(args) => {
1865            let inner = args
1866                .iter()
1867                .map(|arg| arg.visit(visit_expression, ctx))
1868                .collect::<_>();
1869
1870            Ok(hir::ArgumentList::Positional(inner))
1871        }
1872        ast::ArgumentList::Named(args) => {
1873            let inner = args
1874                .iter()
1875                .map(|arg| match arg {
1876                    ast::NamedArgument::Full(name, expr) => {
1877                        Ok(hir::expression::NamedArgument::Full(
1878                            name.clone(),
1879                            expr.visit(visit_expression, ctx),
1880                        ))
1881                    }
1882                    ast::NamedArgument::Short(name) => {
1883                        let expr = ast::Expression::Identifier(Path::ident_with_loc(name.clone()))
1884                            .at_loc(name);
1885
1886                        Ok(hir::expression::NamedArgument::Short(
1887                            name.clone(),
1888                            expr.visit(visit_expression, ctx),
1889                        ))
1890                    }
1891                })
1892                .collect::<Result<_>>()?;
1893
1894            Ok(hir::ArgumentList::Named(inner))
1895        }
1896    }
1897}
1898
1899pub fn visit_call_kind(
1900    kind: &ast::CallKind,
1901    ctx: &mut Context,
1902) -> Result<hir::expression::CallKind> {
1903    Ok(match kind {
1904        ast::CallKind::Function => hir::expression::CallKind::Function,
1905        ast::CallKind::Entity(loc) => hir::expression::CallKind::Entity(*loc),
1906        ast::CallKind::Pipeline(loc, depth) => {
1907            let depth = depth
1908                .try_map_ref(|e| visit_type_expression(e, &TypeSpecKind::PipelineInstDepth, ctx))?;
1909            hir::expression::CallKind::Pipeline {
1910                inst_loc: *loc,
1911                depth,
1912                depth_typeexpr_id: ctx.idtracker.next(),
1913            }
1914        }
1915    })
1916}
1917
1918pub fn visit_turbofish(
1919    t: &Loc<ast::TurbofishInner>,
1920    ctx: &mut Context,
1921) -> Result<Loc<hir::ArgumentList<TypeExpression>>> {
1922    t.try_map_ref(|args| match args {
1923        ast::TurbofishInner::Named(fishes) => fishes
1924            .iter()
1925            .map(|fish| match &fish.inner {
1926                ast::NamedTurbofish::Short(name) => {
1927                    let arg = ast::TypeExpression::TypeSpec(Box::new(
1928                        ast::TypeSpec::Named(Path::ident_with_loc(name.clone()), None).at_loc(name),
1929                    ));
1930
1931                    let arg =
1932                        visit_type_expression(&arg, &TypeSpecKind::Turbofish, ctx)?.at_loc(name);
1933                    Ok(hir::expression::NamedArgument::Short(name.clone(), arg))
1934                }
1935                ast::NamedTurbofish::Full(name, arg) => {
1936                    let arg =
1937                        visit_type_expression(arg, &TypeSpecKind::Turbofish, ctx)?.at_loc(arg);
1938                    Ok(hir::expression::NamedArgument::Full(name.clone(), arg))
1939                }
1940            })
1941            .collect::<Result<Vec<_>>>()
1942            .map(|params| hir::ArgumentList::Named(params)),
1943        ast::TurbofishInner::Positional(args) => args
1944            .iter()
1945            .map(|arg| {
1946                arg.try_map_ref(|arg| visit_type_expression(arg, &TypeSpecKind::Turbofish, ctx))
1947            })
1948            .collect::<Result<_>>()
1949            .map(hir::ArgumentList::Positional),
1950    })
1951}
1952
1953fn visit_expression_result(e: &ast::Expression, ctx: &mut Context) -> Result<hir::ExprKind> {
1954    match e {
1955        ast::Expression::IntLiteral(val) => {
1956            let kind = match &val.inner {
1957                ast::IntLiteral::Unsized(_) => IntLiteralKind::Unsized,
1958                ast::IntLiteral::Signed { val: _, size } => IntLiteralKind::Signed(size.clone()),
1959                ast::IntLiteral::Unsigned { val: _, size } => {
1960                    IntLiteralKind::Unsigned(size.clone())
1961                }
1962            };
1963            Ok(hir::ExprKind::IntLiteral(
1964                val.inner.clone().as_signed(),
1965                kind,
1966            ))
1967        }
1968        ast::Expression::BoolLiteral(val) => Ok(hir::ExprKind::BoolLiteral(val.inner)),
1969        ast::Expression::StrLiteral(val) => Err(Diagnostic::error(
1970            val,
1971            "Unicode strings are not supported inside expressions",
1972        )
1973        .primary_label("Unicode strings are not supported in expressions")
1974        .span_suggest_insert_before("Consider using an ASCII string", val, "b")),
1975        ast::Expression::TriLiteral(lit) => {
1976            let result = match lit.inner {
1977                ast::BitLiteral::Low => hir::expression::TriLiteral::Low,
1978                ast::BitLiteral::High => hir::expression::TriLiteral::High,
1979                ast::BitLiteral::HighImp => hir::expression::TriLiteral::HighImp,
1980            };
1981            Ok(hir::ExprKind::TriLiteral(result))
1982        }
1983        ast::Expression::CreatePorts => Ok(hir::ExprKind::CreatePorts),
1984        ast::Expression::BinaryOperator(lhs, tok, rhs) => {
1985            let lhs = lhs.visit(visit_expression, ctx);
1986            let rhs = rhs.visit(visit_expression, ctx);
1987
1988            let operator = |op: BinaryOperator| {
1989                hir::ExprKind::BinaryOperator(Box::new(lhs), op.at_loc(tok), Box::new(rhs))
1990            };
1991
1992            match tok.inner {
1993                ast::BinaryOperator::Add => Ok(operator(BinaryOperator::Add)),
1994                ast::BinaryOperator::Sub => Ok(operator(BinaryOperator::Sub)),
1995                ast::BinaryOperator::Mul => Ok(operator(BinaryOperator::Mul)),
1996                ast::BinaryOperator::Div => Ok(operator(BinaryOperator::Div)),
1997                ast::BinaryOperator::Mod => Ok(operator(BinaryOperator::Mod)),
1998                ast::BinaryOperator::Equals => Ok(operator(BinaryOperator::Eq)),
1999                ast::BinaryOperator::NotEquals => Ok(operator(BinaryOperator::NotEq)),
2000                ast::BinaryOperator::Gt => Ok(operator(BinaryOperator::Gt)),
2001                ast::BinaryOperator::Lt => Ok(operator(BinaryOperator::Lt)),
2002                ast::BinaryOperator::Ge => Ok(operator(BinaryOperator::Ge)),
2003                ast::BinaryOperator::Le => Ok(operator(BinaryOperator::Le)),
2004                ast::BinaryOperator::LeftShift => Ok(operator(BinaryOperator::LeftShift)),
2005                ast::BinaryOperator::RightShift => Ok(operator(BinaryOperator::RightShift)),
2006                ast::BinaryOperator::ArithmeticRightShift => {
2007                    Ok(operator(BinaryOperator::ArithmeticRightShift))
2008                }
2009                ast::BinaryOperator::LogicalAnd => Ok(operator(BinaryOperator::LogicalAnd)),
2010                ast::BinaryOperator::LogicalOr => Ok(operator(BinaryOperator::LogicalOr)),
2011                ast::BinaryOperator::LogicalXor => Ok(operator(BinaryOperator::LogicalXor)),
2012                ast::BinaryOperator::BitwiseOr => Ok(operator(BinaryOperator::BitwiseOr)),
2013                ast::BinaryOperator::BitwiseAnd => Ok(operator(BinaryOperator::BitwiseAnd)),
2014                ast::BinaryOperator::BitwiseXor => Ok(operator(BinaryOperator::BitwiseXor)),
2015            }
2016        }
2017        ast::Expression::UnaryOperator(operator, operand) => {
2018            let operand = operand.visit(visit_expression, ctx);
2019
2020            let unop = |op: hir::expression::UnaryOperator| {
2021                hir::ExprKind::UnaryOperator(op.at_loc(operator), Box::new(operand))
2022            };
2023            match operator.inner {
2024                ast::UnaryOperator::Sub => Ok(unop(hir::expression::UnaryOperator::Sub)),
2025                ast::UnaryOperator::Not => Ok(unop(hir::expression::UnaryOperator::Not)),
2026                ast::UnaryOperator::BitwiseNot => {
2027                    Ok(unop(hir::expression::UnaryOperator::BitwiseNot))
2028                }
2029                ast::UnaryOperator::Dereference => {
2030                    Ok(unop(hir::expression::UnaryOperator::Dereference))
2031                }
2032                ast::UnaryOperator::Reference => {
2033                    Ok(unop(hir::expression::UnaryOperator::Reference))
2034                }
2035            }
2036        }
2037        ast::Expression::Parenthesized(expr) => visit_expression_result(expr, ctx),
2038        ast::Expression::TupleLiteral(exprs) => {
2039            let exprs = exprs
2040                .iter()
2041                .map(|e| e.visit(visit_expression, ctx))
2042                .collect::<Vec<_>>();
2043            Ok(hir::ExprKind::TupleLiteral(exprs))
2044        }
2045        ast::Expression::ArrayLiteral(exprs) => {
2046            ctx.in_new_scope(|ctx| {
2047                // First, resolve any labels we find
2048                for (i, (label, _expr)) in exprs.iter().enumerate() {
2049                    if let Some(label) = label {
2050                        ctx.symtab.add_thing(
2051                            Path::ident(label.clone()),
2052                            Thing::ArrayLabel(i.at_loc(label)),
2053                            None,
2054                        );
2055                    }
2056                }
2057
2058                // Then generate the result
2059                let exprs = exprs
2060                    .iter()
2061                    .map(|(_label, e)| e.visit(visit_expression, ctx))
2062                    .collect::<Vec<_>>();
2063
2064                Ok(hir::ExprKind::ArrayLiteral(exprs))
2065            })
2066        }
2067        ast::Expression::ArrayShorthandLiteral(expr, amount) => {
2068            Ok(hir::ExprKind::ArrayShorthandLiteral(
2069                Box::new(visit_expression(expr, ctx).at_loc(expr)),
2070                visit_const_generic(amount, ctx)?.map(|c| c.with_id(ctx.idtracker.next())),
2071            ))
2072        }
2073        ast::Expression::Index(target, index) => {
2074            let target = target.visit(visit_expression, ctx);
2075            let index = index.visit(visit_expression, ctx);
2076            Ok(hir::ExprKind::Index(Box::new(target), Box::new(index)))
2077        }
2078        ast::Expression::RangeIndex { target, start, end } => {
2079            let target = target.visit(visit_expression, ctx);
2080            Ok(hir::ExprKind::RangeIndex {
2081                target: Box::new(target),
2082                start: visit_const_generic(start, ctx)?.map(|c| c.with_id(ctx.idtracker.next())),
2083                end: visit_const_generic(end, ctx)?.map(|c| c.with_id(ctx.idtracker.next())),
2084            })
2085        }
2086        ast::Expression::TupleIndex {
2087            target,
2088            index,
2089            deprecated_syntax,
2090        } => {
2091            if *deprecated_syntax {
2092                let loc = ().between_locs(&target, &index);
2093                ctx.diags.errors.push(
2094                    Diagnostic::warning(loc, "Deprecated tuple syntax indexing")
2095                        .primary_label("`#` syntax for tuple indexing is deprecated")
2096                        .note("replace `#` with `.`"),
2097                );
2098            }
2099
2100            Ok(hir::ExprKind::TupleIndex(
2101                Box::new(target.visit(visit_expression, ctx)),
2102                *index,
2103            ))
2104        }
2105        ast::Expression::FieldAccess(target, field) => Ok(hir::ExprKind::FieldAccess(
2106            Box::new(target.visit(visit_expression, ctx)),
2107            field.clone(),
2108        )),
2109        ast::Expression::MethodCall {
2110            kind,
2111            target,
2112            name,
2113            args,
2114            turbofish,
2115        } => {
2116            let target = target.visit(visit_expression, ctx);
2117            Ok(hir::ExprKind::MethodCall {
2118                target: Box::new(target),
2119                name: name.clone(),
2120                args: args.try_map_ref(|args| visit_argument_list(args, ctx))?,
2121                call_kind: visit_call_kind(kind, ctx)?,
2122                turbofish: turbofish
2123                    .as_ref()
2124                    .map(|t| visit_turbofish(t, ctx))
2125                    .transpose()?,
2126                safety: ctx.safety,
2127            })
2128        }
2129        ast::Expression::If(cond, ontrue, onfalse) => {
2130            let cond = cond.visit(visit_expression, ctx);
2131            let ontrue = ontrue.visit(visit_expression, ctx);
2132            let onfalse = onfalse.visit(visit_expression, ctx);
2133
2134            Ok(hir::ExprKind::If(
2135                Box::new(cond),
2136                Box::new(ontrue),
2137                Box::new(onfalse),
2138            ))
2139        }
2140        ast::Expression::TypeLevelIf(cond, ontrue, onfalse) => {
2141            let cond = visit_const_generic(cond, ctx)?;
2142            let ontrue = ontrue.visit(visit_expression, ctx);
2143            let onfalse = onfalse.visit(visit_expression, ctx);
2144
2145            Ok(hir::ExprKind::TypeLevelIf(
2146                cond.map(|cond| cond.with_id(ctx.idtracker.next())),
2147                Box::new(ontrue),
2148                Box::new(onfalse),
2149            ))
2150        }
2151        ast::Expression::Match(expression, branches) => {
2152            let e = expression.visit(visit_expression, ctx);
2153
2154            if branches.is_empty() {
2155                return Err(Diagnostic::error(branches, "Match body has no arms")
2156                    .primary_label("This match body has no arms"));
2157            }
2158
2159            let b = branches
2160                .iter()
2161                .map(|(pattern, if_condition, result)| {
2162                    ctx.symtab.new_scope();
2163                    let p = pattern.try_visit(visit_pattern, ctx)?;
2164                    let c = if_condition
2165                        .as_ref()
2166                        .map(|c| c.visit(visit_expression, ctx));
2167                    let r = result.visit(visit_expression, ctx);
2168                    ctx.symtab.close_scope();
2169                    Ok((p, c, r))
2170                })
2171                .collect::<Result<Vec<_>>>()?;
2172
2173            Ok(hir::ExprKind::Match(Box::new(e), b))
2174        }
2175        ast::Expression::Block(block) => {
2176            Ok(hir::ExprKind::Block(Box::new(visit_block(block, ctx)?)))
2177        }
2178        ast::Expression::Unsafe(block) => {
2179            let outside = ::core::mem::replace(&mut ctx.safety, Safety::Unsafe);
2180            if outside == Safety::Unsafe {
2181                ctx.diags.errors.push(
2182                    Diagnostic::warning(block.loc(), "Unnecessary unsafe block")
2183                        .note("This block is already in unsafe context"),
2184                );
2185            }
2186            let res = visit_block(block, ctx);
2187            ctx.safety = outside;
2188            Ok(hir::ExprKind::Block(Box::new(res?)))
2189        }
2190        ast::Expression::Lambda { .. } => {
2191            let outside = ::core::mem::replace(&mut ctx.safety, Safety::Default);
2192            let res = visit_lambda(e, ctx);
2193            ctx.safety = outside;
2194            res
2195        }
2196        ast::Expression::Call {
2197            kind,
2198            callee,
2199            args,
2200            turbofish,
2201        } => {
2202            let (name_id, _) = ctx.symtab.lookup_unit(callee)?;
2203            let args = visit_argument_list(args, ctx)?.at_loc(args);
2204
2205            let kind = visit_call_kind(kind, ctx)?;
2206
2207            let turbofish = turbofish
2208                .as_ref()
2209                .map(|t| visit_turbofish(t, ctx))
2210                .transpose()?;
2211
2212            Ok(hir::ExprKind::Call {
2213                kind,
2214                callee: name_id.at_loc(callee),
2215                args,
2216                turbofish,
2217                safety: ctx.safety,
2218            })
2219        }
2220        ast::Expression::Identifier(path) => {
2221            // If the identifier isn't a valid variable, report as "expected value".
2222            match ctx.symtab.lookup_variable(path) {
2223                Ok(id) => Ok(hir::ExprKind::Identifier(id)),
2224                Err(LookupError::IsAType(_, _)) => {
2225                    let ty = ctx.symtab.lookup_type_symbol(path)?;
2226                    let (name, ty) = &ty;
2227                    match ty.inner {
2228                        TypeSymbol::GenericMeta(
2229                            MetaType::Int | MetaType::Uint | MetaType::Number | MetaType::Str,
2230                        ) => Ok(hir::ExprKind::TypeLevelInteger(name.clone())),
2231                        TypeSymbol::GenericMeta(_) | TypeSymbol::GenericArg { traits: _ } => {
2232                            Err(Diagnostic::error(
2233                                path,
2234                                format!("Generic type {name} is a type but it is used as a value"),
2235                            )
2236                            .primary_label(format!("{name} is a type"))
2237                            .secondary_label(ty, format!("{name} is declared here"))
2238                            .span_suggest_insert_before(
2239                                format!("Consider making `{name}` a type level integer"),
2240                                ty,
2241                                "#int ",
2242                            )
2243                            .span_suggest_insert_before(
2244                                format!("or a type level uint"),
2245                                ty,
2246                                "#uint ",
2247                            ))
2248                        }
2249                        TypeSymbol::Declared(_, _) | TypeSymbol::Alias(_) => Err(
2250                            Diagnostic::error(path, format!("Type {name} is used as a value"))
2251                                .primary_label(format!("{name} is a type")),
2252                        ),
2253                    }
2254                }
2255                Err(LookupError::NotAVariable(path, ref was @ Thing::EnumVariant(_)))
2256                | Err(LookupError::NotAVariable(
2257                    path,
2258                    ref was @ Thing::Alias {
2259                        loc: _,
2260                        path: _,
2261                        in_namespace: _,
2262                    },
2263                )) => {
2264                    let (name_id, variant) = match ctx.symtab.lookup_enum_variant(&path) {
2265                        Ok(res) => res,
2266                        Err(_) => return Err(LookupError::NotAValue(path, was.clone()).into()),
2267                    };
2268                    if variant.params.0.is_empty() {
2269                        // NOTE: This loc is a little bit approximate because it is unlikely
2270                        // that any error will reference the empty argument list.
2271                        let callee = name_id.at_loc(&path);
2272                        let args = hir::ArgumentList::Positional(vec![]).at_loc(&path);
2273                        Ok(hir::ExprKind::Call {
2274                            kind: hir::expression::CallKind::Function,
2275                            callee,
2276                            args,
2277                            turbofish: None,
2278                            safety: ctx.safety,
2279                        })
2280                    } else {
2281                        Err(LookupError::NotAValue(path, was.clone()).into())
2282                    }
2283                }
2284                Err(LookupError::NotAVariable(path, was)) => {
2285                    Err(LookupError::NotAValue(path, was).into())
2286                }
2287                Err(err) => Err(err.into()),
2288            }
2289        }
2290        ast::Expression::PipelineReference {
2291            stage_kw_and_reference_loc,
2292            stage,
2293            name,
2294        } => {
2295            let stage = match stage {
2296                ast::PipelineStageReference::Relative(offset) => {
2297                    hir::expression::PipelineRefKind::Relative(offset.try_map_ref(|t| {
2298                        visit_type_expression(t, &TypeSpecKind::PipelineInstDepth, ctx)
2299                    })?)
2300                }
2301                ast::PipelineStageReference::Absolute(name) => {
2302                    hir::expression::PipelineRefKind::Absolute(
2303                        ctx.symtab
2304                            .lookup_type_symbol(&Path::ident(name.clone()).at_loc(name))?
2305                            .0
2306                            .at_loc(name),
2307                    )
2308                }
2309            }
2310            .at_loc(stage_kw_and_reference_loc);
2311
2312            let pipeline_ctx = ctx
2313                .pipeline_ctx
2314                .as_ref()
2315                .expect("Expected to have a pipeline context");
2316
2317            let path = Path::ident_with_loc(name.clone());
2318            let (name_id, declares_name) = match ctx.symtab.try_lookup_variable(&path)? {
2319                Some(id) => (id.at_loc(name), false),
2320                None => {
2321                    let pipeline_offset = ctx.symtab.current_scope() - pipeline_ctx.scope;
2322                    let undecleared_lookup = ctx.symtab.declarations[pipeline_ctx.scope].get(name);
2323
2324                    if let Some(DeclarationState::Undecleared(name_id)) = undecleared_lookup {
2325                        (name_id.clone().at_loc(name), false)
2326                    } else {
2327                        let name_id = ctx
2328                            .symtab
2329                            .add_undecleared_at_offset(pipeline_offset, name.clone());
2330                        (name_id.at_loc(name), true)
2331                    }
2332                }
2333            };
2334
2335            Ok(hir::ExprKind::PipelineRef {
2336                stage,
2337                name: name_id,
2338                declares_name,
2339                depth_typeexpr_id: ctx.idtracker.next(),
2340            })
2341        }
2342        ast::Expression::LabelAccess { label, field } => {
2343            let (_, label_target) = ctx.symtab.lookup_thing(label)?;
2344
2345            match label_target {
2346                Thing::ArrayLabel(val) => match field.inner.as_str() {
2347                    "index" => Ok(hir::ExprKind::IntLiteral(
2348                        BigInt::from_usize(val.inner).unwrap(),
2349                        IntLiteralKind::Unsized,
2350                    )),
2351                    _ => Err(Diagnostic::error(field, "Array labels only support .index")
2352                        .primary_label("Unknown field on array label")
2353                        .secondary_label(val, "Array label defined here")),
2354                },
2355                other => Err(Diagnostic::error(
2356                    label,
2357                    format!("Expected a label, found {}", other.kind_string()),
2358                )
2359                .primary_label("Expected label")
2360                .secondary_label(other.name_loc(), format!("{} is defined here", label))),
2361            }
2362        }
2363        ast::Expression::StageReady => Ok(hir::ExprKind::StageReady),
2364        ast::Expression::StageValid => Ok(hir::ExprKind::StageValid),
2365        ast::Expression::StaticUnreachable(message) => {
2366            Ok(hir::ExprKind::StaticUnreachable(message.clone()))
2367        }
2368    }
2369}
2370
2371fn check_for_undefined(ctx: &mut Context) {
2372    for problem in &ctx.symtab.get_undefined_declarations() {
2373        match problem {
2374            (undefined, DeclarationState::Undefined(_)) => {
2375                Err(
2376                    Diagnostic::error(undefined, "Declared variable is not defined")
2377                        .primary_label("This variable is declared but not defined")
2378                        // FIXME: Suggest removing the declaration (with a diagnostic suggestion) only if the variable is unused.
2379                        .help(format!("Define {undefined} with a let or reg binding"))
2380                        .help("Or, remove the declaration if the variable is unused"),
2381                )
2382            }
2383            (undecleared, DeclarationState::Undecleared(_)) => Err(Diagnostic::error(
2384                undecleared,
2385                "Could not find referenced variable",
2386            )
2387            .primary_label("This variable could not be found")),
2388            (_, _) => Ok(()),
2389        }
2390        .handle_in(&mut ctx.diags);
2391    }
2392}
2393
2394#[recursive]
2395#[tracing::instrument(skip_all, fields(kind=e.variant_str()))]
2396pub fn visit_expression(e: &ast::Expression, ctx: &mut Context) -> hir::Expression {
2397    let new_id = ctx.idtracker.next();
2398
2399    let kind = match visit_expression_result(e, ctx) {
2400        Ok(kind) => kind,
2401        Err(e) => {
2402            ctx.diags.errors.push(e);
2403            ExprKind::Error
2404        }
2405    };
2406
2407    kind.with_id(new_id)
2408}
2409
2410fn visit_block(b: &ast::Block, ctx: &mut Context) -> Result<hir::Block> {
2411    ctx.symtab.new_scope();
2412    let statements = b
2413        .statements
2414        .iter()
2415        .map(|statement| visit_statement(statement, ctx))
2416        .collect::<Vec<_>>()
2417        .into_iter()
2418        .flatten()
2419        .collect::<Vec<_>>();
2420
2421    let result = b.result.as_ref().map(|o| o.visit(visit_expression, ctx));
2422
2423    check_for_undefined(ctx);
2424
2425    ctx.symtab.close_scope();
2426
2427    Ok(hir::Block { statements, result })
2428}
2429
2430fn visit_register(reg: &Loc<ast::Register>, ctx: &mut Context) -> Result<Vec<Loc<hir::Statement>>> {
2431    let (reg, loc) = reg.split_loc_ref();
2432
2433    let pattern = reg.pattern.try_visit(visit_pattern, ctx)?;
2434
2435    let clock = reg.clock.visit(visit_expression, ctx);
2436
2437    let reset = if let Some((trig, value)) = &reg.reset {
2438        Some((
2439            trig.visit(visit_expression, ctx),
2440            value.visit(visit_expression, ctx),
2441        ))
2442    } else {
2443        None
2444    };
2445
2446    let initial = reg.initial.as_ref().map(|i| i.visit(visit_expression, ctx));
2447
2448    let value = reg.value.visit(visit_expression, ctx);
2449
2450    let value_type = if let Some(value_type) = &reg.value_type {
2451        Some(visit_type_spec(
2452            value_type,
2453            &TypeSpecKind::BindingType,
2454            ctx,
2455        )?)
2456    } else {
2457        None
2458    };
2459
2460    let mut stmts = vec![];
2461
2462    let attributes = reg
2463        .attributes
2464        .lower(&mut |attr| match &attr.inner {
2465            ast::Attribute::Fsm { state } => {
2466                let name_id = if let Some(state) = state {
2467                    ctx.symtab
2468                        .lookup_variable(&Path::ident_with_loc(state.clone()))?
2469                } else if let PatternKind::Name { name, .. } = &pattern.inner.kind {
2470                    name.inner.clone()
2471                } else {
2472                    return Err(Diagnostic::error(
2473                        attr,
2474                        "#[fsm] without explicit name on non-name pattern",
2475                    )
2476                    .secondary_label(&pattern, "This is a pattern")
2477                    .span_suggest(
2478                        "Consider specifying the name of the s ignal containing the state",
2479                        attr,
2480                        "#[fsm(<name>)]",
2481                    ));
2482                };
2483
2484                Ok(Some(hir::Attribute::Fsm { state: name_id }))
2485            }
2486            ast::Attribute::WalSuffix { suffix } => {
2487                // All names in the pattern should have the suffix applied to them
2488                for name in pattern.get_names() {
2489                    stmts.push(
2490                        hir::Statement::WalSuffixed {
2491                            suffix: suffix.inner.clone(),
2492                            target: name.clone(),
2493                        }
2494                        .at_loc(suffix),
2495                    );
2496                }
2497                Ok(None)
2498            }
2499            _ => Err(attr.report_unused("a register")),
2500        })
2501        .handle_in(&mut ctx.diags)
2502        .unwrap_or_else(|| hir::AttributeList::empty());
2503
2504    stmts.push(
2505        hir::Statement::Register(hir::Register {
2506            pattern,
2507            clock,
2508            reset,
2509            initial,
2510            value,
2511            value_type,
2512            attributes,
2513        })
2514        .at_loc(&loc),
2515    );
2516
2517    Ok(stmts)
2518}