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#[tracing::instrument(skip_all, fields(name=%param.name()))]
154pub fn visit_type_param(param: &ast::TypeParam, ctx: &mut Context) -> Result<hir::TypeParam> {
155 match ¶m {
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#[tracing::instrument(skip_all, fields(name=%param.name()))]
203pub fn re_visit_type_param(param: &ast::TypeParam, ctx: &Context) -> Result<hir::TypeParam> {
204 match ¶m {
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
242pub 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 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 let (base_id, base_t) = ctx.symtab.lookup_type_symbol(path)?;
324
325 match &base_t.inner {
327 TypeSymbol::Declared(generic_args, _) => {
328 let visited_params = params
331 .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 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 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 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 FreeStanding,
565 ImplBlock(Loc<hir::TypeSpec>),
567 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 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 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
658fn 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 ""
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 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#[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 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 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#[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 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 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 body.is_none() {
1247 ctx.symtab.close_scope();
1248 return Ok(hir::Item::ExternUnit(unit_name, head));
1249 }
1250
1251 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 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 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 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 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 event!(Level::INFO, "Trait definition");
1435 Ok(vec![])
1436 }
1437 ast::Item::Type(_) => {
1438 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 .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 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 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 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 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 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 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 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 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 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 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 .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)) = ®.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) = ®.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 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}