Repository: j3-fortran/fortran_proposals Branch: master Commit: 3732db5150dc Files: 26 Total size: 155.8 KB Directory structure: gitextract_o6bns626/ ├── CODE_OF_CONDUCT.md ├── README.md ├── augmented_assignment/ │ └── augmented_assignment.txt ├── proposal-template.txt └── proposals/ ├── assert/ │ └── design_by_contract.txt ├── c23_interop/ │ ├── 26-112.txt │ └── 26-122.txt ├── cmplx-real-pointers.txt ├── conditional_expressions/ │ └── 21-165.txt ├── default_optional_arguments/ │ └── proposal.txt ├── fp_interop/ │ ├── 25-189r1.txt │ ├── 25-203.txt │ └── 25-203r1.txt ├── namelist_delimiters/ │ └── namelist_proposal.txt ├── namespace_modules/ │ ├── 19-246.txt │ └── 20-108.txt ├── protected_attribute.txt ├── run-time_polymorphism/ │ ├── Examples/ │ │ ├── AppendixA/ │ │ │ └── taylor.f90 │ │ ├── AppendixB/ │ │ │ ├── taylor.f90 │ │ │ ├── taylor.java │ │ │ └── taylor.rs │ │ └── Text/ │ │ └── taylor_basic.f90 │ ├── Fortran_OOP.bib │ └── Fortran_OOP.tex ├── traits/ │ └── 20-109.txt └── worklist/ └── 26-116-din3a-recommend.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing or otherwise unacceptable behavior may be reported by contacting one of the project maintainers at ondrej@certik.us or zjibben@lanl.gov. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] [homepage]: https://contributor-covenant.org [version]: https://contributor-covenant.org/version/1/4/ ================================================ FILE: README.md ================================================ # Fortran Proposals This repository contains proposals for the Fortran Standard Committee in the [Issues](https://github.com/j3-fortran/fortran_proposals/issues) section. The idea for this repository is to act as a public facing discussion tool to collaborate with the user community to gather proposals for the Fortran language and systematically track all discussions for each proposal. ## Workflow 1. Open a new issue with a new addition that you would like to see eventually included in the Fortran standard. 2. The issue gets discussed. 3. Start drafting a formal proposal to be submitted into [Documents](https://j3-fortran.org/doc/meeting) for the US committee. One has to find an advocate at the committee to advocate for the proposal. 4. If the draft proposal receives negative feedback from the committee, then we will close the issue and we will have documented reasons why the idea got rejected. One can open a new issue, reference the closed issue and advocate why it should be revisited. 5. Getting the proposal through the Committee is a long process, with multiple approvals, formal requirements, specifications, edits to the standard, etc. We will document this step in more detail. During this process, we can document the latest status at the issue. ### Proposal Format Proposals should be text files, using [proposal-template.txt](proposal-template.txt) as a template. The reference line is optional, and is used reference previous J3 papers (e.g. 12-345r6, not a full link). The top line should not be modified--the XX-XXX is automatically modified when uploaded. Proposals are limited to 75 characters per line, and are generally around 50 - 350 lines of text. Proposals benefit from minimal code examples and use cases. While J3 will accept PDF proposals, such detailed documents are usually be better suited as a reference, so the proposal itself can be concise. ## Plan For This Repository Currently this repository is maintained by [Ondřej Čertík](https://github.com/certik), [Zach Jibben](https://github.com/zjibben) and [Gary Klimowicz](https://github.com/gklimowicz) (all members of the Fortran Committee). We plan to maintain this repository, keep it up to date with what the committee is doing and use it to collaborate on ideas and proposals with the Fortran community. ## Code of Conduct Please note that all participants of this project are expected to follow our Code of Conduct. By participating in this project you agree to abide by its terms. See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). ## Links * [US Fortran Standards Committee](https://j3-fortran.org/) * [International Fortran Standard Committee](https://wg5-fortran.org/) ================================================ FILE: augmented_assignment/augmented_assignment.txt ================================================ To: J3 J3/XX-XXX From: Leonard Reuter Subject: Augmented Assignment Operators Date: 2022-July-11 Proposal for Fortran Standard: 202y 1. Introduction The proposal is to introduce augmented assignment operators. Example: i +.= 1 Here, `i` is incremented by 1. In the same manner, the operators `-.=`, `*.=`, `/.=`, `**.=` are proposed to be defined as well as the operator `.op.=` for a user-defined operator `.op.`. The intrinsic interpretation of e.g. '+.=' can not be overridden but should always call 'operator(+)' and 'assignment(=)'. This proposal originated at the J3 GitHub repository at [1]. The proposed syntax originated from a comment by Reinhold Bader in the J3 mail list [2]. 2. Motivation Augmented assignment operators improve the readability of code in making immediately clear to the reader, that the new value of the variable depends on the old one. Furthermore, if a function is called during the evaluation of the left-hand side, the augmented assignment operators show, that this function is only called once: real :: A(n) ... A(get_index()) +.= x If the above example were to be written without augmented assigment, how often `get_index` is called would depend on the compiler. The dot is added between the operator and `=`, since `/=` is already in use as an alias to the '.ne.' relational operator. 3. Discussing Objections 3.1. Conflict with '/=' [4] This is circumvented by introducing the dot, i.e. '/.=' 3.2. The compiler can optimize 'a = a + b*c' better than 'a +.= b*c' [5] "What happens to 'a += b * c' is often optimized to an FMA if the compiler flags or defaults allow ISO/IEEE violation" [6]. 3.3. Instead of augmented assignment, an additional symbol can be introduced to replace the LHS [7,8] If this symbol were '.LHS.', it would allow for further simplification: real :: a ... a = LOG(a) could be replaced with real :: a ... a = LOG(.LHS.) While discussing this may prove worthwhile, it lacks the intuitivity of simple augmented assignment and should be discussed independent of it. 3.4 Cognitive load [9] Most programmers are used to augmented assignment from other languages, it is more cognitive load to remember, that this is not possible in Fortran than to actually use it. 4. References [1] https://github.com/j3-fortran/fortran_proposals/issues/113 [2] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013261.html [3] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013251.html [4] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013254.html [5] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013262.html [6] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013279.html [7] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013287.html [8] https://wg5-fortran.org/N1951-N2000/N1972.pdf [9] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013277.html ================================================ FILE: proposal-template.txt ================================================ To: J3 J3/XX-XXX From: Subject: Date: #Reference: ================================================ FILE: proposals/assert/design_by_contract.txt ================================================ To: J3 J3/XX-XXX From: William B. Clodius Subject: Facilitate design by contract Date: August 1, 2020 #Reference: 04-142.txt, 04-143.txt, 04-144.txt, 04-212.txt, 04-219.txt, 04-376.txt, 04-414.txt Proposal for Fortran Standard: 202y (NOT 202x) 1. Introduction This paper contains a proposal to allow programmers to use "Design by Contract". Inspired by languages such as C++, Java, and Ada, Fortran is considering incorporating exception handling into the language. In contrast, C++'s standard committees have decided that its users have been overusing exception handling, and has been seeking to incorporate other, simpler, error handing systems.[1] One approach, that has been incorporated into C++ 2020, is design by contract.[2,3] I believe Fortran would benefit by incorporating design by contract into the language, at a cost much less than would be incurred by incorporating exception handling. Design by contract (DbC) is intended to address programmer errors and only those errors. The idea is to specify: first the logical preconditions the inputs to a procedure are required to satisfy for its correct operation; second, the conditions the code is asserted to satisfy at various stages of its logic if the code logic is correct, and finally, the post-conditions the code ensures it will satisfy if the pre-conditions are met. To implement these checks, C++ 2020 has defined the attributes: expects, assert, and ensures, respectively. During the debugging phase of development these serve to detect when the conditions are not satisfied, stop processing when they are not satisfied, and report errors in a consistent format. In code deployment the expects attribute documents the pre-conditions the calling code must satisfy, and the ensures attribute documents the post-conditions the calling code can expect will be satisfied, providing a "contract" between the calling code and the callee. Normally the processor provides flags such that the logical tests can be active during testing, and inactive on deployment. DbC can be met with a minimum of one new statement, for testing anywhere in the code body, say ASSERT, but ideally there would be three new statements: one the equivalent of ASSERT, one to be used at the start of the procedure to test pre-conditions, say REQUIRE, and one to be used immediately before the procedure's return to test post-conditions, say ENSURE. All three statements would be simple in form and have simple semantics. 2. Problem Currently a programmer typically tests for user errors using one of: 1. Hardcoding the tests for errors using IF or SELECT CASE blocks; 2. Writing the equivalent of an ASSERT subroutine: or 3. Writing the equivalent of an ASSERT macro. The first is the most natural for single checks, but requires care to give a consistent response to the user, and adds an overhead in deployment unless commented or edited out. The second allows a consistent response to the user, but has a larger overhead than the first, that can only be removed by being commented or edited out. The third allows a consistent response to the user that can be clearer about the problem location, and can have zero overhead on deployment with appropriate pre-processor flags, but does require a dependence on a preprocessor. None document for the user the pre and post conditions the user can expect for properly working code. 3. Use cases A simple function that requires non-negative integer arguments. REAL FUNCTION FACTORIAL( n ) INTEGER, INTENT(IN) :: N INTEGER :: I REQUIRE( N >= 0 ) FACTORIAL = 1 DO I=1, N FACTORIAL = FACTORIAL * I END DO ENSURE (FACTORIAL >= N ) RETURN END FUNCTION FACTORIAL A simple subroutine that swaps targets. SUBROUTINE TARGET_SWAP( A, B ) TYPE(EXAMPLE), POINTER, INTENT(INOUT) :: A, B TYPE(EXAMPLE), POINTER :: C REQUIRE( ASSOCIATED(A) ) ! Requires pointer status of A be ! defined REQUIRE( ASSOCIATED(B) ) ! Requires pointer status of B be ! defined C => A A => B B => C ENSURE( ASSOCIATED(A) ) ENSURE( ASSOCIATED(B) ) RETURN END SUBROUTINE TARGET_SWAP A procedure that requires one optional argument to be present if the other optional argument is present. SUBROUTINE OPTIONALS( A, B ) TYPE(EXAMPLE), OPTIONAL, INTENT(IN) :: A, B REQUIRE( PRESENT(A) .EQV. PRESENT(B) ) ... RETURN END SUBROUTINE OPTIONALS A test of a problematic procedure call SUBROUTINE PROBLEM_CALL ( ..., A, ... ) ... TYPE(EXAMPLE), POINTER :: A ... CALL PROBLEM( A ) ASSERT( ASSOCIATED( A ) ) ... RETURN END SUBROUTINE PROBLEM_CALL 4. Prior art The term design by contract as applied to programming languages has its origins in the language Eiffel.[4,5] Since then, in addition to C++ it has been adopted by Ada,[6] D,[7] and Fortress. The C standard library comes with the assert macro so C can be said to partially support DbC. To our knowledge no Fortran compiler has implemented anything to support DbC. Many users implement their own equivalent of an ASSERT subroutine or macro so there is some demand for these capabilities. 5. Specification All three statements will have similar behavior. All three will take as their arguments a list of scalar logical expressions, that if all .TRUE. does nothing, and if one is .FALSE. writes output to the ERROR_UNIT of ISO_FORTRAN_ENV and stops as if ERROR STOP was invoked. The writes will all indicate the location of the executed statement: either the procedure name with the name of the enclosing module, or the name of the file including the statement and the line number of the statement, or both. All three will write out the text of the logical expression that failed, with a prefix. The prefix will differ between the three statements. Example prefixes for the REQUIRE statement is "PRE-CONDITION FAILED: ", for the ASSERT statement is "ASSERTION FAILED: ". and for the ENSURE statement is "POST-CONDITION FAILED: ". The semantics of all three statements are similar. They all should respond the same to compiler flags that invoke different behavior in development versus in deployment. In development, they each perform a logical test with the .FALSE. branch writing to ERROR_UNIT, and then stopping with an stop code, and the other branch a null operation that continues processing. In deployment they are as if they were commented out. These semantics are similar to a logical test with one branch invoking ERROR STOP and the other branch continuing processing. They should have restrictions similar to those of ERROR STOP, being allowed in PURE procedures, but not in ELEMENTAL procedures. 4. Syntactic options The syntax for the statements depend on whether they should be considered executable statements, and should go in the executable part, or attributes of the sub-programs, and should go in the specification part. It is difficult to think of ASSERT as any thing other than an executable statement. For ASSERT I think the following syntax is appropriate assert-stmt is ASSERT( assertion-list ) where assertion is logical-expr REQUIRE and ENSURE can be thought of as either sub-program attributes or executable statements. If treated as sub-program attributes then the syntax could be something like requires-stmt is REQUIRES :: requires-expr-list ensures-stmt is ENSURES :: ensures-expr-list requires-expr is logical-expr ensures-expr is logical-expr Constraint: requires-expr shall be a restricted expression. If thought of as run time expressions then the appropriate syntax may be require-stmt is REQUIRE( require-expr-list ) ensure-stmt is ENSURE( ensure-expr-list ) In all of the syntactical forms each item in the effective logical-expr-list shall be tested in sequence, and a error message will be generated for the first item in the list that tests as .FALSE. that shows the logical expression that failed the test. 5. Backward compatibility The three proposed statements do not match statements implemented by any processor that I am aware of, and so their addition should not change the meaning of any directly supported application. It is likely, however, that some users already have an ASSERT macro, that approximates the ASSERT statement's semantics. I suspect they will welcome the new ASSERT, but cannot be certain. This problem could be minimized by using a name other than ASSERT, say CHECK. 6. Impact on the standard My guess is that describing each statement will take about half a page of main text, plus a half page note describing for all three statements the semantics of a quality of implementation, plus a few sentences to describe changes from the previous standard and define terms. Maybe three pages total. References [1] Herb Sutter, "Zero-overhead deterministic exceptions: Throwing values", http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0709r4.pdf [2] G. Dos Reis, J. D. Garcia, J. Lakos, A. Meredith, N. Myers, and B. Stroustrup, "A Contract Design" http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0380r1.pdf [3] G. Dos Reis, J. D. Garcia, J. Lakos, A. Meredith, N. Myers, and B. Stroustrup, "Support for contract based programming in C++" http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0542r5.html [4] B. Meyer, "Eiffel: The Language," Prentice Hall, second printing, 1992. [5] B. Meyer, "Applying 'Design by Contract'," in Computer (IEEE) 25, pp. 40-51, Oct. 1992. http://se.ethz.ch/~meyer/publications/computer/contract.pdf [6] R. Dewar, "Ada 2012: Ada with Contracts", Dr. Dobb's, Apr. 9, 2013. https://www.drdobbs.com/architecture-and-design/ada-2012-ada-with-contracts/240150569 [7] W. Bright, "D Programming Language, Contract Programming", Digital Mars. https://dlang.org/spec/contracts.html ================================================ FILE: proposals/c23_interop/26-112.txt ================================================ To: J3 J3/26-112 From: Patrick Fasano Subject: Edits for updating references from C17 to C23 Date: 2026-January-14 References: ISO/IEC 9899:2024 Programming languages -- C ("C23") 25-203r1 US05: Edits for C interchange and extended floating types ISO/IEC 9899:2018 Programming languages -- C ("C17") 25-007r1 Fortran 2028 Working Draft 1 Background ============ The ISO C23 specification (ISO/IEC 9899:2024) is the latest revision of the standard for the C language. At meeting #238 in January 2026, J3 passed paper 25-203r1 with edits to add support to Fortran 202Y for interoperability with new floating-point types added in C23. As such, 25-203r1 introduced references to the C23 specification. This paper provides edits to update and replace references to the ISO C17 specification (ISO/IEC 9899:2018) with references to the ISO C23 specification (ISO/IEC 9899:2024). The edits in this paper complement those presented in 25-203r1 for work item US05. 2 Edits (relative to 25-007r1) ============================== [2:6] Replace the line: ISO/IEC 9899:2018, Programming languages -- C with the line ISO/IEC 9899:2024, Programming languages -- C Note to editor: This edit is the same as that in 25-203r1. ------------------------------------------------------------------------ [7:22] Replace ISO/IEC 9899:2018 with ISO/IEC 9899:2024 ------------------------------------------------------------------------ [50:36] Replace ISO/IEC 9899:2018, 5.1.2.2.3 and 7.22.4.4 with ISO/IEC 9899:2024, 5.2.2.3.4 and 7.24.5.4 ------------------------------------------------------------------------ [50:39] Replace ISO/IEC 9899:2018, 7.22.4.4 with ISO/IEC 9899:2024, 7.24.5.4 ------------------------------------------------------------------------ [57:note] In the first paragraph, replace ISO/IEC 9899:2018 with ISO/IEC 9899:2024 and in the second paragraph, replace ISO/IEC 9899:2018, 6.7.6.3 with ISO/IEC 9899:2024, 6.7.7.4 ------------------------------------------------------------------------ [102:27] Replace ISO/IEC 9899:2018, 6.7.2.2 with ISO/IEC 9899:2024, 6.7.3.3 ------------------------------------------------------------------------ [103:note 2] Replace ISO/IEC 9899:2018 with ISO/IEC 9899:2024 and replace ISO/IEC 9899:2018, 6.7.2.2 with ISO/IEC 9899:2024, 6.7.3.3 Note to J3/editor: ISO/IEC 9899:2024 does not actually guarantee that enumeration constants fit in a C int. This will be addressed by rewriting or removing this Note in a forthcoming edit paper. ------------------------------------------------------------------------ [103:note 3] Replace ISO/IEC 9899:2018 with ISO/IEC 9899:2024 ------------------------------------------------------------------------ [115:note] Replace ISO/IEC 9899:2018 with ISO/IEC 9899:2024 and replace ISO/IEC 9899:2018, 6.4.3 with ISO/IEC 9899:2024, 6.4.4 ------------------------------------------------------------------------ [252:19] Replace ISO/IEC 9899:2018, 7.21.2 with ISO/IEC 9899:2024, 7.23.2 ------------------------------------------------------------------------ [538:4] Replace ISO/IEC 9899:2018, 6.7.6.3 with ISO/IEC 9899:2024, 6.7.7.4 ------------------------------------------------------------------------ [538:8] Replace ISO/IEC 9899:2018, 6.2.2 with ISO/IEC 9899:2024, 6.2.2 ------------------------------------------------------------------------ [538:30] Replace two instances of: (_Bool) with: (bool) Note to editor: This edit is the same as that in 25-203r1. ------------------------------------------------------------------------ [539:23] Replace ISO/IEC 9899:2018, 5.2.1 and 5.2.2 with ISO/IEC 9899:2024, 5.3.1 and 5.3.3 ------------------------------------------------------------------------ [540:16-17] Replace ISO/IEC 9899:2018, 6.3.2.3 and 6.5.9 with ISO/IEC 9899:2024, 6.3.3.3 and 6.5.10 ------------------------------------------------------------------------ [543:note] Replace ISO/IEC 9899:2018, 6.2.5 with ISO/IEC 9899:2024, 6.2.5 ------------------------------------------------------------------------ [545:26] Replace ISO/IEC 9899:2018, 6.5.3.2 with ISO/IEC 9899:2024, 6.5.4.3 ------------------------------------------------------------------------ [546:note] Replace ISO/IEC 9899:2018, 6.5.3.2 with ISO/IEC 9899:2024, 6.5.4.3 ------------------------------------------------------------------------ [546:10] Replace ISO/IEC 9899:2018, 6.5.3.4 with ISO/IEC 9899:2024, 6.5.4.5 ------------------------------------------------------------------------ [547:13] Replace the line The C types mentioned in Table 18.2 are defined in ISO/IEC 9899:2018, 6.2.5, 7.19, and 7.20.1. with the line The C types mentioned in Table 18.2 are defined in ISO/IEC 9899:2024, 6.2.5, 7.21, 7.22, and H.2. Note to editor: This edit is corrected relative to the one that appears in 25-203r1. ------------------------------------------------------------------------ [548:table 18.2] Replace the following: _Bool with: bool Note to editor: This edit is the same as that in 25-203r1. ------------------------------------------------------------------------ [548:note] Replace ISO/IEC 9899:2018 with ISO/IEC 9899:2024 ------------------------------------------------------------------------ [548:note 1] Replace ISO/IEC 9899:2018 with ISO/IEC 9899:2024 ------------------------------------------------------------------------ [549:note 4] Replace ISO/IEC 9899:2018 with ISO/IEC 9899:2024 and replace ISO/IEC 9899:2018, 6.2.7 with ISO/IEC 9899:2024, 6.2.7 ------------------------------------------------------------------------ [551:16-17] Delete (ISO/IEC 9899:2018, 6.2.5, 7.19, and 7.20.1) [551:note 1] After "The referenced type of a C pointer type", insert: (ISO/IEC 9899:2024, 6.2.5) such that the note reads: <> The <> of a C pointer type (ISO/IEC 9899:2024, 6.2.5) is the C type of the object that the C pointer type points to. For example, the referenced type of the pointer type \texttt{int *} is \texttt{int}. ------------------------------------------------------------------------ [551:note 2] Replace ISO/IEC 9899:2018, 7.16 with ISO/IEC 9899:2024, 7.16 ------------------------------------------------------------------------ [557:table 18.4] Replace the following: +---------------+-------+ | CFI_type_Bool | _Bool | +---------------+-------+ with: +---------------+------+ | CFI_type_Bool | bool | | CFI_type_bool | bool | +---------------+------+ Note to editor: This edit is the same as that in 25-203r1. ------------------------------------------------------------------------ [561:8-9] Replace ISO/IEC 9899:2018, 3.2 with ISO/IEC 9899:2024, 3.2 ------------------------------------------------------------------------ [564:18-19] Replace ISO/IEC 9899:2018, 3.2 with ISO/IEC 9899:2024, 3.2 ------------------------------------------------------------------------ [566:26] Replace ISO/IEC 9899:2018, 3.4.3 with ISO/IEC 9899:2024, 3.5.3 ------------------------------------------------------------------------ [566:31-32] Replace ISO/IEC 9899:2018, 6.2.4 with ISO/IEC 9899:2024, 6.2.4 ------------------------------------------------------------------------ [568:20] Replace ISO/IEC 9899:2018, 6.2.2 with ISO/IEC 9899:2024, 6.2.2 ------------------------------------------------------------------------ [568:23] Replace ISO/IEC 9899:2018 with ISO/IEC 9899:2024 ------------------------------------------------------------------------ [569:4] Replace ISO/IEC 9899:2018, 7.13 with ISO/IEC 9899:2024, 7.13 ------------------------------------------------------------------------ [569:7] Replace ISO/IEC 9899:2018, 7.14.1 with ISO/IEC 9899:2024, 7.14.1 ------------------------------------------------------------------------ [569:note] Replace ISO/IEC 9899:2018 with ISO/IEC 9899:2024 ------------------------------------------------------------------------ [569:25] Replace ISO/IEC 9899:2018, 7.14.1 with ISO/IEC 9899:2024, 7.14.1 ===END=== ================================================ FILE: proposals/c23_interop/26-122.txt ================================================ To: J3 J3/26-122 From: Patrick Fasano & HPC Subject: Reqs/specs for interoperability with C23 enumerations Date: 2026-February-23 Reference: ISO/IEC-9899:2018 ! Programming languages -- C ("C17") Reference: ISO/IEC-9899:2024 ! Programming languages -- C ("C23") Reference: WG5/N2212 ! The new features of Fortran 2023 Reference: WG14/N3029 ! Improved Normal Enumerations Reference: WG14/N3030 ! Enhanced Enumerations Reference: 24-007 ! Fortran 2023 Reference: 26-112 ! Edits for updating references from C17 to C23 1 Background ============ bg01. The ISO C23 specification (ISO/IEC 9899:2024) introduced changes to enumerations that create an incompatibility with the current specification of Fortran interoperable enumerations and enum types. Specifically, WG14 adopted two proposals affecting enumerations in C23: * N3029: "Improved Normal Enumerations", which allows enumeration values greater than INT_MAX and smaller than INT_MIN. * N3030: "Enhanced Enumerations", which allows specifying an underlying type for enumerations. bg02. WG14/N3029 "Improved Normal Enumerations" changes the rules for the types of enumeration constants and the underlying type of the enumeration. In C17, all enumeration constants were required to be representable as an int. In C23, enumeration constants may have values that are not representable as an int; the compiler determines an underlying integer type that can represent all the values defined in the enumeration. bg03. WG14/N3030 "Enhanced Enumerations" adds the ability to specify the underlying type of an enumeration. In C17, the underlying type of an enumeration was implementation-defined (subject to being able to represent all the values). In C23, the underlying type can be explicitly specified in the enum declaration (e.g., `enum x : long long { ... }`). bg04. The current Fortran specification (24-007, subclause 7.6.1) relies on the assumption that C enumeration constants fit within a C int. This assumption is explicitly stated in Note 2 of the subclause: "ISO/IEC 9899:2018 guarantees the enumeration constants fit in a C int (ISO/IEC 9899:2018, 6.7.2.2). Therefore, the Fortran processor can evaluate all enumerator values using the integer type with kind parameter C_INT, and then determine the kind parameter of the integer type that is interoperable with the corresponding C enumerated type." With the adoption of WG14/N3029 in C23, this guarantee no longer holds. C enumerations may now have values that cannot be represented in a C int. bg06. The edits in paper 26-112 adopted by J3 at meeting #238 in January 2026 replaced references to C17 with C23 in the Fortran specification. That introduced a factually incorrect statement regarding C enumerations in Note 2 of subclause 7.6.1, which was not addressed in that paper. This paper addresses this issue by proposing options for resolving the incompatibility and providing specifications for each option. 2 Requirements ============== rq01. The Fortran standard should be updated to address the incompatibility between Fortran interoperable enumerations and C23 enumerations. 3 Straw polls ============= Straw poll ---------- How should the incompatibility with C23 enumerations be resolved? OPTION A: Restrict interoperability to C enums compatible with C_INT. Add a precondition that says interoperability requires that the C enum have a type that fits within an INTEGER(C_INT). OPTION B: Implicitly determine KIND via new C23 rules. Remove the incorrect note and implicitly require that the Fortran processor determine the KIND of the enumerators via the new C23 rules; do not allow explicitly declaring the KIND. Optionally, add a new note on how the Fortran processor evaluates the enumerator values to determine the interoperable KIND parameter. OPTION C: Implicitly determine KIND via new C23 rules and add explicit KIND parameter to ENUM. Require that the Fortran processor determine the KIND of the enumerators via the new C23 rules; add an explicit (optional) KIND parameter to ENUM declarations which override any implicit determination. The subgroup recommends OPTION A. Straw poll analysis ------------------- PROS OPTION A: AP01. Minimizes expected implementation burden: no syntax change, no runtime change. AP02. Implicitly fixes the problem with Note 2 (making it true by restriction, after some minor rephrasing). AP03. Should not require any Fortran compiler backend changes, just spec wording. CONS OPTION A: AC01. Does not allow interoperability with C's new "wider" (64-bit) enum types, or even explicitly "narrower" enum types (e.g. 8-bit). AC02. Must add spec constraint to restrict allowable enumerators. AC03. Compiler changes to check the new constraint are required. PROS OPTION B: BP01. No syntax change. BP02. Maintains interoperability with the implicit behavior of C enums. BP03. No normative standard changes required. BP04. Compatible with C paper WG14/N3029. CONS OPTION B: BC01. Likely requires some compiler changes to accommodate new wider enums. BC02. Does not provide full interoperability with all C enums, because it cannot express all enums that are possible in C23 (specifically those with specified underlying types defined in WG14/N3030). PROS OPTION C: CP01. Maintains full interoperability with all C enums. CONS OPTION C: CC01. Requires introducing new syntax. CC02. Requires compiler parsing and backend changes (hence might be considered a new feature). 4 Specifications ================ 4.1 Specifications for Option A ------------------------------- sa01. The definition of "interoperable enumerator" should be constrained to require the values of the enumerators be representable in the integer type with kind parameter C_INT. sa02. Update or remove Note 2 in subclause 7.6.1 to reflect that interoperability is restricted to enumerations where constants fit in a C int. 4.2 Specifications for Option B ------------------------------- sb01. Remove Note 2 in subclause 7.6.1 which incorrectly states that C enumeration constants are guaranteed to fit in a C int. sb02. The Fortran processor shall determine the interoperable kind of the enumerators based on the type chosen by the companion processor for the corresponding C enumerated type, which may be wider than C_INT. sb03. Optionally, add a replacement Note to subclause 7.6.1 to explain that C23 allows enumeration constants that do not fit in a C int, and that the Fortran processor may need to evaluate all enumerator values using an integer type with wider range than the integer type with kind parameter C_INT, before determining the kind parameter that is interoperable with the C enumerated type. 4.3 Specifications for Option C ------------------------------- sc01. Extend the syntax of the ENUM statement to allow an optional KIND parameter to specify the underlying integer type. One possible syntax (shown only for illustrative purposes): ENUM[(KIND=scalar-int-constant-expr)], BIND(C) [:: enum-type-name] Thus, for a C23 enumerator declared as this: enum my_long_enum : long { red = 4, blue = 9, yellow }; a corresponding Fortran interoperable enumeration could be declared as: ENUM(KIND=C_LONG), BIND(C) :: my_long_enum ENUMERATOR :: RED = 4, BLUE = 9 ENUMERATOR YELLOW END ENUM sc02. If the KIND parameter is present, the kind type parameter of the enumerators is the value specified by the KIND parameter. The corresponding interoperable C enumerated type is one that uses a compatible underlying type (in addition to the other requirements specified in 24-007, sec 7.6.1 para 2). sc03. If the KIND parameter is absent, the Fortran processor shall determine the interoperable kind of the enumerators based on the type chosen by the companion processor for the corresponding C enumerated type, which may be wider than C_INT. sc04. Add a Note to subclause 7.6.1 to explain that C23 allows enumeration constants that do not fit in a C int, and that the Fortran processor may need to evaluate all enumerator values using an integer type with wider range than the integer type with kind parameter C_INT, before determining the kind parameter that is interoperable with the C enumerated type. ==END== ================================================ FILE: proposals/cmplx-real-pointers.txt ================================================ To: J3 J3/24-129 From: Pierre Hugonnet Subject: allow complex pointer to reals and vice versa Date: 2024-June-10 #Reference: 1. Introduction The proposal is to allow a complex pointer to be associated to a real array, and similarly to allow a real pointer to be associated to a complex scalar or array. 2. Motivation In short, the motivation is to save memory and cpu-memory bandwidth in some HPC contexts, typically when dealing with very large or huge data volumes. When working in the Fourier domain, it is common to use in-place real to complex Fourier transforms. Before the forward transform one generally stores data in a real array, and the transform outputs complex numbers in the same real array, with interleaved real and imaginary parts. Once in the Fourier domain the complex type is more appropriate to perform computations, but this requires either: - duplicating the data into a complex array after the transform, thus cancelling the advantage of the in-place transform. Note that in HPC, and particularly when dealing with large data volumes, duplicating the data is really something that we want to avoid whenever possible. - or using various non-standard tricks that rely on the fact that virtually all compilers store a complex number by the sequence real part / imaginary part. For instance, consider a code that performs a FFT based convolution at some point: program foo real, allocatable :: r1(:), r2(:), r3(:) integer, parameter :: n = 100000 ! ... allocate( r1(n), r2(n), r3(n) ) ! ... some computations on r1(:) and r2(:) as reals call rfft(r1) ; call rfft(r2) call fftconvol(r1,r2,r3,n/2) ! called without any interface, ! hence type mismatchs call irfft(r3) ! ... some computations on r3(:) as real end program ! dangling routine (not contained and not in a module) subroutine fftconvol(c1,c2,c3,n) integer, intent(in) :: n complex, intent(in) :: c1(n), c2(n) complex, intent(out) :: c3(n) c3(:) = c1(:) * c2(:) end subroutine With modern Fortran another trick has got quite popular, by using the C interoperability features. Nonetheless the trick is non standard: program foo use iso_c_binding real, allocatable :: r1(:), r2(:), r3(:) integer, parameter :: n = 100000 complex, pointer :: c1(:), c2(:), c3(:) ! ... allocate( r1(n), r2(n), r3(n) ) ! ... some computations on r1(:) and r2(:) as reals call rfft(r1) ; call rfft(r2) ! non-standard trick call c_f_pointer( c_loc(r1), c1, [n/2] ) call c_f_pointer( c_loc(r2), c2, [n/2] ) call c_f_pointer( c_loc(r3), c3, [n/2] ) c3(:) = c1(:) * c2(:) call irfft(r3) ! ... some computations on r3(:) as real end program The `c_f_pointer()` trick is even mentioned in the official documentation of FFTW, which is one of the most popular FFT library [1]. More generally, this is a recurrent topic on forums or on StackOverflow [2] 3. Proposed solution The proposal essentially aims at standardizing the usual practice above, by allowing a real array to be viewed as a complex array and vice-versa. The above code would become: program foo real, allocatable, target :: r1(:), r2(:), r3(:) integer, parameter :: n = 100000 complex, pointer :: c1(:), c2(:), c3(:) ! ... allocate( r1(n), r2(n), r3(n) ) ! ... some computations on r1(:) and r2(:) as reals call rfft(r1) ; call rfft(r2) c1 => r1 ; c2 => r2 ; c3 => r3 c3(:) = c1(:) * c2(:) call irfft(r3) ! ... some computations on r3(:) as real end program 3.1. Syntax No new syntax need to be created, the usual pointer association can be used, with additional rules and restrictions: `c => r` - `r` shall be a *contiguous* real array, which has either the target or the pointer attribute - `c` shall be a complex array pointer of the same kind as `r`, and of the same rank by default (but pointer rank remapping can be used) - `c` could also be a complex scalar pointer, in the case r is a rank-1 array of size 2 - the size of the first dimension of `r` shall be even - `c%re` shall refer to the same storage as `r(1::2)` (rank-1), or `r(1::2,:)` (rank-2), etc... - `c%im` shall refer to the same storage as `r(2::2)` (rank-1), or `r(2::2,:)` (rank-2), etc... `r => c` - the exact opposite - `c` shall be a *contiguous* complex array or a complex scalar, which has either the target or the pointer attribute - `r` shall be a real array pointer of the same kind as `c`, and of the same rank by default (but pointer rank remapping can be used) - if `c` is a scalar, then `r` shall be a rank-1 pointer of size 2 - same other rules as above 3.2 Alternative syntaxes If one wants to make the association between two different types more explicit and more type-safe, a new syntax may be introduced: Alternative syntax 1 (kind of pointer casting): ``` c => complex :: r r => real :: c ``` Alternative syntax 2: ``` c => complex_pointer(r) r => real_pointer(c) ``` Alternative syntax 3 (similar to the c_f_pointer() subroutine): ``` call complex_pointer(r,c[,shape][,lower]) call real_pointer(c,r[,shape][,lower]) ``` Alternative syntax 4 (more generic, in case other inter-type associations would be allowed in future versions of the standard): ``` call storage_associate(r,c[,shape][,lower]) call storage_associate(r,c[,shape][,lower]) ``` 3.3 Intermediate representation During the discussions it has been proposed tu use an intermediate representation between the real and complex view, which would be a real view with an augmented rank and first dimension of size 2. Instead of going directly from `r(n)` to `c(n/2)` one would define by rank remapping: `r2(1:2,1:n/2) => r(1:n)`, then `c => r2` And the other way: `r2 => c`, then `r(1:n) => r2(1:2,1:n/2)` This could also be coupled with a new pseudo-component notation `c%reim` that would be equivalent the `r2` respresentation (i.e. `c%reim` would be `real`, with a shape [2,n/2]). I personally find that such an intermediate representation just makes the proposal more complex than it needs to be, and that nothing really useful can be performed with it. Moreover the `c%reim` notation would require another change in the standard to allow non zero ranks on both sides of `%`. 3.4 Prototyping, etc... I think that this proposal doesn't need to have a preliminary prototype implementation, as it essentially consists in standardizing an already existing and common practice. A prototype implementation would do nothing else than mimicing the `c_f_pointer()` trick. 4. Issues / Objections / Limitations 4.1. Memory layout This proposal would constraint the memory layout of the complex type beyond what the current standard does. However, the required layout seems to be the de-facto standard, used by virtually all existing compilers (i.e. storing a complex scalar by the sequence real part/ imaginary part, in that order). Also, the proposal would not prevent alternative memory layouts for the arrays in future versions of the standard, such as the row major storage. For instance, in the case of a row major 2D array, `c%re` would refer to the same storage as `r(:,1::2)` instead of `r(1::2,:)` for the column major case. More generally the obtained view would be layout dependent, and the standard would have to describe the view for each layout. Similarly, the proposal would be compatible with the so-called split-complex layout, if introduced later (without assessing here if such a layout is possible at all). In this layout, a complex array is stored with all the real parts first, then all the imaginary parts. In this case, `c%re` would refer to the same storage as `r(1:n/2)`. 4.2. Argument association Allowing a real actual argument to be associated to a complex dummy argument -and vice-versa- has also been considered, but it would raise backward compatibility issues. So this part has been dropped from the proposal. 4.3. Alignment Considering for instance a default real type stored on 4 bytes, the default complex type is stored on 8 bytes. Compilers may prefer/want to align the complex arrays on 8 bytes boundaries, which cannot be guaranteed if a complex pointer is associated to an arbitrary real array (e.g. `c => r(2:)`). If this is a problem, the pointer association may be allowed only the other way (real pointer associated to a complex array), where alignement should not be a problem. 5. References [1] https://www.fftw.org/fftw3_doc/Reversing-array-dimensions.html [2] https://fortran-lang.discourse.group/t/ implicit-real-complex-conversion-in-fortran/7381 https://stackoverflow.com/questions/31590004/ is-the-storage-of-complex-in-fortran-guaranteed-to-be-two-reals https://stackoverflow.com/questions/36977737/ pass-a-real-array-if-a-complex-array-is-expected Discussions related to this proposal: https://fortran-lang.discourse.group/t/complex-type-storage-again/7020 https://github.com/j3-fortran/fortran_proposals/issues/323 https://github.com/j3-fortran/fortran_proposals/pull/325 ================================================ FILE: proposals/conditional_expressions/21-165.txt ================================================ To: J3 J3/21-165 From: Ondrej Certik, Milan Curcic, Zach Jibben Subject: Conditional expressions using intrinsic function Date: 2021-June-27 Reference: 21-157, 21-159 Paper 21-157 proposes two syntactic forms for conditional expressions. The paper 21-159 proposes an alternative form. This paper proposes to implement conditional expressions using an intrinsic function. The syntactic forms in 21-157 are too verbose, as argued in 21-159. The form in 21-159 is concise, but based on the poll [1], it is not the most popular either. We propose the following syntactic form is ifthen( predicate, consequent, alternative ) where the `predicate` is a scalar logical expression and the `consequent` and `alternative` are compatible expressions. The `alternative` can be another `ifthen` as shown in an example below. Examples: res = ifthen(x >= 0.0, sqrt(x), -0.0) res = ifthen(present(x), a, ifthen(present(b), b, 0)) Objections raised in the past: 1) It is harder to do chaining due to the extra parenthesis at the end. Answer: it seems in practice it is not too bad, as in the second example above. Here is a longer chain: res = ifthen(present(x), x, ifthen(present(y), y, ifthen(present(z), z, 0))) 2) `ifthen` does not behave like a regular function, it is effectively new syntax, so we might as well actually introduce new syntax, to make it less confusing There is a precedent in optional arguments, where the argument is not evaluated but passed through through nested functions until it hits the function `present`. In a similar way, the arguments to `ifthen` are not evaluated ahead of time, but rather passed into the function `ifthen` itself, similar to how `present(x)` works. Both `ifthen` and `present` must be implemented by the compiler itself, they cannot be implemented in standard Fortran code. References: [1] https://fortran-lang.discourse.group/t/ poll-fortran-202x-conditional-expressions-syntax/1425 ================================================ FILE: proposals/default_optional_arguments/proposal.txt ================================================ To: J3 J3/XX-XXX From: Milan Curcic, Jeremie Vandenplas, Zach Jibben Subject: Default value for optional arguments Date: 2020-January-10 Reference: 18-136r1, 18-122r1 1. Introduction This paper contains a proposal for Fortran 202y, to allow a programmer to specify a default value for optional dummy arguments. This would allow the programmer to then safely reference such variables in expressions regardless of whether the actual argument is present or not. 2. Problem Currently, standard Fortran does not allow setting a default value for optional arguments. Default value of an optional argument is the value that the dummy argument would take if the corresponding actual argument is not present. If declaring a dummy argument as optional, the user must: * Explicitly test for the presence of the actual argument using the intrinsic function present(); * Use a separate variable inside the procedure to assign the value because the optional dummy argument that is not present must not be referenced in expressions other than as actual argument to the intrinsic function present(). This example function illustrates the problem: real function quadratic(x, a, b, c) ! returns a + b * x + c * x**2 if c is present ! and a + b * x otherwise real, intent(in) :: x, a, b real, intent(in), optional :: c real :: c_tmp ! use another var. to reference the missing arg c_tmp = 0 ! default value if c is not present if (present(c)) c_tmp = c quadratic = a + b * x + c_tmp * x**2 end function quadratic For any dummy argument with the optional attribute, the programmer must use the intrinsic function present() to check for the presence of the argument. Furthermore, if the optional dummy argument is meant to be used in multiple places in the procedure, the programmer is likely to use the pattern from the example above, where a "temporary" variable is declared and used in place of the dummy argument, which disconnects the implementation from the user interface. Furthermore, this requires at least 3 lines of code (declaration of c_tmp, initialization of c_tmp, and testing for the presence of c) only to handle the scenario of a missing optional argument. This proposal seeks to address the issue that explicitly checking for presence of the optional dummy argument and using a helper variable is cumbersome and error-prone. The primary benefit of this feature is the reduction in source code needed to handle optional arguments. This benefit is even greater in scenarios where the optional argument is used in many places in the procedure, and a helper variable is used for its value instead. Reduction in needed source code would result in more readable and more correct programs. The secondary benefit of this is programmer happiness, as working with optional arguments would require less typing. 3. Proposed solution As suggested by Van Snyder in 18-136r1, the problem could be solved by allowing an optional argument to be initialized using a constant expression. The optional argument would then only be initialized if the corresponding actual argument is not provided by the caller. Example: real function quadratic(x, a, b, c) ! returns a + b * x + c * x**2 if c is present ! and a + b * x otherwise real, intent(in) :: x, a, b real, intent(in), optional :: c = 0 quadratic = a + b * x + c * x**2 end function quadratic In this snippet, we use the assignment operator (=) to specify the default value of the optional dummy argument. Like initializer, the optional argument can be assigned any constant expression, as defined in Section 10.1.12 of 18-007r1. While there may be concerns that the same syntax is used to implicitly set the save attribute for variables in procedures, there is no conflict because the language prohibits dummy arguments from having an initializer, or the save attribute. The change to the standard to allow this feature would thus be to allow an optional dummy argument to have an initializer, which would be triggered only when corresponding actual argument is not passed by the caller. This improvement has already been suggested by Van Snyder in 18-136r1, has received votes in the user survey for 202X (see isotc.iso.org/livelink/livelink?func=ll&objId=19530634&objAction=Open), and appeared on Data subgroup's wishlist at meeting 215 (see 18-122r1). 4. Backward compatibility This addition to the language would not break any existing standard conforming Fortran program, and thus preserves Fortran's backward compatibility. 5. Related behavior 5.1. present() There are two possible behaviors of the function present() when an initializer is provided. The first behavior would be that the function present() always returns .true. when the optional dummy argument has an initializer, and independently of the fact that an actual argument is passed or not by the caller. Therefore, with such a behavior, the function present() becomes useless for optional arguments with a default value. Example: real function foo(a, b) real, intent(in), optional :: a real, intent(in), optional :: b = 0 print*, present(a) !.true. if an actual argument is provided !by the caller, .false. otherwise print*, present(b) !always .true. due to the initializer end function foo The second behavior would be that the function present() returns .true. or .false. when an actual argument is passed or not by the caller, and independently of the fact that the optional dummy argument has, or hasn't an initializer. A use case of this behavior of the function present() could be: real function foo(a, b) real, intent(in) :: a real, intent(in), optional :: b = 1.234 if (present(b)) then ... !expensive input validation here end if ... end function foo 5.2. intent(out) There are several different possible behaviors for intent(out) variables. One option is to disallow default values on optional intent(out) variables. However, there are use cases where it is desirable to return the value of a variable if a caller requests it, without requiring the added logic of present() and "temporary" variables described above. Alternatively, we might treat intent(in) and intent(out) variables identically, so that the following would be valid: real function foo(a) real, intent(out), optional :: a = 0 end function foo The benefit is that, just like proposed for intent(in) variables, intent(out) variables would be usable regardless of whether the value is actually passed back to the caller, removing the need for present() and "temporary" variables. The complication is that a present intent(out) variable is initially undefined, meaning the logic of present() might still be necessary in this case. Furthermore, the behavior suggested above where present() = .true. always for variables with a specified default would not be desired for intent(out) variables. A possible use case for this behavior could be: integer function open(filename, mode, iostat) result(u) character(*), intent(in) :: filename character(*), intent(in), optional :: mode integer, intent(out), optional :: iostat = 0 character(3) :: mode_ character(:),allocatable :: action_, position_, status_, access_ character(:),allocatable :: form_ !some code !.... open(newunit=u, file=filename, & action = action_, position = position_, status = status_, & access = access_, form = form_, & iostat = iostat) end function A third option is that optional intent(out) variables with a specified default are always initialized to that default, even when present, rather than leaving them undefined. 5.3 intent(inout) Dummy arguments with intent(inout) or no specified intent follow from the behavior for intent(in) and intent(out). If an optional intent(inout) variable is present, the existing behavior is followed. The dummy variable has the value of the actual argument on invocation of the procedure, its data may be used or manipulated during the scope of the procedure, then it is returned to the scoping unit. If an actual argument is not provided for an optional dummy argument with a specified default and intent(inout), the variable recieves the default value and may be used or modified throughout the procedure. The data is then not returned to the scoping unit, since no actual argument was provided. This would allow the following routine: subroutine foo(x) integer, intent(inout), optional :: x = 0 ! do stuff that could use or assign to x end subroutine foo This would be equivalent to: subroutine foo(x) integer, intent(inout), optional :: x integer :: x_ if (present(x)) then x_ = x else x_ = 0 end if ! do stuff that could use or assign to x_ if (present(x)) x = x_ end subroutine foo 5.4 Arrays Default values for array dummy arguments present a difficulty, particularly expressions involving a scalar. Some decision must be made for the behavior in the following instances: real, intent(in), optional :: a(:) = 0 real, intent(inout), optional :: b(:) = 0 real, intent(inout), allocatable, optional :: c(:) = 0 In each case, the size of the array is not determined if an actual argument is not provided. One option is of course to forbid defaults for arrays. This isn't preferable, but allowing defaults for scalars only may still address the majority of use cases. Another option is to forbid array broadcasting as a default specifier. That is, allow only defaults such as [0,0] for arrays, where the length is clear. 6. Further discussion Online discussion that led to this proposal can be found at https://github.com/j3-fortran/fortran_proposals/issue/22. ================================================ FILE: proposals/fp_interop/25-189r1.txt ================================================ To: J3 J3/25-189r1 From: Patrick Fasano Subject: Requirements and specifications for interoperability with C interchange and extended types Date: 2025-October-28 References: ISO/IEC 9899:2024 Programming languages -- C ("C23") (working draft N3220) WG5/N2249 Fortran 202Y Work Items 23-176 Add extended floating-point types defined in ISO C23 to ISO_C_BINDING ISO/IEC 60559:2020 Floating-Point arithmetic IEEE 754-2019 IEEE Standard for Floating-Point Arithmetic 25-189 Requirements and specifications for interoperability with C interchange and extended types 1 Background ============ bg01. The ISO C23 specification (ISO/IEC 9899:2024) added support for interchange and extended floating-point types (C23 Annex H). The current Fortran 202Y work list WG5/N2249 includes adding interoperability with these types as accepted work item US05. At meeting #230 in June 2023, J3 accepted paper 23-176 as background/use-case for additional interchange types. bg03. This paper contains formal requirements and specifications for Fortran 202Y proposal US05, "Add extended floating-point types defined in ISO C23 to ISO_C_BINDING". bg05. The C23 standard adds a large number of new types, importing significant portions of ISO/IEC 60559 (a.k.a IEEE 754) into its specification. During discussion of 25-189 at meeting #237, a straw poll of J3 indicated strong consensus for adding support for extended binary floating types (rq07-rq09) and decimal floating types (rq11), but strong consensus against extended decimal floating types (25-189 rq13) and arbitrary-size floating types (25-189 rq15-rq17). This revision amends the paper to reflect this consensus. 2 Requirements ============== rq02. The ISO_C_BINDING module shall provide integer named constants for KIND parameters corresponding to the C interchange floating types '_Float16', '_Float32', '_Float64', and '_Float128'. rq03. If the companion processor defines '__STDC_IEC_60559_BFP__' and '__STDC_IEC_60559_TYPES__', then the KIND parameters corresponding to '_Float32' and '_Float64' shall equal those corresponding to 'float' and 'double', respectively. rq05. The ISO_C_BINDING module shall provide integer named constants for KIND parameters corresponding to the C interchange complex types '_Float16_Complex', '_Float32_Complex', '_Float64_Complex', and '_Float128_Complex'. The values of the named constants shall equal those of the respective constants for the interchange floating types. rq07. The ISO_C_BINDING module shall provide integer named constants for KIND parameters corresponding to the C extended floating types '_Float32x', '_Float64x', and '_Float128x'. rq09. The ISO_C_BINDING module shall provide integer named constants for KIND parameters corresponding to the C extended complex types '_Float32x_Complex', '_Float64x_Complex', and '_Float128x_Complex'. The values of the named constants shall match those of the respective constants for the extended floating types. rq11. The ISO_C_BINDING module shall provide integer named constants for KIND parameters corresponding to the C interchange decimal floating types '_Decimal32', '_Decimal64', and '_Decimal128'. 3 Specifications ================ sp01. For all integer named constants to be added to the ISO_C_BINDING module, the value shall be a valid real kind type parameter or shall be -1 if the companion processor's type does not have a precision equal to the precision of any of the Fortran processor's real kinds, -2 if the companion processor's type does not have a range equal to the range of any of the Fortran processor's real kinds, -3 if the companion processor's type has neither the precision nor range of any of the Fortran processor's real kinds, -4 if there is no interoperating Fortran processor kind for other reasons, and -5 if the companion processor does not define the corresponding C type. sp02. To the ISO_C_BINDING module, add the following integer named constants for real KIND parameters: +----------------+--------------------+ | Named constant | C interchange type | +----------------+--------------------+ | C_FLOAT16 | _Float16 | | C_FLOAT32 | _Float32 | | C_FLOAT64 | _Float64 | | C_FLOAT128 | _Float128 | +----------------+--------------------+ If the companion processor defines '__STDC_IEC_60559_BFP__' and '__STDC_IEC_60559_TYPES__', then the constants 'C_FLOAT32' and 'C_FLOAT64' shall have the same values as 'C_FLOAT' and 'C_DOUBLE', respectively (C2023 H.2.4.2). sp05. To the ISO_C_BINDING module, add the following integer named constants for real KIND parameters: +--------------------+--------------------+ | Named constant | C interchange type | +--------------------+--------------------+ | C_FLOAT16_COMPLEX | _Float16_Complex | | C_FLOAT32_COMPLEX | _Float32_Complex | | C_FLOAT64_COMPLEX | _Float64_Complex | | C_FLOAT128_COMPLEX | _Float128_Complex | +--------------------+--------------------+ The values of each of these named constants shall be the same as the respective constants defined in sp01. sp07. To the ISO_C_BINDING module, add the following integer named constants for real KIND parameters: +----------------+-----------------+ | Named constant | C extended type | +----------------+-----------------+ | C_FLOAT32X | _Float32x | | C_FLOAT64X | _Float64x | | C_FLOAT128X | _Float128x | +----------------+-----------------+ sp09. To the ISO_C_BINDING module, add the following integer named constants for real KIND parameters: +---------------------+--------------------+ | Named constant | C extended type | +---------------------+--------------------+ | C_FLOAT32X_COMPLEX | _Float32x_Complex | | C_FLOAT64X_COMPLEX | _Float64x_Complex | | C_FLOAT128X_COMPLEX | _Float128x_Complex | +---------------------+--------------------+ sp11. To the ISO_C_BINDING module, add the following integer named constants for real KIND parameters: +----------------+--------------------+ | Named constant | C interchange type | +----------------+--------------------+ | C_DECIMAL32 | _Decimal32 | | C_DECIMAL64 | _Decimal64 | | C_DECIMAL128 | _Decimal128 | +----------------+--------------------+ === END === ================================================ FILE: proposals/fp_interop/25-203.txt ================================================ To: J3 J3/25-203 From: Patrick Fasano Subject: US05: Edits for C interchange and extended floating types Date: 2025-October-30 References: ISO/IEC 9899:2024 Programming languages -- C ("C23") (working draft N3220) WG5/N2249 Fortran 202Y Work Items ISO/IEC 60559:2020 Floating-Point arithmetic 25-189r1 Requirements and specifications for interoperability with C interchange and extended types 25-007r1 Fortran 2028 Working Draft 1 Background ============ The ISO C23 specification (ISO/IEC 9899:2024) added support for interchange and extended floating-point types (C23 Annex H). The current Fortran 202Y work list WG5/N2249 includes adding interoperability with these types as accepted work item US05. At meeting #237 in October 2025, J3 passed paper 25-189r1 as requirements and specifications for additional floating types. This paper provides edits for Fortran 202Y proposal US05, "Add C interoperability for new interchange floating point types in C". The ISO C23 specification also added 'bool' as a keyword with an alternate spelling of '_Bool'. This paper optionally provides edits for Fortran 202Y to propagate this change. 2 Edits (relative to 25-007r1) ============================== ------------------------------------------------------------------------ [xv] Add to "Intrinsic modules" the sentences: Named constants have been added to the ISO_C_BINDING module to allow interoperability with C interchange and extended floating-point formats defined in ISO/IEC 9899:2024 Annex H. ------------------------------------------------------------------------ [539:7-14] Replace 18.2.2 paragraph 4 with the following paragraphs: The values of C_FLOAT, C_DOUBLE, C_LONG_DOUBLE, C_FLOAT16, C_FLOAT32, C_FLOAT64, C_FLOAT128, C_FLOAT32X, C_FLOAT64X, C_FLOAT128X, C_DECIMAL32 C_DECIMAL64, and C_DECIMAL128 shall each be a valid value for a real kind type parameter on the processor or shall be -1 if the companion processor's type does not have a precision equal to the precision of any of the Fortran processor's real kinds, -2 if the companion processor's type does not have a range equal to the range of any of the Fortran processor's real kinds, -3 if the companion processor's type has neither the precision nor range of any of the Fortran processor's real kinds, -4 if there is no interoperating Fortran processor kind for other reasons, or -5 if the companion processor does not define the corresponding C type. The values of C_FLOAT_COMPLEX, C_DOUBLE_COMPLEX, C_LONG_DOUBLE_COMPLEX, C_FLOAT16_COMPLEX, C_FLOAT32_COMPLEX, C_FLOAT64_COMPLEX, C_FLOAT128_COMPLEX, C_FLOAT32X_COMPLEX, C_FLOAT64X_COMPLEX, and C_FLOAT128X_COMPLEX shall be the same as those of C_FLOAT, C_DOUBLE, C_LONG_DOUBLE, C_FLOAT16, C_FLOAT32, C_FLOAT64, C_FLOAT128, C_FLOAT32X, C_FLOAT64X, and C_FLOAT128X, respectively. If the companion processor defines the C preprocessor macros __STDC_IEC_60559_BFP__ and __STDC_IEC_60559_TYPES__, the values of C_FLOAT32 and C_FLOAT64 shall be the same as those of C_FLOAT and C_DOUBLE, respectively. ------------------------------------------------------------------------ [2:6] Insert a new line: ISO/IEC 9899:2024, Programming languages -- C ------------------------------------------------------------------------ [547:13] Replace the line The C types mentioned in Table 18.2 are defined in ISO/IEC 9899:2018, 6.2.5, 7.19, and 7.20.1. with the line The C types mentioned in Table 18.2 are defined in ISO/IEC 9899:2024, 6.2.5, 7.22, and H.2. ------------------------------------------------------------------------ [547:table 18.2] Add the following rows to the REAL section of the table: +--------------+-------------+ | C_FLOAT16 | _Float16 | | C_FLOAT32 | _Float32 | | C_FLOAT64 | _Float64 | | C_FLOAT128 | _Float128 | | C_FLOAT32X | _Float32x | | C_FLOAT64X | _Float64x | | C_FLOAT128X | _Float128x | | C_DECIMAL32 | _Decimal32 | | C_DECIMAL64 | _Decimal64 | | C_DECIMAL128 | _Decimal128 | +--------------+-------------+ ------------------------------------------------------------------------ [547-548:table 18.2] Add the following rows to the COMPLEX section of the table: +---------------------+---------------------+ | C_FLOAT16_COMPLEX | _Float16 _Complex | | C_FLOAT32_COMPLEX | _Float32 _Complex | | C_FLOAT64_COMPLEX | _Float64 _Complex | | C_FLOAT128_COMPLEX | _Float128 _Complex | | C_FLOAT32X_COMPLEX | _Float32x _Complex | | C_FLOAT64X_COMPLEX | _Float64x _Complex | | C_FLOAT128X_COMPLEX | _Float128x _Complex | +---------------------+---------------------+ ------------------------------------------------------------------------ [557:table 18.4] Add the following rows after the row for CFI_type_long_double: +---------------------+-------------+ | CFI_type_Float16 | _Float16 | | CFI_type_Float32 | _Float32 | | CFI_type_Float64 | _Float64 | | CFI_type_Float128 | _Float128 | | CFI_type_Float32x | _Float32x | | CFI_type_Float64x | _Float64x | | CFI_type_Float128x | _Float128x | | CFI_type_Decimal32 | _Decimal32 | | CFI_type_Decimal64 | _Decimal64 | | CFI_type_Decimal128 | _Decimal128 | +---------------------+-------------+ ------------------------------------------------------------------------ [557:table 18.4] Add the following rows after the row for CFI_type_long_double_Complex: +----------------------------+---------------------+ | CFI_type_Float16_Complex | _Float16 _Complex | | CFI_type_Float32_Complex | _Float32 _Complex | | CFI_type_Float64_Complex | _Float64 _Complex | | CFI_type_Float128_Complex | _Float128 _Complex | | CFI_type_Float32X_Complex | _Float32x _Complex | | CFI_type_Float64X_Complex | _Float64x _Complex | | CFI_type_Float128X_Complex | _Float128x _Complex | +----------------------------+---------------------+ ------------------------------------------------------------------------ [538:30] Replace two instances of: (_Bool) with: (bool) ------------------------------------------------------------------------ [548:table 18.2] Replace the following: _Bool with: bool ------------------------------------------------------------------------ [557:table 18.4] Replace the following: +---------------+-------+ | CFI_type_Bool | _Bool | +---------------+-------+ with: +---------------+------+ | CFI_type_Bool | bool | | CFI_type_bool | bool | +---------------+------+ ------------------------------------------------------------------------ ===END=== ================================================ FILE: proposals/fp_interop/25-203r1.txt ================================================ To: J3 J3/25-203r1 From: Patrick Fasano Subject: US05: Edits for C interchange and extended floating types Date: 2026-January-12 References: ISO/IEC 9899:2024 Programming languages -- C ("C23") (working draft N3220) WG5/N2249 Fortran 202Y Work Items ISO/IEC 60559:2020 Floating-Point arithmetic 25-189r1 Requirements and specifications for interoperability with C interchange and extended types 25-007r1 Fortran 2028 Working Draft 1 Background ============ The ISO C23 specification (ISO/IEC 9899:2024) added support for interchange and extended floating-point types (C23 Annex H). The current Fortran 202Y work list WG5/N2249 includes adding interoperability with these types as accepted work item US05. At meeting #237 in October 2025, J3 passed paper 25-189r1 as requirements and specifications for additional floating types. This paper provides edits for Fortran 202Y proposal US05, "Add C interoperability for new interchange floating point types in C". The ISO C23 specification also added 'bool' as a keyword with an alternate spelling of '_Bool'. This paper additionally provides edits for Fortran 202Y to propagate this change. 2 Edits (relative to 25-007r1) ============================== ------------------------------------------------------------------------ [xv] Add to "Intrinsic modules" the sentences: Named constants have been added to the ISO_C_BINDING module to allow interoperability with C interchange and extended floating-point formats defined in ISO/IEC 9899:2024 Annex H. ------------------------------------------------------------------------ [539:7-14] Replace 18.2.2 paragraph 4 with the following paragraphs: The values of C_FLOAT, C_DOUBLE, C_LONG_DOUBLE, C_FLOAT16, C_FLOAT32, C_FLOAT64, C_FLOAT128, C_FLOAT32X, C_FLOAT64X, C_FLOAT128X, C_DECIMAL32 C_DECIMAL64, and C_DECIMAL128 shall each be a valid value for a real kind type parameter on the processor or shall be -1 if the companion processor's type does not have a precision equal to the precision of any of the Fortran processor's real kinds, -2 if the companion processor's type does not have a range equal to the range of any of the Fortran processor's real kinds, -3 if the companion processor's type has neither the precision nor range of any of the Fortran processor's real kinds, -4 if there is no interoperating Fortran processor kind for other reasons, or -5 if the companion processor does not define the corresponding C type. The values of C_FLOAT_COMPLEX, C_DOUBLE_COMPLEX, C_LONG_DOUBLE_COMPLEX, C_FLOAT16_COMPLEX, C_FLOAT32_COMPLEX, C_FLOAT64_COMPLEX, C_FLOAT128_COMPLEX, C_FLOAT32X_COMPLEX, C_FLOAT64X_COMPLEX, and C_FLOAT128X_COMPLEX shall be the same as those of C_FLOAT, C_DOUBLE, C_LONG_DOUBLE, C_FLOAT16, C_FLOAT32, C_FLOAT64, C_FLOAT128, C_FLOAT32X, C_FLOAT64X, and C_FLOAT128X, respectively. If the companion processor defines the C preprocessor macros __STDC_IEC_60559_BFP__ and __STDC_IEC_60559_TYPES__, the values of C_FLOAT32 and C_FLOAT64 shall be the same as those of C_FLOAT and C_DOUBLE, respectively. ------------------------------------------------------------------------ [2:6] Replace the line: ISO/IEC 9899:2018, Programming languages -- C with the line ISO/IEC 9899:2024, Programming languages -- C ------------------------------------------------------------------------ [547:13] Replace the line The C types mentioned in Table 18.2 are defined in ISO/IEC 9899:2018, 6.2.5, 7.19, and 7.20.1. with the line The C types mentioned in Table 18.2 are defined in ISO/IEC 9899:2024, 6.2.5, 7.22, and H.2. ------------------------------------------------------------------------ [547:table 18.2] Add the following rows to the REAL section of the table: +--------------+-------------+ | C_FLOAT16 | _Float16 | | C_FLOAT32 | _Float32 | | C_FLOAT64 | _Float64 | | C_FLOAT128 | _Float128 | | C_FLOAT32X | _Float32x | | C_FLOAT64X | _Float64x | | C_FLOAT128X | _Float128x | | C_DECIMAL32 | _Decimal32 | | C_DECIMAL64 | _Decimal64 | | C_DECIMAL128 | _Decimal128 | +--------------+-------------+ ------------------------------------------------------------------------ [547-548:table 18.2] Add the following rows to the COMPLEX section of the table: +---------------------+---------------------+ | C_FLOAT16_COMPLEX | _Float16 _Complex | | C_FLOAT32_COMPLEX | _Float32 _Complex | | C_FLOAT64_COMPLEX | _Float64 _Complex | | C_FLOAT128_COMPLEX | _Float128 _Complex | | C_FLOAT32X_COMPLEX | _Float32x _Complex | | C_FLOAT64X_COMPLEX | _Float64x _Complex | | C_FLOAT128X_COMPLEX | _Float128x _Complex | +---------------------+---------------------+ ------------------------------------------------------------------------ [557:table 18.4] Add the following rows after the row for CFI_type_long_double: +---------------------+-------------+ | CFI_type_Float16 | _Float16 | | CFI_type_Float32 | _Float32 | | CFI_type_Float64 | _Float64 | | CFI_type_Float128 | _Float128 | | CFI_type_Float32x | _Float32x | | CFI_type_Float64x | _Float64x | | CFI_type_Float128x | _Float128x | | CFI_type_Decimal32 | _Decimal32 | | CFI_type_Decimal64 | _Decimal64 | | CFI_type_Decimal128 | _Decimal128 | +---------------------+-------------+ ------------------------------------------------------------------------ [557:table 18.4] Add the following rows after the row for CFI_type_long_double_Complex: +----------------------------+---------------------+ | CFI_type_Float16_Complex | _Float16 _Complex | | CFI_type_Float32_Complex | _Float32 _Complex | | CFI_type_Float64_Complex | _Float64 _Complex | | CFI_type_Float128_Complex | _Float128 _Complex | | CFI_type_Float32X_Complex | _Float32x _Complex | | CFI_type_Float64X_Complex | _Float64x _Complex | | CFI_type_Float128X_Complex | _Float128x _Complex | +----------------------------+---------------------+ ------------------------------------------------------------------------ [538:30] Replace two instances of: (_Bool) with: (bool) ------------------------------------------------------------------------ [548:table 18.2] Replace the following: _Bool with: bool ------------------------------------------------------------------------ [557:table 18.4] Replace the following: +---------------+-------+ | CFI_type_Bool | _Bool | +---------------+-------+ with: +---------------+------+ | CFI_type_Bool | bool | | CFI_type_bool | bool | +---------------+------+ ------------------------------------------------------------------------ ===END=== ================================================ FILE: proposals/namelist_delimiters/namelist_proposal.txt ================================================ To: J3 J3/XX-XXX From: Marshall Ward Subject: Require delimiters for character arrays in namelist output Date: 19 November 2019 Proposal for Fortran Standard: 202y 1. Introduction According to the current standard, a WRITE statement can write a namelist file that does not conform to the namelist specification. This happens when the namelist group contains a character array and the DELIM specifier has a value of NONE. In particular, this is the default behavior of a WRITE statement whose input is a namelist. Our proposal is to require delimiters when using WRITE to write a namelist to a file, by either requiring a value of DELIM which is namelist-compliant, or by overriding a value of NONE when the input is a namelist. 2. Motivation The namelist format is described in section 13.11 of the standard, and 13.11.3.3p7 requires that character arrays in namelist groups must be delimited with single or double quotes. When the next effective item is of type character, the input form consists of a sequence of zero or more rep-chars whose kind type parameter is implied by the kind of the corresponding list item, delimited by apostrophes or quotes. Any namelist whose character arrays are non-delimited is non-conformant. Any parsing of this output is therefore considered to be unformatted, and the interpretation is at the discretion of the interpreter. Without delimiters, many character arrays become unparseable. If a character array contains any lexical namelist tokens, such as `&` or `/`, then any non-delimited values may be misinterpreted as part of the namelist object structure. The standard acknowledges the limitations of non-delimited character array parsing, and specifically directs the interpreter to ignore the value of DELIM when reading a namelist (12.5.6.8). The scalar-default-char-expr shall evaluate to APOSTROPHE, QUOTE, or NONE. The DELIM= specifier is permitted only for a connection for formatted input/output. It specifies the delimiter mode (12.6.2.8) for list-directed (13.10.4) and namelist (13.11.4.2) output for the connection. This mode has no effect on input. However, despite the acknowledgment of the issues above, the default behavior of a WRITE command is to produce non-delimited character arrays. From 13.11.4.2p1, Values in namelist output records are edited as for list-directed output (13.10.4). This is done despite the fact that list-directed output follows different I/O rules for character arrays. From 13.10.3.1p7 (my emphasis), When the next effective item is of type character, the input form consists of a **possibly** delimited sequence of zero or more rep-chars whose kind type parameter is implied by the kind of the effective item. The namelist specification 13.11.3.3p7 deliberately omits "possibly" from its description. In other words, list-directed output permits non-delimited arrays, whereas namelists do not. In addition, the default value of DELIM is to revert back to its value in the OPEN call. From 12.5.6.8p1, If this specifier is omitted in an OPEN statement that initiates a connection, the default value is NONE. The default behavior of a WRITE call using namelist data is therefore to produce an output which is non-conformant with the namelist standard. 3. Example Consider the program listed below, which will produce a namelist containing a single group `sample_nml`, containing a single character array, `input`. program writenml implicit none character(len=20) :: input namelist /sample_nml/ input input = trim("some/path/to/file") open(5, file="out.nml") write(5, nml=sample_nml) end program writenml According to the interpretation above, the absence of a DELIM argument means that `input` is formatted with no delimiter. A standard-conforming output would be &SAMPLE_NML INPUT = some/path/to/file / For this example, we have used the output produced by the Intel Fortran Compiler 19.0.5.281. Now consider the following program, which reads this namelist. program readnml implicit none character(len=20) :: input namelist /sample_nml/ input open(5, file='out.nml') read(5, nml=sample_nml) open(6, file='new.nml') write(6, nml=sample_nml) end program readnml The namelist `new.nml` produced by this program is the following. &SAMPLE_NML INPUT = some / The namelist group `sample_nml` is terminated after the first `/` token, and any characters following the token are ignored. Although the interpretation is correct, it also means that a write statement of the following form write(unit, nml=filename) where the DELIM argument is unset will produce namelists which are non-conforming. The fact that this is not only possible, but is the default behavior, is counterintuitive and is likely to introduce errors into namelist I/O operations. As an aside, we note that GNU Fortran explicitly breaks from the standard and does produce a quote-delimited namelist, such as the one shown below. &SAMPLE_NML INPUT="some/path/to/file ", / This namelist above was produced by GNU Fortran 9.2.1. 4. Proposal We propose one of the following additions to the *io-control-spec-list*, detailed in 12.6.2.1. A. If *namelist-group-name* appears, then a DELIM= specifier with the value of either APOSTROPHE or QUOTE shall also appear. Option A would take the current recommended advice to always use DELIM when writing namelist output and turn it into an explicit rule. The following statement would constitute an error write(unit, nml=filename) and would require the user to include a DELIM argument, e.g. write(unit, nml=filename, delim="quote") This would also mean that currently compliant code missing a DELIM would be non-compliant, and may require modifications if used by future interpreters. B. If *namelist-group-name* appears and a DELIM= specifier has the value of NONE, then this value is ignored and the data transfer uses a value of APOSTROPHE. Option B would change the behavior of existing standard-compliant interpreters, in that non-delimited character arrays would be replaced with apostrophe-delimited arrays. But existing source code would otherwise remain compliant and continue to compile on both older and newer interpreters. 5. Reference Discussion of this issue on the Intel Fortran forums: https://software.intel.com/en-us/forums/intel-fortran-compiler/topic/831685 Discussion of GNU Fortran's decision to use quote delimiters: https://gcc.gnu.org/ml/gcc-patches/2014-03/msg00030.html Initial submission and discussion to the J3 Fortran Github repository: https://github.com/j3-fortran/fortran_proposals/pull/94 ================================================ FILE: proposals/namespace_modules/19-246.txt ================================================ To: J3 J3/19-246 From: Ondrej Certik Subject: Namespace For Modules Date: 2019-October-16 Proposal for Fortran Standard: 202y (NOT 202x) 1. Introduction The proposal is to allow import a module as a namespace and access its members using the % operator. Example: use, namespace :: utils ... call utils%savetxt(...) Where `utils` is the only name that is imported in the local namespace. `savetxt` is not accesible directly, only via `utils%`. 2. Motivation Fortran module usage is equivalent to Python: Python Fortran from A import foo use A, only: foo from A import foo as Afoo use A, only: Afoo => foo from A import * use A Except: Python Fortran import A N/A import A as B N/A This proposal proposes to fill in the missing functionality as follows: Python Fortran import A use, namespace :: A import A as B use, namespace :: B => A 3. Use Cases 3.1 Same function names in multiple modules In Python a very common idiom is: import math import numpy as np import sympy as sym ... e1 = np.sin(np.pi) # NumPy expression e2 = math.sin(math.pi) # Built-in Python math expression e3 = sym.sin(sym.pi) # SymPy expression In Fortran currently one has to do: use math, only: math_sin => sin, math_pi => pi use numpy, only: np_sin => sin, np_pi => pi use sympy, only: sym_sin => sin, sym_pi => pi ... e1 = np_sin(np_pi) ! NumPy expression e2 = math_sin(math_pi) ! Built-in Python math expression e3 = sym_sin(sym_pi) ! SymPy expression With this proposal one could also do: use, namespace :: math use, namespace :: np => numpy use, namespace :: sym => sympy ... e1 = np%sin(np%pi) ! NumPy expression e2 = math%sin(math%pi) ! Built-in Python math expression e3 = sym%sin(sym%pi) ! SymPy expression 3.2 Need to import lots of functions from a module Exising code (https://github.com/certik/fortran-utils/blob/ b43bd24cd421509a5bc6d3b9c3eeae8ce856ed88/src/linalg.f90): use lapack, only: dsyevd, dsygvd, ilaenv, zgetri, zgetrf, zheevd, & dgeev, zgeev, zhegvd, dgesv, zgesv, dgetrf, dgetri, dgelsy, & zgelsy, dgesvd, zgesvd, dgeqrf, dorgqr, dpotrf, dtrtrs ... call dgeev('N', 'V', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & work, lwork, info) ... call dgetrf(n, n, Amt, lda, ipiv, info) ... Instead, one can write it as: use, namespace :: lapack ... call lapack%dgeev('N', 'V', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & work, lwork, info) ... call lapack%dgetrf(n, n, Amt, lda, ipiv, info) ... Then when another subroutine must be called from the `lapack` module, one can just call it, without having to modify the `use` statement. ================================================ FILE: proposals/namespace_modules/20-108.txt ================================================ To: J3 J3/20-108 From: Ondrej Certik Subject: Namespace For Modules Date: 2020-February-23 Proposal for Fortran Standard: 202y (NOT 202x) 1. Introduction The proposal is to allow import a module as a namespace and access its members using the % operator. Example: use, namespace :: utils ... call utils%savetxt(...) Where `utils` is the only name that is imported in the local namespace. `savetxt` is not accesible directly, only via `utils%`. This proposal originated at the J3 GitHub repository at [1]. 2. Motivation Fortran module usage is equivalent to Python: Python Fortran from A import foo use A, only: foo from A import foo as Afoo use A, only: Afoo => foo from A import * use A Except: Python Fortran import A N/A import A as B N/A This proposal proposes to fill in the missing functionality as follows: Python Fortran import A use, namespace :: A import A as B use, namespace :: B => A 3. Use Cases 3.1 Same function names in multiple modules In Python a very common idiom is: import math import numpy as np import sympy as sym ... e1 = np.sin(np.pi) # NumPy expression e2 = math.sin(math.pi) # Built-in Python math expression e3 = sym.sin(sym.pi) # SymPy expression In Fortran currently one has to do: use math, only: math_sin => sin, math_pi => pi use numpy, only: np_sin => sin, np_pi => pi use sympy, only: sym_sin => sin, sym_pi => pi ... e1 = np_sin(np_pi) ! NumPy expression e2 = math_sin(math_pi) ! Built-in Python math expression e3 = sym_sin(sym_pi) ! SymPy expression With this proposal one could also do: use, namespace :: math use, namespace :: np => numpy use, namespace :: sym => sympy ... e1 = np%sin(np%pi) ! NumPy expression e2 = math%sin(math%pi) ! Built-in Python math expression e3 = sym%sin(sym%pi) ! SymPy expression 3.2 Need to import lots of functions from a module Existing code (https://github.com/certik/fortran-utils/blob/ b43bd24cd421509a5bc6d3b9c3eeae8ce856ed88/src/linalg.f90): use lapack, only: dsyevd, dsygvd, ilaenv, zgetri, zgetrf, zheevd, & dgeev, zgeev, zhegvd, dgesv, zgesv, dgetrf, dgetri, dgelsy, & zgelsy, dgesvd, zgesvd, dgeqrf, dorgqr, dpotrf, dtrtrs ... call dgeev('N', 'V', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & work, lwork, info) ... call dgetrf(n, n, Amt, lda, ipiv, info) ... Instead, one can write it as: use, namespace :: lapack ... call lapack%dgeev('N', 'V', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & work, lwork, info) ... call lapack%dgetrf(n, n, Amt, lda, ipiv, info) ... Then when another subroutine must be called from the `lapack` module, one can just call it, without having to modify the `use` statement. 4. References [1] https://github.com/j3-fortran/fortran_proposals/issues/1 ================================================ FILE: proposals/protected_attribute.txt ================================================ To: J3 J3/##-### From: Balint Aradi Subject: Protected attribute for derived type components Date: 2019-October-21 Proposal for Fortran Standard: 202y (NOT 202x) 1. Introduction The proposal is to allow specify the protected attribute for components in derived types. Example: type :: prot_t integer, allocatable, protected :: array(:) end type prot_t 2. Motivation Data hiding using derived types with private components allows programmer to provide robust types, where internals can only be changed in controlled fashion. The usual implementation is to provide setter and getter routines for writing and reading components. Both operations involve copying the passed data, which can be inefficient when storing large arrays in the derived type instance. While in the setter routine the copying serves robustness by "disentangling" the data stored in the derived type from the original data, the getter routine and the connected copy operation might be superfluous, when the consumer only wants to read but not modify the data stored in the derived type intance. By allowing for a direct read-only access, one could enhance efficiency in those cases without sacrificing the consistency of the encapsulated data. Direct access is possible with current Fortran already, but only if the respective derived type component is public. This would jeopardize data consistency, though, as a consumer could change the respective component arbitrarily, without using the provided setter routine. This proposal suggests to allow the "protected" attribute already used for module variables being used for derived type components as well. It would also remedy the asymmetry between the attributes "private"/"public" and the attribute "protected", as the former two can be applied to both, module variables and derived type components, while the latter only for module variables so far. 3. Use Cases Derived types storing large amount of data could enable read-only access to components without the necessity of a getter routine and the connected copy operation: module data_m implicit none type :: prot_t ! Large array component integer, allocatable, protected :: array(:) contains procedure :: set end type prot_t contains subroutine set(this, array) type(prot_t), intent(out) :: this this%array = array end subroutine set end module data_m program use_data use data_m implicit none type(prot_t) :: storage integer, allocatable :: large_array(:) ! Filling up and allocating the large array ! ... ! Storing the large array in the derived type instance call storage%set(large_array) ! Accessing large array stored in the derived type directly ! No getter routine and no copy necessary ! Dummy argument of the called routine must be intent(in) call some_routine_processing_but_not_changing_the_array(storage%array) ! Inconsistent change, consumer is supposed to call set() to change ! encapsulated data. ! Uncommenting the next line should trigger a compiler error. !storage%array(1) = -1 end program use_data 4. Online discussion This proposal derived from the discussion on the j3-fortran github page: https://github.com/j3-fortran/fortran_proposals/issues/16 ================================================ FILE: proposals/run-time_polymorphism/Examples/AppendixA/taylor.f90 ================================================ ! Fortran implementation of the Taylor series example program based on ! subclassing given in Appendix A of the proposal "Improved run-time ! polymorphism for Fortran". ! module derivs type, abstract :: Deriv contains procedure(pderiv), deferred, nopass :: deriv1 end type Deriv abstract interface subroutine pderiv() end subroutine pderiv end interface type, extends(Deriv) :: DerivF contains procedure, nopass :: deriv1 => deriv1f end type DerivF type, extends(DerivF) :: HDerivF contains procedure, nopass :: deriv2 => deriv2f end type HDerivF type, extends(Deriv) :: DerivG contains procedure, nopass :: deriv1 => deriv1g end type DerivG type, extends(DerivG) :: HDerivG contains procedure, nopass :: deriv2 => deriv2g end type HDerivG contains subroutine deriv1f() write(*,*) ' 1st derivative of function F!' end subroutine deriv1f subroutine deriv2f() write(*,*) ' 2nd derivative of function F!' end subroutine deriv2f subroutine deriv1g() write(*,*) ' 1st derivative of function G!' end subroutine deriv1g subroutine deriv2g() write(*,*) ' 2nd derivative of function G!' end subroutine deriv2g end module derivs module series use derivs, only: Deriv, HDerivF, HDerivG type :: Taylor class(Deriv), allocatable :: calc contains procedure :: term1 procedure :: evaluate end type Taylor type, extends(Taylor) :: HTaylor contains procedure :: term2 => hterm2 procedure :: evaluate => hevaluate end type HTaylor contains subroutine term1(self) class(Taylor), intent(in) :: self call self%calc%deriv1() end subroutine term1 subroutine evaluate(self) class(Taylor), intent(in) :: self write(*,*) 'Evaluating Taylor series using' call self%term1() end subroutine evaluate subroutine hterm2(self) class(HTaylor), intent(in) :: self select type ( calc => self%calc ) class is ( HDerivF ) call calc%deriv2() class is ( HDerivG ) call calc%deriv2() class default write(*,*) 'Unknown type!' end select end subroutine hterm2 subroutine hevaluate(self) class(HTaylor), intent(in) :: self write(*,*) 'Evaluating Taylor series using' call self%term1() call self%term2() end subroutine hevaluate end module series program client use derivs, only: DerivG, HDerivG, Deriv use series, only: Taylor, HTaylor class(Deriv), allocatable :: derv class(Taylor), allocatable :: teval derv = DerivG() teval = Taylor(derv) call teval%evaluate() write(*,*) derv = HDerivG() teval = HTaylor(derv) call teval%evaluate() end program client ================================================ FILE: proposals/run-time_polymorphism/Examples/AppendixB/taylor.f90 ================================================ ! Fortran implementation of the Taylor series example program based on ! subtyping given in Appendix B of the proposal "Improved run-time ! polymorphism for Fortran". ! module interfaces abstract interface :: IDeriv subroutine deriv1() end subroutine deriv1 end interface IDeriv abstract interface, extends(IDeriv) :: IHDeriv subroutine deriv2() end subroutine deriv2 end interface IHDeriv end module interfaces module derivs use interfaces, only: IDeriv, IHDeriv type, implements(IDeriv) :: DerivF contains procedure, nopass :: deriv1 => deriv1f end type DerivF type, implements(IHDeriv) :: HDerivF contains procedure, nopass :: deriv1 => deriv1f procedure, nopass :: deriv2 => deriv2f end type HDerivF type, implements(IDeriv) :: DerivG contains procedure, nopass :: deriv1 => deriv1g end type DerivG type, implements(IHDeriv) :: HDerivG contains procedure, nopass :: deriv1 => deriv1g procedure, nopass :: deriv2 => deriv2g end type HDerivG contains subroutine deriv1f() write(*,*) " 1st derivative of function F!" end subroutine deriv1f subroutine deriv2f() write(*,*) " 2nd derivative of function F!" end subroutine deriv2f subroutine deriv1g() write(*,*) " 1st derivative of function G!" end subroutine deriv1g subroutine deriv2g() write(*,*) " 2nd derivative of function G!" end subroutine deriv2g end module derivs module series use interfaces, only: IDeriv, IHDeriv type :: Taylor class(IDeriv), allocatable :: calc contains procedure :: term1 procedure :: evaluate end type Taylor type :: HTaylor class(IHDeriv), allocatable :: calc contains procedure :: term1 => hterm1 procedure :: term2 => hterm2 procedure :: evaluate => hevaluate end type HTaylor contains subroutine term1(self) class(Taylor), intent(in) :: self call self%calc%deriv1() end subroutine term1 subroutine evaluate(self) class(Taylor), intent(in) :: self write(*,*) 'Evaluating Taylor series using' call self%term1() end subroutine evaluate subroutine hterm1(self) class(HTaylor), intent(in) :: self call self%calc%deriv1() end subroutine hterm1 subroutine hterm2(self) class(HTaylor), intent(in) :: self call self%calc%deriv2() end subroutine hterm2 subroutine hevaluate(self) class(HTaylor), intent(in) :: self write(*,*) 'Evaluating Taylor series using' call self%term1() call self%term2() end subroutine hevaluate end module series program client use derivs, only: DerivG, HDerivG use series, only: Taylor, HTaylor type(Taylor), allocatable :: teval type(HTaylor), allocatable :: hteval teval = Taylor( DerivG() ) call teval%evaluate() write(*,*) hteval = HTaylor( HDerivG() ) call hteval%evaluate() end program client ================================================ FILE: proposals/run-time_polymorphism/Examples/AppendixB/taylor.java ================================================ // Java implementation of the Taylor series example program based on // subtyping given in Appendix B of the proposal "Improved run-time // polymorphism for Fortran". // interface IDeriv { void deriv1(); } interface IHDeriv extends IDeriv { void deriv2(); } class DerivF implements IDeriv { public void deriv1() { System.out.println(" 1st derivative of function F!"); } } class HDerivF implements IHDeriv { public void deriv1() { System.out.println(" 1st derivative of function F!"); } public void deriv2() { System.out.println(" 2nd derivative of function F!"); } } class DerivG implements IDeriv { public void deriv1() { System.out.println(" 1st derivative of function G!"); } } class HDerivG implements IHDeriv { public void deriv1() { System.out.println(" 1st derivative of function G!"); } public void deriv2() { System.out.println(" 2nd derivative of function G!"); } } class Taylor { IDeriv calc; Taylor(IDeriv calculator) { calc = calculator; } public void term1() { calc.deriv1(); } public void evaluate() { System.out.println("Evaluating Taylor series using"); term1(); } } class HTaylor { IHDeriv calc; HTaylor(IHDeriv calculator) { calc = calculator; } public void term1() { calc.deriv1(); } public void term2() { calc.deriv2(); } public void evaluate() { System.out.println("Evaluating Taylor series using"); term1(); term2(); } } class ClientApp { public static void main(String[] args) { Taylor eval = new Taylor( new DerivG() ); eval.evaluate(); System.out.println(""); HTaylor heval = new HTaylor( new HDerivG() ); heval.evaluate(); } } ================================================ FILE: proposals/run-time_polymorphism/Examples/AppendixB/taylor.rs ================================================ // Rust implementation of the Taylor series example program based on // subtyping given in Appendix B of the proposal "Improved run-time // polymorphism for Fortran". // pub mod interfaces { pub trait IDeriv { fn deriv1(&self); } pub trait IHDeriv { fn deriv1(&self); fn deriv2(&self); } } pub mod derivs { use interfaces::IDeriv; use interfaces::IHDeriv; pub struct DerivF { } impl IDeriv for DerivF { fn deriv1(&self) { println!(" 1st derivative of function F!"); } } pub struct HDerivF { } impl IHDeriv for HDerivF { fn deriv1(&self) { println!(" 1st derivative of function F!"); } fn deriv2(&self) { println!(" 2nd derivative of function F!"); } } pub struct DerivG { } impl IDeriv for DerivG { fn deriv1(&self) { println!(" 1st derivative of function G!"); } } pub struct HDerivG { } impl IHDeriv for HDerivG { fn deriv1(&self) { println!(" 1st derivative of function G!"); } fn deriv2(&self) { println!(" 2nd derivative of function G!"); } } } pub mod series { use interfaces::IDeriv; use interfaces::IHDeriv; pub struct Taylor { pub calc: Box } impl Taylor { pub fn term1(&self) { self.calc.deriv1(); } pub fn evaluate(&self) { println!("Evaluating Taylor series using"); self.term1(); } } pub struct HTaylor { pub calc: Box } impl HTaylor { pub fn term1(&self) { self.calc.deriv1(); } pub fn term2(&self) { self.calc.deriv2(); } pub fn evaluate(&self) { println!("Evaluating Taylor series using"); self.term1(); self.term2(); } } } fn main() { use derivs::DerivG; use derivs::HDerivG; use series::Taylor; use series::HTaylor; let derivg = Box::new( DerivG{} ); let eval = Box::new( Taylor{calc: derivg} ); eval.evaluate(); println!(""); let hderivg = Box::new( HDerivG{} ); let heval = Box::new( HTaylor{calc: hderivg} ); heval.evaluate(); } ================================================ FILE: proposals/run-time_polymorphism/Examples/Text/taylor_basic.f90 ================================================ module derivs type :: DerivF contains procedure, nopass :: deriv1 => deriv1f end type DerivF contains subroutine deriv1f() write(*,*) ' 1st derivative of function F!' end subroutine deriv1f end module derivs module series use derivs, only: DerivF type :: Taylor type(DerivF), allocatable :: calc contains procedure :: term1 procedure :: evaluate end type Taylor contains subroutine term1(self) class(Taylor), intent(in) :: self call self%calc%deriv1() end subroutine term1 subroutine evaluate(self) class(Taylor), intent(in) :: self write(*,*) 'Evaluating Taylor series using' call self%term1() end subroutine evaluate end module series program client use derivs, only: DerivF use series, only: Taylor type(Taylor), allocatable :: teval teval = Taylor( DerivF() ) call teval%evaluate() end program client ================================================ FILE: proposals/run-time_polymorphism/Fortran_OOP.bib ================================================ @INPROCEEDINGS{Snyder86, author = {Alan Snyder}, title = {Encapsulation and Inheritance in Object-Oriented Programming Languages}, booktitle = {OOPSLA'86 Proceedings}, year = {1986}, month = "September", pages = {38--45}, publisher = "ACM" } @book{Bates_et_al_09, author = "Bates, Bert and Sierra, Kathy and Freeman, Eric and Robson, Elisabeth", title = "Head First Design Patterns", year = "2009", publisher = "O'Reilly Media", address = "" } @book{Gamma_et_al_94, author = "Gamma, Erich and Helm, Richard and Johnson, Ralph and Vlissides, John", title = "Design Patterns. Elements of Reusable Object-Oriented Software", year = "1994", publisher = "Prentice Hall", address = "" } @book{Martin_03, author = "Martin, Robert C.", title = "Agile Software Development: Principles, Patterns, and Practices", year = "2002", publisher = "Pearson", address = "" } @misc{Weck_Szyperski, author = "Weck, Wolfgang and Szyperski, Clemens", title = "{Do We Need Inheritance?}", howpublished = {\url{https://www.researchgate.net/publication/2297653_Do_We_Need_Inheritance}}, note = {Accessed February 4, 2020}, } ================================================ FILE: proposals/run-time_polymorphism/Fortran_OOP.tex ================================================ \documentclass[11pt,oneside]{article} \usepackage[a4paper, total={6.5in, 9in}]{geometry} \usepackage{natbib} \usepackage{url} \usepackage{listings} \usepackage{xcolor} \usepackage[title]{appendix} \frenchspacing \definecolor{codegreen}{rgb}{0,0.6,0} \definecolor{codegray}{rgb}{0.5,0.5,0.5} \definecolor{codepurple}{rgb}{0.58,0,0.82} \definecolor{backcolour}{rgb}{0.95,0.95,0.92} \lstdefinestyle{mystyle}{ % backgroundcolor=\color{backcolour}, commentstyle=\color{codegreen}, % keywordstyle=\color{magenta}, numberstyle=\tiny\color{codegray}, stringstyle=\color{codepurple}, basicstyle=\ttfamily\footnotesize, breakatwhitespace=false, breaklines=true, captionpos=b, keepspaces=true, % numbers=left, numbers=none, numbersep=5pt, showspaces=false, showstringspaces=false, showtabs=false, tabsize=2 } \lstset{style=mystyle} \begin{document} \title{Improved run-time polymorphism for Fortran \\ [1em] \large{Proposal to J3 for inclusion into Fortran 202y} } \author{K. Kifonidis} \maketitle \abstract{A case is made for extending Fortran's support for object-oriented (OO) programming (i.e. run-time polymorphism) in order to enable and encourage the use of best practices for the design and implementation of flexible OO software, and to improve the safety and efficiency of the language. It is proposed to introduce multiple inheritance of specification, in order to supplement the single inheritance of implementation that is already contained in the language. The new feature extends Fortran's abstract interfaces and derived types by functionality akin to what is present in Java, and some other, recent, languages. It is essential for writing code that conforms to the dependency inversion principle of OO programming. This will encourage the development of software that forgoes dependency upon concretions in favor of dependency on abstractions. The decoupling of classes that can be achieved in this way will lead to significantly more flexible and extensible numerical codes, that are also more efficient. The new feature enables a programming style that dispenses with the use of implementation inheritance, and therefore helps to eliminate type conflicts whose resolution requires ``select type'' (down-casting) statements. These, and their associated run-time overhead, have been a frequent gripe of Fortran OO programmers since the introduction of Fortran 2003. As a bonus, the feature should be relatively easy to implement in Fortran compilers that adhere to at least the Fortran 2003 standard.} \section{Introduction} Over the last decades, a significant body of experience has accumulated concerning the practical use of object-oriented programming (OOP). This experience has markedly shaped the design of some very recent programming languages, like Rust and Go, and it has also led to some notable revision of our notions of what actually constitutes OOP. In this modern view, the very essence of OOP, and in fact its ultimate upshot, is the use of (run-time) polymorphism to manage (i.e. minimize) code dependencies \cite{Martin_03,Bates_et_al_09}. Viewed in this way, OOP is simply a means to organize code so that it achieves a maximum amount of \emph{decoupling}. The crucial idea in this context is that polymorphism has the unique ability to allow one to invert the flow of dependency in a computer code, which normally points from higher level modules to lower level modules (read ``classes'' in the OOP context). Polymorphism makes it possible to replace (``invert'') the dependency of a higher level class upon a lower level class by a dependency of both these classes upon a pure abstraction, which is called an interface (or a contract, protocol, or trait). Thereby, high-level policy gets decoupled from low-level implementation detail. This is known as the dependency inversion principle \cite{Martin_03}. If strictly followed, this principle leads to the development of loosely coupled, easily extensible, easily testable, modular, maintainable, and thus future-proof software. It is this decoupling which allows for the re-usability and flexibility of well-written OO code. The situation encountered in practice is usually a different one, though. Experience has shown that OO software which is written in Fortran 2003 (and its successors) seldom displays these desirable characteristics. It will be argued in the following that it is the combination of a lack of appropriate features in the language, for encouraging (or even enforcing) a programming style that upholds the dependency inversion principle, and the consequent indiscriminate use of features that the language \emph{does} provide (in particular inheritance of implementation), that has to be blamed for this situation. \section{Deficiencies in Fortran 2018 addressed by this proposal} \label{sect:F08_deficits} \subsection{No explicit distinction between different inheritance types} In statically typed languages, like Fortran, run-time polymorphism is tightly bound to inheritance. In order to manage dependencies in the way it was described above, the users of these languages must employ some form of inheritance mechanism. It is important to realize that it is \emph{inheritance of (object) specification}, i.e. inheritance of interfaces, that is required for this purpose. It is this (restricted) form of inheritance that is today viewed as being indispensable for OOP. Interface inheritance allows for the aforementioned interface-based programming style, that embodies the dependency inversion principle. Fortran, on the other hand, makes exclusive use of \emph{inheritance of implementation}, i.e. inheritance of classes. A class encompasses both an interface (given by the union of its methods' signatures, through which it communicates with the external world), \emph{and} the implementation details of an OO algorithm (in the form of data variables, and the implementation bodies of its methods). A class is therefore not a proper abstraction (for other classes or procedures) to depend upon. It is not sufficiently generic. It contains implementation-dependent detail, which other classes end up being needlessly coupled to, if they make use of it via either inheritance or object composition. Only the class's interface is free from such detail and should be depended upon for these purposes, to achieve a maximum amount of decoupling in an application. Fortran presently makes no explicit distinction between implementation inheritance and specification inheritance. The latter can only be \emph{emulated} in Fortran by \emph{re-purposing} class inheritance, and exploiting (or rather abusing) the fact that classes intermingle implementation with specification. Fortran offers abstract types (i.e. abstract classes) for related use cases, which can contain deferred (i.e. abstract) procedures, for which ``abstract interfaces'' have to be specified by the user. However, abstract classes (as all other classes) are explicitly allowed to \emph{also} contain variables and concrete methods, i.e. state, as well as implementation code, which makes abstract classes dangerous to depend upon for inheriting specification from. In contrast to Java, C\#, D, Swift, Rust, Go and some other modern languages, Fortran does not offer a means to inherit specification directly from its ``abstract interfaces'', which --- though available, and guaranteed to be completely free of implementation detail --- play only a subordinate, instead of a cardinal role in the language. Hence it is not possible to enforce, at the language level, the strict separation of the implementation of an OO algorithm from its interface, that is required for adherence to the dependency inversion principle. \subsection{Problems due to the lack of distinction between inheritance types} \label{sect:problems} Interface inheritance has the sole purpose of enabling polymorphism, in order to invert dependencies and thereby make parts of an algorithm interchangeable (i.e. to achieve high-level code reuse \cite{Gamma_et_al_94}). This is also called \emph{sub-typing}. In contrast, class inheritance is meant to be used primarily for sharing common implementation code between parent and child classes. This is also called \emph{sub-classing}, and can be viewed as a form of low-level code reuse. There is a significant body of evidence that demonstrates that sub-classing is a dangerous practice, which, moreover, is not essential to OOP. Snyder \cite{Snyder86} showed already in the eighties that sub-classing breaks encapsulation. Weck and Szyperski re-emphasized this \cite{Weck_Szyperski}. They proposed to abolish sub-classing, since they regard it as being prone to abuse. Even moderately incautious use of sub-classing typically leads to rigid, brittle code, that is tightly coupled to the details of concrete (implementation) classes, and is therefore unmaintainable and in-extensible. This is especially the case when sub-classing is used in combination with method overriding, or changes to a base object's state \cite{Weck_Szyperski}. Moreover, when sub-classing is employed along with object composition in an application (which is inevitable in practice), type conflicts can often result. These can then only be resolved by down-casting, i.e. by the use of \texttt{select type} statements in Fortran, which lead to unnecessary run-time overhead. This can be viewed as resulting from a violation of the dependency inversion principle, which states that inheritance should be used \emph{exclusively} for sub-typing. All these issues could be avoided, if Fortran would be extended to allow for (multiple) inheritance of interfaces, in order to provide its programmers with easy and safe access to sub-typing. Sub-classing (i.e. type extension) could then be mostly shunned in the development of new codes. Clear guidelines could then be given to Fortran programmers to rely instead on object composition for low-level, and on the features proposed in this proposal for high-level code reuse, in the vast majority of their code. A coding style that is in accordance with the dependency inversion principle would thus be encouraged that would eliminate type conflicts, the need for \texttt{select type} statements, and their run-time overhead. A potential drawback of object composition is the need for some boilerplate code, i.e. for methods that delegate functionality to composed classes. However, efficiency can still be expected to improve over present Fortran OOP codes, especially in cases where \texttt{select type} statements appear in low-level code, e.g. in computational kernels. The importance of such a coding style is actually not some entirely new insight. Among the very first recommendations that one finds in the pioneering work of Gamma et al. \cite{Gamma_et_al_94} on OO design patterns are the statements ``program to an interface not an implementation'' and ``favor object composition over class inheritance''. What had apparently not been recognized widely is that these are also recommendations for language design. This has changed recently, with the availability of the Rust and Go languages. In these languages, interface inheritance is the \emph{only} type of inheritance that is supported. These languages therefore \emph{enforce} an OOP style that is in accordance with the dependency inversion principle. \section{Proposed additions to Fortran 202y} \label{sect:propositions} The new features proposed to solve the problems discussed in the last section are \begin{itemize} \item A possibility to declare \emph{named} versions of Fortran's abstract interfaces, from which derived types would then be able to inherit specification. \item An \texttt{extends} attribute-specifier for the declaration header of these named abstract interfaces, to allow hierarchies of such (named) abstract interfaces to be built\footnote{Hierarchies of interfaces do not pose the dangers that sub-classing poses, as only \emph{specification} is shared, see \cite{Weck_Szyperski}.}. \item A new \texttt{implements} type-attribute-specifier for derived types, to enable these types to inherit specification from one or more named abstract interfaces. \item An extension of Fortran's \texttt{class} declaration-type-specifier for polymorphic variables, to accept named abstract interfaces. This would enable one to declare polymorphic variables of named abstract interfaces, in order to make use of sub-typing polymorphism. \end{itemize} \subsection{Extended declaration syntax for abstract interfaces} \label{sect:declare} The following listing shows an example of an extended syntax for interface declarations. In a module named \texttt{interfaces}, four named abstract interfaces are declared: \texttt{ISolver}, \texttt{IPrinter}, \texttt{IClient}, and \texttt{IRelaxationSolver}. \texttt{ISolver} and \texttt{IPrinter} contain the declarations of subroutines \texttt{solve\_system}, and \texttt{print\_result}, respectively. Notice that \texttt{IClient}, on the other, hand contains declarations for \emph{two} procedures: subroutines \texttt{solve\_system}, \emph{and} \texttt{print\_result}, which it inherits from \texttt{ISolver} and \texttt{IPrinter}, respectively, by means of the \texttt{extends} attribute-specifier. \begin{lstlisting}[language=Fortran] module interfaces abstract interface :: ISolver subroutine solve_system(a,b,x) real, dimension(:,:), intent(in) :: a real, dimension(:), intent(in) :: b real, dimension(:), intent(out) :: x end subroutine solve_system end interface ISolver abstract interface :: IPrinter subroutine print_result(x) real, dimension(:), intent(in) :: x end subroutine print_result end interface IPrinter abstract interface, extends(ISolver,IPrinter) :: IClient end interface IClient abstract interface :: IRelaxationSolver subroutine driver(self,d,rhs,x) import :: IRelaxationSolver class(IRelaxationSolver), intent(in) :: self real, dimension(:,:,:,:), intent(in) :: d real, dimension(:,:,:), intent(in) :: rhs real, dimension(:,:,:), intent(out) :: x end subroutine driver subroutine relax(blksolver,d,rhs,x) import :: ISolver class(ISolver), intent(in) :: blksolver real, dimension(:,:,:,:), intent(in) :: d real, dimension(:,:,:), intent(in) :: rhs real, dimension(:,:,:), intent(out) :: x end subroutine relax end interface IRelaxationSolver end module interfaces \end{lstlisting} \label{list:interface} Finally, the declaration of the \texttt{IRelaxationSolver} interface illustrates that a named abstract interface may depend on other named abstract interfaces, not only via an \texttt{extends} attribute-specifier (as shown above), but also through the procedure declarations that it contains. Namely, the latter may contain polymorphic arguments that are declared with the help of the \texttt{class} declaration-type-specifier (see Sect.~\ref{sect:declarations}), to give these procedures access to either the data fields (variables) of the type that \emph{implements} the abstract interface (i.e. to the descendant of that interface) or to the descendants of \emph{other} abstract interfaces. In these cases, Fortran's \texttt{import} statement would be required in order to bring such external names into the procedure declarations' own scope. Other ways to express and implement the proposed functionality are possible, and need to be considered. The one discussed here has the advantages that abstract interfaces are already a part of the language and are merely extended here by functionality that does not affect their other uses. It does, however, elevate their status in the language as it makes them central to expressing interface inheritance. This syntax also makes the distinction between sub-typing and sub-classing apparent, which is a major plus. It has the further advantage that named abstract interfaces could also be used in future extensions of the language, e.g. in connection with \emph{intrinsic} types, and with generics. This is the approach taken in the Swift programming language where (named) abstract interfaces (called ``protocols'' there) are so central to its functionality, that Swift has been called a ``protocol-based'' language. It is not our purpose here to delve deeper into syntactic or extensibility issues. We rather need some syntax that is able to express the following code examples, and the present one does so satisfactorily. \subsection{Facility to implement interfaces} Once a named abstract interface has been declared, it can be implemented by a derived type that needs to conform to that interface, by using the new \texttt{implements} type-attribute-specifier as follows \begin{lstlisting}[language=Fortran] module lu_decomp use interfaces, only: ISolver type, implements(ISolver) :: LUDecomposition contains procedure, nopass :: solve_system end type LUDecomposition contains subroutine solve_system(a,b,x) real, dimension(:,:), intent(in) :: a real, dimension(:), intent(in) :: b real, dimension(:), intent(out) :: x ! Implementation of LU decomposition goes here end subroutine solve_system end module lu_decomp \end{lstlisting} In case the derived type is abstract, it is allowed to provide an only partial implementation of the interface(s) that it implements. Any non-abstract derived type that extends the abstract type must, however, provide a full implementation. \subsection{Polymorphism via sub-typing} \label{sect:declarations} To make actual use of the sub-typing polymorphism that we have just set up, polymorphic variables of named abstract interfaces would then be declared with the help of the (extended) \texttt{class} declaration-type-specifier; for instance, as arguments of a procedure, or within another derived type. They could then be used in the actual implementation of this derived type's bound procedures, e.g. as follows: \begin{lstlisting}[language=Fortran] type, implements(IClient) :: Client class(ISolver), allocatable :: solver class(IPrinter), pointer :: printer => null() contains procedure, nopass :: solve_system procedure, nopass :: print_result end type Client \end{lstlisting} Here, two polymorphic variables, \texttt{solver} and \texttt{printer}, that conform to the \texttt{ISolver} and \texttt{IPrinter} interfaces, respectively, are declared for the purpose of object composition within type \texttt{Client}, which itself implements the \texttt{IClient} interface, and thus has to provide implementations for the procedures \texttt{solve\_system} and \texttt{print\_result} (whose actual code is omitted here, and would simply employ delegation to \texttt{solver} and \texttt{printer} to provide the required functionality). Notice also, that the proposed features allow multiple interface inheritance. In case \texttt{Client} has to conform to either \texttt{ISolver} or \texttt{IPrinter}, one could, for instance, provide an implementation much as in the last example, but would use the \texttt{implements} specifier as follows \begin{lstlisting}[language=Fortran] type, implements(ISolver,IPrinter) :: Client class(ISolver), allocatable :: solver class(IPrinter), pointer :: printer => null() contains procedure, nopass :: solve_system procedure, nopass :: print_result end type Client \end{lstlisting} The next example shows, in detail, how sub-typing polymorphism would be used together with delegation, and also how type-bound procedures with the \texttt{pass} attribute would be handled \begin{lstlisting}[language=Fortran] module jacobi use interfaces, only: IRelaxationSolver, ISolver type, implements(IRelaxationSolver) :: BlockJacobi class(ISolver), allocatable :: blksolver contains procedure, pass :: driver procedure, nopass :: relax end type BlockJacobi contains subroutine driver(self,d,rhs,x) class(BlockJacobi), intent(in) :: self real, dimension(:,:,:,:), intent(in) :: d real, dimension(:,:,:), intent(in) :: rhs real, dimension(:,:,:), intent(out) :: x call self%relax(self%blksolver,d,rhs,x) end subroutine driver subroutine relax(blksolver,d,rhs,x) class(ISolver), intent(in) :: blksolver real, dimension(:,:,:,:), intent(in) :: d real, dimension(:,:,:), intent(in) :: rhs real, dimension(:,:,:), intent(out) :: x integer :: i, j do j = 1, size(x,3) do i = 1, size(x,2) call blksolver%solve_system(d(:,:,i,j),rhs(:,i,j),x(:,i,j)) end do end do end subroutine relax end module jacobi \end{lstlisting} Notice that \texttt{BlockJacobi} is an implementor (i.e. descendant) of \texttt{IRelaxationSolver} and that therefore the above signature of method \texttt{driver} is in accordance with its declaration in module \texttt{interfaces}. To use \texttt{BlockJacobi}, one would have to simply inject into it, via a constructor, an instance of \texttt{LUDecomposition} (or any other object that implements the \texttt{ISolver} interface) in order to intialize the allocatable, polymorphic variable \texttt{blksolver}. \subsection{Combination of sub-classing with sub-typing} The new features need to be inter-operable also with type extension (i.e. sub-classing). They need to enable one to declare derived types that conform to some interfaces, and inherit at the same time implementation from some other derived type, e.g. the procedure \texttt{solve\_system} implemented by type \texttt{LUDecomposition} in the following example: \begin{lstlisting}[language=Fortran] type, extends(LUDecomposition), implements(ISolver,IPrinter) :: Client class(IPrinter), pointer :: printer => null() contains procedure, nopass :: print_result end type Client \end{lstlisting} In the Java language, from which this idea is borrowed, the convention is that \texttt{extends} shall precede \texttt{implements}. The combination of sub-typing and sub-classing will allow advanced users of the language, who have a good understanding of OO design, to employ some advanced techniques that make use of both the flexibility that sub-typing affords one, with the raw reduction of code lines that sub-classing permits. \section{Example of a use case: a Taylor series application} To illustrate the difficulties that were discussed in Sect.~\ref{sect:problems}, and to demonstrate how the use of the new features will contribute to the solution of these problems, we will present here a small case study that exhibits a pattern that is encountered often in practical applications, namely the coupling of two separate inheritance hierarchies through object composition. It is truly only the pattern that is of importance here, but in order to provide a concrete example, we will present it in the framework of a somewhat fictional application that calculates the Taylor series expansion of some function up to certain orders of accuracy. \subsection{The basic design} \label{sect:basic_design} Suppose that we have an application that needs to calculate the Taylor series of a function $F(x)$ for some concrete $x$ --- and it needs to do so only up to the linear term, i.e. it only needs access to the first derive, $F'(x)$, of $F$ with respect to $x$. Ignoring all the details of how this first derivative might be calculated, a code skeleton for using it within our application might look as follows. Declare a derived type \texttt{DerivF} that contains a procedure \texttt{deriv1} that calculates $F'(x)$, and a separate type, named \texttt{Taylor}, that makes use of type \texttt{DerivF} via object composition in order to \texttt{evaluate} the actual Taylor series approximation itself (whose details, like calculation of the zeroth order term, etc., are immaterial here): \begin{lstlisting}[language=Fortran] module derivs type :: DerivF contains procedure, nopass :: deriv1 => deriv1f end type DerivF contains subroutine deriv1f() write(*,*) ' 1st derivative of function F!' end subroutine deriv1f end module derivs module series use derivs, only: DerivF type :: Taylor type(DerivF), allocatable :: calc contains procedure :: term1 procedure :: evaluate end type Taylor contains subroutine term1(self) class(Taylor), intent(in) :: self call self%calc%deriv1() end subroutine term1 subroutine evaluate(self) class(Taylor), intent(in) :: self write(*,*) 'Evaluating Taylor series using' call self%term1() end subroutine evaluate end module series \end{lstlisting} Our client application would then set up an object \texttt{teval} of type \texttt{Taylor} in order to evaluate the Taylor series approximation it needs, e.g. as follows \newpage \begin{lstlisting}[language=Fortran] program client use derivs, only: DerivF use series, only: Taylor type(Taylor), allocatable :: teval teval = Taylor( DerivF() ) call teval%evaluate() end program client \end{lstlisting} Suppose now, that our requirements on the application have changed. We need some additional functionality. We want the application to be able to also calculate, if required, a higher-order accurate approximation to the Taylor series of $F$, say up to the quadratic term. In addition, we also want it to be able to use some other function, say $G$, instead of $F$. Moreover, we do not want to change any of the present code structure, if possible. We only wish to extend it by the new functionality. \subsection{Extension by sub-classing} \label{sect:sub-classing-example} We will concentrate on implementing the functionality related to the higher-order-accuracy capability first. To accomplish this, we will use Fortran's \texttt{extends} feature for derived types (i.e. sub-classing). We extend type \texttt{DerivF} by a child type, \texttt{HDerivF}, that contains a procedure \texttt{deriv2} to calculate also the higher (i.e. second) order derivative of $F$. \begin{lstlisting}[language=Fortran] type :: DerivF contains procedure, nopass :: deriv1 => deriv1f end type DerivF type, extends(DerivF) :: HDerivF contains procedure, nopass :: deriv2 => deriv2f end type HDerivF \end{lstlisting} We also extend the type \texttt{Taylor} by a child type, \texttt{HTaylor}, to deal with the evaluation of the higher order Taylor series: \begin{lstlisting}[language=Fortran] type :: Taylor type(DerivF), allocatable :: calc contains procedure :: term1 procedure :: evaluate end type Taylor type, extends(Taylor) :: HTaylor contains procedure :: term2 => hterm2 procedure :: evaluate => hevaluate end type HTaylor \end{lstlisting} Here, we inherited method \texttt{term1} from the parent type, and added method \texttt{term2} to enable us to calculate also the second-order Taylor term. We also had to override the \texttt{evaluate} method of the parent type, since the higher-order evaluation of the complete series needs to account for this additional, i.e. second-order, term. We have thus implemented the skeleton of the high-order functionality, using types that make up two separate inheritance hierarchies. However, we are still bound to exclusive use of the function $F$. To implement the capability to use different functions, we are going to employ the strategy pattern \cite{Gamma_et_al_94}. We introduce a further inheritance hierarchy made up of two new types, \texttt{DerivG}, and \texttt{HDerivG}, to provide the same functionality for function $G$, as it was done above for function $F$. We also need to make these hierarchies, connected to $F$ and $G$, interchangeable, so that we can use either of them within the code that evaluates the Taylor series. For this purpose, we introduce the following abstract type \begin{lstlisting}[language=Fortran] type, abstract :: Deriv contains procedure(pderiv), deferred, nopass :: deriv1 end type Deriv abstract interface subroutine pderiv() end subroutine pderiv end interface \end{lstlisting} Now we can make both the inheritance hierarchies connected to $F$ and $G$ derive from this abstract type, and hence merge them into a single hierarchy. Its $G$-related branch, for instance, looks as follows \begin{lstlisting}[language=Fortran] type, extends(Deriv) :: DerivG contains procedure, nopass :: deriv1 => deriv1g end type DerivG type, extends(DerivG) :: HDerivG contains procedure, nopass :: deriv2 => deriv2g end type HDerivG \end{lstlisting} whereas the $F$-related branch looks completely analogous. The last step, in implementing the strategy pattern, is to transform the variable \texttt{calc} in type \texttt{Taylor} into a polymorphic variable of \texttt{class(Deriv)} \begin{lstlisting}[language=Fortran] type :: Taylor class(Deriv), allocatable :: calc contains \end{lstlisting} in order to accept \texttt{Deriv} types from either branch of our merged hierarchy. The complete code, that we end up with, is given in Appendix~\ref{sect:example1}. This code\footnote{compiled with gfortran Version 9} produces the following output \begin{lstlisting}[language=Fortran] Evaluating Taylor series using 1st derivative of function G! Evaluating Taylor series using 1st derivative of function G! 2nd derivative of function G! \end{lstlisting} That is, the code calculates first the low-order approximation of $G$, and subsequently its high-order approximation, as intended. It works correctly. Yet, it is extremely bad, rigid code! The most conspicuous symptom of its rigidity is the appearance of the \texttt{select type} statement in the subroutine \texttt{hterm2}, that we reproduce here for illustration: \begin{lstlisting}[language=Fortran] subroutine hterm2(self) class(HTaylor), intent(in) :: self select type ( calc => self%calc ) class is ( HDerivF ) call calc%deriv2() class is ( HDerivG ) call calc%deriv2() class default write(*,*) 'Unknown type!' end select end subroutine hterm2 \end{lstlisting} Obviously, this routine expects the \texttt{calc} instance to be of either type \texttt{HDerivF} or \texttt{HDerivG}, whereas this has been inherited from the parent type \texttt{Taylor} being of type \texttt{class(Deriv)}, \begin{lstlisting}[language=Fortran] type :: Taylor class(Deriv), allocatable :: calc contains \end{lstlisting} Our abstract type \texttt{Deriv} does not contain a declaration for the method \texttt{deriv2} that the subroutine needs. Since the compiler can't know at compile time whether at run-time \texttt{calc} will be of the right type-extension to contain the \texttt{deriv2} method, it will refuse to compile subroutine \texttt{hterm2} if the \texttt{select type} statement is omitted. By coupling two inheritance hierarchies (based on \texttt{Deriv} and \texttt{Taylor}) together, via object composition, we have created a (potential) type conflict that now requires a type check every time we run through the subroutine. If we had written a real production application along these lines, that would evaluate the Taylor series for a single scalar $x$ per call, the type-checking would have completely killed performance! Type extension (i.e. sub-classing) has locked us into a straight-jacket of rigid inheritance hierarchies that denies us the flexibility to provide our procedures with the right data types, and has thereby forced us to circumvent the static type system of the language, in order to get the code to work at all. But this is not the only problem. The entire code relies on concrete derived types, i.e. on concrete implementations. Except for type \texttt{Deriv}, it does not use any abstractions. Should the implementation of some concrete type need to be changed, all types that depend on it would need to be checked and possibly changed, too, in order not to break the program. \subsection{Extension by sub-typing and object composition} Now, let us go back to the point we were at at the end of Sect.~\ref{sect:basic_design}. Let us, furthermore, suppose that Fortran doesn't support sub-classing, and instead supports only object composition and sub-typing (via the features proposed in Sect.~\ref{sect:propositions}), akin to Rust or Go. We will again focus first on the extension of our application to higher order of accuracy. We still contemplate to introduce this functionality through four derived types \texttt{DerivF}, \texttt{HDerivF}, \texttt{Taylor}, and \texttt{HTaylor}. But since we no longer have sub-classing at our disposal, the only way to make these types cooperate, on solving the task, is the following \begin{lstlisting}[language=Fortran] type :: Taylor type(DerivF), allocatable :: calc contains procedure :: term1 procedure :: evaluate end type Taylor type :: HTaylor type(HDerivF), allocatable :: calc contains procedure :: term1 => hterm1 procedure :: term2 => hterm2 procedure :: evaluate => hevaluate end type HTaylor \end{lstlisting} that is, we need to employ object composition \emph{twice}, once in \texttt{Taylor} and once in \texttt{HTaylor}. This is already sufficient to avoid the appearance of a \texttt{select type} statement in subroutine \texttt{hterm2}, because now our instance of \texttt{calc} will be of type \texttt{HDerivF}, i.e. of the proper type for doing high-order calculations. We will, of course, see to it that it contains the procedure \texttt{deriv2} which calculates the second order derivative required by the high-order Taylor series term. The above code fragment still has a problem, though. It relies on the concrete types \texttt{DerivF} and \texttt{HDerivF}. Our copy of ``Fortran 202y Explained'' tells us that we should rely on abstractions instead of concretions, and depend on polymorphic variables of abstract interfaces within our derived types, instead. We therefore think about the functionality that these two concrete types need to provide to their clients. We already mentioned that \texttt{HDerivF} will have to provide an implementation of subroutine \texttt{deriv2}. However, and as it was the case in Sect.~\ref{sect:sub-classing-example}, \texttt{HDerivF} also needs to implement procedure \texttt{deriv1}, while \texttt{DerivF} needs to implement \emph{only} the latter procedure. Hence, we distill the signatures of these methods into two abstract interfaces \begin{lstlisting}[language=Fortran] abstract interface :: IDeriv subroutine deriv1() end subroutine deriv1 end interface IDeriv abstract interface, extends(IDeriv) :: IHDeriv subroutine deriv2() end subroutine deriv2 end interface IHDeriv \end{lstlisting} and then make use of these interfaces to modify our code for the Taylor types as follows \begin{lstlisting}[language=Fortran] type :: Taylor class(IDeriv), allocatable :: calc contains procedure :: term1 procedure :: evaluate end type Taylor type :: HTaylor class(IHDeriv), allocatable :: calc contains procedure :: term1 => hterm1 procedure :: term2 => hterm2 procedure :: evaluate => hevaluate end type HTaylor \end{lstlisting} With this single stroke, we have just solved our entire problem, including the requirement of being able to calculate the Taylor series of different functions! Moreover, we have accomplished this goal with ease. The only things that remain to be done are to make \texttt{DerivF} and \texttt{HDerivF} simply implement the two abstract interfaces, e.g. as follows \begin{lstlisting}[language=Fortran, caption={Implementation of interfaces for derivatives of function $F$.}, label={listing:implderivf}] type, implements(IDeriv) :: DerivF contains procedure, nopass :: deriv1 => deriv1f end type DerivF type, implements(IHDeriv) :: HDerivF contains procedure, nopass :: deriv1 => deriv1f procedure, nopass :: deriv2 => deriv2f end type HDerivF \end{lstlisting} and to add two more types, \texttt{DerivG}, and \texttt{HDerivG}, in exactly the same fashion, in order to provide implementations of the same functionality for function $G$. We are finished! Since both \texttt{DerivF} and \texttt{DerivG} now conform to the abstract \texttt{IDeriv} interface, we can easily swap instances of either of them into type \texttt{Taylor} at will, in order to calculate the Taylor series of either $F$ or $G$. Similar functionality is exhibited by the types that implement the high-order series. We didn't even have to spend a single thought on using the strategy pattern for these purposes. We used it naturally without thinking about it because it was encouraged by the features contained in the language. The complete code, that uses this sub-typing approach, is given in Appendix~\ref{sect:example2}. It is an almost literal translation of a Java as well as a Rust code, that were used to test and verify the presented ideas. The code in Appendix~\ref{sect:example2} is vastly superior compared to the one that uses sub-classing and is given in Appendix~\ref{sect:example1}. Both codes have roughly the same number of lines, but in the one relying on sub-typing there are \emph{no} \texttt{select type} statements, \emph{no} sub-classing hierarchies, and \emph{no} dependencies on concrete types. We have inverted all the dependencies in the algorithm, i.e. the only dependencies are on abstract interfaces. We have thus attained the highest possible degree of decoupling. The \emph{only} part of the code that depends on concrete types is the main client program, whose sole purpose is to organize the entire process of injecting instances of the correct concrete types via (Fortran default) constructors into the lower level code. This decoupling of types leads to multiple benefits. For instance (and provided we have compiled the \texttt{interfaces} module), we gain the freedom to compile the \texttt{derivs} and \texttt{series} modules in any order we like, even in parallel if we wish to. We thereby neutralized the biggest drawback of a module system (like Fortran's) for separate compilation, namely serialization of the compilation process. We also avoid re-compilation cascades in case we need to change the implementation of some concrete type, as long as we leave its (abstract) interface intact. We are, moreover, free to take any of the aforementioned modules and reuse it in another application, without having to drag along numerous dependencies. In summary, using the new features we have not only accomplished to make the code more efficient, but also much more flexible, much more maintainable, and much more reusable. \subsection{Combination of the two inheritance types} The code given in Appendix~\ref{sect:example2} strictly conforms to the dependency inversion principle. By relaxing this requirement, and allowing for the use of both sub-typing and sub-classing, we could have written the code fragment in Listing~\ref{listing:implderivf} above somewhat more concise as \begin{lstlisting}[language=Fortran] type, implements(IDeriv) :: DerivF contains procedure, nopass :: deriv1 => deriv1f end type DerivF type, extends(DerivF), implements(IHDeriv) :: HDerivF contains procedure, nopass :: deriv2 => deriv2f end type HDerivF \end{lstlisting} This saves us the labor of providing an implementation for procedure \texttt{deriv1} within type \texttt{HDerivF}, at the significant cost of \texttt{HDerivF} depending now on a concretion, i.e. on type \texttt{DerivF}. In this particular, trivial, example, we have only saved a single line of code in this way. But had \texttt{DerivF} contained half a dozen or so methods, which we would have been forced to implement also in \texttt{HDerivF}, the convenience that the combination of \texttt{extends} and \texttt{implements} would have afforded us for reducing the number of code-lines could hardly have been passed up. \subsection{Summary} The features proposed in this document would significantly enhance Fortran's OOP capabilities without affecting backwards compatibility. Moreover, Fortran compilers that adhere to at least the Fortran 2003 standard could implement them relatively easily, as most of the functionality required to support them is already present in some form in such compilers for the support of sub-classing, i.e. the extension of derived types. \bibliographystyle{plain} \bibliography{Fortran_OOP} \lstdefinestyle{mystyle}{ % backgroundcolor=\color{backcolour}, commentstyle=\color{codegreen}, % keywordstyle=\color{magenta}, numberstyle=\tiny\color{codegray}, stringstyle=\color{codepurple}, basicstyle=\ttfamily\footnotesize, breakatwhitespace=false, breaklines=true, captionpos=b, keepspaces=true, numbers=left, numbersep=5pt, showspaces=false, showstringspaces=false, showtabs=false, tabsize=2 } \lstset{style=mystyle} \begin{appendices} \section{Taylor series example based on sub-classing} \label{sect:example1} \begin{lstlisting}[language=Fortran] module derivs type, abstract :: Deriv contains procedure(pderiv), deferred, nopass :: deriv1 end type Deriv abstract interface subroutine pderiv() end subroutine pderiv end interface type, extends(Deriv) :: DerivF contains procedure, nopass :: deriv1 => deriv1f end type DerivF type, extends(DerivF) :: HDerivF contains procedure, nopass :: deriv2 => deriv2f end type HDerivF type, extends(Deriv) :: DerivG contains procedure, nopass :: deriv1 => deriv1g end type DerivG type, extends(DerivG) :: HDerivG contains procedure, nopass :: deriv2 => deriv2g end type HDerivG contains subroutine deriv1f() write(*,*) ' 1st derivative of function F!' end subroutine deriv1f subroutine deriv2f() write(*,*) ' 2nd derivative of function F!' end subroutine deriv2f subroutine deriv1g() write(*,*) ' 1st derivative of function G!' end subroutine deriv1g subroutine deriv2g() write(*,*) ' 2nd derivative of function G!' end subroutine deriv2g end module derivs module series use derivs, only: Deriv, HDerivF, HDerivG type :: Taylor class(Deriv), allocatable :: calc contains procedure :: term1 procedure :: evaluate end type Taylor type, extends(Taylor) :: HTaylor contains procedure :: term2 => hterm2 procedure :: evaluate => hevaluate end type HTaylor contains subroutine term1(self) class(Taylor), intent(in) :: self call self%calc%deriv1() end subroutine term1 subroutine evaluate(self) class(Taylor), intent(in) :: self write(*,*) 'Evaluating Taylor series using' call self%term1() end subroutine evaluate subroutine hterm2(self) class(HTaylor), intent(in) :: self select type ( calc => self%calc ) class is ( HDerivF ) call calc%deriv2() class is ( HDerivG ) call calc%deriv2() class default write(*,*) 'Unknown type!' end select end subroutine hterm2 subroutine hevaluate(self) class(HTaylor), intent(in) :: self write(*,*) 'Evaluating Taylor series using' call self%term1() call self%term2() end subroutine hevaluate end module series program client use derivs, only: DerivG, HDerivG, Deriv use series, only: Taylor, HTaylor class(Deriv), allocatable :: derv class(Taylor), allocatable :: teval derv = DerivG() teval = Taylor(derv) call teval%evaluate() write(*,*) derv = HDerivG() teval = HTaylor(derv) call teval%evaluate() end program client \end{lstlisting} \section{Taylor series example based on sub-typing} \label{sect:example2} \begin{lstlisting}[language=Fortran] module interfaces abstract interface :: IDeriv subroutine deriv1() end subroutine deriv1 end interface IDeriv abstract interface, extends(IDeriv) :: IHDeriv subroutine deriv2() end subroutine deriv2 end interface IHDeriv end module interfaces module derivs use interfaces, only: IDeriv, IHDeriv type, implements(IDeriv) :: DerivF contains procedure, nopass :: deriv1 => deriv1f end type DerivF type, implements(IHDeriv) :: HDerivF contains procedure, nopass :: deriv1 => deriv1f procedure, nopass :: deriv2 => deriv2f end type HDerivF type, implements(IDeriv) :: DerivG contains procedure, nopass :: deriv1 => deriv1g end type DerivG type, implements(IHDeriv) :: HDerivG contains procedure, nopass :: deriv1 => deriv1g procedure, nopass :: deriv2 => deriv2g end type HDerivG contains subroutine deriv1f() write(*,*) " 1st derivative of function F!" end subroutine deriv1f subroutine deriv2f() write(*,*) " 2nd derivative of function F!" end subroutine deriv2f subroutine deriv1g() write(*,*) " 1st derivative of function G!" end subroutine deriv1g subroutine deriv2g() write(*,*) " 2nd derivative of function G!" end subroutine deriv2g end module derivs module series use interfaces, only: IDeriv, IHDeriv type :: Taylor class(IDeriv), allocatable :: calc contains procedure :: term1 procedure :: evaluate end type Taylor type :: HTaylor class(IHDeriv), allocatable :: calc contains procedure :: term1 => hterm1 procedure :: term2 => hterm2 procedure :: evaluate => hevaluate end type HTaylor contains subroutine term1(self) class(Taylor), intent(in) :: self call self%calc%deriv1() end subroutine term1 subroutine evaluate(self) class(Taylor), intent(in) :: self write(*,*) 'Evaluating Taylor series using' call self%term1() end subroutine evaluate subroutine hterm1(self) class(HTaylor), intent(in) :: self call self%calc%deriv1() end subroutine hterm1 subroutine hterm2(self) class(HTaylor), intent(in) :: self call self%calc%deriv2() end subroutine hterm2 subroutine hevaluate(self) class(HTaylor), intent(in) :: self write(*,*) 'Evaluating Taylor series using' call self%term1() call self%term2() end subroutine hevaluate end module series program client use derivs, only: DerivG, HDerivG use series, only: Taylor, HTaylor type(Taylor), allocatable :: teval type(HTaylor), allocatable :: hteval teval = Taylor( DerivG() ) call teval%evaluate() write(*,*) hteval = HTaylor( HDerivG() ) call hteval%evaluate() end program client \end{lstlisting} \end{appendices} \end{document} ================================================ FILE: proposals/traits/20-109.txt ================================================ To: J3 J3/20-109 From: Brad Richardson Subject: Traits for Types Date: 2020-February-23 Proposal for Fortran Standard: 202y (NOT 202x) 1. Problem Currently, in order to pass a derived type to some library expected a type derived from it's own abstract type requires the addition of a "wrapper" type to be used. This requires a cumbersome amount of boiler plate code and is not conducive to the type of "generic" code one would like to be able to write. This proposal seeks to address the issue that writing "generic" libraries is either not possible, or places undue burden on its users. The main benefits are the reduction in repeated logic or boiler plate wrapper types required for generic libraries, thereby enabling more code reuse and easier to maintain code bases. 2. Proposed Solution The solution would require the addition of a few attributes and variable declaration capabilities. First is the addition of the `trait` attribute to a type specification. This requires that the type be abstract, and contain no components. I.e. type, abstract, trait :: Show_t contains procedure(show_i), deferred :: show end type Show_t abstract interface function show_i(self) result(string) class(Show_t), intent(in) :: self character(len=:), allocatable :: string end function show_i end interface Second is the addition of the `implements` attribute for a type specification. The `implements` attribute requires a list of at least one trait the type is to implement, requiring the type to contain the procedures with the interfaces defined by the trait(s). I.e. type, implements(Show_t) :: Thing_t character(len=:), allocatable :: the_string contains procedure :: show end type Thing_t function show(self) result(string) class(Thing_t), intent(in) :: self character(len=:), allocatable :: string string = self%the_string end function show Third is the addition of a variable declaration form using `trait`, in a similar fashion to `class`. The `trait` specification would require a list of at least one trait, and must either be allocatable, or a dummy argument of a procedure. In a similar manner that the instantiated value of a class variable must be of that type, or an extended type, the instantiated value of a trait variable must be of a type which implements the given trait(s). Thus, no components of the instantiated value are accessible, only the procedures defined by the trait(s). 3. Backward compatibility This addition to the language would not break any existing standard conforming Fortran program, and thus preserves Fortran's backward compatibility. 4. Further discussion Online discussion that led to this proposal can be found at https://github.com/j3-fortran/fortran_proposals/issues/125 ================================================ FILE: proposals/worklist/26-116-din3a-recommend.txt ================================================ To: J3 J3/26-116 From: HPC Subject: Recommendation for DIN3a "Support atomic operations in local memory" Date: 2026-February-11 References: 24-167, WG5/N2230, WG5/N2234, WG5/N2249 1. Background ============= WG5/N2249 provides the list of accepted work items for the next revision of ISO/IEC 1539:2023 (Fortran 202Y). That list currently includes accepted work item: - DIN3a Add support for atomic operations in local memory Ref: N2230 "DIN Suggestions for F202Y" (other features requested in DIN3 were not accepted.) This work item was initially added to the list by WG5 at the June 2024 meeting in WG5/N2234. As indicated above, it comprises a small subset of the broader item DIN3 proposed in WG5/N2230 (and subsequently mostly declined by WG5) to expand support for APU processing. The remaining subset relevant to atomic operations amounts to a two-sentence request with no specific rationale or use cases. To our knowledge, no follow-up J3 papers have been published to further support work item DIN3a or elaborate a proposed design for this feature in isolation. Nevertheless, Info paper 24-167 rated item DIN3a with difficulty level 5 ("Technical change likely to need more than two J3 meetings to develop"). In June 2025, WG5 voted to remove the proposed asynchronous tasking feature from Fortran 202Y work list item US04. With the deferral of asynchronous tasking, it's even less clear how the underspecified DIN3a work item is motivated to fit into the remaining language features in Fortran 202Y. 2. Subgroup Recommendation ========================== The J3 HPC subgroup recommends that item DIN3a be removed (without prejudice) from the Fortran 202Y work list. Proponents are welcome to resubmit the feature request for a future revision, along with a more substantive rationale and design sketch. 3. Resolution ============= In passing this paper, INCITS/Fortran (J3) recommends to WG5 that item DIN3a be removed from the work list for Fortran 202Y. ===END===