Back in 2019 eons ago, a proposal was made to the Fortran standard committee on a related matter, but it “died” on the administrative vine as is typical with J3/WG5 committees where few proposals pass and those that do depend entirely on who is asking and worse, how they ask, meaning there is something deeply nefarious about the whole process akin to the extremely funny illustration in a popular sitcom in the US c.f. https://youtu.be/RqlQYBcsq54?si=EzzxrqbK3xhkinfL
Since Claude is mentioned here, here is it’s audit of this thread and the proposal:
Click to see!
Here’s my audit, grounded in what practitioners actually said in that thread and a close technical read of 19-186.
What the Discourse thread actually establishes
Stripping away the tangents (GPU performance, ISO process complaints, finalizer semantics), the thread converges on a few points that bear directly on 19-186:
1. The TYPE/CLASS split is a storage-model consequence, not a stylistic one. A CLASS entity needs deferred or borrowed storage (POINTER, ALLOCATABLE, or dummy argument) precisely because its dynamic type — and therefore its size — isn’t fixed at compile time. TYPE entities are fixed-layout values. This matters for auditing 19-186 because the proposal’s whole mechanism rides on this fact: an inextensible type has a genuinely fixed layout forever, so there’s no reason its passed-object dummy argument should ever need to carry a type descriptor.
2. Practitioners want TYPE-based passed-object dummies for concrete, non-negotiable reasons — not just taste. RonShepard and rwmsu independently asked why type-bound procedures must take CLASS() dummies at all, citing (a) faster execution, and (b) compile-time argument-mismatch detection instead of runtime. jorgeg’s own benchmark (class(*) → type(my_type) giving a 10% uplift) is direct empirical support. This is exactly the performance case 19-186 makes in its Introduction.
3. kkifonidis’s historical account is the sharpest indictment of the status quo, and it’s structurally important here: J3 apparently drafted an early requirement that types opt in to extensibility via an explicit extensible attribute, then dropped it in favor of “extensible by default.” That reversal is the root cause of the exact problem 19-186 is trying to patch back in, 16 years later, as an opt-out (SEALED) rather than the cleaner opt-in the committee itself once considered.
4. khb’s post is the best articulation of why this matters for reusable libraries, and it should anchor any evaluation of 19-186: the point of Fortran OOP was never to be “comp-sci friendly,” it was to let library authors hide implementation complexity (his sparse-matrix example) behind a stable object. An extensible-by-default type undermines exactly that goal, because nothing stops a downstream consumer from extending the type and overriding behavior in ways that violate the library’s contract — which is precisely kkifonidis’s Liskov Substitution Principle objection, stated more rigorously elsewhere in the thread.
5. There’s already latent community consensus that a sealed-like attribute is worth having — jwmwalrus’s Traits proposal sketch independently proposes a sealed attribute for derived types, arrived at from a completely different design direction (traits/interface-inheritance rather than “just seal the leaf of a class-inheritance chain”). Two independent proposals converging on the same keyword is a decent signal that the need is real, whatever one thinks of either specific mechanism.
So the community backdrop gives 19-186 a legitimate, twice-independently-motivated use case: (a) protect a library’s sealed data structure from behavioral corruption via override, and (b) recover static dispatch/inlining and compile-time argument checking that CLASS currently forecloses.
Auditing 19-186 itself
Strengths
- It’s minimally invasive. Rather than the wholesale “rip out implementation inheritance” restructuring debated at length in the thread (and which PierU correctly predicted has “absolutely no chance” of happening within 15 years given existing code bases), 19-186 just adds one type attribute and reuses the existing C760 machinery that already conditions the TYPE/CLASS choice of a passed-object dummy on whether the type is extensible. That’s a genuinely elegant, low-cost lever — the “wiring is mostly there,” as the proposal’s author later put it in the GitHub issue tracking this paper’s afterlife.
- The performance claim is appropriately hedged (“it is also expected… a few or all… processor implementations will be able to”) rather than overpromised — consistent with the thread’s more tentative empirical findings (nvfortran’s inlining/
pure quirks, the RAM cost of -ipo, etc., all suggest compiler behavior here is uneven, so caution is warranted).
- The example is realistic: a vendor (ACME) shipping
SpecializedWidget_t to customers, wanting to prevent them from further subclassing it — this is precisely khb’s “shield users from complexity, but don’t let them corrupt the contract” scenario.
A genuine defect: the proposal contradicts its own example.
The Formal Specification states:
Cxxx (Rxxx) If EXTENDS appears, SEALED shall not appear
But the very next section, “Syntax,” gives as the motivating illustration:
type, extends(GeneralizedWidget_t), SEALED :: SpecializedWidget_t
These cannot both be true. As drafted, the constraint would make the paper’s own centerpiece example illegal. This isn’t a nitpick — it goes to the heart of what the feature is for. The most valuable use case in practice (per khb’s library-shielding argument) is exactly this one: a library derives a specialized type from a more general extensible base, then seals that leaf so its own customers can’t extend further. If SEALED can only be applied to a type with no EXTENDS at all, the feature only protects root types, not the tips of a hierarchy — which is a much weaker and less useful facility than the snippet advertises. My read is that the constraint as written is simply wrong and should be dropped or inverted (something closer to “no constraint against EXTENDS + SEALED together,” since nothing about fixed layout requires the type to be a hierarchy root — it only requires that its own descendants not exist).
Secondary gaps worth flagging for a committee-level review:
-
Missing explicit ABSTRACT/SEALED constraint. C734 (“if ABSTRACT appears, the type shall be extensible”) already makes ABSTRACT and SEALED mutually exclusive transitively, through the redefinition of “extensible type.” But leaving this as an inference rather than a stated constraint invites exactly the kind of drafting ambiguity that produced the EXTENDS bug above. It should be spelled out.
-
Inherited (non-overridden) type-bound procedures aren’t addressed. If SpecializedWidget_t is SEALED but inherits a TBP from GeneralizedWidget_t without overriding it, that inherited procedure still has whatever passed-object dummy (CLASS(GeneralizedWidget_t)) it was originally declared with — sealing the child doesn’t retroactively change the parent’s binding. So the performance/static-dispatch benefit 19-186 advertises only accrues to procedures newly bound or overridden at the sealed level, not the whole inherited surface. The paper should say this explicitly rather than let the reader assume “sealed = all calls become static.”
-
“Corrupt or otherwise misuse” is loose framing. Type extension by itself doesn’t compromise memory safety if components are properly PRIVATE — the real risk, as kkifonidis’s LSP argument in the thread makes precise, is behavioral substitutability violation via method override, not data corruption per se. The proposal would read as more rigorous, and better aligned with the thread’s own best argument for it, if it grounded the “security” motivation in LSP/contract-integrity terms rather than “corrupt… objects.”
-
No interaction discussion with CLASS(*)/SELECT TYPE. Worth a sentence clarifying that sealing forbids extension, not polymorphic use — a SEALED type can still be held via CLASS(*) or dispatched through SELECT TYPE; it just can never be the parent of an EXTENDS. This is consistent with intent but is exactly the kind of edge case that trips up implementers and users unless stated.
-
Precedent argument is available but unused. The proposal could strengthen its case by noting (as its author does elsewhere) that Fortran already treats its own intrinsic derived types this way — C_PTR/C_FUNPTR are effectively inextensible today by carve-out. 19-186 is really just asking for the same courtesy to be extendable to user-defined types via a declarable attribute, which is a rhetorically stronger framing than the “security/business manager” framing it leads with.
Historical postscript
Worth noting for completeness: this proposal never reached a WG5/J3 vote. According to the author’s own later account, it was submitted for the August 2019 Tokyo meeting but the Fortran 202X worklist had already closed, and it was never picked back up. Whatever the drafting issues above, the paper died administratively, not on technical merits — which is itself an illustration of the process critique several thread participants (certik, jwmwalrus) made independently: J3’s worklist-driven, waterfall-like process struggles to accommodate small, well-motivated, low-cost proposals that arrive between the fixed windows of a standard cycle.
Net assessment: the underlying need is real and doubly attested — both from a performance/compile-time-checking angle (RonShepard, rwmsu, jorgeg) and from a library-encapsulation/LSP angle (khb, kkifonidis) — and 19-186’s mechanism (reuse C760, add one attribute) is a sound, minimal way to deliver it without reopening the much larger and more contentious “should implementation inheritance exist at all” fight that dominates the rest of the thread. But the paper as drafted has a self-contradicting constraint that would gut its own headline use case, and it leaves several interaction questions (inherited TBPs, ABSTRACT, CLASS(*)) implicit rather than specified — any of which a working committee reviewer would reasonably kick back for revision before it could be voted into a standard.
Here’s the take by Grok:
Click to see!
The Fortran Discourse thread (started June 17, 2026, Advocacy category, ~65 replies) is a substantive, earnest discussion among experienced Fortran users—particularly those working in performance-sensitive scientific, technical, and HPC computing—about a fundamental design choice in Fortran’s object-oriented features (introduced in Fortran 2003 and refined later).
The thread does not focus on trivial syntax complaints; it grapples with how the current model affects real-world library design, encapsulation, performance, maintainability, and the language’s fitness for reusable scientific codebases. The original post highlights a concrete performance observation (switching from CLASS(*) to a concrete TYPE in an OpenMP target offload context yielded ~10% uplift), then the discussion broadens into why the TYPE/CLASS separation exists and why it feels costly.
Core Technical Root Cause Identified in the Thread
Fortran 2003 made derived types extensible by default (via type extension / implementation inheritance). Earlier drafts reportedly considered an explicit EXTENSIBLE attribute (opt-in), but the final design chose default extensibility for syntactic simplicity.
Because of this, the standard requires the passed-object dummy argument in type-bound procedures (TBPs) to be polymorphic (CLASS) precisely when the type is extensible. A key constraint (roughly C1236 / related rules) states that if a derived-type-spec specifies an extensible type, CLASS must be used; otherwise TYPE is used/allowed.
This forces the TYPE vs. CLASS distinction throughout the language (declarations, allocations, dummy arguments, SELECT TYPE, etc.). The thread calls the adoption of type extension + runtime type inspection (SELECT TYPE) as the foundation of Fortran OO the “cardinal sin” or “fatal/major design mistake” of that era—contrasted with the better choice (in the same era) to use types + modules for class-like constructs, inspired by Oberon-2 rather than deep Simula-67-style inheritance.
Consequences for users (especially library authors in scientific computing):
- Even when you have no intention of extending a type and do not need runtime polymorphism, you are often forced into the polymorphic path (
CLASS + dynamic dispatch descriptors + runtime type information).
- This incurs measurable overhead in hot paths (as in the OP’s 10% example with OpenMP offload / GPU contexts). Users report preferring “F90 style” (static
TYPE) for computationally expensive sections.
- Library design and encapsulation suffer: Authors cannot easily prevent downstream users or other teams from extending a type in ways that break invariants, corrupt internal state, or violate assumptions. This undermines trust and predictability in reusable libraries—the exact use case highlighted in your query.
- Type extension is viewed by many in the thread as antithetical to modern OO principles (composition over inheritance, data abstraction, Liskov Substitution Principle adherence, explicit interfaces/traits). It leads to fragile hierarchies and makes traits/generics proposals more complex than necessary.
- Workarounds mentioned or implied: heavy use of
PRIVATE components, abstract types + composition, final procedures, avoiding deep inheritance, or simply not using the OOP features for performance-critical code.
What users genuinely want (earnest feedback, not abstract advocacy):
- Control over extensibility — the ability to “seal” or mark certain types as inextensible/final so that library authors can protect specialized implementations delivered to customers or collaborators.
- Performance when polymorphism is not needed — the ability to use non-polymorphic (
TYPE) passed-object dummies in TBPs for sealed types, enabling better compiler optimizations and avoiding unnecessary runtime overhead. This is repeatedly tied to scientific/HPC workloads.
- A path toward modern OO via traits, generics, or interfaces (multiple “inheritance” of behavior without the problems of implementation inheritance). LFortran’s planned traits work (including a
sealed attribute for derived types, and even trait implementation for intrinsic types) is cited positively as a way to opt out of the bad default and regain TYPE for passed-objects with all the attendant benefits.
- Acknowledgment that while existing code using type extension must be preserved for compatibility (some users note it would be impractical to delete features used in “modern Fortran” codes), making type extension obsolescent or providing strong opt-out mechanisms is desirable. There is broad consensus that inheritance-based extension feels “half-baked,” “troublesome, outdated, and hence pointless” for contemporary scientific software.
The tone is constructive but frustrated: users are not anti-OO; they want Fortran’s OO facilities to better serve the domain of reusable, performant, trustworthy scientific and technical libraries without forcing unnecessary complexity or performance costs.
Review and Audit of J3 Proposal 19-186 (July 28, 2019)
The proposal is titled “Option to derive an inextensible type” and directly targets the root cause discussed in the 2026 thread. I have the full document content from the official source.
Summary of the Proposal:
- Add a new type attribute:
SEALED (alternative name suggested: INEXTENSIBLE).
- A derived type (that is not abstract) with
SEALED cannot be extended. The definition of “extensible type” is updated to exclude those with SEALED, BIND, or SEQUENCE (plus the usual C_PTR/C_FUNPTR exceptions).
- New constraint:
EXTENDS and SEALED are mutually exclusive.
- Consequence (via existing standard rule C760 on passed-object dummy arguments): For a sealed type, all type-bound procedures must declare the passed-object dummy with
TYPE(...) (non-polymorphic), not CLASS(...). This is not a new rule; it follows automatically once the type is non-extensible.
- Example (slightly abbreviated):
module SpecializedWidget_m
use GeneralizedWidget_m, only : GeneralizedWidget_t
type, extends(GeneralizedWidget_t), SEALED :: SpecializedWidget_t
private
! ...
contains
private
procedure, pass(this), public :: SuperWidgetProc
end type
interface
module subroutine SuperWidgetProc(this, SuperResults)
type(SpecializedWidget_t), intent(in) :: this ! non-polymorphic
type(SuperResults_t), intent(out) :: SuperResults
end subroutine
end interface
end module
Rationale in the proposal (paraphrased/quoted essence):
- In scientific and technical computing, certain data structures must be “sealed” for security, predictability, and encapsulation of business/technical logic. Arbitrary extension risks unintended manipulation or corruption, undermining reliability and trust across teams and libraries.
- Sealed types enable performance optimizations because non-polymorphic passed-object dummies allow simpler (non-polymorphic) descriptors.
- Fully backward-compatible; existing code unaffected. New code opting into
SEALED gets the protection and potential performance win.
Audit against the thread’s genuine feedback:
Alignment and Strengths (very strong):
- Directly solves the core pain point. The thread identifies default extensibility + forced
CLASS/polymorphism as the “cardinal sin.” This proposal gives library authors the missing opt-out (SEALED) without changing defaults or breaking existing code. This is exactly what users need for reusable scientific libraries.
- Enables the performance benefit users are manually chasing. By making a type non-extensible, it unlocks
TYPE (non-polymorphic) passed-objects in TBPs—the same optimization that gave the original poster a 10% uplift in an offload scenario. Compilers can then generate more efficient code.
- Encapsulation and trust for libraries — matches the thread’s emphasis on protecting invariants in shared scientific code. The “SpecializedWidget” example maps cleanly to real sci-comp cases (e.g., a sealed specialized solver, matrix format, or physics model type delivered in a library).
- Minimal and elegant change. It leverages the existing machinery (the extensibility → passed-object polymorphism rule) rather than inventing new syntax or semantics. No impact on
ABSTRACT, BIND(C), SEQUENCE, or intrinsics.
- Prescient. Submitted in 2019; the 2026 thread shows the issue remains live and community sentiment supports exactly this kind of control (plus the related
sealed direction in LFortran traits work).
Weaknesses / Areas for Improvement (constructive):
- Naming:
SEALED is reasonable and aligns with modern usage (Java sealed classes, Scala sealed traits, upcoming LFortran sealed). INEXTENSIBLE is more literally descriptive of the title. FINAL is also common terminology. A short community poll or alignment with LFortran’s implementation would help. Minor issue.
- Scope is intentionally narrow — it only solves the extensibility control + consequent performance issue. It does not address the broader modernization many thread participants want (traits, generics, composition-first OO, making inheritance obsolescent). This is not a flaw per se; a focused proposal is easier to adopt. It can (and should) coexist with or complement traits work.
- Limited examples and use cases. The widget example is fine but generic. Stronger impact would come from sci-comp-specific illustrations (e.g., sealing a base sparse matrix type, an ODE integrator, or a device abstraction in a library so that internal representation and invariants cannot be broken by extension). The thread’s emphasis on real performance in HPC/offload and library trust would make compelling additions.
- No discussion of interactions with parameterized derived types (PDTs), finalization, or user-defined derived-type I/O. These are edge cases but worth clarifying in an update.
- Implementation and optimization details are high-level (“potential performance improvements”). In practice, the benefit is real (as users demonstrate), but adding notes on descriptor simplification or expected compiler gains would strengthen it.
- Historical/strategic context missing. It was marked “No Action.” The 2026 thread provides fresh evidence of ongoing demand. Reviving it now, explicitly referencing the Discourse discussion and LFortran traits direction, would be powerful. It could be positioned as a small, high-value, low-risk addition that makes existing OOP safer and faster while the community explores traits for the longer-term model.
- Potential user pushback (anticipated, not a flaw in the proposal itself): Some downstream users might want to extend a sealed type for customization. The counter-argument (already implicit) is that sealing is opt-in by the library author; if extensibility is desired, simply do not seal. This encourages better design—explicit extension points via composition or interfaces rather than fragile inheritance.
Overall Assessment:
The proposal is technically sound, minimal, backward-compatible, and highly relevant to the genuine, earnest needs expressed in the 2026 Fortran Discourse thread. It gives Fortran library authors in scientific and technical computing precisely the tool they lack today: the ability to seal types against unwanted extension while automatically gaining the performance advantages of non-polymorphic passed-object arguments in type-bound procedures.
It does not claim to solve every shortcoming of Fortran’s F2003 OOP model (the thread acknowledges the complexity and the need to preserve existing code), but it surgically addresses one of the most criticized aspects—uncontrolled default extensibility and its costs to performance and encapsulation. In that sense, it is a pragmatic, high-value increment that would meaningfully improve the experience of building and using reusable scientific libraries.
Recommendations (for J3 or community follow-up):
- Revive/update the proposal for current standards cycles, incorporating feedback from the active Discourse thread and alignment with LFortran’s
sealed + traits work.
- Strengthen with concrete scientific computing examples and performance rationale drawn from user reports.
- Consider naming harmonization (
SEALED appears favored by emerging implementation work).
- Explore synergies: a sealed type should be able to participate cleanly in any future traits system.
This proposal, if advanced, would be a concrete step toward making Fortran’s OOP facilities more suitable for the demanding, performance-sensitive, library-centric workflows that the thread participants—and the broader scientific Fortran community—rely on. It respects the language’s history and performance ethos while giving users the control they have been earnestly requesting for years.
And ChatGPT says:
Click to expand!
From the sources I examined:
- The Fortran Discourse thread revolves around long-standing concerns with the design of Fortran 2003 OOP, especially that all derived types are extensible by default, forcing
CLASS passed-object dummy arguments for type-bound procedures and introducing runtime polymorphism even where no inheritance is intended. Participants also discuss impacts on optimization, compiler implementation, library design, and scientific software engineering.
- J3 paper 19-186 (“Option to derive an inextensible type”) was submitted in 2019, received “No Action”, but subsequently appeared in WG5’s collected suggestions for the next revision of the language, indicating the underlying idea continued to be considered valuable.
What the Discourse thread reveals
Although participants disagree on the historical reasons and on ideal object-oriented design, there is a remarkably consistent set of practical concerns.
1. Scientific programmers overwhelmingly want “mostly static” OOP
The users are generally not asking for more dynamic object systems.
Instead they want OOP primarily for
- reusable libraries,
- encapsulation,
- abstraction,
- maintainability,
- generic algorithms,
- cleaner APIs,
- interchangeable implementations,
while still preserving
- predictable performance,
- compile-time checking,
- aggressive optimization,
- simple data layouts.
This is a very different use case from enterprise application programming.
2. Default extensibility creates costs even when unused
Several users point out that because every derived type is extensible,
- passed-object dummy arguments must usually be
class(my_type)
instead of
type(my_type)
which introduces runtime polymorphism.
That affects
- optimization,
- dispatch,
- compile-time diagnostics,
- implementation complexity.
One participant even reports a measurable performance improvement after replacing polymorphic entities with concrete types in performance-critical code, while others ask why type() is not allowed for many type-bound procedures when extension is unnecessary.
3. Library authors especially dislike paying for flexibility they do not want
This is probably the strongest theme.
Reusable numerical libraries frequently define types such as
- vectors
- matrices
- sparse matrices
- meshes
- FFT plans
- random generators
- solvers
whose authors never intend users to derive further subclasses.
Yet the language currently assumes they might.
That inversion of the common case is exactly what motivates proposals like 19-186.
Audit of J3 paper 19-186
Based on the proposal and the context above:
Overall assessment
I think the proposal addresses a genuine language design deficiency.
It is
- conceptually simple,
- backward compatible,
- beneficial for compiler optimization,
- beneficial for library authors,
- aligned with modern language evolution.
The weakness of the proposal is not the motivation.
Its weakness is that it is relatively narrow.
Strengths
1. It restores programmer intent
Today
type :: matrix
implicitly means
somebody might derive from this.
Most programmers actually mean
this is the complete representation.
The proposal lets the source code express that directly.
That improves readability and API documentation.
2. Better optimization opportunities
If a type is guaranteed inextensible then the compiler knows
- exact dynamic type
- exact procedure bindings
- exact object layout
throughout compilation.
That potentially enables
- devirtualization,
- inlining,
- dead code elimination,
- simpler alias analysis.
Whether every compiler exploits this immediately is another matter, but the semantic information is valuable.
3. Better library engineering
Scientific libraries often expose public types.
Library authors frequently need
- stable ABI,
- stable semantics,
- predictable behavior.
Allowing arbitrary user extension complicates this.
An inextensible type becomes a much stronger contract.
4. Fits existing Fortran philosophy
Fortran generally favors explicit programmer intent.
Examples include
- PURE
- ELEMENTAL
- CONTIGUOUS
- VALUE
- NON_OVERRIDABLE
An explicit “cannot be extended” attribute fits naturally alongside these.
Weaknesses
1. It does not solve the entire OOP problem
The proposal addresses one symptom.
It does not address
- protocols/interfaces
- traits
- constrained generics
- structural abstraction
- cleaner polymorphism
Many participants in the discussion see those as larger issues.
2. Existing TBP rules would still need reconsideration
Even with an inextensible type,
users naturally expect to write
type(my_type) :: this
for passed-object arguments.
If the proposal merely forbids extension but still requires
class(my_type)
then much of the practical benefit disappears.
The proposal therefore really ought to specify the consequences for
- passed-object dummies,
- dispatch,
- binding resolution.
3. Interaction with existing code
Backward compatibility is preserved because existing code keeps today’s behavior.
But care would be needed regarding
- inheritance chains,
- SELECT TYPE,
- unlimited polymorphism,
- generic resolution.
Those semantic interactions would require precise specification.
Was “No Action” technically justified?
From today’s perspective, I do not think the technical motivation has become weaker.
If anything, developments since 2019 strengthen it.
The community has become much more focused on
- reusable libraries,
- package management,
- modern APIs,
- compiler optimization,
- maintainable large code bases,
as reflected in work on the Fortran ecosystem and its tooling.
My overall evaluation
If I were reviewing the proposal today, I would rate it approximately as follows:
| Criterion |
Assessment |
| Solves a real user problem |
Excellent |
| Benefits scientific libraries |
Excellent |
| Backward compatibility |
Excellent |
| Compiler optimization potential |
High |
| Specification complexity |
Low–Moderate |
| Addresses all OOP concerns |
No |
| Worth standardization |
Yes, in my view |
The main improvement I would recommend is expanding the proposal beyond simply introducing an “inextensible” attribute. A more complete design would explicitly connect inextensibility to the rest of the object model—for example, permitting TYPE rather than CLASS for passed-object dummy arguments when dispatch cannot vary, clarifying dispatch semantics, and documenting the optimization and diagnostic opportunities this enables.
Viewed alongside the concerns expressed in the Discourse discussion, the proposal appears to be a targeted response to one of the most frequently raised practical issues: enabling library authors to opt out of runtime polymorphism when they neither need nor want extensibility. It would not by itself modernize Fortran’s entire OOP system, but it would remove a significant friction point for scientific and technical software developers while remaining compatible with existing code.