Skip to content

Conversation

klausler
Copy link
Contributor

@klausler klausler commented Oct 3, 2025

As a common language extension, this compiler accepts a structure constructor whose first value has no keyword and whose type matches an ancestral type as if the constructor had had a keyword whose name was the ancestral type. For example, given
TYPE PARENT; REAL X; END TYPE
TYPE, EXTENDS(PARENT) :: CHILD; END TYPE
we accept the nonconforming constructor "child(parent(1.))" as if it had been the conforming "child(1.)" or "child(parent=parent(1.))".

The detection of this case needs to be constrained a bit to avoid a false positive misinterpretation of conforming code in the case where the actual first component of the derived type is a POINTER or ALLOCATABLE whose type and rank would allow it to correspond with the keywordless first value in the component value list.

Fixes #161887.

As a common language extension, this compiler accepts a structure
constructor whose first value has no keyword and whose type matches
an ancestral type as if the constructor had had a keyword whose
name was the ancestral type.  For example, given
  TYPE PARENT; REAL X; END TYPE
  TYPE, EXTENDS(PARENT) :: CHILD; END TYPE
we accept the nonconforming constructor "child(parent(1.))" as if it
had been the conforming "child(1.)" or "child(parent=parent(1.))".

The detection of this case needs to be constrained a bit to avoid
a false positive misinterpretation of conforming code in the case
where the actual first component of the derived type is a POINTER
or ALLOCATABLE whose type and rank would allow it to correspond
with the keywordless first value in the component value list.

Fixes llvm#161887.
@llvmbot llvmbot added flang Flang issues not falling into any other category flang:semantics labels Oct 3, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 3, 2025

@llvm/pr-subscribers-flang-semantics

Author: Peter Klausler (klausler)

Changes

As a common language extension, this compiler accepts a structure constructor whose first value has no keyword and whose type matches an ancestral type as if the constructor had had a keyword whose name was the ancestral type. For example, given
TYPE PARENT; REAL X; END TYPE
TYPE, EXTENDS(PARENT) :: CHILD; END TYPE
we accept the nonconforming constructor "child(parent(1.))" as if it had been the conforming "child(1.)" or "child(parent=parent(1.))".

The detection of this case needs to be constrained a bit to avoid a false positive misinterpretation of conforming code in the case where the actual first component of the derived type is a POINTER or ALLOCATABLE whose type and rank would allow it to correspond with the keywordless first value in the component value list.

Fixes #161887.


Full diff: https://github.com/llvm/llvm-project/pull/161910.diff

