Add struct defaults, methods, and operator overloading decorators - #847
Draft
Ryan-D-Gast wants to merge 22 commits into
Draft
Add struct defaults, methods, and operator overloading decorators#847Ryan-D-Gast wants to merge 22 commits into
Ryan-D-Gast wants to merge 22 commits into
Conversation
Pre-register struct methods before compiling their bodies so forward and mutual method recursion resolves correctly at bytecode compile time.\n\nAdd VM support for compiling into reserved method chunks.\n\nExpand struct-method edge-case tests in parser, typechecker, and interpreter suites.
Allow 'Self { ... }' in struct-construction expressions when typechecking inside struct methods.
Add parser/typechecker/interpreter coverage for 'Self { ... }' usage, including constructor and instance method paths.
Previously, struct instances stored fields as Vec<Value> and VM field access used a mutable/remove-style path. Non-generic struct instantiation in the typechecker also rebuilt equivalent instantiated StructInfo values repeatedly.
This change:
- stores Value::StructInstance fields as Arc<[Value]>;
- builds struct instances directly as Arc<[Value]> in the VM;
- accesses struct fields via borrowed slice + clone of selected field (no swap_remove path);
- updates FFI struct construction/unpacking callsites for the shared immutable field storage;
- caches non-generic struct instantiations in the typechecker and invalidates on struct redefinition.
Example of prior inefficiency in Numbat:
struct Builder {
a: Scalar,
b: Scalar,
c: Scalar,
fn new() = Builder { a: 0, b: 0, c: 0 }
fn with_a(self, a: Scalar) = Self { a: a, b: self.b, c: self.c }
fn with_b(self, b: Scalar) = Self { a: self.a, b: b, c: self.c }
fn with_c(self, c: Scalar) = Self { a: self.a, b: self.b, c: c }
}
Builder::new().with_a(1).with_b(2).with_c(3)
In this chained builder pattern, cloning intermediate struct values is now much cheaper because struct payload clones are Arc refcount bumps rather than Vec payload clones.
Reduce overlapping struct-method coverage while preserving unique parser/typechecker/runtime behavior checks.
Test suite changes:
- Removed duplicated interpreter struct-method scenarios that were already covered by focused namespace/recursion tests.
- Removed duplicated typechecker success cases for simple Self-return updates now covered by broader nested/generic/where/Self scenarios.
- Kept distinct negative-path checks (constructor-vs-instance misuse, wrong arity, dimension mismatch, missing method, Self outside methods).
Docs/examples changes:
- Clarified struct method semantics in the book (Self { ... } support and immutable return semantics).
- Updated syntax example source to use Self { ... } for constructor/update methods.
- Regenerated the autogenerated syntax reference page from examples/numbat_syntax.nbt.
Validation:
- cargo test -p numbat
Author
|
Follow-up on this draft: the branch now also supports explicit reverse operator decorators for rhs-owned dispatch. Added decorators:
Resolution order is intentionally Python-like and directional:
Example: This stays explicit and directional; it does not infer commutativity or auto-generate reversed overloads. |
Author
|
Added two follow-up commits on top of this draft:
Example: This is mainly meant to demonstrate the intended use case for the new indexing/operator-overloading features. I didn't want to do PR spam so I just included it in this draft. Obviously a built in Matrix/Tensor type is optimal so this example of a pure native numbat matrix type is more so to show the use case rather then recommending this be in the standard library. |
Author
This was referenced Mar 23, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This draft consolidates the changes from #845 and #846 into one branch. Read this PR as the combined follow-up, not as an independent line of work.
Summary
This adds struct-owned operator overloads via inline method decorators and moves
+/-/*//resolution onto the same deferred-constraint path used for other inference-sensitive operations.This branch is currently stacked on top of:
So this PR should be read as a stacked draft for now, not as an independently mergeable branch against
mainin its current form.It is also closely related to:
Add/SuboperationsMotivation
The immediate motivation is #462: built-in
DateTimeoperator overloading currently fails when an intermediate type variable is involved, for example:This change fixes that deferred-resolution case and also exposes a constrained, coherence-friendly way for user-defined structs to participate in operator overloading.
The implementation follows the same general direction that fixed #459: do not require the expression type to already be concrete at the first lookup site, and instead carry a deferred constraint until more type information is available.
Syntax
Operator overloads are declared on instance methods inside a
structbody:Supported decorators in this PR:
@add@sub@mul@divFor these decorators, the compiler infers:
selfThese decorators are only valid on struct instance methods. Top-level operator decorators are rejected, and overloads remain directional rather than generating an implicit reversed form.
Design
This intentionally takes a narrower path than a full trait/typeclass system.
Instead of introducing user-visible
Add/Subtraits and qualified operator bounds, operator implementations are owned by the lhs struct and lowered into an internal operator-implementation table. Binary expressions can emit deferred operator constraints, which are resolved after more type information is known.That gives us:
DateTimecase from Add/Sub type classes for operator overloading #462What this does not do is infer or expose most-general operator-bounded function types such as a hypothetical:
That remains future work if Numbat eventually grows a real trait/typeclass mechanism.
Implementation Notes
@add/@sub/@mul/@divon struct methods.DateTimeoperator behavior and struct-owned operator methods through the same deferred path.Tests
Added coverage for:
id(now()) + 4 daysdeferred-resolution case from Add/Sub type classes for operator overloading #462Addresses #462.
Related: #459, #845, #846.