diff --git a/README.md b/README.md index a649538..bdbb05c 100644 --- a/README.md +++ b/README.md @@ -416,7 +416,7 @@ mismatches reject before the body; a bad return does not roll back completed wri The per-call return definition remains rooted until success/error cleanup. See [function-types.endor](examples/function-types.endor) for actual Endor source. -Declared-field construction/write checks are described above. Static inference, override variance, generics, +Declared-field construction/write checks are described above. Static inference, generics, static effect typing and complete native signature coverage remain pending. Native APIs retain their domain-specific value and resource checks. @@ -424,7 +424,7 @@ Native methods publish positional argument intervals through Aster CallArity. Th Native typed contracts now cover measure(Block, Mount, Constraints): MeasureResult, arrange(Block, Mount, Bounds): Arrangement, paint(Block, Mount, Bounds): DisplayList and hitTest(Block, Mount, Point): Bool, plus Point geometry queries and Window, Block/Container/Page, Mount, Task, Event, Subscription and ObjectList.at methods. The explicit receiver is checked before binding dispatch. Aster checks types before invoking the implementation and checks actual returns, including final results delegated to Endor functions. Signature definitions are traced managed edges; pending delegated return checks retain execution roots and use the existing VM frame/budget machinery. Wrong returns release temporary results but do not roll back earlier writes. -Host CallEffect distinguishes ReadOnly, Mutating and External. A declared mutating/external call is rejected before invocation during read-only rendering; declaring ReadOnly does not bypass the existing heap write barrier. Legacy missing metadata remains unknown, and trusted native code still must obey its I/O contract. CallType supports single types and flat nonempty unions, retaining every branch definition. Page.open accepts Window or nil, Page.window returns Window or nil, and Block.on accepts Bool or String for its optional phase. Mount parent/child/sibling/Page queries have nullable results; bounds() returns an actual Bounds object so Point.inside(mount.bounds()) composes directly. Scope/resource eligibility, numeric ranges and phase names keep their domain checks. This adds native contract metadata, not Endor union syntax. Constructors and other management APIs are not yet fully covered. Source override variance, trait type/effect compatibility and static effect inference remain pending. Existing [visual-protocols.endor](examples/visual-protocols.endor) and [layout-negotiation.endor](examples/layout-negotiation.endor) exercise the typed visual methods with actual Endor code. +Host CallEffect distinguishes ReadOnly, Mutating and External. A declared mutating/external call is rejected before invocation during read-only rendering; declaring ReadOnly does not bypass the existing heap write barrier. Legacy missing metadata remains unknown, and trusted native code still must obey its I/O contract. CallType supports single types and flat nonempty unions, retaining every branch definition. Page.open accepts Window or nil, Page.window returns Window or nil, and Block.on accepts Bool or String for its optional phase. Mount parent/child/sibling/Page queries have nullable results; bounds() returns an actual Bounds object so Point.inside(mount.bounds()) composes directly. Scope/resource eligibility, numeric ranges and phase names keep their domain checks. This adds native contract metadata, not Endor union syntax. Constructors and other management APIs are not yet fully covered. Source overrides now check contravariant parameters and covariant returns at class construction, method replacement and before invocation with dynamically resolved annotations. Ancestor replacement checks overriding descendants before mutation. Forward names may defer until invocation, where unresolved or incompatible contracts fail before the body. Known native effects may only shrink; source effects remain unknown and read-only execution retains its barriers. Trait type/effect compatibility and static effect inference remain pending. See [method-contracts.endor](examples/method-contracts.endor) for real Endor widening parameters to Object and returning a MeasureResult subclass. Existing [visual-protocols.endor](examples/visual-protocols.endor) and [layout-negotiation.endor](examples/layout-negotiation.endor) exercise the typed visual methods with actual Endor code. Event listeners and onClick callbacks must be Functions with known metadata accepting zero arguments or one Event. If both forms work, the dispatcher passes Event. Binding computes and Task callbacks must accept zero arguments, checked before replacing a binding, adding a connection or scheduling work. Native and source callbacks follow the same rule. See [native-signatures.endor](examples/native-signatures.endor) for real native one-argument and zero-argument handlers. Event and ObjectList subclass allocators also check declared field suffixes before allocation. diff --git a/examples/method-contracts.endor b/examples/method-contracts.endor new file mode 100644 index 0000000..28432af --- /dev/null +++ b/examples/method-contracts.endor @@ -0,0 +1,29 @@ +class Sized: MeasureResult {} +class BaseRect: RectBlock { + function measure(self: BaseRect, mount: Mount, constraints: Constraints): MeasureResult { + return RectBlock.measure(self, mount, constraints) + } +} +class WideRect: BaseRect { + function measure(self: WideRect, mount: Object, constraints: Object): Sized { + measured = BaseRect.measure(self, mount, constraints) + return Sized(measured.width, measured.height) + } +} +class Actions { + rect: WideRect + count: Object + function widen(self: Actions): Unit { + self.rect.width = 260 + self.count.increment() + } +} +rect = WideRect(180, 60, 8, "#ED6A5A") +count = ui.state(0) +actions = Actions(rect, count) +button = Button("扩大矩形") +button.onClick(actions.widen) +root = Column(24, 16, TextBlock("参数可以更宽,返回类型可以更具体", 2), rect, button, ui.textOf(count, "value")) +page = Page() +page.title = "方法覆写契约" +page.render(root) diff --git a/src/README.md b/src/README.md index fecaa25..dac71b9 100644 --- a/src/README.md +++ b/src/README.md @@ -7,7 +7,7 @@ | activity.rs | Bounded in-memory HTTP exchanges with marked previews and allowlisted request headers | | endor_host.rs | Aster compilation, fresh PUT candidates retained after successful publication, shared Console/program VM, asynchronous completion/fault boundaries, bounded execution, root cleanup and AST developer tree for function/class/member assignment nodes | | endor_script.rs | Batch complete inputs for Aster's Interactive parser/compiler, retain Module entry, and rebase Aster spans to the original snapshot | -| ui_bridge.rs | Inheritable native nominal constructors with source fields, shared type method tables and bound receiver dispatch, configured unified Function classification and source/field annotation checks with native constructor preflight and shared native arity intervals plus resolved types/unions/effects on methods, tab registry, stable-slot insertion, source-dispatched tab reorder/drop and callback registration checks, Page lifecycle/navigation and immutable frame extraction | +| ui_bridge.rs | Inheritable native nominal constructors with source fields, shared type method tables and bound receiver dispatch, configured unified Function classification and source/field/override checks with native constructor preflight and shared native arity intervals plus resolved types/unions/effects on methods, tab registry, stable-slot insertion, source-dispatched tab reorder/drop and callback registration checks, Page lifecycle/navigation and immutable frame extraction | | binding.rs | Target-owned managed property bindings, weak Subscription tokens, zero-argument callback preflight, read-only compute and borrowed dependency/output caches | | block.rs | Aster-managed Block/Cell/data graph, stable child slots, content-owned field storage, shared assignment, cycle validation and explicit text bindings | | snapshot.rs | Property and visual-method identity dependencies across shared field-storage paths, reverse parent links and cached immutable visual derivation | diff --git a/src/api.rs b/src/api.rs index e185ae6..8b6213e 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1097,13 +1097,13 @@ Native wheel POST /events uses kind scroll with the normal window/session/layout ui.after(owner,key,milliseconds,callback) returns Task. The zero-argument callback runs on the owning VM worker after at least the integer 0..86400000 ms delay and must return Unit. Idle polling can add up to 100 ms; execution load may delay delivery further. Task.cancel/active/status expose cancellation, eligibility and pending/running/completed/cancelled/failed state. One owner/key replaces the prior pending task. Owner scopes and callback captures are managed strong edges; scheduler and owner links are weak. ui is an application owner. Hidden live objects continue; Page close cancels its and its Mount tasks, even close/reopen within one input. Page movement preserves Page tasks but replaces Mounts. Window close cancels its Pages/tasks; reset/DELETE/program replacement discard old scheduler/VM and generation checks reject stale publication. Failed PUT preserves old tasks. Callback failure stops the domain and cancels remaining tasks; earlier writes do not roll back. Read-only rendering cannot schedule/cancel. Limits: 64 pending per owner, 1024 per runtime, 64 delivered per poll, 128 frames/100000 instructions per callback. Providers currently support timers; network/file providers and await are pending. -Source function parameter annotations are checked before the body and return annotations before returning. Object accepts any live value; Unit accepts nil; Number, String/Text and Bool match scalar kinds. Function includes source/native functions and bound methods, while callable class definitions remain Type. View is a compatibility alias for Block. Other names resolve actual Type/Trait objects in the function's globals then captured builtins at each call. The method's own class uses the receiver's actual owning definition, preserving old bound methods after REPL redefinition. Other type names are not frozen at definition. UnknownAnnotation and TypeMismatch report failures; bad returns do not roll back earlier writes. Declared-field construction and set_attr writes are also checked, as described below. Static inference, override variance, generic and effect typing remain pending. +Source function parameter annotations are checked before the body and return annotations before returning. Object accepts any live value; Unit accepts nil; Number, String/Text and Bool match scalar kinds. Function includes source/native functions and bound methods, while callable class definitions remain Type. View is a compatibility alias for Block. Other names resolve actual Type/Trait objects in the function's globals then captured builtins at each call. The method's own class uses the receiver's actual owning definition, preserving old bound methods after REPL redefinition. Other type names are not frozen at definition. UnknownAnnotation and TypeMismatch report failures; bad returns do not roll back earlier writes. Declared-field construction and set_attr writes are also checked, as described below. Static inference, generic and static effect typing remain pending. -Declared fields are checked at construction and set_attr writes through the same enabled type-check switch. Native subclass fields are validated before native allocation/opening effects. Failure preserves the field; declared deletion is rejected. Inherited fields use their declaring definition/environment, the declaring class name keeps its actual identity after REPL redefinition, and other names resolve dynamically in declaration globals/builtins. Shared Block forwarding selects the current content type/storage. This does not scan old objects or revalidate all referring fields after shared-reference changes; trusted payload/storage/attach operations and complete initialization remain host responsibilities. Static/effect typing, override variance and local/captured type annotations remain pending. See examples/field-contracts.endor. +Declared fields are checked at construction and set_attr writes through the same enabled type-check switch. Native subclass fields are validated before native allocation/opening effects. Failure preserves the field; declared deletion is rejected. Inherited fields use their declaring definition/environment, the declaring class name keeps its actual identity after REPL redefinition, and other names resolve dynamically in declaration globals/builtins. Shared Block forwarding selects the current content type/storage. This does not scan old objects or revalidate all referring fields after shared-reference changes; trusted payload/storage/attach operations and complete initialization remain host responsibilities. Static/effect typing and local/captured type annotations remain pending. See examples/field-contracts.endor. Native methods now publish positional CallArity intervals, checked by the VM before invocation. Source defaults and bound receivers participate in the same query. Traits validate acceptance of their canonical count including self and reject native methods without metadata. Event listeners/onClick require Function metadata accepting zero arguments or one Event, preferring Event if both work. Binding computes and Task callbacks require zero-argument acceptance before registration/replacement. Generic Type constructor reflection and complete native signature coverage remain separate gaps. See examples/native-signatures.endor. -Native CallSignature checks resolved parameter/return types for visual measure/arrange/paint/hitTest, Point queries, Window/Block/Container/Page/Mount/Task/Event/Subscription methods and ObjectList.at. Signature definitions are traced managed edges. Parameters fail before dispatch; final results delegated to source/native functions must satisfy all pending native return contracts, with rooted definitions and the same frame/instruction budgets. Wrong returns release scoped results but do not undo earlier writes. CallEffect ReadOnly allows result allocation; Mutating/External calls are rejected before invocation during read-only execution. Existing heap barriers still apply, and this does not sandbox trusted native I/O. CallType unions preserve every branch definition. Page.open accepts Window or nil; page.window returns Window or nil; Block.on accepts Bool or String for its optional phase, while phase names retain domain validation. Mount relation queries are nullable, and bounds() is a real Bounds object usable by Point.inside. Native union metadata does not add Endor union syntax. Constructors/other API coverage, override variance and static effect inference remain pending. Unknown legacy metadata remains unknown. See examples/visual-protocols.endor and examples/layout-negotiation.endor. +Native CallSignature checks resolved parameter/return types for visual measure/arrange/paint/hitTest, Point queries, Window/Block/Container/Page/Mount/Task/Event/Subscription methods and ObjectList.at. Signature definitions are traced managed edges. Parameters fail before dispatch; final results delegated to source/native functions must satisfy all pending native return contracts, with rooted definitions and the same frame/instruction budgets. Wrong returns release scoped results but do not undo earlier writes. CallEffect ReadOnly allows result allocation; Mutating/External calls are rejected before invocation during read-only execution. Existing heap barriers still apply, and this does not sandbox trusted native I/O. CallType unions preserve every branch definition. Page.open accepts Window or nil; page.window returns Window or nil; Block.on accepts Bool or String for its optional phase, while phase names retain domain validation. Mount relation queries are nullable, and bounds() is a real Bounds object usable by Point.inside. Native union metadata does not add Endor union syntax. Source method overrides now enforce parameter contravariance, return covariance and accepted-count preservation at class creation/method replacement, with call-time rechecks for dynamic annotation names. Ancestor replacement checks overriding descendants atomically. Unknown forward names may defer until invocation; known incompatibilities reject before effects. Native effects may only shrink; source effects remain unknown and runtime read-only barriers still apply. See examples/method-contracts.endor. Constructors/other API coverage, trait type/effect requirements and static effect inference remain pending. Unknown legacy metadata remains unknown. See examples/visual-protocols.endor and examples/layout-negotiation.endor. Trait definitions Visual, EventTarget, Children and Presentable are managed nominal capabilities. ui.implement(type,capability) explicitly declares an implementation after required-method/positional-arity checks; ui.implements(type,capability) and ui.supports(value,capability) query it. Block declares Visual/EventTarget; ContainerBlock declares Children, inherited by Page. Same-name methods alone never declare an implementation. Source subclass construction and host method replacement validate inherited/affected descendant contracts. Source/native positional intervals are checked, and missing native arity metadata prevents a capability declaration; source parameter/return annotations are checked at invocation. Trait type/effect compatibility and formal trait syntax remain pending. EventTarget requires canonical on/dispatch/onEvent arities 3/2/1 after self; native optional convenience forms remain. diff --git a/src/endor_host.rs b/src/endor_host.rs index c6bd7e6..a1c5f3a 100644 --- a/src/endor_host.rs +++ b/src/endor_host.rs @@ -2951,6 +2951,47 @@ page.render(block) assert!(session.refresh_document().unwrap().is_none()); } + #[test] + fn source_overrides_accept_wider_inputs_and_more_specific_results() { + let source = include_str!("../examples/method-contracts.endor"); + let (mut session, document) = ReplSession::load(source, "app.main", 164, 1).unwrap(); + document.validate().unwrap(); + assert_eq!( + session + .execute("rect.mounts().at(0).bounds().width", "app.main") + .unwrap() + .value, + "180" + ); + session.execute("button.click()", "app.main").unwrap(); + assert_eq!( + session + .execute("rect.mounts().at(0).bounds().width", "app.main") + .unwrap() + .value, + "260" + ); + assert_eq!( + session.execute("count.value", "app.main").unwrap().value, + "1" + ); + for (source, reason) in [ + ( + source.replace("mount: Object", "mount: Point"), + "override narrows a parameter type", + ), + ( + source.replace("): Sized {", "): Object {"), + "override widens the return type", + ), + ] { + let error = ReplSession::load(&source, "app.main", 165, 1) + .err() + .expect("invalid override"); + assert!(error.to_string().contains(reason), "{error:#}"); + } + } + #[test] fn nullable_page_methods_union_event_options_and_nominal_mount_bounds_compose() { let (mut session, _) = ReplSession::load( diff --git a/vendor/aster b/vendor/aster index 6b4cce1..e26e227 160000 --- a/vendor/aster +++ b/vendor/aster @@ -1 +1 @@ -Subproject commit 6b4cce1fee6007cb6db2669d7e57b1dc458f11af +Subproject commit e26e227dd0a3dcbd1aaf61765ebdcd4d61ce05e5