2 Files Affected:

  • (modified) flang/lib/Semantics/expression.cpp (+23-11)
  • (added) flang/test/Semantics/structconst11.f90 (+89)
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index fc268886c5feb..126dc4c3c62d0 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -2171,17 +2171,29 @@ MaybeExpr ExpressionAnalyzer::CheckStructureConstructor(
       // T(1) or T(PT=PT(1)).  There may be multiple parent components.
       if (nextAnonymous == components.begin() && parentComponent && valueType &&
           context().IsEnabled(LanguageFeature::AnonymousParents)) {
-        for (auto parent{components.begin()};
-             parent != afterLastParentComponentIter; ++parent) {
-          if (auto parentType{DynamicType::From(*parent)}; parentType &&
-              parent->test(Symbol::Flag::ParentComp) &&
-              valueType->IsEquivalentTo(*parentType)) {
-            symbol = &*parent;
-            nextAnonymous = ++parent;
-            Warn(LanguageFeature::AnonymousParents, source,
-                "Whole parent component '%s' in structure constructor should not be anonymous"_port_en_US,
-                symbol->name());
-            break;
+        auto parent{components.begin()};
+        if (!parent->test(Symbol::Flag::ParentComp)) {
+          // Ensure that the first value can't initialize the first actual
+          // component.
+          if (auto firstComponentType{DynamicType::From(*parent)}) {
+            if (firstComponentType->IsTkCompatibleWith(*valueType) &&
+                value.Rank() == parent->Rank()) {
+              parent = afterLastParentComponentIter; // skip next loop
+            }
+          }
+        }
+        for (; parent != afterLastParentComponentIter; ++parent) {
+          if (auto parentType{DynamicType::From(*parent)}) {
+            if (parent->test(Symbol::Flag::ParentComp) &&
+                valueType->IsEquivalentTo(*parentType) &&
+                value.Rank() == 0 /* scalar only */) {
+              symbol = &*parent;
+              nextAnonymous = ++parent;
+              Warn(LanguageFeature::AnonymousParents, source,
+                  "Whole parent component '%s' in structure constructor should not be anonymous"_port_en_US,
+                  symbol->name());
+              break;
+            }
           }
         }
       }
diff --git a/flang/test/Semantics/structconst11.f90 b/flang/test/Semantics/structconst11.f90
new file mode 100644
index 0000000000000..8cf4e6a4cda29
--- /dev/null
+++ b/flang/test/Semantics/structconst11.f90
@@ -0,0 +1,89 @@
+!RUN: %flang_fc1 -fdebug-unparse %s | FileCheck %s
+program test
+
+  type t1p
+    type(t1p), pointer :: arr(:)
+  end type
+  type, extends(t1p) :: t1c
+  end type
+  type t2p
+    type(t2p), pointer :: scalar
+  end type
+  type, extends(t2p) :: t2c
+  end type
+  type t3p
+    type(t3p), allocatable :: arr(:)
+  end type
+  type, extends(t3p) :: t3c
+  end type
+  type t4p
+    type(t4p), allocatable :: scalar
+  end type
+  type, extends(t4p) :: t4c
+  end type
+  type t5p
+    class(*), pointer :: arr(:)
+  end type
+  type, extends(t5p) :: t5c
+  end type
+  type t6p
+    class(*), pointer :: scalar
+  end type
+  type, extends(t6p) :: t6c
+  end type
+  type t7p
+    class(*), allocatable :: arr(:)
+  end type
+  type, extends(t7p) :: t7c
+  end type
+  type t8p
+    class(*), allocatable :: scalar
+  end type
+  type, extends(t8p) :: t8c
+  end type
+
+  type(t1p), target :: t1pt(1)
+  type(t1p), pointer :: t1pp(:)
+  type(t2p), target :: t2pt
+  type(t2p), pointer :: t2pp
+  type(t3p) t3pa(1)
+  type(t4p) t4ps
+
+  type(t1c) x1
+  type(t2c) x2
+  type(t3c) x3
+  type(t4c) x4
+  type(t5c) x5
+  type(t6c) x6
+  type(t7c) x7
+  type(t8c) x8
+
+!CHECK: x1=t1c(arr=t1pt)
+  x1 = t1c(t1pt)
+!CHECK: x1=t1c(arr=t1pp)
+  x1 = t1c(t1pp)
+!CHECK: x2=t2c(scalar=t2pt)
+  x2 = t2c(t2pt)
+!CHECK: x2=t2c(scalar=t2pp)
+  x2 = t2c(t2pp)
+!CHECK: x3=t3c(arr=t3pa)
+  x3 = t3c(t3pa)
+!CHECK: x4=t4c(scalar=t4ps)
+  x4 = t4c(t4ps)
+!CHECK: x4=t4c(scalar=t4p(scalar=NULL()))
+  x4 = t4c(t4p())
+!CHECK: x5=t5c(arr=t1pt)
+  x5 = t5c(t1pt)
+!CHECK: x5=t5c(arr=t1pp)
+  x5 = t5c(t1pp)
+!CHECK: x6=t6c(scalar=t2pt)
+  x6 = t6c(t2pt)
+!CHECK: x6=t6c(scalar=t2pp)
+  x6 = t6c(t2pp)
+!CHECK: x7=t7c(arr=t3pa)
+  x7 = t7c(t3pa)
+!CHECK: x8=t8c(scalar=t4ps)
+  x8 = t8c(t4ps)
+!CHECK: x8=t8c(scalar=t4p(scalar=NULL()))
+  x8 = t8c(t4p())
+end

Copy link
Contributor

@akuhlens akuhlens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@DanielCChen DanielCChen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.
Thanks.

@klausler klausler merged commit 8b93089 into llvm:main Oct 10, 2025
12 checks passed
@klausler klausler deleted the bug161887 branch October 10, 2025 17:08
DharuniRAcharya pushed a commit to DharuniRAcharya/llvm-project that referenced this pull request Oct 13, 2025
…lvm#161910)

As a common language extension, this compiler accepts a structure
constructor whose first value has no keyword and whose type matches an
ancestral type as if the constructor had had a keyword whose name was
the ancestral type. For example, given
  TYPE PARENT; REAL X; END TYPE
  TYPE, EXTENDS(PARENT) :: CHILD; END TYPE
we accept the nonconforming constructor "child(parent(1.))" as if it had
been the conforming "child(1.)" or "child(parent=parent(1.))".

The detection of this case needs to be constrained a bit to avoid a
false positive misinterpretation of conforming code in the case where
the actual first component of the derived type is a POINTER or
ALLOCATABLE whose type and rank would allow it to correspond with the
keywordless first value in the component value list.

Fixes llvm#161887.
akadutta pushed a commit to akadutta/llvm-project that referenced this pull request Oct 14, 2025
…lvm#161910)

As a common language extension, this compiler accepts a structure
constructor whose first value has no keyword and whose type matches an
ancestral type as if the constructor had had a keyword whose name was
the ancestral type. For example, given
  TYPE PARENT; REAL X; END TYPE
  TYPE, EXTENDS(PARENT) :: CHILD; END TYPE
we accept the nonconforming constructor "child(parent(1.))" as if it had
been the conforming "child(1.)" or "child(parent=parent(1.))".

The detection of this case needs to be constrained a bit to avoid a
false positive misinterpretation of conforming code in the case where
the actual first component of the derived type is a POINTER or
ALLOCATABLE whose type and rank would allow it to correspond with the
keywordless first value in the component value list.

Fixes llvm#161887.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

flang:semantics flang Flang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[flang] Structure constructor issue with parent component

4 participants