Skip to main content

veryl_analyzer/conv/
declaration.rs

1use crate::analyzer_error::{AnalyzerError, MismatchTypeKind, UnevaluableValueKind};
2use crate::attribute::{AllowItem, Attribute};
3use crate::attribute_table;
4use crate::conv::checker::alias::{AliasType, check_alias_target};
5use crate::conv::checker::bind::check_bind_target;
6use crate::conv::checker::clock_domain::check_clock_domain;
7use crate::conv::checker::generic::{
8    check_generic_bound, check_generic_bound_item, check_generic_expression,
9    check_generic_refereence,
10};
11use crate::conv::checker::import::check_import;
12use crate::conv::checker::inst::check_inst;
13use crate::conv::checker::modport::{check_modport, check_modport_default, check_modport_in_port};
14use crate::conv::checker::port::{check_direction, check_port_default_value, check_port_direction};
15use crate::conv::utils::{
16    TypePosition, eval_assign_statement, eval_clock, eval_const_assign, eval_expr,
17    eval_generate_for_range, eval_reset, eval_size, eval_type, eval_variable, expand_connect,
18    expand_connect_const, get_component, get_overridden_params, get_port_connects, get_return_str,
19    insert_port_connect, try_infer_decl_type, try_infer_var_assign, var_path_to_assign_destination,
20};
21use crate::conv::{Affiliation, Context, Conv};
22use crate::ir::{
23    self, Comptime, FuncArg, FuncPath, InstanceKind, IrResult, Shape, Signature, TypeKind,
24    ValueVariant, VarId, VarIndex, VarKind, VarPath, VarPathSelect, VarSelect, Variable,
25};
26use crate::namespace::DefineContext;
27use crate::symbol::{
28    ClockDomain, Direction, GenericBoundKind, Symbol, SymbolKind, TbComponentKind,
29};
30use crate::symbol_path::{GenericSymbolPath, SymbolPathNamespace};
31use crate::symbol_table;
32use crate::value::Value;
33use crate::{HashMap, ir_error};
34use std::sync::Arc;
35use veryl_parser::resource_table::{self, StrId};
36use veryl_parser::token_range::TokenRange;
37use veryl_parser::veryl_grammar_trait::*;
38
39impl Conv<&GenerateItem> for ir::DeclarationBlock {
40    fn conv(context: &mut Context, value: &GenerateItem) -> IrResult<Self> {
41        match value {
42            GenerateItem::LetDeclaration(x) => Ok(ir::DeclarationBlock::new(Conv::conv(
43                context,
44                x.let_declaration.as_ref(),
45            )?)),
46            GenerateItem::VarDeclaration(x) => Ok(ir::DeclarationBlock::new(Conv::conv(
47                context,
48                x.var_declaration.as_ref(),
49            )?)),
50            GenerateItem::AlwaysFfDeclaration(x) => {
51                let token: TokenRange = x.always_ff_declaration.as_ref().into();
52                match Conv::conv(context, x.always_ff_declaration.as_ref()) {
53                    Ok(decl) => Ok(ir::DeclarationBlock::new(decl)),
54                    Err(_) if context.in_generic => Ok(ir::DeclarationBlock::default()),
55                    Err(_) => Ok(ir::DeclarationBlock::new(ir::Declaration::Unsupported(
56                        token,
57                    ))),
58                }
59            }
60            GenerateItem::AlwaysCombDeclaration(x) => {
61                let token: TokenRange = x.always_comb_declaration.as_ref().into();
62                match Conv::conv(context, x.always_comb_declaration.as_ref()) {
63                    Ok(decl) => Ok(ir::DeclarationBlock::new(decl)),
64                    Err(_) if context.in_generic => Ok(ir::DeclarationBlock::default()),
65                    Err(_) => Ok(ir::DeclarationBlock::new(ir::Declaration::Unsupported(
66                        token,
67                    ))),
68                }
69            }
70            GenerateItem::GenerateIfDeclaration(x) => {
71                Conv::conv(context, x.generate_if_declaration.as_ref())
72            }
73            GenerateItem::GenerateForDeclaration(x) => {
74                Conv::conv(context, x.generate_for_declaration.as_ref())
75            }
76            GenerateItem::GenerateBlockDeclaration(x) => Conv::conv(
77                context,
78                x.generate_block_declaration.generate_named_block.as_ref(),
79            ),
80            GenerateItem::ConstDeclaration(x) => Ok(ir::DeclarationBlock::new(Conv::conv(
81                context,
82                x.const_declaration.as_ref(),
83            )?)),
84            GenerateItem::GenDeclaration(x) => Ok(ir::DeclarationBlock::new(Conv::conv(
85                context,
86                x.gen_declaration.as_ref(),
87            )?)),
88            GenerateItem::AssignDeclaration(x) => {
89                let token: TokenRange = x.assign_declaration.as_ref().into();
90                match Conv::conv(context, x.assign_declaration.as_ref()) {
91                    Ok(decl) => Ok(ir::DeclarationBlock::new(decl)),
92                    Err(_) if context.in_generic => Ok(ir::DeclarationBlock::default()),
93                    Err(_) => Ok(ir::DeclarationBlock::new(ir::Declaration::Unsupported(
94                        token,
95                    ))),
96                }
97            }
98            GenerateItem::FunctionDeclaration(x) => {
99                let in_generic = context.in_generic;
100                if x.function_declaration.function_declaration_opt.is_some() {
101                    context.in_generic = true;
102                }
103
104                let ret: IrResult<()> = Conv::conv(context, x.function_declaration.as_ref());
105
106                if x.function_declaration.function_declaration_opt.is_some() {
107                    context.in_generic = in_generic;
108                }
109
110                ret?;
111                Ok(ir::DeclarationBlock::default())
112            }
113            GenerateItem::StructUnionDeclaration(x) => {
114                let in_generic = context.in_generic;
115                if x.struct_union_declaration
116                    .struct_union_declaration_opt
117                    .is_some()
118                {
119                    context.in_generic = true;
120                }
121
122                let ret: IrResult<()> = Conv::conv(context, x.struct_union_declaration.as_ref());
123
124                if x.struct_union_declaration
125                    .struct_union_declaration_opt
126                    .is_some()
127                {
128                    context.in_generic = in_generic;
129                }
130
131                ret?;
132                Ok(ir::DeclarationBlock::default())
133            }
134            GenerateItem::EnumDeclaration(x) => {
135                let _: () = Conv::conv(context, x.enum_declaration.as_ref())?;
136                Ok(ir::DeclarationBlock::default())
137            }
138            GenerateItem::InitialDeclaration(x) => {
139                let token: TokenRange = x.initial_declaration.as_ref().into();
140                match Conv::conv(context, x.initial_declaration.as_ref()) {
141                    Ok(decl) => Ok(ir::DeclarationBlock::new(decl)),
142                    Err(_) if context.in_generic => Ok(ir::DeclarationBlock::default()),
143                    Err(_) => Ok(ir::DeclarationBlock::new(ir::Declaration::Unsupported(
144                        token,
145                    ))),
146                }
147            }
148            GenerateItem::FinalDeclaration(x) => {
149                let token: TokenRange = x.final_declaration.as_ref().into();
150                match Conv::conv(context, x.final_declaration.as_ref()) {
151                    Ok(decl) => Ok(ir::DeclarationBlock::new(decl)),
152                    Err(_) if context.in_generic => Ok(ir::DeclarationBlock::default()),
153                    Err(_) => Ok(ir::DeclarationBlock::new(ir::Declaration::Unsupported(
154                        token,
155                    ))),
156                }
157            }
158            GenerateItem::InstDeclaration(x) => {
159                let token: TokenRange = x.inst_declaration.as_ref().into();
160                match Conv::conv(context, x.inst_declaration.as_ref()) {
161                    Ok(decl) => Ok(ir::DeclarationBlock::new(decl)),
162                    Err(_) if context.in_generic => Ok(ir::DeclarationBlock::default()),
163                    Err(_) => Ok(ir::DeclarationBlock::new(ir::Declaration::Unsupported(
164                        token,
165                    ))),
166                }
167            }
168            GenerateItem::ConnectDeclaration(x) => {
169                Conv::conv(context, x.connect_declaration.as_ref())
170            }
171            GenerateItem::UnsafeBlock(x) => Conv::conv(context, x.unsafe_block.as_ref()),
172            GenerateItem::ImportDeclaration(x) => {
173                Conv::conv(context, x.import_declaration.as_ref())
174            }
175            GenerateItem::BindDeclaration(x) => {
176                let _: () = Conv::conv(context, x.bind_declaration.as_ref())?;
177                Ok(ir::DeclarationBlock::default())
178            }
179            GenerateItem::AliasDeclaration(x) => {
180                let _: () = Conv::conv(context, x.alias_declaration.as_ref())?;
181                Ok(ir::DeclarationBlock::default())
182            }
183            GenerateItem::TypeDefDeclaration(x) => {
184                let _: () = Conv::conv(context, x.type_def_declaration.as_ref())?;
185                Ok(ir::DeclarationBlock::default())
186            }
187            GenerateItem::EmbedDeclaration(x) => {
188                let _: () = Conv::conv(context, x.embed_declaration.as_ref())?;
189                Ok(ir::DeclarationBlock::default())
190            }
191        }
192    }
193}
194
195fn get_label(block: &GenerateOptionalNamedBlock, default: StrId) -> StrId {
196    if let Some(x) = &block.generate_optional_named_block_opt {
197        x.identifier.text()
198    } else {
199        default
200    }
201}
202
203impl Conv<&GenerateIfDeclaration> for ir::DeclarationBlock {
204    fn conv(context: &mut Context, value: &GenerateIfDeclaration) -> IrResult<Self> {
205        let label = value.generate_named_block.identifier.text();
206
207        let (comptime, _) = eval_expr(context, None, value.expression.as_ref(), false)?;
208        let cond = comptime.get_value()?;
209
210        if cond.to_usize().unwrap_or(0) != 0 {
211            context.push_hierarchy(label);
212
213            let block = context.block(|c| {
214                let block: ir::DeclarationBlock =
215                    Conv::conv(c, value.generate_named_block.as_ref())?;
216                Ok(block)
217            });
218
219            context.pop_hierarchy();
220            block
221        } else {
222            for x in &value.generate_if_declaration_list {
223                let (comptime, _) = eval_expr(context, None, x.expression.as_ref(), false)?;
224
225                let cond = comptime.get_value()?;
226
227                if cond.to_usize().unwrap_or(0) != 0 {
228                    let label = get_label(&x.generate_optional_named_block, label);
229
230                    context.push_hierarchy(label);
231
232                    let block = context.block(|c| {
233                        let block: ir::DeclarationBlock =
234                            Conv::conv(c, x.generate_optional_named_block.as_ref())?;
235                        Ok(block)
236                    });
237
238                    context.pop_hierarchy();
239                    return block;
240                }
241            }
242
243            if let Some(x) = &value.generate_if_declaration_opt {
244                let label = get_label(&x.generate_optional_named_block, label);
245
246                context.push_hierarchy(label);
247
248                let block = context.block(|c| {
249                    let block: ir::DeclarationBlock =
250                        Conv::conv(c, x.generate_optional_named_block.as_ref())?;
251                    Ok(block)
252                });
253
254                context.pop_hierarchy();
255                block
256            } else {
257                Ok(ir::DeclarationBlock::default())
258            }
259        }
260    }
261}
262
263impl Conv<&GenerateNamedBlock> for ir::DeclarationBlock {
264    fn conv(context: &mut Context, value: &GenerateNamedBlock) -> IrResult<Self> {
265        let mut ret = vec![];
266        for x in &value.generate_named_block_list {
267            let items: Vec<_> = x.generate_group.as_ref().into();
268            for item in items {
269                let item: IrResult<ir::DeclarationBlock> = Conv::conv(context, item);
270
271                if let Ok(mut item) = item {
272                    ret.append(&mut item.0);
273                }
274            }
275        }
276        Ok(ir::DeclarationBlock(ret))
277    }
278}
279
280impl Conv<&GenerateOptionalNamedBlock> for ir::DeclarationBlock {
281    fn conv(context: &mut Context, value: &GenerateOptionalNamedBlock) -> IrResult<Self> {
282        let mut ret = vec![];
283        for x in &value.generate_optional_named_block_list {
284            let items: Vec<_> = x.generate_group.as_ref().into();
285            for item in items {
286                let item: IrResult<ir::DeclarationBlock> = Conv::conv(context, item);
287
288                if let Ok(mut item) = item {
289                    ret.append(&mut item.0);
290                }
291            }
292        }
293        Ok(ir::DeclarationBlock(ret))
294    }
295}
296
297impl Conv<&GenerateForDeclaration> for ir::DeclarationBlock {
298    fn conv(context: &mut Context, value: &GenerateForDeclaration) -> IrResult<Self> {
299        let token: TokenRange = (&value.identifier.identifier_token).into();
300        let label = value.generate_named_block.identifier.text();
301
302        let rev = value.generate_for_declaration_opt.is_some();
303        let step = value
304            .generate_for_declaration_opt0
305            .as_ref()
306            .map(|x| (x.assignment_operator.as_ref(), x.expression.as_ref()));
307
308        let range = eval_generate_for_range(context, &value.range, rev, step, token)?;
309
310        let mut ret = ir::DeclarationBlock::default();
311
312        for i in range {
313            let label = format!("{}[{}]", label, i);
314            let label = resource_table::insert_str(&label);
315
316            let index = value.identifier.text();
317            let path = VarPath::new(index);
318            let kind = VarKind::Const;
319            let comptime = Comptime::create_value(Value::new(i as u64, 32, false), token);
320
321            context.push_hierarchy(label);
322
323            let block = context.block(|c| {
324                let id = c.insert_var_path(path.clone(), comptime.clone());
325                let array_limit = c.config.evaluate_array_limit;
326                let variable = Variable::new(
327                    id,
328                    path,
329                    kind,
330                    comptime.r#type.clone(),
331                    vec![comptime.get_value().unwrap().clone()],
332                    c.get_affiliation(),
333                    &token,
334                    array_limit,
335                );
336                c.insert_variable(id, variable);
337
338                let block: IrResult<ir::DeclarationBlock> =
339                    Conv::conv(c, value.generate_named_block.as_ref());
340                block
341            });
342
343            context.pop_hierarchy();
344
345            if let Ok(mut block) = block {
346                ret.0.append(&mut block.0);
347            }
348        }
349
350        Ok(ret)
351    }
352}
353
354impl Conv<&WithGenericParameterItem> for () {
355    fn conv(context: &mut Context, value: &WithGenericParameterItem) -> IrResult<Self> {
356        if let Ok(symbol) = symbol_table::resolve(value.identifier.as_ref())
357            && let SymbolKind::GenericParameter(x) = &symbol.found.kind
358        {
359            check_generic_bound_item(context, &x.bound, &symbol.found.namespace);
360            if let Some(y) = &value.with_generic_parameter_item_opt {
361                let token: TokenRange = y.with_generic_argument_item.as_ref().into();
362
363                match y.with_generic_argument_item.as_ref() {
364                    WithGenericArgumentItem::Number(_)
365                    | WithGenericArgumentItem::BooleanLiteral(_) => {
366                        if !matches!(x.bound, GenericBoundKind::Proto(_)) {
367                            context.insert_error(AnalyzerError::mismatch_assignment(
368                                "number",
369                                &x.bound.to_string(),
370                                &token,
371                                &[],
372                            ));
373                        }
374                    }
375                    WithGenericArgumentItem::FixedType(_) => {
376                        if !matches!(x.bound, GenericBoundKind::Type) {
377                            context.insert_error(AnalyzerError::mismatch_assignment(
378                                "type",
379                                &x.bound.to_string(),
380                                &token,
381                                &[],
382                            ));
383                        }
384                    }
385                    WithGenericArgumentItem::GenericArgIdentifier(_) => {
386                        // TODO
387                    }
388                }
389            }
390            Ok(())
391        } else {
392            let token: TokenRange = value.identifier.as_ref().into();
393            Err(ir_error!(token))
394        }
395    }
396}
397
398impl Conv<&WithParameterItem> for () {
399    fn conv(context: &mut Context, value: &WithParameterItem) -> IrResult<Self> {
400        let define_context: DefineContext = (&value.colon.colon_token).into();
401        if !define_context.is_default() {
402            return Ok(());
403        }
404
405        if let Ok(symbol) = symbol_table::resolve(value.identifier.as_ref())
406            && let SymbolKind::Parameter(x) = &symbol.found.kind
407        {
408            let path = VarPath::new(symbol.found.token.text);
409            let kind: VarKind = (&x.kind).into();
410            let token: TokenRange = value.into();
411            let variable_token: TokenRange = (&value.identifier.identifier_token).into();
412
413            let r#type = x.r#type.to_ir_type(context, TypePosition::Variable)?;
414
415            // parameter default value
416            let expr = if context.is_affiliated(Affiliation::ProtoModule) {
417                let comptime = Comptime::create_unknown(token);
418                let expr = ir::Expression::Term(Box::new(ir::Factor::Unknown(comptime.clone())));
419                (comptime, expr)
420            } else {
421                let Some(expr) = &x.value else {
422                    context.insert_error(AnalyzerError::missing_default_argument(
423                        &value.identifier.text().to_string(),
424                        &value.identifier.as_ref().into(),
425                    ));
426                    return Err(ir_error!(token));
427                };
428
429                eval_expr(context, Some(r#type.clone()), expr, false)?
430            };
431
432            // Get overridden parameter if it exists
433            let mut expr = context.get_override(&path).cloned().unwrap_or(expr);
434
435            let dst = ir::AssignDestination {
436                id: VarId::default(),
437                path: path.clone(),
438                index: VarIndex::default(),
439                select: VarSelect::default(),
440                comptime: Comptime::from_type(r#type, ClockDomain::None, TokenRange::default()),
441                token: variable_token,
442            };
443
444            eval_const_assign(context, kind, &dst, &mut expr)?;
445
446            Ok(())
447        } else {
448            let token: TokenRange = value.identifier.as_ref().into();
449            Err(ir_error!(token))
450        }
451    }
452}
453
454impl Conv<&PortDeclarationItem> for () {
455    fn conv(context: &mut Context, value: &PortDeclarationItem) -> IrResult<Self> {
456        let define_context: DefineContext = (&value.colon.colon_token).into();
457        if !define_context.is_default() {
458            return Ok(());
459        }
460
461        let token: TokenRange = value.into();
462
463        if let Ok(symbol) = symbol_table::resolve(value.identifier.as_ref())
464            && let SymbolKind::Port(x) = &symbol.found.kind
465        {
466            check_modport_in_port(context, value);
467            check_port_direction(context, value);
468
469            let path = VarPath::new(symbol.found.token.text);
470            let variable_token: TokenRange = (&value.identifier.identifier_token).into();
471
472            let pos = match x.direction {
473                Direction::Modport => TypePosition::Modport,
474                _ => TypePosition::Variable,
475            };
476
477            let r#type = x.r#type.to_ir_type(context, pos)?;
478            let clock_domain = x.clock_domain;
479
480            // Function args share the PortDeclarationItem grammar; skip here
481            // so they don't shadow same-named module ports in port_types.
482            if !context.is_affiliated(Affiliation::Function) {
483                context.insert_port_type(path.clone(), r#type.clone(), clock_domain);
484            }
485
486            let kind = match x.direction {
487                Direction::Input => VarKind::Input,
488                Direction::Output => VarKind::Output,
489                Direction::Inout => VarKind::Inout,
490                Direction::Modport => {
491                    match &r#type.kind {
492                        ir::TypeKind::Modport(sig, name) => {
493                            let component = get_component(context, sig, token)?;
494                            let base = value.identifier.text();
495                            let ir::Component::Interface(component) = component.as_ref() else {
496                                return Err(ir_error!(token));
497                            };
498                            let component = component.clone();
499                            context.extract_interface_member(
500                                base,
501                                &r#type.array,
502                                component,
503                                Some(*name),
504                                clock_domain,
505                                variable_token,
506                            );
507
508                            // insert path of modport instance
509                            let path = VarPath::new(value.identifier.text());
510                            let r#type = {
511                                let mut t = ir::Type::new(TypeKind::Modport(sig.clone(), *name));
512                                t.array = r#type.array;
513                                t
514                            };
515
516                            let comptime =
517                                Comptime::from_type(r#type.clone(), clock_domain, variable_token);
518                            context.insert_var_path(path.clone(), comptime);
519                        }
520                        ir::TypeKind::SystemVerilog => (),
521                        _ => {
522                            context.insert_error(AnalyzerError::mismatch_type(
523                                MismatchTypeKind::SymbolKind {
524                                    name: symbol.found.token.to_string(),
525                                    expected: "modport".to_string(),
526                                    actual: symbol.found.kind.to_kind_name(),
527                                },
528                                &variable_token,
529                            ));
530                        }
531                    }
532                    // inserting modport is completed in this block
533                    return Ok(());
534                }
535                Direction::Interface => {
536                    if let ir::TypeKind::AbstractInterface(_) = &r#type.kind {
537                        let path = VarPath::new(value.identifier.text());
538                        let comptime =
539                            Comptime::from_type(r#type.clone(), clock_domain, variable_token);
540                        context.insert_var_path(path.clone(), comptime);
541                    }
542                    return Ok(());
543                }
544                _ => {
545                    return Err(ir_error!(token));
546                }
547            };
548
549            let default_value = if let Some(x) = &x.default_value {
550                let allow_anonymous = kind == VarKind::Output;
551                let default_value = eval_expr(context, Some(r#type.clone()), x, allow_anonymous);
552
553                check_port_default_value(context, value, &default_value, kind, x);
554
555                let (comptime, _) = default_value?;
556
557                if x.is_anonymous_expression() {
558                    None
559                } else {
560                    let value = comptime.get_value()?.clone();
561                    Some((value, comptime))
562                }
563            } else {
564                None
565            };
566
567            if let Some((value, comptime)) = default_value {
568                let mut comptime = comptime.clone();
569                comptime.value = ValueVariant::Numeric(value.clone());
570                comptime.r#type = r#type.clone();
571
572                // TODO for array
573                let id = context.insert_var_path(path.clone(), comptime);
574                let array_limit = context.config.evaluate_array_limit;
575                let variable = Variable::new(
576                    id,
577                    path,
578                    kind,
579                    r#type,
580                    vec![value.clone()],
581                    context.get_affiliation(),
582                    &variable_token,
583                    array_limit,
584                );
585                context.insert_variable(id, variable);
586            } else {
587                eval_variable(context, &path, kind, &r#type, clock_domain, variable_token);
588            }
589            Ok(())
590        } else {
591            Err(ir_error!(token))
592        }
593    }
594}
595
596impl Conv<&AlwaysFfDeclaration> for ir::Declaration {
597    fn conv(context: &mut Context, value: &AlwaysFfDeclaration) -> IrResult<Self> {
598        let clock = eval_clock(context, value)?;
599        let reset = eval_reset(context, value)?;
600
601        if let Some(reset) = &reset {
602            check_clock_domain(
603                context,
604                &clock.comptime,
605                &reset.comptime,
606                &value.always_ff.always_ff_token.token,
607            );
608        }
609
610        context.current_clock = Some(clock.comptime.clone());
611
612        context.push_affiliation(Affiliation::AlwaysFf);
613
614        let statements: IrResult<ir::StatementBlock> =
615            context.block(|c| Conv::conv(c, value.statement_block.as_ref()));
616
617        context.pop_affiliation();
618
619        Ok(ir::Declaration::new_ff(clock, reset, statements?.0))
620    }
621}
622
623impl Conv<&AlwaysCombDeclaration> for ir::Declaration {
624    fn conv(context: &mut Context, value: &AlwaysCombDeclaration) -> IrResult<Self> {
625        context.push_affiliation(Affiliation::AlwaysComb);
626
627        let statements: IrResult<ir::StatementBlock> =
628            context.block(|c| Conv::conv(c, value.statement_block.as_ref()));
629
630        context.pop_affiliation();
631
632        Ok(ir::Declaration::new_comb(statements?.0))
633    }
634}
635
636impl Conv<&VarDeclaration> for ir::Declaration {
637    fn conv(context: &mut Context, value: &VarDeclaration) -> IrResult<Self> {
638        let define_context: DefineContext = (&value.var.var_token).into();
639        if !define_context.is_default() {
640            return Ok(ir::Declaration::Null);
641        }
642
643        let token: TokenRange = value.into();
644
645        if let Ok(symbol) = symbol_table::resolve(value.identifier.as_ref())
646            && let SymbolKind::Variable(x) = &symbol.found.kind
647        {
648            let path = VarPath::new(symbol.found.token.text);
649            let kind = VarKind::Variable;
650            let variable_token: TokenRange = (&value.identifier.identifier_token).into();
651
652            let r#type = x.r#type.to_ir_type(context, TypePosition::Variable)?;
653            let clock_domain = x.clock_domain;
654
655            eval_variable(context, &path, kind, &r#type, clock_domain, variable_token);
656            Ok(ir::Declaration::Null)
657        } else {
658            Err(ir_error!(token))
659        }
660    }
661}
662
663impl Conv<&LetDeclaration> for ir::Declaration {
664    fn conv(context: &mut Context, value: &LetDeclaration) -> IrResult<Self> {
665        let define_context: DefineContext = (&value.r#let.let_token).into();
666        if !define_context.is_default() {
667            return Ok(ir::Declaration::Null);
668        }
669
670        let token: TokenRange = value.into();
671
672        if let Ok(symbol) = symbol_table::resolve(value.identifier.as_ref())
673            && let SymbolKind::Variable(x) = &symbol.found.kind
674        {
675            let path = VarPath::new(symbol.found.token.text);
676            let kind = VarKind::Let;
677            let variable_token: TokenRange = (&value.identifier.identifier_token).into();
678
679            let inferred = try_infer_decl_type(
680                context,
681                &x.r#type,
682                &value.expression,
683                value.identifier.identifier_token.token.id,
684                token,
685            )?;
686            let r#type = if let Some((ref comptime, _)) = inferred {
687                comptime.r#type.clone()
688            } else {
689                x.r#type.to_ir_type(context, TypePosition::Variable)?
690            };
691            let clock_domain = x.clock_domain;
692
693            eval_variable(context, &path, kind, &r#type, clock_domain, variable_token);
694
695            let (id, comptime) = context.find_path(&path).ok_or_else(|| ir_error!(token))?;
696
697            let mut dst = ir::AssignDestination {
698                id,
699                path,
700                index: VarIndex::default(),
701                select: VarSelect::default(),
702                comptime,
703                token: variable_token,
704            };
705
706            let mut expr = if let Some(inferred) = inferred {
707                inferred
708            } else {
709                eval_expr(context, Some(r#type.clone()), &value.expression, false)?
710            };
711
712            let statements = eval_assign_statement(context, &mut dst, &mut expr, token)?;
713            Ok(ir::Declaration::new_comb(statements))
714        } else {
715            Err(ir_error!(token))
716        }
717    }
718}
719
720impl Conv<&ConstDeclaration> for ir::Declaration {
721    fn conv(context: &mut Context, value: &ConstDeclaration) -> IrResult<Self> {
722        let define_context: DefineContext = (&value.r#const.const_token).into();
723        if !define_context.is_default() {
724            return Ok(ir::Declaration::Null);
725        }
726
727        let token: TokenRange = value.into();
728
729        if let Ok(symbol) = symbol_table::resolve(value.identifier.as_ref())
730            && let SymbolKind::Parameter(x) = &symbol.found.kind
731        {
732            let path = VarPath::new(symbol.found.token.text);
733            let kind: VarKind = (&x.kind).into();
734            let variable_token: TokenRange = (&value.identifier.identifier_token).into();
735
736            let inferred = try_infer_decl_type(
737                context,
738                &x.r#type,
739                &value.expression,
740                value.identifier.identifier_token.token.id,
741                token,
742            )?;
743            let r#type = if let Some((ref comptime, _)) = inferred {
744                comptime.r#type.clone()
745            } else {
746                x.r#type.to_ir_type(context, TypePosition::Variable)?
747            };
748
749            let Some(expr) = &x.value else {
750                context.insert_error(AnalyzerError::missing_default_argument(
751                    &value.identifier.text().to_string(),
752                    &value.identifier.as_ref().into(),
753                ));
754                return Err(ir_error!(token));
755            };
756
757            let (comptime, expr) = if let Some(inferred) = inferred {
758                inferred
759            } else {
760                eval_expr(context, Some(r#type.clone()), expr, false)?
761            };
762            if !comptime.is_const {
763                context.insert_error(AnalyzerError::unevaluable_value(
764                    UnevaluableValueKind::ConstValue,
765                    &expr.token_range(),
766                ));
767            }
768
769            let dst = ir::AssignDestination {
770                id: VarId::default(),
771                path: path.clone(),
772                index: VarIndex::default(),
773                select: VarSelect::default(),
774                comptime: Comptime::from_type(r#type, ClockDomain::None, TokenRange::default()),
775                token: variable_token,
776            };
777
778            eval_const_assign(context, kind, &dst, &mut (comptime, expr))?;
779
780            Ok(ir::Declaration::Null)
781        } else {
782            Err(ir_error!(token))
783        }
784    }
785}
786
787impl Conv<&GenDeclaration> for ir::Declaration {
788    fn conv(context: &mut Context, value: &GenDeclaration) -> IrResult<Self> {
789        let define_context: DefineContext = (&value.r#gen.gen_token).into();
790        if !define_context.is_default() {
791            return Ok(ir::Declaration::Null);
792        }
793
794        if let Ok(symbol) = symbol_table::resolve(value.identifier.as_ref())
795            && let SymbolKind::GenericConst(x) = &symbol.found.kind
796        {
797            check_generic_bound_item(context, &x.bound, &symbol.found.namespace);
798            check_generic_expression(
799                context,
800                &x.value,
801                &x.bound,
802                None,
803                value.identifier.as_ref().into(),
804            );
805            Ok(ir::Declaration::Null)
806        } else {
807            Err(ir_error!(value.into()))
808        }
809    }
810}
811
812impl Conv<&AssignDeclaration> for ir::Declaration {
813    fn conv(context: &mut Context, value: &AssignDeclaration) -> IrResult<Self> {
814        let define_context: DefineContext = (&value.assign.assign_token).into();
815        if !define_context.is_default() {
816            return Ok(ir::Declaration::Null);
817        }
818
819        let token: TokenRange = value.into();
820
821        match value.assign_destination.as_ref() {
822            AssignDestination::HierarchicalIdentifier(x) => {
823                let ident = x.hierarchical_identifier.as_ref();
824
825                let inferred =
826                    if let Ok(symbol) = symbol_table::resolve(x.hierarchical_identifier.as_ref()) {
827                        try_infer_var_assign(context, &symbol.found, &value.expression, token)?
828                    } else {
829                        None
830                    };
831
832                let dst: VarPathSelect = Conv::conv(context, ident)?;
833
834                if let Some(mut dst) = dst.to_assign_destination(context, false) {
835                    let mut expr = if let Some(inferred) = inferred {
836                        inferred
837                    } else {
838                        eval_expr(
839                            context,
840                            Some(dst.comptime.r#type.clone()),
841                            &value.expression,
842                            false,
843                        )?
844                    };
845
846                    let statements = eval_assign_statement(context, &mut dst, &mut expr, token)?;
847
848                    Ok(ir::Declaration::new_comb(statements))
849                } else {
850                    if let Ok(symbol) = symbol_table::resolve(x.hierarchical_identifier.as_ref())
851                        && let SymbolKind::Variable(x) = &symbol.found.kind
852                        && x.affiliation == Affiliation::Module
853                    {
854                        let ident_token = ident.identifier.identifier_token.token;
855                        context.insert_error(AnalyzerError::referring_before_definition(
856                            &ident_token.text.to_string(),
857                            &ident_token.into(),
858                        ));
859                    }
860                    Err(ir_error!(token))
861                }
862            }
863            AssignDestination::LBraceAssignConcatenationListRBrace(x) => {
864                let items: Vec<_> = x.assign_concatenation_list.as_ref().into();
865
866                let mut dst = vec![];
867                for item in items {
868                    let ident = item.hierarchical_identifier.as_ref();
869                    let x: VarPathSelect = Conv::conv(context, ident)?;
870                    if let Some(x) = x.to_assign_destination(context, false) {
871                        dst.push(x);
872                    } else {
873                        if let Ok(symbol) =
874                            symbol_table::resolve(item.hierarchical_identifier.as_ref())
875                            && let SymbolKind::Variable(x) = &symbol.found.kind
876                            && x.affiliation == Affiliation::Module
877                        {
878                            let ident_token = ident.identifier.identifier_token.token;
879                            context.insert_error(AnalyzerError::referring_before_definition(
880                                &ident_token.text.to_string(),
881                                &ident_token.into(),
882                            ));
883                        }
884                        return Err(ir_error!(token));
885                    }
886                }
887
888                let mut width = Some(0);
889                for x in &dst {
890                    if let Some(x) = x.total_width(context)
891                        && let Some(width) = &mut width
892                    {
893                        *width += x;
894                    } else {
895                        width = None;
896                    }
897                }
898                if let Some(x) = width {
899                    width = context.check_size(x, token);
900                }
901
902                let r#type = {
903                    let mut t = ir::Type::new(TypeKind::Logic);
904                    t.set_concrete_width(Shape::new(vec![width]));
905                    t
906                };
907
908                let (_, expr) = eval_expr(context, Some(r#type), &value.expression, false)?;
909                let statement = ir::Statement::Assign(ir::AssignStatement {
910                    dst,
911                    width,
912                    expr,
913                    token,
914                });
915                Ok(ir::Declaration::Comb(ir::CombDeclaration {
916                    statements: vec![statement],
917                }))
918            }
919        }
920    }
921}
922
923impl Conv<(&FunctionDeclaration, Option<&FuncPath>)> for () {
924    fn conv(
925        context: &mut Context,
926        value: (&FunctionDeclaration, Option<&FuncPath>),
927    ) -> IrResult<Self> {
928        let (func_def, path) = value;
929        let token: TokenRange = (&func_def.identifier.identifier_token).into();
930
931        if let Some(x) = &func_def.function_declaration_opt {
932            check_generic_bound(context, &x.with_generic_parameter);
933        }
934
935        if let Ok(symbol) = symbol_table::resolve(func_def.identifier.as_ref())
936            && let SymbolKind::Function(_) = &symbol.found.kind
937        {
938            let port_declaration = func_def
939                .function_declaration_opt0
940                .as_ref()
941                .map(|x| x.port_declaration.as_ref());
942            let statement_block = Some(func_def.statement_block.as_ref());
943            conv_function(
944                context,
945                path,
946                &symbol.found,
947                port_declaration,
948                statement_block,
949                token,
950            )
951        } else {
952            Err(ir_error!(token))
953        }
954    }
955}
956
957impl Conv<&FunctionDeclaration> for () {
958    fn conv(context: &mut Context, value: &FunctionDeclaration) -> IrResult<Self> {
959        // ignore IrError of generic function
960        if value.function_declaration_opt.is_some() {
961            context.ignore_var_func = true;
962        }
963
964        let ret = Conv::conv(context, (value, None));
965
966        if value.function_declaration_opt.is_some() {
967            context.ignore_var_func = false;
968            Ok(())
969        } else {
970            ret
971        }
972    }
973}
974
975impl Conv<(&ProtoFunctionDeclaration, Option<&FuncPath>)> for () {
976    fn conv(
977        context: &mut Context,
978        value: (&ProtoFunctionDeclaration, Option<&FuncPath>),
979    ) -> IrResult<Self> {
980        let (func_def, path) = value;
981        let token: TokenRange = (&func_def.identifier.identifier_token).into();
982
983        if let Some(x) = &func_def.proto_function_declaration_opt {
984            check_generic_bound(context, &x.with_generic_parameter);
985        }
986
987        if let Ok(symbol) = symbol_table::resolve(func_def.identifier.as_ref())
988            && let SymbolKind::ProtoFunction(_) = &symbol.found.kind
989        {
990            let port_declaration = func_def
991                .proto_function_declaration_opt0
992                .as_ref()
993                .map(|x| x.port_declaration.as_ref());
994            conv_function(context, path, &symbol.found, port_declaration, None, token)
995        } else {
996            Err(ir_error!(token))
997        }
998    }
999}
1000
1001fn conv_function(
1002    context: &mut Context,
1003    path: Option<&FuncPath>,
1004    symbol: &Symbol,
1005    port_declaratoin: Option<&PortDeclaration>,
1006    statement_block: Option<&StatementBlock>,
1007    token: TokenRange,
1008) -> IrResult<()> {
1009    // insert VarPath for function before statement_block conv
1010    // because it may be refered by recursive function
1011    let (path, name, namespace) = if let Some(path) = path {
1012        let name = resource_table::insert_str(&path.sig.to_string());
1013        (path.clone(), name, path.sig.namespace())
1014    } else {
1015        let path = FuncPath::new(symbol.id);
1016        (path, symbol.token.text, symbol.inner_namespace())
1017    };
1018
1019    if context.func_paths.contains_key(&path) {
1020        // The given function has already been added through function reference before definition.
1021        return Ok(());
1022    }
1023    let id = context.insert_func_path(path.clone());
1024
1025    let proeprty = match &symbol.kind {
1026        SymbolKind::Function(x) => x,
1027        SymbolKind::ProtoFunction(x) => x,
1028        _ => unreachable!(),
1029    };
1030
1031    let ret_type = if let Some(ret_type) = proeprty.ret.as_ref() {
1032        let mut r#type = ret_type.to_ir_type(context, TypePosition::Variable)?;
1033        if r#type.is_struct() {
1034            r#type.set_concrete_width(Shape::new(vec![r#type.total_width()]));
1035            r#type.kind = if r#type.is_2state() {
1036                TypeKind::Bit
1037            } else {
1038                TypeKind::Logic
1039            };
1040        }
1041        Comptime::from_type(r#type, ClockDomain::None, token)
1042    } else {
1043        let r#type = ir::Type::new(TypeKind::Void);
1044        Comptime::from_type(r#type, ClockDomain::None, token)
1045    };
1046
1047    let mut args = vec![];
1048    let ports: Vec<_> = if let Some(port_declaratoin) = port_declaratoin
1049        && let Some(ref x) = port_declaratoin.port_declaration_opt
1050    {
1051        x.port_declaration_list.as_ref().into()
1052    } else {
1053        vec![]
1054    };
1055    let arity = ports.len();
1056
1057    context.push_affiliation(Affiliation::Function);
1058    context.push_hierarchy(name);
1059    context.push_namespace(namespace);
1060
1061    let token: TokenRange = symbol.token.into();
1062    let func = context.block(|c| {
1063        let ret_id = if ret_type.r#type.kind != TypeKind::Void {
1064            let path = VarPath::new(get_return_str());
1065            let kind = VarKind::Variable;
1066            let r#type = ret_type.r#type.clone();
1067
1068            // insert return value as variable
1069            if let Some(_total_array) = r#type.total_array()
1070                && let Some(total_width) = r#type.total_width()
1071            {
1072                // type.expand is not necessary
1073                // because member access is not allowed for return value
1074                // All elements are initialized to the same x-state
1075                // value, so a single template suffices.
1076                let values = vec![Value::new_x(total_width, false)];
1077
1078                let ret_id = c.insert_var_path(path.clone(), ret_type.clone());
1079                let array_limit = c.config.evaluate_array_limit;
1080                let variable = Variable::new(
1081                    ret_id,
1082                    path,
1083                    kind,
1084                    r#type,
1085                    values,
1086                    c.get_affiliation(),
1087                    &token,
1088                    array_limit,
1089                );
1090                c.insert_variable(ret_id, variable);
1091                Some(ret_id)
1092            } else {
1093                None
1094            }
1095        } else {
1096            None
1097        };
1098
1099        let mut arg_map = HashMap::default();
1100        for port in ports {
1101            let _: IrResult<()> = Conv::conv(c, port);
1102
1103            let name = port.identifier.text();
1104            let path = VarPath::new(name);
1105            if let Some((_, comptime)) = c.var_paths.get(&path).cloned() {
1106                let modport_members = comptime.r#type.expand_modport(c, &path, token)?;
1107                if modport_members.is_empty() {
1108                    let mut members = vec![];
1109                    if let Some((id, comptime)) = c.var_paths.get(&path).cloned() {
1110                        let variable = c.variables.get(&id).ok_or_else(|| ir_error!(token))?;
1111                        let direction = match variable.kind {
1112                            VarKind::Input => Direction::Input,
1113                            VarKind::Output => Direction::Output,
1114                            _ => unreachable!(),
1115                        };
1116
1117                        arg_map.insert(path.clone(), id);
1118                        members.push((path.clone(), comptime, direction));
1119                    }
1120                    let arg = FuncArg {
1121                        name: path.first(),
1122                        comptime,
1123                        members,
1124                    };
1125                    args.push(arg);
1126                } else {
1127                    let mut members = vec![];
1128                    for (path, direction) in modport_members {
1129                        if let Some((id, comptime)) = c.var_paths.get(&path) {
1130                            arg_map.insert(path.clone(), *id);
1131                            members.push((path, comptime.clone(), direction));
1132                        }
1133                    }
1134                    let arg = FuncArg {
1135                        name: path.first(),
1136                        comptime,
1137                        members,
1138                    };
1139                    args.push(arg);
1140                }
1141            }
1142        }
1143
1144        let body = if let Some(block) = statement_block {
1145            let disable_const_opt = c.disalbe_const_opt;
1146            c.disalbe_const_opt = true;
1147            let statements: IrResult<ir::StatementBlock> = Conv::conv(c, block);
1148            c.disalbe_const_opt = disable_const_opt;
1149
1150            vec![ir::FunctionBody {
1151                ret: ret_id,
1152                arg_map,
1153                statements: statements?.0,
1154            }]
1155        } else {
1156            vec![]
1157        };
1158
1159        Ok((args, body))
1160    });
1161
1162    context.pop_affiliation();
1163    context.pop_hierarchy();
1164    context.pop_namespace();
1165
1166    let (args, body) = func?;
1167    let func = ir::Function {
1168        name,
1169        id,
1170        path,
1171        r#type: ret_type,
1172        array: Shape::default(),
1173        arity,
1174        args,
1175        is_const: proeprty.constantable.unwrap(),
1176        functions: body,
1177        token,
1178    };
1179
1180    // function should be inserted outside the function scope
1181    context.insert_function(id, func);
1182    Ok(())
1183}
1184
1185impl Conv<&StructUnionDeclaration> for () {
1186    fn conv(context: &mut Context, value: &StructUnionDeclaration) -> IrResult<Self> {
1187        eval_type(
1188            context,
1189            &value.identifier.as_ref().into(),
1190            TypePosition::TypeDef,
1191        )?;
1192
1193        if let Some(x) = &value.struct_union_declaration_opt {
1194            check_generic_bound(context, &x.with_generic_parameter);
1195        }
1196
1197        if context.is_affiliated(Affiliation::Interface) {
1198            let kind = match value.struct_union.as_ref() {
1199                StructUnion::Struct(_) => "struct",
1200                StructUnion::Union(_) => "union",
1201            };
1202            context.insert_error(AnalyzerError::invalid_type_declaration(kind, &value.into()));
1203        }
1204
1205        Ok(())
1206    }
1207}
1208
1209impl Conv<&EnumDeclaration> for () {
1210    fn conv(context: &mut Context, value: &EnumDeclaration) -> IrResult<Self> {
1211        eval_type(
1212            context,
1213            &value.identifier.as_ref().into(),
1214            TypePosition::TypeDef,
1215        )?;
1216
1217        if context.is_affiliated(Affiliation::Interface) {
1218            context.insert_error(AnalyzerError::invalid_type_declaration(
1219                "enum",
1220                &value.into(),
1221            ));
1222        }
1223
1224        Ok(())
1225    }
1226}
1227
1228impl Conv<&InitialDeclaration> for ir::Declaration {
1229    fn conv(context: &mut Context, value: &InitialDeclaration) -> IrResult<Self> {
1230        let statements: ir::StatementBlock = Conv::conv(context, value.statement_block.as_ref())?;
1231        Ok(ir::Declaration::Initial(ir::InitialDeclaration {
1232            statements: statements.0,
1233        }))
1234    }
1235}
1236
1237impl Conv<&FinalDeclaration> for ir::Declaration {
1238    fn conv(context: &mut Context, value: &FinalDeclaration) -> IrResult<Self> {
1239        let statements: ir::StatementBlock = Conv::conv(context, value.statement_block.as_ref())?;
1240        Ok(ir::Declaration::Final(ir::FinalDeclaration {
1241            statements: statements.0,
1242        }))
1243    }
1244}
1245
1246impl Conv<&InstDeclaration> for ir::Declaration {
1247    fn conv(context: &mut Context, value: &InstDeclaration) -> IrResult<Self> {
1248        let define_context: DefineContext = (&value.inst.inst_token).into();
1249        if !define_context.is_default() {
1250            return Ok(ir::Declaration::Null);
1251        }
1252
1253        let in_module = context.is_affiliated(Affiliation::Module);
1254        let token = value.inst.inst_token.token;
1255        check_inst(context, in_module, &token, &value.component_instantiation);
1256
1257        let path: SymbolPathNamespace = value
1258            .component_instantiation
1259            .scoped_identifier
1260            .as_ref()
1261            .into();
1262        if let Ok(symbol) = symbol_table::resolve(&path)
1263            && let SymbolKind::TbComponent(ref tb_prop) = symbol.found.kind
1264        {
1265            if !context.in_test_module {
1266                let token: TokenRange = value
1267                    .component_instantiation
1268                    .scoped_identifier
1269                    .as_ref()
1270                    .into();
1271                context.insert_error(AnalyzerError::invalid_tb_usage(&token));
1272                return Ok(ir::Declaration::Null);
1273            }
1274
1275            // Generate clock/reset variable for $tb component instances
1276            let inst_token: TokenRange = value.component_instantiation.identifier.as_ref().into();
1277            let inst_name = value
1278                .component_instantiation
1279                .identifier
1280                .identifier_token
1281                .token
1282                .text;
1283            let var_path = ir::VarPath::new(inst_name);
1284            let type_kind = match tb_prop.kind {
1285                TbComponentKind::ClockGen => ir::TypeKind::Clock,
1286                TbComponentKind::ResetGen => ir::TypeKind::Reset,
1287            };
1288            let ir_type = {
1289                let mut t = ir::Type::new(type_kind);
1290                t.set_concrete_width(ir::Shape::new(vec![Some(1)]));
1291                t
1292            };
1293            eval_variable(
1294                context,
1295                &var_path,
1296                ir::VarKind::Variable,
1297                &ir_type,
1298                ClockDomain::None,
1299                inst_token,
1300            );
1301
1302            // Suppress UnassignVariable for $tb component variables
1303            attribute_table::insert(inst_token, Attribute::Allow(AllowItem::UnassignVariable));
1304
1305            if let Some(ref opt1) = value.component_instantiation.component_instantiation_opt1
1306                && let Some(ref param_opt) = opt1.inst_parameter.inst_parameter_opt
1307            {
1308                let items: Vec<_> = param_opt.inst_parameter_list.as_ref().into();
1309                for item in items {
1310                    let param_name = item.identifier.identifier_token.token.text.to_string();
1311                    match (&tb_prop.kind, param_name.as_str()) {
1312                        (TbComponentKind::ResetGen, "cycles") => {
1313                            if let Some(ref opt) = item.inst_parameter_item_opt {
1314                                let (_, expr) = eval_expr(context, None, &opt.expression, false)?;
1315                                context.tb_reset_cycles.insert(inst_name, expr);
1316                            }
1317                        }
1318                        (TbComponentKind::ClockGen, "period") => {
1319                            if let Some(ref opt) = item.inst_parameter_item_opt {
1320                                let (_, expr) = eval_expr(context, None, &opt.expression, false)?;
1321                                context.tb_clock_period.insert(inst_name, expr);
1322                            }
1323                        }
1324                        _ => {}
1325                    }
1326                }
1327            }
1328
1329            let mut connected_clk: Option<StrId> = None;
1330            if let Some(ref opt2) = value.component_instantiation.component_instantiation_opt2
1331                && let Some(ref port_opt) = opt2.inst_port.inst_port_opt
1332            {
1333                let items: Vec<_> = port_opt.inst_port_list.as_ref().into();
1334                for item in items {
1335                    let port_name = item.identifier.identifier_token.token.text;
1336                    let port_name_str =
1337                        resource_table::get_str_value(port_name).unwrap_or_default();
1338                    let port_token: TokenRange = item.identifier.as_ref().into();
1339                    match (&tb_prop.kind, port_name_str.as_str()) {
1340                        (TbComponentKind::ResetGen, "clk") => {
1341                            let signal_name = if let Some(ref x) = item.inst_port_item_opt {
1342                                let dst: Vec<VarPathSelect> =
1343                                    Conv::conv(context, x.expression.as_ref())?;
1344                                dst.first().and_then(|p| p.0.0.first().copied())
1345                            } else {
1346                                Some(port_name)
1347                            };
1348                            if let Some(name) = signal_name {
1349                                connected_clk = Some(name);
1350                            }
1351                        }
1352                        _ => {
1353                            let type_name = match tb_prop.kind {
1354                                TbComponentKind::ClockGen => "$tb::clock_gen",
1355                                TbComponentKind::ResetGen => "$tb::reset_gen",
1356                            };
1357                            context.insert_error(AnalyzerError::unknown_tb_port(
1358                                type_name,
1359                                &port_name_str,
1360                                &port_token,
1361                            ));
1362                        }
1363                    }
1364                }
1365            }
1366
1367            if matches!(tb_prop.kind, TbComponentKind::ResetGen) {
1368                if let Some(clk) = connected_clk {
1369                    context.tb_reset_clock.insert(inst_name, clk);
1370                } else {
1371                    let type_name: TokenRange = value
1372                        .component_instantiation
1373                        .scoped_identifier
1374                        .as_ref()
1375                        .into();
1376                    context.insert_error(AnalyzerError::missing_tb_port(
1377                        "$tb::reset_gen",
1378                        "clk",
1379                        &type_name,
1380                    ));
1381                }
1382            }
1383
1384            return Ok(ir::Declaration::Null);
1385        }
1386
1387        let clock_domain = if let Ok(symbol) =
1388            symbol_table::resolve(value.component_instantiation.identifier.as_ref())
1389            && let SymbolKind::Instance(x) = &symbol.found.kind
1390        {
1391            x.clock_domain
1392        } else {
1393            ClockDomain::None
1394        };
1395
1396        let value = value.component_instantiation.as_ref();
1397        let token: TokenRange = value.identifier.as_ref().into();
1398        let generic_path: GenericSymbolPath = value.scoped_identifier.as_ref().into();
1399
1400        check_generic_refereence(context, &generic_path);
1401
1402        let mut sig =
1403            Signature::from_path(context, generic_path).ok_or_else(|| ir_error!(token))?;
1404        let symbol = symbol_table::get(sig.symbol).unwrap();
1405
1406        let parameters = symbol.kind.get_parameters();
1407        let overridden_params = get_overridden_params(context, value)?;
1408        for x in parameters {
1409            let path = VarPath::new(x.name);
1410            if let Some(value) = overridden_params.get(&path) {
1411                sig.add_parameter(x.name, value.0.value.clone());
1412            }
1413        }
1414
1415        context.push_override(overridden_params);
1416
1417        let component = context.block(|c| get_component(c, &sig, token));
1418
1419        context.pop_override();
1420
1421        let component_arc = component?;
1422        match component_arc.as_ref() {
1423            ir::Component::Module(component) => {
1424                let mut inputs = vec![];
1425                let mut outputs = vec![];
1426                if let Some(x) = &value.component_instantiation_opt2
1427                    && let Some(x) = &x.inst_port.inst_port_opt
1428                {
1429                    let ports: Vec<_> = x.inst_port_list.as_ref().into();
1430                    let mut clock_domain_table = HashMap::default();
1431
1432                    for port in ports {
1433                        let name = port.identifier.text();
1434                        let path = VarPath::new(name);
1435                        let token: TokenRange = port.identifier.as_ref().into();
1436                        if let Some((dst_type, clock_domain)) = component.port_types.get(&path) {
1437                            let connects =
1438                                get_port_connects(context, component, port, &path, dst_type, token);
1439                            let Ok(connects) = connects else {
1440                                continue;
1441                            };
1442
1443                            let dst_comptime =
1444                                Comptime::from_type(dst_type.clone(), *clock_domain, token);
1445
1446                            for (path, dst, mut expr) in connects {
1447                                if let Some(id) = component.ports.get(&path)
1448                                    && let Some(variable) = component.variables.get(id)
1449                                {
1450                                    let expr_comptime = expr.eval_comptime(context, None);
1451
1452                                    if let Some(x) =
1453                                        clock_domain_table.get(&dst_comptime.clock_domain)
1454                                    {
1455                                        check_clock_domain(context, x, expr_comptime, &token.beg);
1456                                    } else {
1457                                        clock_domain_table.insert(
1458                                            dst_comptime.clock_domain,
1459                                            expr_comptime.clone(),
1460                                        );
1461                                    }
1462
1463                                    insert_port_connect(
1464                                        context,
1465                                        variable,
1466                                        dst,
1467                                        expr,
1468                                        &mut inputs,
1469                                        &mut outputs,
1470                                    );
1471                                }
1472                            }
1473                        }
1474                    }
1475                }
1476
1477                // TOOD modport parameter override
1478
1479                let name = value.identifier.text();
1480                Ok(ir::Declaration::Inst(Box::new(ir::InstDeclaration {
1481                    name,
1482                    inputs,
1483                    outputs,
1484                    component: component_arc,
1485                })))
1486            }
1487            ir::Component::Interface(component) => {
1488                let mut array = Shape::default();
1489                if let Some(x) = &value.component_instantiation_opt0 {
1490                    let exprs: Vec<_> = x.array.as_ref().into();
1491                    for expr in exprs {
1492                        let (_, value) = eval_size(context, expr, false)?;
1493                        array.push(value)
1494                    }
1495                }
1496
1497                let base = value.identifier.text();
1498                context.extract_interface_member(
1499                    base,
1500                    &array,
1501                    component.clone(),
1502                    None,
1503                    clock_domain,
1504                    token,
1505                );
1506
1507                // insert path of interface instance
1508                let path = VarPath::new(value.identifier.text());
1509                let r#type = {
1510                    let mut t = ir::Type::new(TypeKind::Instance(sig, InstanceKind::Interface));
1511                    t.array = array;
1512                    t
1513                };
1514
1515                let comptime = Comptime::from_type(r#type.clone(), clock_domain, token);
1516                context.insert_var_path(path.clone(), comptime);
1517
1518                Ok(ir::Declaration::Null)
1519            }
1520            ir::Component::SystemVerilog(component) => {
1521                // SV blackboxes are small (just a connects list) and the
1522                // conv path mutates them here to add ports — take an owned
1523                // copy instead of sharing through the Arc cache.
1524                let mut component = component.clone();
1525                if let Some(x) = &value.component_instantiation_opt2
1526                    && let Some(x) = &x.inst_port.inst_port_opt
1527                {
1528                    let ports: Vec<_> = x.inst_port_list.as_ref().into();
1529                    let mut prev_port = None;
1530                    for port in ports {
1531                        let dst_paths = if let Some(x) = &port.inst_port_item_opt {
1532                            let dst: Vec<VarPathSelect> =
1533                                Conv::conv(context, x.expression.as_ref())?;
1534                            dst
1535                        } else {
1536                            let name = port.identifier.text();
1537                            let path = VarPath::new(name);
1538                            let path = VarPathSelect(
1539                                path,
1540                                VarSelect::default(),
1541                                port.identifier.identifier_token.token.into(),
1542                            );
1543                            vec![path]
1544                        };
1545
1546                        let mut expanded_paths = vec![];
1547                        for dst_path in dst_paths {
1548                            if let Some((_, comptime)) = context.find_path(&dst_path.0) {
1549                                if comptime.r#type.is_interface() {
1550                                    let paths = comptime.r#type.expand_interface(
1551                                        context,
1552                                        &dst_path.0,
1553                                        comptime.token,
1554                                    )?;
1555                                    for x in paths {
1556                                        let path =
1557                                            VarPathSelect(x.0, dst_path.1.clone(), dst_path.2);
1558                                        expanded_paths.push(path);
1559                                    }
1560                                } else {
1561                                    expanded_paths.push(dst_path);
1562                                }
1563                            }
1564                        }
1565
1566                        let mut dst_paths =
1567                            var_path_to_assign_destination(context, expanded_paths, true);
1568
1569                        for dst_path in &dst_paths {
1570                            if let Some((_, comptime)) = context.find_path(&dst_path.path) {
1571                                // All port of SV instance should have the same clock domain
1572                                if let Some(prev) = &prev_port {
1573                                    check_clock_domain(context, &comptime, prev, &token.beg);
1574                                }
1575
1576                                // Check implicit reset to SV instance
1577                                if comptime.r#type.is_reset()
1578                                    && !comptime.r#type.is_explicit_reset()
1579                                {
1580                                    context.insert_error(AnalyzerError::sv_with_implicit_reset(
1581                                        &token,
1582                                    ));
1583                                }
1584
1585                                prev_port = Some(comptime);
1586                            }
1587                        }
1588
1589                        component.connects.append(&mut dst_paths);
1590                    }
1591                }
1592                let name = value.identifier.text();
1593                let component = Arc::new(ir::Component::SystemVerilog(component));
1594                Ok(ir::Declaration::Inst(Box::new(ir::InstDeclaration {
1595                    name,
1596                    inputs: vec![],
1597                    outputs: vec![],
1598                    component,
1599                })))
1600            }
1601        }
1602    }
1603}
1604
1605impl Conv<&ConnectDeclaration> for ir::DeclarationBlock {
1606    fn conv(context: &mut Context, value: &ConnectDeclaration) -> IrResult<Self> {
1607        let token: TokenRange = value.into();
1608
1609        let lhs: VarPathSelect = Conv::conv(context, value.hierarchical_identifier.as_ref())?;
1610        let rhs: Vec<VarPathSelect> = Conv::conv(context, value.expression.as_ref())?;
1611
1612        let (comptime, _) = eval_expr(context, None, value.expression.as_ref(), false)?;
1613
1614        let statements = if comptime.is_const {
1615            expand_connect_const(context, lhs, comptime, token)?
1616        } else {
1617            if rhs.len() != 1 {
1618                context.insert_error(AnalyzerError::mismatch_type(
1619                    MismatchTypeKind::ConnectMultipleExpression,
1620                    &token,
1621                ));
1622                return Err(ir_error!(token));
1623            }
1624
1625            let rhs = rhs[0].clone();
1626
1627            expand_connect(context, lhs, rhs, token)?
1628        };
1629
1630        let ret = ir::CombDeclaration { statements };
1631        let ret = ir::Declaration::Comb(ret);
1632        Ok(ir::DeclarationBlock(vec![ret]))
1633    }
1634}
1635
1636impl Conv<&UnsafeBlock> for ir::DeclarationBlock {
1637    fn conv(context: &mut Context, value: &UnsafeBlock) -> IrResult<Self> {
1638        let mut ret = vec![];
1639        for x in &value.unsafe_block_list {
1640            let items: Vec<_> = x.generate_group.as_ref().into();
1641            for item in items {
1642                let item: IrResult<ir::DeclarationBlock> = Conv::conv(context, item);
1643
1644                if let Ok(mut item) = item {
1645                    ret.append(&mut item.0);
1646                }
1647            }
1648        }
1649        Ok(ir::DeclarationBlock(ret))
1650    }
1651}
1652
1653impl Conv<&ImportDeclaration> for ir::DeclarationBlock {
1654    fn conv(context: &mut Context, value: &ImportDeclaration) -> IrResult<Self> {
1655        check_import(context, value);
1656        Ok(ir::DeclarationBlock::default())
1657    }
1658}
1659
1660impl Conv<&BindDeclaration> for () {
1661    fn conv(context: &mut Context, value: &BindDeclaration) -> IrResult<Self> {
1662        if let Ok(symbol) = symbol_table::resolve(value.scoped_identifier.as_ref()) {
1663            if !check_bind_target(context, &value.scoped_identifier, &symbol.found) {
1664                return Ok(());
1665            }
1666
1667            let in_module = symbol.found.is_module(true);
1668            let token = value.bind.bind_token.token;
1669            check_inst(context, in_module, &token, &value.component_instantiation);
1670        }
1671
1672        Ok(())
1673    }
1674}
1675
1676impl Conv<&EmbedDeclaration> for () {
1677    fn conv(context: &mut Context, value: &EmbedDeclaration) -> IrResult<Self> {
1678        for x in &value.embed_content.embed_content_list {
1679            if let EmbedItem::EmbedScopedIdentifier(x) = x.embed_item.as_ref() {
1680                let path = x.embed_scoped_identifier.scoped_identifier.as_ref();
1681
1682                let token: TokenRange = path.identifier().into();
1683                let generic_path: GenericSymbolPath = path.into();
1684
1685                // Call get_component for identifier in embed declaration to check generic instances
1686                if let Ok(symbol) = symbol_table::resolve(path)
1687                    && matches!(
1688                        symbol.found.kind,
1689                        SymbolKind::Module(_) | SymbolKind::Interface(_)
1690                    )
1691                {
1692                    let sig = Signature::from_path(context, generic_path)
1693                        .ok_or_else(|| ir_error!(token))?;
1694                    let _component = context.block(|c| get_component(c, &sig, token));
1695                }
1696            }
1697        }
1698
1699        Ok(())
1700    }
1701}
1702
1703impl Conv<&AliasDeclaration> for () {
1704    fn conv(context: &mut Context, value: &AliasDeclaration) -> IrResult<Self> {
1705        let r#type = match value.alias_declaration_group.as_ref() {
1706            AliasDeclarationGroup::Module(_) => AliasType::Module,
1707            AliasDeclarationGroup::Interface(_) => AliasType::Interface,
1708            AliasDeclarationGroup::Package(_) => AliasType::Package,
1709        };
1710
1711        check_alias_target(context, &value.scoped_identifier, r#type);
1712
1713        Ok(())
1714    }
1715}
1716
1717impl Conv<&TypeDefDeclaration> for () {
1718    fn conv(context: &mut Context, value: &TypeDefDeclaration) -> IrResult<Self> {
1719        if let Ok(symbol) = symbol_table::resolve(value.identifier.as_ref())
1720            && let SymbolKind::TypeDef(x) = &symbol.found.kind
1721        {
1722            x.r#type.to_ir_type(context, TypePosition::TypeDef)?;
1723        }
1724
1725        Ok(())
1726    }
1727}
1728
1729impl Conv<&ModportDeclaration> for () {
1730    fn conv(context: &mut Context, value: &ModportDeclaration) -> IrResult<Self> {
1731        context.push_affiliation(Affiliation::Modport);
1732
1733        let ret = context.block(|c| {
1734            if let Some(x) = &value.modport_declaration_opt {
1735                let items: Vec<_> = x.modport_list.as_ref().into();
1736                for item in &items {
1737                    check_modport(c, item);
1738                    check_direction(c, &item.direction);
1739                }
1740            }
1741
1742            if let Some(x) = &value.modport_declaration_opt0 {
1743                check_modport_default(c, x.modport_default.as_ref(), value.identifier.text());
1744            }
1745
1746            if let Ok(symbol) = symbol_table::resolve(value.identifier.as_ref())
1747                && let SymbolKind::Modport(x) = &symbol.found.kind
1748            {
1749                let sig = if let Some(x) = c.get_current_signature() {
1750                    x.clone()
1751                } else {
1752                    Signature::new(x.interface)
1753                };
1754
1755                let name = value.identifier.text();
1756                let path = VarPath::new(name);
1757                let r#type = ir::Type::new(ir::TypeKind::Modport(sig, symbol.found.token.text));
1758                let token: TokenRange = value.identifier.as_ref().into();
1759
1760                let comptime = Comptime::from_type(r#type, ClockDomain::None, token);
1761                c.insert_var_path(path, comptime);
1762
1763                let mut members = vec![];
1764                for x in &x.members {
1765                    let symbol = symbol_table::get(*x).unwrap();
1766                    match &symbol.kind {
1767                        SymbolKind::ModportVariableMember(x) => {
1768                            members.push((symbol.token.text, x.direction));
1769                        }
1770                        SymbolKind::ModportFunctionMember(_) => {
1771                            members.push((symbol.token.text, Direction::Import));
1772                        }
1773                        _ => (),
1774                    }
1775                }
1776                c.insert_modport(name, members);
1777            }
1778            Ok(())
1779        });
1780
1781        context.pop_affiliation();
1782        ret
1783    }
1784}