Skip to content

super: walk up inheritance chain past empty intermediates#2594

Merged
borisbat merged 1 commit intomasterfrom
super-walk-up-skip-empty
May 6, 2026
Merged

super: walk up inheritance chain past empty intermediates#2594
borisbat merged 1 commit intomasterfrom
super-walk-up-skip-empty

Conversation

@borisbat
Copy link
Copy Markdown
Collaborator

@borisbat borisbat commented May 6, 2026

Summary

Fixes three related gaps in the super machinery when an intermediate class/struct defines no constructor, no method, or no finalizer.

  • super() constructor. Was crashing with SIGSEGV on super(arg) because the rewrite ran in InferTypes::preVisit(ExprCall*), where arg->type is still nullptr and findMatchingFunctions segfaults on null type lookups. Moved the rewrite to visit (post-visit), where children are typed. Also added the walk-up loop and "no matching super constructor" / "too many candidates" diagnostics for parity with super.method().
  • super.method(). Already had walk-up logic in visit(ExprCall*); left untouched, now exercised by new tests.
  • delete super.self. Was always casting to the immediate parent, so a chain like class C : B {} / class B : A { def operator delete } silently dropped A's finalizer when invoked from C. Now walks selfStruct->parent->parent… and stops at the nearest ancestor whose type resolves a user-defined finalize, filtering out auto-generated generateStructureFinalizer / makeClassFinalize via Function::generated (those don't chain).

The walk-up matches by argument types, so overloaded super(args) calls and super.method(args) overrides still pick the correct ancestor.

What changed

  • src/ast/ast_infer_type.cpp — moved super constructor rewrite from preVisit to post-visit; added walk-up + diagnostics for delete super.self.
  • tests/language/super.das — 4 new skip-level tests (default ctor, ctor with args, method, method with args).
  • tests/language/super_finalize.das — 4 new tests: 1-empty intermediate class, 2-empty deep class, mid-chain (only some ancestors define finalizers), struct skip-level.
  • doc/source/reference/language/classes.rst — added a "walks up the inheritance chain to the nearest ancestor that does" subsection with a worked Base/Mid/Leaf example; updated the delete super.self rewrite description from cast<Base> to cast<Ancestor>.
  • doc/source/reference/language/structs.rst — same one-line correction for delete super.self.

Test plan

  • tests/language/super.das — 12/12 pass under interpreter and AOT (was 8 before; 4 new skip-level cases)
  • tests/language/super_finalize.das — 8/8 pass under interpreter and AOT (was 4 before; 4 new skip-level cases)
  • Full tests/language/ — 925/925 pass under interpreter (was 917 before adding the 8 new cases)
  • Full tests/language/ under AOT (test_aot -use-aot) — 925/925 pass
  • tests/language/cant_delete_super_self.das (negative test) — still produces the same expected compile errors
  • Sphinx build clean (no warnings) on the two updated RST files
  • examples/wip.das (the original repro) compiles and runs

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 6, 2026 20:53
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes several inheritance “skip-level” edge cases in daScript’s super lowering by making super()/super.method()/delete super.self resolve past empty intermediate classes/structs, and adds tests + documentation updates to lock in the intended behavior.

Changes:

  • Move super(args) constructor rewriting to post-visit type inference and add ancestor walk-up + improved diagnostics.
  • Update delete super.self lowering to search up the inheritance chain for the nearest ancestor finalizer (skipping empty intermediates).
  • Add language tests for skip-level inheritance cases and update the reference docs accordingly.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/ast/ast_infer_type.cpp Adjusts inference-time rewrites for super() constructors, super calls, and delete super.self to walk up inheritance chains and improve diagnostics.
tests/language/super.das Adds tests covering skip-level constructor and method resolution through an empty intermediate class.
tests/language/super_finalize.das Adds tests ensuring delete super.self chains correctly past empty intermediates for both classes and structs.
doc/source/reference/language/classes.rst Documents super walk-up behavior and updates delete super.self rewrite description.
doc/source/reference/language/structs.rst Updates delete super.self rewrite description for structs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ast/ast_infer_type.cpp
Comment thread doc/source/reference/language/structs.rst Outdated
@borisbat borisbat force-pushed the super-walk-up-skip-empty branch from 3cda894 to 7a012af Compare May 6, 2026 21:20
@borisbat borisbat requested a review from Copilot May 6, 2026 21:21
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment thread src/ast/ast_infer_type.cpp
Fixes three related gaps when an intermediate class/struct in the chain
defines neither the constructor, the method being overridden, nor a
finalizer.

* `super()` constructor: rewritten in InferTypes::visit (post-visit),
  not preVisit, so argument types are populated. Walks up
  classParent->parent->... and matches by argument types.
  Previously crashed with SIGSEGV on `super(arg)` because preVisit fed
  null arg->type into findMatchingFunctions.

* `super.method()` already had walk-up logic (visit ExprCall) but was
  missing this fix.

* `delete super.self`: walks up selfStruct->parent looking for the
  nearest ancestor whose type resolves a user-defined `finalize`.
  Filters out auto-generated finalizers (generateStructureFinalizer /
  makeClassFinalize) via Function::generated, since they don't chain
  to ancestors. Previously cast unconditionally to the immediate
  parent, silently dropping ancestor finalizers when the parent was
  empty.

Tests/language: 4 new skip-level cases for super/super.method in
super.das and 4 new cases (1-empty, 2-empty, mid-chain, struct-skip)
in super_finalize.das. classes.rst and structs.rst updated to
describe the walk-up.

925/925 tests/language pass under both interpreter and AOT.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@borisbat borisbat force-pushed the super-walk-up-skip-empty branch from 7a012af to ae69669 Compare May 6, 2026 21:34
@borisbat borisbat requested a review from Copilot May 6, 2026 21:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comment thread src/ast/ast_infer_type.cpp
Comment thread src/ast/ast_infer_type.cpp
Comment thread src/ast/ast_infer_type.cpp
@borisbat borisbat merged commit 7cc094c into master May 6, 2026
35 checks passed
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