Skip to content

Comments

Allow resolving method calls to methods declared in hierarchical parents#1688

Open
mampcs wants to merge 1 commit intoMikePopoloski:masterfrom
mampcs:hier_resolve
Open

Allow resolving method calls to methods declared in hierarchical parents#1688
mampcs wants to merge 1 commit intoMikePopoloski:masterfrom
mampcs:hier_resolve

Conversation

@mampcs
Copy link
Contributor

@mampcs mampcs commented Feb 23, 2026

VCS allows a child module instance to refer to a method declared in a parent module (as long as all parents of that child module have a definition of that method).

@MikePopoloski
Copy link
Owner

Man, this is cursed. It seems like all commercial tools allow this though -- I can kind of see how they could interpret this as the rule from 23.8 and 23.8.1, even though I don't really agree. It probably makes sense for slang to allow this without a flag though, since all other tools allow it.

// Normal name lookup and resolution.
LookupResult result;
Lookup::name(syntax, context, flags, result);

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think instead of doing another lookup, you can instead check for this prior to doing the lookup and set the AlwaysAllowUpward flag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what i tried first :-)
But that leads to other issues. Let me add it back and detail the error that it was leading to. I believe it was causing one of the unit tests to fail where there was a class method declared outside the the class using templates.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making the following code change resulted in multiple unit test failures:

@@ -1006,9 +1006,11 @@ Expression& Expression::bindName(Compilation& comp, const NameSyntax& syntax,
                                  const ArrayOrRandomizeMethodExpressionSyntax* withClause,
                                  const ASTContext& context) {
     bitmask<LookupFlags> flags = LookupFlags::None;
-    if ((invocation && invocation->arguments) ||
-        comp.hasFlag(CompilationFlags::AllowUseBeforeDeclare)) {
-        flags |= LookupFlags::AllowDeclaredAfter;
+    if (invocation && invocation->arguments) {
+        if (comp.hasFlag(CompilationFlags::AllowUseBeforeDeclare)) {
+            flags |= LookupFlags::AllowDeclaredAfter;
+        }
+        flags |= LookupFlags::AlwaysAllowUpward;
     }

Unit test failures:

-------------------------------------------------------------------------------
Subroutine lookup
-------------------------------------------------------------------------------
/.../slang/tests/unittests/ast/LookupTests.cpp:452
...............................................................................

/.../slang/tests/unittests/ast/LookupTests.cpp:471: FAILED:
explicitly with message:
  source:7:20: error: expression is not callable
          int b = foo();
                  ~~~^

/.../slang/tests/unittests/ast/LookupTests.cpp:476: FAILED:
  CHECK( block.find<VariableSymbol>("b").getInitializer()->eval(context).integer() == 4 )
due to unexpected exception with message:
  std::get: wrong index for variant

-------------------------------------------------------------------------------
Spurious inheritance from generic param error -- GH #1058
-------------------------------------------------------------------------------
/.../slang/tests/unittests/ast/ClassTests.cpp:3356
...............................................................................

/.../slang/tests/unittests/ast/ClassTests.cpp:3405: FAILED:
explicitly with message:
  source:37:8: error: use of undeclared identifier 'get_name'
      if(get_name() == "string") begin
         ^~~~~~~~

-------------------------------------------------------------------------------
Driver full compilation with defines and param overrides
-------------------------------------------------------------------------------
/.../slang/tests/unittests/DriverTests.cpp:389
...............................................................................

/.../slang/tests/unittests/DriverTests.cpp:401: FAILED:
  CHECK( driver.runFullCompilation() )
with expansion:
  false

/.../slang/tests/unittests/DriverTests.cpp:402: FAILED:
  CHECK( stdoutContains("Build succeeded") )
with expansion:
  false

-------------------------------------------------------------------------------
Used-before-declared flag with parameters
-------------------------------------------------------------------------------
/.../slang/tests/unittests/ast/LookupTests.cpp:2595
...............................................................................

/.../slang/tests/unittests/ast/LookupTests.cpp:2636: FAILED:
explicitly with message:
  source:4:16: error: identifier 'WIDTH' used before its declaration
    output wire [WIDTH-1:0] rd, // use before declaration
                 ^~~~~
  source:11:13: note: declared here
    parameter WIDTH = 16;
              ^
  source:7:15: error: identifier 'WIDTH' used before its declaration
    input wire [WIDTH-1:0] wd // use before declaration
                ^~~~~
  source:11:13: note: declared here
    parameter WIDTH = 16;
              ^
  source:29:23: error: identifier 'COLS' used before its declaration
      input   [7+$clog2(COLS):0]   A;
                        ^~~~
  source:32:16: note: declared here
      localparam COLS=4;

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because your patch disables AllowUseBeforeDeclare for non-subroutines. The correct patch looks like:

--- a/source/ast/Expression.cpp
+++ b/source/ast/Expression.cpp
@@ -1006,10 +1006,11 @@ Expression& Expression::bindName(Compilation& comp, const NameSyntax& syntax,
                                  const ArrayOrRandomizeMethodExpressionSyntax* withClause,
                                  const ASTContext& context) {
     bitmask<LookupFlags> flags = LookupFlags::None;
-    if ((invocation && invocation->arguments) ||
-        comp.hasFlag(CompilationFlags::AllowUseBeforeDeclare)) {
+    if (invocation && invocation->arguments)
+        flags |= LookupFlags::AllowDeclaredAfter | LookupFlags::AlwaysAllowUpward;
+
+    if (comp.hasFlag(CompilationFlags::AllowUseBeforeDeclare))
         flags |= LookupFlags::AllowDeclaredAfter;
-    }

Then there's only one failing test, Spurious inheritance from generic param error -- GH #1058, which is actually an "issue" with the AlwaysAllowUpward lookup handling. I think this patch is the right fix for that:

--- a/source/ast/Lookup.cpp
+++ b/source/ast/Lookup.cpp
@@ -1170,8 +1170,12 @@ void Lookup::name(const NameSyntax& syntax, const ASTContext& context, bitmask<L

     if (!result.found) {
         if (flags.has(LookupFlags::AlwaysAllowUpward)) {
+            LookupResult originalResult = result;
             if (!lookupUpward({}, name, context, flags, result))
                 return;
+
+            if (!result.found)
+                result = originalResult;
         }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry my mistake. Your first patch is kind of what i had earlier and it was leading to that exact same failure (Spurious inheritance from generic param error). Let me take a look as your second patch.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically the saved LookupResult contains the flag indicating that we found a class with an error in its base class resolution, which prevents us from issuing a follow-on error. If upward lookup fails to find anything we want to grab that original result back so we don't lose that flag.

@codecov
Copy link

codecov bot commented Feb 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.53%. Comparing base (67e0e90) to head (074410a).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1688      +/-   ##
==========================================
- Coverage   94.54%   94.53%   -0.01%     
==========================================
  Files         235      235              
  Lines       53764    53769       +5     
==========================================
+ Hits        50830    50833       +3     
- Misses       2934     2936       +2     
Files with missing lines Coverage Δ
source/ast/Expression.cpp 96.98% <100.00%> (+<0.01%) ⬆️
source/ast/Lookup.cpp 97.04% <100.00%> (-0.07%) ⬇️

... and 1 file with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 67e0e90...074410a. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Major vendors allows a child module instance to refer to a method declared
in a parent module (as long as all parents of that child module have a
definition of that method).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants