E81068: the 'operator close' of an open or abstract type can never call a method a subtype may override, nor hand out 'this' - a subtype's own properties are closed before this body runs
What went wrong
The 'operator close' of an OPEN or ABSTRACT type calls a method a subtype may override, or hands out 'this'. A subtype's close closes the subtype's own properties first and then the super's close runs, so a method the subtype overrides, called from the super's close, works on resources that are already closed - a write after close, or a flush that is lost.
It is the same rule E08061 applies to the constructor of an open or abstract type, at the other end of the object's life: there the subtype's properties are not set yet, here they are already closed.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 1860 (Which of my types must declare their own operator close, and what may that close do?)
- ek9 -q 1396 (Why can't my constructor call a method before every property is set?)
How to fix it
Call only what no subtype can replace: a PRIVATE helper, or the methods of the properties this type itself holds (the [EXAMPLE] section below: an open base whose close calls a private 'tidy()', and one whose close writes to the 'Out' property it holds). If a subtype needs work done when it closes, it does that work in its own close; its generated close then closes its own properties and calls this close.
Common causes
- A "template method" close in a base class that calls a hook subtypes override.
- Passing 'this' to a logger or a registry from a base class's close.
- Calling super.someMethod() from an open type's close - the super's method can itself call an override.
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E81068: the 'operator close' of an open or abstract type can never call a method a subtype may override, nor hand out 'this' - a subtype's own properties are closed before this body runs
#!ek9 defines module error.e81068.bad defines class Out name as String? default private Out() Out() -> label as String name: label write() as pure -> text as String require text? default operator ? operator close as pure require name? <?- BAD: an open type's close calls a method a subtype may override -?> FlushingBase as open label as String? FlushingBase() label: "base" flush() as pure require label? default operator ? operator close as pure flush() <?- BAD: the same call written through 'this' -?> ThisFlushingBase as open label as String? ThisFlushingBase() label: "base" flush() as pure require label? default operator ? operator close as pure this.flush() <?- BAD: an abstract type's close calls its own abstract method -?> AbstractBase as abstract label as String? AbstractBase() label: "abstract" describe() as pure abstract <- rtn as String? default operator ? operator close as pure text <- describe() require text? <?- BAD: an open type's close hands out 'this' -?> ReportingBase as open label as String? ReportingBase() label: "reporting" default operator ? operator close as pure report(this) <?- BAD: a call to the super's method from an open type's close - the super's method can call back into an override -?> ChainedBase is FlushingBase as open out as Out? ChainedBase() super() out: Out("chained") default operator ? override operator close as pure super.flush() <?- VALID: a private helper cannot be overridden -?> PrivateHelperBase as open label as String? PrivateHelperBase() label: "private" private tidy() as pure require label? default operator ? operator close as pure tidy() <?- VALID: calling methods of a property the type holds, and closing it -?> HolderBase as open out as Out? HolderBase() out: Out("held") default operator ? operator close as pure out.write("final") <?- VALID: an overriding close chaining to the super's close -?> HolderSub is HolderBase as open extra as Out? HolderSub() super() extra: Out("extra") default operator ? default operator close <?- VALID: a closed type has no subtype, so its close may call its own methods and use 'this' -?> ClosedType label as String? ClosedType() label: "closed" flush() as pure require label? default operator ? operator close as pure flush() report(this) defines function report() as pure -> item as Any require item?
Fixed: In an open type, close only calls private helpers and what its properties hold
#!ek9 defines module error.e81068.good defines class Out name as String? default private Out() Out() -> label as String name: label write() as pure -> text as String require text? default operator ? operator close as pure require name? PrivateHelperBase as open label as String? PrivateHelperBase() label: "private" private tidy() as pure require label? default operator ? operator close as pure tidy() HolderBase as open out as Out? HolderBase() out: Out("held") default operator ? operator close as pure out.write("final")
Questions and answers
These Q&A entries show E81068 and its fix: