Repository: veryl-lang/veryl (Rust)

Issue:
Wrong namespace prefix for member reference of const struct
Wrong namespace prefix added to member reference of const struct.

Example:

Veryl
```systemverilog
package a_pkg::<V: u32> {
    struct a_struct {
      a: u32,
    }

    const A: a_struct = a_struct'{ a: V };
}

package b_pkg::<V: u32> {
   const B: u32 = V;
}

alias package APkg = a_pkg::<32>;
alias package BPkg = b_pkg::<APkg::A.a>;
```

Generated SV
```systemverilog
package project___a_pkg__32;
    typedef struct packed {
        int unsigned a;
    } a_struct;

    localparam a_struct A = '{a: 32};
endpackage

package project___b_pkg____a_pkg__32_A_a;
    localparam int unsigned B = project___a_pkg__32.project_a_pkg::A.a; // should be `project___a_pkg__32::A.a`
endpackage
```


Discussion:

Patch:
diff --git a/crates/emitter/src/emitter.rs b/crates/emitter/src/emitter.rs
index cd801ad15..1a53d63d6 100644
--- a/crates/emitter/src/emitter.rs
+++ b/crates/emitter/src/emitter.rs
@@ -1422,6 +1422,7 @@ impl Emitter {
                 let text = symbol_string(
                     &identifier.identifier_token,
                     &symbol.found,
+                    &symbol.found.namespace,
                     &symbol.full_path,
                     &symbol.generic_tables,
                     &context,
@@ -1741,6 +1742,7 @@ impl Emitter {
         let text = symbol_string(
             token,
             &target_type,
+            &target_type.namespace,
             &target_path,
             &target_tables,
             &context,
@@ -2390,6 +2392,7 @@ impl VerylWalker for Emitter {
                     let text = symbol_string(
                         arg.identifier(),
                         &symbol.found,
+                        &symbol.found.namespace,
                         &symbol.full_path,
                         &symbol.generic_tables,
                         &context,
@@ -5669,6 +5672,7 @@ fn get_generic_instance(symbol: &Symbol, generic_tables: &GenericTables) -> Opti
 pub fn symbol_string(
     token: &VerylToken,
     symbol: &Symbol,
+    symbol_namespace: &Namespace,
     full_path: &[SymbolId],
     generic_tables: &GenericTables,
     context: &SymbolContext,
@@ -5699,21 +5703,17 @@ pub fn symbol_string(
         | SymbolKind::Union(_)
         | SymbolKind::TypeDef(_)
         | SymbolKind::Enum(_) => {
-            let visible = namespace.included(&symbol.namespace)
+            let visible = namespace.included(symbol_namespace)
                 || symbol.imported.iter().any(|x| x.namespace == namespace);
             if (scope_depth == 1) & visible & !context.in_import {
                 ret.push_str(&token_text);
             } else {
-                ret.push_str(&namespace_string(
-                    &symbol.namespace,
-                    generic_tables,
-                    context,
-                ));
+                ret.push_str(&namespace_string(symbol_namespace, generic_tables, context));
                 ret.push_str(&token_text);
             }
         }
         SymbolKind::EnumMember(x) => {
-            let mut enum_namespace = symbol.namespace.clone();
+            let mut enum_namespace = symbol_namespace.clone();
             enum_namespace.pop();
 
             // if enum definition is scoped or it is not visible, explicit namespace is required
@@ -5750,11 +5750,7 @@ pub fn symbol_string(
             );
             let add_namespace = (scope_depth >= 2) | !visible | top_level;
             if add_namespace {
-                ret.push_str(&namespace_string(
-                    &symbol.namespace,
-                    generic_tables,
-                    context,
-                ));
+                ret.push_str(&namespace_string(symbol_namespace, generic_tables, context));
             }
             if context.build_opt.hashed_mangled_name {
                 let name = symbol
@@ -5791,23 +5787,31 @@ pub fn symbol_string(
         }
         SymbolKind::StructMember(_) | SymbolKind::UnionMember(_) => {
             // for this case, struct member/union member is given as generic argument
-            for x in full_path
+            let symbols: Vec<_> = full_path
                 .iter()
                 .enumerate()
                 .filter(|(i, _)| (i + 1) < full_path.len())
                 .map(|(_, id)| symbol_table::get(*id).unwrap())
-                .filter(|x| !matches!(x.kind, SymbolKind::Namespace | SymbolKind::Package(_)))
-            {
-                let text = symbol_string(
-                    token,
-                    &x,
-                    &[],
-                    &GenericTables::default(),
-                    context,
-                    scope_depth,
-                );
-                ret.push_str(&text);
-                ret.push('.');
+                .collect();
+            for (i, symbol) in symbols.iter().enumerate() {
+                if !(matches!(symbol.kind, SymbolKind::Namespace) || symbol.is_package(false)) {
+                    let symbol_namespace = if i == 0 {
+                        &symbol.namespace
+                    } else {
+                        &symbols[i - 1].inner_namespace()
+                    };
+                    let text = symbol_string(
+                        token,
+                        symbol,
+                        symbol_namespace,
+                        &[],
+                        &GenericTables::default(),
+                        context,
+                        1,
+                    );
+                    ret.push_str(&text);
+                    ret.push('.');
+                }
             }
             ret.push_str(&token_text);
         }
diff --git a/crates/emitter/src/expaneded_modport.rs b/crates/emitter/src/expaneded_modport.rs
index 014580f9e..e05ade948 100644
--- a/crates/emitter/src/expaneded_modport.rs
+++ b/crates/emitter/src/expaneded_modport.rs
@@ -344,6 +344,7 @@ impl ExpandedModportPortTable {
                 let text = symbol_string(
                     namespace_token,
                     &interface_symbol,
+                    &interface_symbol.namespace,
                     &interface_path,
                     &interface_tables,
                     context,


Test patch:
diff --git a/crates/emitter/src/tests.rs b/crates/emitter/src/tests.rs
index bc2ce729c..2e602ace1 100644
--- a/crates/emitter/src/tests.rs
+++ b/crates/emitter/src/tests.rs
@@ -2034,6 +2034,48 @@ module prj_ModuleB;
     endfunction
     logic _a; always_comb _a = __Func__Pkg_QUX_baz();
 endmodule
+//# sourceMappingURL=test.sv.map
+"#;
+
+    let metadata: Metadata =
+        toml::from_str(&Metadata::create_default_toml("prj").unwrap()).unwrap();
+
+    let ret = if cfg!(windows) {
+        emit(&metadata, code).replace("\r\n", "\n")
+    } else {
+        emit(&metadata, code)
+    };
+
+    println!("ret\n{}\nexp\n{}", ret, expect);
+    assert_eq!(ret, expect);
+
+    let code = r#"package foo_pkg::<V: u32> {
+    struct foo_struct {
+      foo: u32,
+    }
+
+    const FOO: foo_struct = foo_struct'{ foo: V };
+}
+package bar_pkg::<V: u32> {
+   const BAR: u32 = V;
+}
+
+alias package FooPkg = foo_pkg::<32>;
+alias package BarPkg = bar_pkg::<FooPkg::FOO.foo>;
+"#;
+
+    let expect = r#"package prj___foo_pkg__32;
+    typedef struct packed {
+        int unsigned foo;
+    } foo_struct;
+
+    localparam foo_struct FOO = '{foo: 32};
+endpackage
+package prj___bar_pkg____foo_pkg__32_FOO_foo;
+    localparam int unsigned BAR = prj___foo_pkg__32::FOO.foo;
+endpackage
+
+
 //# sourceMappingURL=test.sv.map
 "#;