I can only talk about my own motivation to continue developing and delivering LFortran. Flang is great, but on its own I do not think it will be enough to fix Fortran. What I want as a user is a compiler that is fast to compile itself (under 30s for LFortran on my Apple M4, and even that is at least 10x too long for me, but we would need to switch from C++ to C, which we might later), that is very easy to contribute to, that can compile Fortran codes as fast as possible (LLVM is unfortunately the bottleneck here, so we are also developing a custom backend that does not use LLVM that is 10x faster), that has good runtime performance (LLVM is great here), that can be interactive (runs in Jupyter notebooks), that creates lean (small) binaries, that fully runs in the browser (both the compiler and the generated code), that has various extensions that users have been asking for, etc. The list is long.
Finally, I have not seen Fortran users complaining that there is more than one compiler. On the contrary, everybody seems very excited that they will soon have several independent high-quality open source compilers. I think it is essential for a healthy language ecosystem to have many good compilers.
When I was in undergrad, so sometime around 2006 or so, I read somewhere that Fortran was actually faster than C in some cases, due I believe to the compiler and certain choices it makes regarding aliasing arrays and whatnot.
I am an aerospace engineer and avid flight simmer, mostly WWII combat sims, and a huge part of the hobby involves arguing with other people about WWII piston aircraft and their performance. Pages upon pages of forum threads replete with PDFs of historical documents in German, Russian, etc. In order to win arguments more effectively, I decided to write a parametric "flight sim" that would accept a set of maneuvers as an input (e.g. "start at 10000m, dive until speed reaches X km/h indicated, zoom climb back up" or "turn in a circle at Y km/h indicated") so that I had something to compare the in-game results and historical data with.
I decided to write the whole thing in Fortran 2003 (the 2008 revision was still a draft, as I recall), because fast.
I bought a copy of Stanley Hooker's book "The Performance of a Supercharged Aero Engine" from the Rolls-Royce heritage press, implemented the method therein. Implemented a numerical vortex panel method for the lifting surfaces, the whole shebang. It was actually a really fun project and it worked pretty well. Fortran is a neat, quaint language and I look back on that project fondly but ultimately I don't miss Fortran. The syntax is needlessly verbose. I/O is a chore. String manipulation is a pain. If you ask me, C++ (despite its many flaws) is superior in every way.
https://jemarch.net/a68-jargon/
(There are also "incestuous unions", which is the actual term used in the spec.)
I think it's a European engineering thing that just sort of caught on, actually. For example when I was in undergrad, my 4th-year computational fluids prof made us use "Code Aster"[0] and "Code Saturne"[1] which are both made by a French lab, I believe. Most of the usage of "code as a countable noun" that I've encountered has origins in English-as-a-second-language projects.
Information. Code. Software. Hardware.
I suspect many people don't even know they are uncountable.
I suppose for software we should just use programs or applications. But that's slightly more specific than software!
In French we can have both: le logiciel as some uncountable mass, or un/des/N logiciels if you need to count them.
Why the hell do I need to cut information into pieces to count it?
Both English and French are cursed languages, but English loses on this one.
And then there's the trousers. And now you need to say "a pair of" to talk about one unit of them. Though to be completely fair we have that for the glasses (lunettes) and the scissors as well.
Well, most English speakers may not know the term, but they can feel the concept just fine.
> In French we can have both: le logiciel as some uncountable mass, or un/des/N logiciels if you need to count them.
This mostly works in English (and other European languages) as well, e.g. "Two teas/beers, please" etc. But in English this turn of phrase is much more restricted which is indeed a shame.
And let's not even start with pluralia tantum.
LFortran can now compile fpm, the Fortran Package Manager. We opened up an issue for it in April 2025, when we started focusing on it as a priority. We closed it on February 7, 2026. fpm is the most complex project that we now successfully built and run. It is an interesting project: it is not computational, but rather a system project which exercises running other programs, reading environment variables, command-line argument parsing, reading and writing files, directories, parsing enough Fortran to understand dependencies, etc. It uses almost all modern Fortran features: classes, inheritance, allocatable components, constructors, arrays of classes, select type, associate, automatic LHS (left-hand side) reallocation, strings, arrays of strings and it exposed dozens and dozens of bugs and missing features in LFortran. And we have now implemented them all. As a result, LFortran is really close to beta, and we are advancing our progress bar to 9/10.
When we started our progress bar towards beta, we thought that compiling 10 codes with increasing complexity and ending with fpm would be enough for beta. But when we got close to compiling fpm, we realized we are still not quite there. Beta quality to us means that when you use LFortran on your code of N lines (say N=10,000), it will compile and run it correctly in about 90% of the cases. We are estimating that we are there with N=500 or maybe N=1,000, but not for larger codes yet. However the bugs are very small, and so we are sprinting by compiling more and more codes and fixing all bugs, which are now simple to fix.
So we decided to cheat a little bit and did not progress the bar on compiling LAPACK, which allows us to progress the bar now and still have one step left and we will progress it to beta once we are confident that we can compile medium-sized codes with high probability.
One of the big subprojects was to completely refactor how we handle classes, virtual functions and inheritance. We studied how Clang handles classes in C++ and implemented a similar approach with some Fortran-specific additions.
As with any big projects that we compile, we got it working a few weeks ago, we set up testing that compiles and runs all fpm tests at our CI (Continuous Integration) for every commit and then we keep fixing bugs from other projects and watch the CI if it ever fails in fpm with flaky bugs. If it does not after lots of other PRs (Pull Requests) are merged, we know that it is working and that it does not have simple flaky bugs. It might still have some bugs that do not show up easily, but all the things that we can see are rock solid, so we are comfortable announcing it.
We also added support for fpm’s five key dependencies: M_CLI2 command-line argument parsing), toml-f (reads TOML configuration files), fortran-regex (handles pattern matching), fortran-shlex (processes shell-like syntax), and Jonquil (manages JSON data).
To build, do:
git clone https://github.com/fortran-lang/fpm
git checkout d0f89957541bdcc354da8e11422f5efcf9fedd0e # latest main
conda create -n fpm lfortran=0.60.0 fpm gfortran
conda activate fpm
fpm --compiler=lfortran test --flag --cpp --flag --realloc-lhs-arrays --flag --use-loop-variable-after-loop
You can also append --fast or --separate-compilation options, both work as well. The gfortran compiler is necessary to pass tests, it is not being used to build any source code. Upstream would need to be improved to not have gfortran hard-coded in testing.
LFortran by default has bounds checking on, and it will give runtime errors when an incorrect shape or an unallocated array is used. This found a genuine upstream bug that we fixed by submitting a PR upstream. Over time we will harden our compile-time and runtime checks so that your code either works or gives an error at compile-time or runtime, but never segfaults or has other undefined behavior.
To get some idea about the speed of compilation, here are the times on Apple M4 MacBook Pro:
$ time fpm build --compiler=gfortran
[...]
libfpm.a done.
main.f90 done.
fpm done.
[100%] Project compiled successfully.
real 42.342s
user 39.902s
sys 2.050s
cpu 99.1%
$ time fpm --compiler=lfortran build --flag --cpp --flag --realloc-lhs-arrays --flag --use-loop-variable-after-loop
[...]
real 16.641s
user 15.533s
sys 0.968s
cpu 99.2%
The exact compiler versions are:
$ gfortran --version
GNU Fortran (conda-forge gcc 15.2.0-18) 15.2.0
$ lfortran --version
LFortran version: 0.60.0
Platform: macOS ARM
LLVM: 19.1.1
Default target: arm64-apple-darwin24.6.0
LSP Version: 3.17.0
JSON_RPC Version: 2.0
Both are single-core compilation of the fpm binary. Overall, not bad for a start.
To get a better idea where LFortran is spending compilation time, you can rerun fpm with --verbose and then manually run longest compilation command, which happens to be:
lfortran -c app/main.f90 --cpp --cpp --realloc-lhs-arrays --use-loop-variable-after-loop -DFPM_RELEASE_VERSION=0.12.0 -Ibuild/dependencies/fortran-regex/src -J build/lfortran_29F2E0FA2D75FE0A -Ibuild/lfortran_29F2E0FA2D75FE0A -Ibuild/lfortran_5D5DD1C987059777 -o build/lfortran_29F2E0FA2D75FE0A/fpm/app_main.f90.o --time-report
Notice the --time-report option that we added at the end. This prints:
Allocator usage of last chunk (MB) 0.284
Allocator chunks 7.000
------------------------------------------------------------
Component name Time (ms)
------------------------------------------------------------
File reading 0.450
Src -> ASR 93.267
ASR passes (total) 178.630
global_stmts 0.000
init_expr 1.885
function_call_in_declaration 2.686
openmp 0.000
implied_do_loops 4.687
array_struct_temporary 13.439
transform_optional_argument_functions 11.940
nested_vars 8.111
forall 1.281
class_constructor 4.553
pass_list_expr 4.867
where 1.652
array_op 4.886
symbolic 0.708
intrinsic_function 29.437
intrinsic_subroutine 5.336
subroutine_from_function 7.500
array_op 5.352
pass_array_by_data 16.393
array_passed_in_function_call 6.463
print_list_tuple 1.797
array_dim_intrinsics_update 6.731
do_loops 6.631
while_else 3.165
select_case 3.459
unused_functions 21.647
unique_symbols 0.000
insert_deallocate 3.432
other processing time 0.026
ASR -> mod 0.410
LLVM IR creation 329.712
LLVM opt 0.000
LLVM -> BIN 12539.240
------------------------------------------------------------
Total time 13204.171
------------------------------------------------------------
Here, ASR means Abstract Semantic Representation, the main internal representation (IR) that LFortran uses. We then run a similar line to figure out how long it takes to link.
So here is the breakdown:
| Task | Time [s] | Percentage |
|---|---|---|
| Compiling all files to one ASR | 3.5s | 21.1% |
| ASR->ASR passes | 0.2s | 1.2% |
| ASR->LLVM IR | 0.3s | 1.8% |
| LLVM IR -> object file | 12.5s | 75.3 % |
| Linking | 0.1s | 0.6% |
| Total | 16.6 | 100% |
The main bottleneck is LLVM IR compilation to binary object code (over 3/4 of all the time). This excludes creating the LLVM IR from ASR as well as linking (both counted on separate lines above). To speed this up, we plan to create our own backend later, we think it might be possible to make it compile about 10x faster, which would bring the total time down to about 5s.
After that, we can start optimizing the other parts. The first line, compilation of all files to one ASR, includes parsing all files, doing semantic analysis, loading all dependent Fortran modules’ .mod files, and saving a .mod file for a given module.
That is not the most efficient way to compile it: a better way would be for the build system (fpm in this case) to give LFortran the dependency graph and files to compile and compiler options for each file and LFortran would compile all the files at once, never saving or loading .mod files from the disk, assembling the final ASR step by step.
It is a very interesting computational problem of taking source code and emitting a binary as fast as we can, and we are excited to start working on that after we reach beta.
Now that we reached this big milestone, we are working on other third-party codes and fixing all bugs that we encounter. We are still in alpha stage. When we feel comfortable expecting LFortran to just work on medium-sized codes, we will announce beta quality. It is becoming easier and easier to compile a new third-party code, the bugs are getting smaller and less frequent. As far as we know, all Fortran features are implemented, except coarrays and parametrized derived types (PDT), which we will tackle after beta. It is hard to predict a timeline, but our best guess is a timeframe of several months (so not weeks, but also not years). We will see if we were right.
We will keep announcing every new significant third-party code that we can compile.
We welcome new contributors to join our journey. If you’re interested, please reach out on Zulip. Working on LFortran is both challenging and rewarding, offering great opportunities for learning and growth.
We want to thank: