[flang-commits] [flang] [flang] Improve error for misplaced statement after CONTAINS in derived type (PR #215886)

via flang-commits flang-commits at lists.llvm.org
Wed Aug 12 13:19:54 PDT 2026


https://github.com/kwyatt-ext created https://github.com/llvm/llvm-project/pull/215886

A statement after `CONTAINS` in a derived type that is not a type-bound procedure binding (e.g. a second `CONTAINS`, an `IMPORT`, or a misplaced subprogram) leaked the intrinsic type-spec parse failures (`expected 'COMPLEX'`, `expected 'INTEGER'`, ...) instead of reporting that a type-bound procedure binding was expected. In the misplaced-subprogram case flang emitted an avalanche of unrelated `expected '<type-keyword>'` errors.

This is a diagnostic regression from #203379, which added `DataComponentDefStmt` as a trailing alternative in the type-bound-proc-binding parser. Its intended `fail<>()` message only fires when `DataComponentDefStmt` fully matches; for any other invalid statement the partial parse into `declarationTypeSpec` displaced the recovery message in `CombineFailedParses`.

This patch wraps the binding alternatives in `withMessage()` so that when none of them match a token a single clear message is emitted:

```
error: expected a type-bound procedure binding (PROCEDURE, GENERIC, or FINAL) after CONTAINS
```

The specific `component definition must precede CONTAINS in a derived type` message is still produced for a genuine misplaced component definition (`withMessage` only overrides when no tokens were matched), and malformed `PROCEDURE`/`GENERIC`/`FINAL`/component statements keep their own specific diagnostics.

`flang/test/Parser/recovery09.f90` is extended to cover the double-`CONTAINS`, `IMPORT`, and misplaced-subprogram cases.


>From 2ce23d7740b3907b799700ad2fe854bde33ef6d5 Mon Sep 17 00:00:00 2001
From: Kevin Wyatt <kwyatt at hpe.com>
Date: Wed, 12 Aug 2026 12:51:56 -0500
Subject: [PATCH] [flang] Improve error for misplaced statement after CONTAINS
 in derived type

A statement after CONTAINS in a derived type that is not a type-bound
procedure binding (e.g. a second CONTAINS, an IMPORT, or a misplaced
subprogram) leaked the intrinsic type-spec parse failures ("expected
'COMPLEX'", "expected 'INTEGER'", ...) instead of reporting that a
type-bound procedure binding was expected. In the misplaced-subprogram
case flang emitted an avalanche of unrelated "expected '<type-keyword>'"
errors.

This was a diagnostic regression from #203379, which added
DataComponentDefStmt as a trailing alternative in the
type-bound-proc-binding parser. Its intended fail<>() message only fires
when DataComponentDefStmt fully matches; for any other invalid statement
the partial parse into declarationTypeSpec displaced the recovery message
in CombineFailedParses.

Wrap the binding alternatives in withMessage() so that when none of them
match a token a single clear message is emitted, while the specific
"component definition must precede CONTAINS in a derived type" message is
still produced for a genuine misplaced component definition and malformed
bindings keep their own diagnostics.

Extend recovery09.f90 to cover the double-CONTAINS, IMPORT, and
misplaced-subprogram cases.
---
 flang/lib/Parser/Fortran-parsers.cpp | 15 +++++++-----
 flang/test/Parser/recovery09.f90     | 35 ++++++++++++++++++++++++----
 2 files changed, 40 insertions(+), 10 deletions(-)

diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index ebae6bfa53cab..72de71bf0ac63 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -567,12 +567,15 @@ TYPE_CONTEXT_PARSER("type bound procedure part"_en_US,
 //        final-procedure-stmt
 TYPE_CONTEXT_PARSER("type bound procedure binding"_en_US,
     recovery(
-        first(construct<TypeBoundProcBinding>(Parser<TypeBoundProcedureStmt>{}),
-            construct<TypeBoundProcBinding>(Parser<TypeBoundGenericStmt>{}),
-            construct<TypeBoundProcBinding>(Parser<FinalProcedureStmt>{}),
-            Parser<DataComponentDefStmt>{} >>
-                fail<TypeBoundProcBinding>(
-                    "component definition must precede CONTAINS in a derived type"_err_en_US)),
+        withMessage(
+            "expected a type-bound procedure binding (PROCEDURE, GENERIC, or FINAL) after CONTAINS"_err_en_US,
+            first(construct<TypeBoundProcBinding>(
+                      Parser<TypeBoundProcedureStmt>{}),
+                construct<TypeBoundProcBinding>(Parser<TypeBoundGenericStmt>{}),
+                construct<TypeBoundProcBinding>(Parser<FinalProcedureStmt>{}),
+                Parser<DataComponentDefStmt>{} >>
+                    fail<TypeBoundProcBinding>(
+                        "component definition must precede CONTAINS in a derived type"_err_en_US))),
         construct<TypeBoundProcBinding>(
             !"END"_tok >> SkipTo<'\n'>{} >> construct<ErrorRecovery>())))
 
diff --git a/flang/test/Parser/recovery09.f90 b/flang/test/Parser/recovery09.f90
index 0642fc98a0674..327d37277da5f 100644
--- a/flang/test/Parser/recovery09.f90
+++ b/flang/test/Parser/recovery09.f90
@@ -1,12 +1,14 @@
 ! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
 
-! Verify that a data component definition appearing after CONTAINS in a
-! derived type gives a clear error instead of misleading
-! "expected 'FINAL'/'GENERIC'/'PROCEDURE'" messages.
+! Verify that a statement appearing after CONTAINS in a derived type that is not
+! a type-bound procedure binding gives a clear error instead of misleading
+! "expected 'FINAL'/'GENERIC'/'PROCEDURE'" or intrinsic-type-spec keyword
+! messages.
 
 module m
   implicit none
 
+  ! A data component definition after CONTAINS names the specific problem.
   type, public :: t1
      real :: x
    contains
@@ -17,10 +19,35 @@ module m
 ! CHECK: error: component definition must precede CONTAINS in a derived type
 ! CHECK-NEXT: {{.*}}real, pointer, dimension(:,:,:), public :: gpoint => null()
      real, pointer, dimension(:,:,:), public :: gpoint => null()
+  end type t1
+
+  ! A second CONTAINS is not a component definition.
+  type :: t2
+   contains
+   contains
+! CHECK: error: expected a type-bound procedure binding (PROCEDURE, GENERIC, or FINAL) after CONTAINS
+  end type t2
+
+  ! An IMPORT after CONTAINS is likewise not a binding.
+  type :: t3
+   contains
+     import
+! CHECK: error: expected a type-bound procedure binding (PROCEDURE, GENERIC, or FINAL) after CONTAINS
+  end type t3
+
+  ! A misplaced subprogram after CONTAINS.
+  type :: t4
+   contains
+     subroutine s
+     end subroutine
+! CHECK: error: expected a type-bound procedure binding (PROCEDURE, GENERIC, or FINAL) after CONTAINS
+  end type t4
+
 ! CHECK-NOT: expected 'FINAL'
 ! CHECK-NOT: expected 'GENERIC'
 ! CHECK-NOT: expected 'PROCEDURE'
-  end type t1
+! CHECK-NOT: expected 'COMPLEX'
+! CHECK-NOT: expected 'INTEGER'
 
 contains
   subroutine init(this)



More information about the flang-commits mailing list