[flang-commits] [clang] [flang] [flang][semantics] Add a flag to relax some of the semantic constraints on C_LOC (PR #195112)

Andre Kuhlenschmidt via flang-commits flang-commits at lists.llvm.org
Mon May 4 09:46:48 PDT 2026


https://github.com/akuhlens updated https://github.com/llvm/llvm-project/pull/195112

>From b5725a12452fe528c687a7dedde9029aeecd8671 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 30 Apr 2026 08:29:40 -0700
Subject: [PATCH 1/9] initial commit

---
 clang/include/clang/Options/FlangOptions.td   | 11 +++++
 .../include/flang/Support/Fortran-features.h  |  4 +-
 flang/lib/Evaluate/intrinsics.cpp             | 47 +++++++++++++------
 flang/lib/Frontend/CompilerInvocation.cpp     |  6 +++
 flang/lib/Support/Fortran-features.cpp        |  1 +
 5 files changed, 53 insertions(+), 16 deletions(-)

diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td
index ffb64646709df..28fb199ff1b61 100644
--- a/clang/include/clang/Options/FlangOptions.td
+++ b/clang/include/clang/Options/FlangOptions.td
@@ -291,6 +291,17 @@ defm unsafe_cray_pointers : BoolOptionWithoutMarshalling<"f", "unsafe-cray-point
   PosFlag<SetTrue, [], [FlangOption, FC1Option], "Optimizations allow for unsafe Cray pointer usages">,
   NegFlag<SetFalse, [], [FlangOption, FC1Option], "Optimizations don't allow for unsafe Cray pointer usages (default)">>;
 
+defm relaxed_c_loc : Flag<["-"], "frelaxed-c-loc">, Group<f_Group>,
+  Visibility<[FlangOption, FC1Option]>,
+  HelpText<"Unsafe relaxation of C_LOC() argument restrictions for compatibility">,
+  DocBrief<[{
+    Unsafe relaxation of C_LOC() argument restrictions for compatibility.
+    All C_LOC values should be passed directly to C calls.
+
+    This is unsafe, because it can be used to create aliases the compiler
+    is unaware of, please fix your applications instead of using this flag.
+    This may be removed in the future.}]>;
+
 def fhermetic_module_files : Flag<["-"], "fhermetic-module-files">, Group<f_Group>,
   HelpText<"Emit hermetic module files (no nested USE association)">;
 
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index df93eaa8b4936..af72b71d9d1e6 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -59,7 +59,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
     PointerPassObject, MultipleIdenticalDATA,
     DefaultStructConstructorNullPointer, AssumedRankIoItem,
     MultipleProgramUnitsOnSameLine, AllocatedForAssociated,
-    OpenMPThreadprivateEquivalence)
+    OpenMPThreadprivateEquivalence, RelaxedCLoc)
 
 // Portability and suspicious usage warnings
 ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
@@ -85,7 +85,7 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
     RealConstantWidening, VolatileOrAsynchronousTemporary, UnusedVariable,
     UsedUndefinedVariable, BadValueInDeadCode, AssumedTypeSizeDummy,
     MisplacedIgnoreTKR, NamelistParameter, ImpureFinalInPure,
-    IgnoredNoReallocateLHS)
+    IgnoredNoReallocateLHS, CLoc)
 
 using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
 using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 84cd2288fcd0b..79ea6414666e3 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -3498,11 +3498,26 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::HandleC_Loc(
     CHECK(arguments.size() == 1);
     CheckForCoindexedObject(context.messages(), arguments[0], "c_loc", "x");
     const auto *expr{arguments[0].value().UnwrapExpr()};
+    SpecificCall specificCall{
+        SpecificIntrinsic{"__builtin_c_loc"s,
+            characteristics::Procedure{
+                characteristics::FunctionResult{DynamicType{
+                    GetBuiltinDerivedType(builtinsScope_, "__builtin_c_ptr")}},
+                characteristics::DummyArguments{},
+                characteristics::Procedure::Attrs{
+                    characteristics::Procedure::Attr::Pure}}},
+        {/*arguments*/}};
     if (expr &&
         !(IsObjectPointer(*expr) ||
             (IsVariable(*expr) && GetLastTarget(GetSymbolVector(*expr))))) {
-      context.messages().Say(arguments[0]->sourceLocation(),
-          "C_LOC() argument must be a data pointer or target"_err_en_US);
+      if (context.languageFeatures().IsEnabled(
+              common::LanguageFeature::RelaxedCLoc)) {
+        context.Warn(common::UsageWarning::CLoc, arguments[0]->sourceLocation(),
+            "C_LOC() argument must be a data pointer or target"_warn_en_US);
+      } else {
+        context.messages().Say(arguments[0]->sourceLocation(),
+            "C_LOC() argument must be a data pointer or target"_err_en_US);
+      }
     }
     if (auto typeAndShape{characteristics::TypeAndShape::Characterize(
             arguments[0], context)}) {
@@ -3539,20 +3554,24 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::HandleC_Loc(
               "C_LOC() argument has non-interoperable intrinsic type or kind"_warn_en_US);
         }
       }
-
       characteristics::DummyDataObject ddo{std::move(*typeAndShape)};
       ddo.intent = common::Intent::In;
-      return SpecificCall{
-          SpecificIntrinsic{"__builtin_c_loc"s,
-              characteristics::Procedure{
-                  characteristics::FunctionResult{
-                      DynamicType{GetBuiltinDerivedType(
-                          builtinsScope_, "__builtin_c_ptr")}},
-                  characteristics::DummyArguments{
-                      characteristics::DummyArgument{"x"s, std::move(ddo)}},
-                  characteristics::Procedure::Attrs{
-                      characteristics::Procedure::Attr::Pure}}},
-          std::move(arguments)};
+      specificCall.specificIntrinsic.characteristics.value()
+          .dummyArguments.emplace_back(std::move(ddo));
+      specificCall.arguments.emplace_back(std::move(arguments[0]));
+      return specificCall;
+    } else if (expr && IsProcedurePointer(*expr)) {
+      auto dummyArg{characteristics::DummyArgument::FromActual(
+          "x", *expr, context, /*forImplicitInterface=*/false)};
+      CHECK(dummyArg.has_value());
+      dummyArg->intent = common::Intent::In;
+      specificCall.specificIntrinsic.characteristics.value()
+          .dummyArguments.emplace_back(std::move(*dummyArg));
+      specificCall.arguments.emplace_back(std::move(arguments[0]));
+      return specificCall;
+    } else {
+      context.messages().Say(arguments[0]->sourceLocation(),
+          "C_LOC() argument must be a object or procedure"_err_en_US);
     }
   }
   return std::nullopt;
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index a1508f40bf490..fbdc0cec67866 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -869,6 +869,12 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
                        args.hasFlag(clang::options::OPT_funsigned,
                                     clang::options::OPT_fno_unsigned, false));
 
+  // -f{no-}relaxed-c-loc
+  opts.features.Enable(Fortran::common::LanguageFeature::RelaxedCLoc,
+                       args.hasFlag(clang::options::OPT_frelaxed_c_loc,
+                                    clang::options::OPT_fno_relaxed_c_loc,
+                                    false));
+
   // -f{no-}xor-operator
   opts.features.Enable(Fortran::common::LanguageFeature::XOROperator,
                        args.hasFlag(clang::options::OPT_fxor_operator,
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index d8f7b4f6e58e7..4cb4e745a4f38 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -213,6 +213,7 @@ LanguageFeatureControl::LanguageFeatureControl() {
   warnUsage_.set(UsageWarning::MisplacedIgnoreTKR);
   warnUsage_.set(UsageWarning::ImpureFinalInPure);
   warnUsage_.set(UsageWarning::IgnoredNoReallocateLHS);
+  warnUsage_.set(UsageWarning::CLoc);
   warnLanguage_.set(LanguageFeature::OpenMPThreadprivateEquivalence);
 }
 

>From d266731b7b4febbfb9b1e56fc14bf925f853790b Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 30 Apr 2026 11:09:25 -0700
Subject: [PATCH 2/9] add forgotten test cases

---
 flang/test/Semantics/c_loc01-relaxed.f90 | 97 ++++++++++++++++++++++++
 1 file changed, 97 insertions(+)
 create mode 100644 flang/test/Semantics/c_loc01-relaxed.f90

diff --git a/flang/test/Semantics/c_loc01-relaxed.f90 b/flang/test/Semantics/c_loc01-relaxed.f90
new file mode 100644
index 0000000000000..59c51e17d7fc2
--- /dev/null
+++ b/flang/test/Semantics/c_loc01-relaxed.f90
@@ -0,0 +1,97 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1 -frelaxed-c-loc -pedantic
+module m
+  use iso_c_binding
+  type haslen(L)
+    integer, len :: L
+  end type
+  integer, target :: targ
+ contains
+  subroutine subr
+  end
+  subroutine test(assumedType, poly, nclen, n)
+    type(*), target :: assumedType
+    class(*), target ::  poly
+    type(c_ptr) cp
+    type(c_funptr) cfp
+    real notATarget
+    !PORTABILITY: Procedure pointer 'pptr' should not have an ELEMENTAL intrinsic as its interface [-Wportability]
+    procedure(sin), pointer :: pptr
+    real, target :: arr(3)
+    type(hasLen(1)), target :: clen
+    type(hasLen(*)), target :: nclen
+    integer, intent(in) :: n
+    character(2), target :: ch
+    character(1,4), target :: unicode
+    real :: arr1(purefun1(c_loc(targ))) ! ok
+    real :: arr2(purefun2(c_funloc(subr))) ! ok
+    character(:), allocatable, target :: deferred
+    character(n), pointer :: p2ch
+    !WARNING: C_LOC() argument must be a data pointer or target [-Wc-loc]
+    cp = c_loc(notATarget)
+    !WARNING: C_LOC() argument must be a data pointer or target [-Wc-loc]
+    cp = c_loc(pptr)
+    !ERROR: C_LOC() argument must be contiguous
+    cp = c_loc(arr(1:3:2))
+    !ERROR: C_LOC() argument may not be a zero-sized array
+    cp = c_loc(arr(3:1))
+    !ERROR: C_LOC() argument must have an intrinsic type, assumed type, or non-polymorphic derived type with no non-constant length parameter
+    cp = c_loc(poly)
+    cp = c_loc(clen) ! ok
+    !ERROR: C_LOC() argument must have an intrinsic type, assumed type, or non-polymorphic derived type with no non-constant length parameter
+    cp = c_loc(nclen)
+    !ERROR: C_LOC() argument may not be zero-length character
+    cp = c_loc(ch(2:1))
+    !WARNING: C_LOC() argument has non-interoperable character length [-Wcharacter-interoperability]
+    cp = c_loc(ch)
+    !WARNING: C_LOC() argument has non-interoperable intrinsic type or kind [-Winteroperability]
+    cp = c_loc(unicode)
+    cp = c_loc(ch(1:1)) ! ok
+    cp = c_loc(deferred) ! ok
+    cp = c_loc(p2ch) ! ok
+    !ERROR: PRIVATE name '__address' is accessible only within module '__fortran_builtins'
+    cp = c_ptr(0)
+    !ERROR: PRIVATE name '__address' is accessible only within module '__fortran_builtins'
+    cfp = c_funptr(0)
+    !ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types TYPE(c_ptr) and TYPE(c_funptr)
+    cp = cfp
+    !ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types TYPE(c_funptr) and TYPE(c_ptr)
+    cfp = cp
+  end
+  pure integer function purefun1(p)
+    type(c_ptr), intent(in) :: p
+    purefun1 = 1
+  end
+  pure integer function purefun2(p)
+    type(c_funptr), intent(in) :: p
+    purefun2 = 1
+  end
+end module
+
+module m2
+  use iso_c_binding
+  ! In this context (structure constructor from intrinsic module being used directly
+  ! in another module), emit only a warning, since this module might have originally
+  ! been a module file that was converted back into Fortran.
+  !WARNING: PRIVATE name '__address' is accessible only within module '__fortran_builtins'
+  type(c_ptr) :: p = c_ptr(0)
+end
+
+module m3
+  use iso_c_binding
+  real, target :: modtarg
+ contains
+  subroutine helper()
+  end subroutine
+  subroutine test
+    type(c_ptr) :: cp
+    real :: notATarget
+    real, target :: localtarg
+    procedure(helper), pointer :: pptr
+    cp = c_loc(modtarg)    ! ok
+    cp = c_loc(localtarg)  ! ok
+    !WARNING: C_LOC() argument must be a data pointer or target [-Wc-loc]
+    cp = c_loc(notATarget)
+    !WARNING: C_LOC() argument must be a data pointer or target [-Wc-loc]
+    cp = c_loc(pptr)
+  end subroutine
+end module

>From 919327cbc07ac291c41e626ea764e7553385c2b6 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 1 May 2026 09:00:04 -0700
Subject: [PATCH 3/9] addressing feedback

---
 clang/include/clang/Options/FlangOptions.td |  2 +-
 flang/lib/Evaluate/intrinsics.cpp           |  5 ++---
 flang/lib/Frontend/CompilerInvocation.cpp   |  9 ++++-----
 flang/lib/Support/Fortran-features.cpp      |  1 +
 flang/test/Semantics/c_loc01-relaxed.f90    | 11 +++++++----
 5 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td
index 28fb199ff1b61..1ab83b6ffbbad 100644
--- a/clang/include/clang/Options/FlangOptions.td
+++ b/clang/include/clang/Options/FlangOptions.td
@@ -291,7 +291,7 @@ defm unsafe_cray_pointers : BoolOptionWithoutMarshalling<"f", "unsafe-cray-point
   PosFlag<SetTrue, [], [FlangOption, FC1Option], "Optimizations allow for unsafe Cray pointer usages">,
   NegFlag<SetFalse, [], [FlangOption, FC1Option], "Optimizations don't allow for unsafe Cray pointer usages (default)">>;
 
-defm relaxed_c_loc : Flag<["-"], "frelaxed-c-loc">, Group<f_Group>,
+def relaxed_c_loc : Flag<["-"], "frelaxed-c-loc">, Group<f_Group>,
   Visibility<[FlangOption, FC1Option]>,
   HelpText<"Unsafe relaxation of C_LOC() argument restrictions for compatibility">,
   DocBrief<[{
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 79ea6414666e3..454905742ac79 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -3513,7 +3513,7 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::HandleC_Loc(
       if (context.languageFeatures().IsEnabled(
               common::LanguageFeature::RelaxedCLoc)) {
         context.Warn(common::UsageWarning::CLoc, arguments[0]->sourceLocation(),
-            "C_LOC() argument must be a data pointer or target"_warn_en_US);
+            "C_LOC() argument should be a data pointer or target"_warn_en_US);
       } else {
         context.messages().Say(arguments[0]->sourceLocation(),
             "C_LOC() argument must be a data pointer or target"_err_en_US);
@@ -3557,14 +3557,13 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::HandleC_Loc(
       characteristics::DummyDataObject ddo{std::move(*typeAndShape)};
       ddo.intent = common::Intent::In;
       specificCall.specificIntrinsic.characteristics.value()
-          .dummyArguments.emplace_back(std::move(ddo));
+          .dummyArguments.emplace_back(characteristics::DummyArgument{"x", std::move(ddo)});
       specificCall.arguments.emplace_back(std::move(arguments[0]));
       return specificCall;
     } else if (expr && IsProcedurePointer(*expr)) {
       auto dummyArg{characteristics::DummyArgument::FromActual(
           "x", *expr, context, /*forImplicitInterface=*/false)};
       CHECK(dummyArg.has_value());
-      dummyArg->intent = common::Intent::In;
       specificCall.specificIntrinsic.characteristics.value()
           .dummyArguments.emplace_back(std::move(*dummyArg));
       specificCall.arguments.emplace_back(std::move(arguments[0]));
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index fbdc0cec67866..e7f4762e167fb 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -869,11 +869,10 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
                        args.hasFlag(clang::options::OPT_funsigned,
                                     clang::options::OPT_fno_unsigned, false));
 
-  // -f{no-}relaxed-c-loc
-  opts.features.Enable(Fortran::common::LanguageFeature::RelaxedCLoc,
-                       args.hasFlag(clang::options::OPT_frelaxed_c_loc,
-                                    clang::options::OPT_fno_relaxed_c_loc,
-                                    false));
+  // -frelaxed-c-loc
+  if (args.hasArg(clang::options::OPT_relaxed_c_loc)) {
+    opts.features.Enable(Fortran::common::LanguageFeature::RelaxedCLoc);
+  }
 
   // -f{no-}xor-operator
   opts.features.Enable(Fortran::common::LanguageFeature::XOROperator,
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 4cb4e745a4f38..54c8931da17d3 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -139,6 +139,7 @@ LanguageFeatureControl::LanguageFeatureControl() {
   disable_.set(LanguageFeature::ImplicitNoneExternal);
   disable_.set(LanguageFeature::DefaultSave);
   disable_.set(LanguageFeature::SaveMainProgram);
+  disable_.set(LanguageFeature::RelaxedCLoc);
   // These features, if enabled, conflict with valid standard usage,
   // so there are disabled here by default.
   disable_.set(LanguageFeature::BackslashEscapes);
diff --git a/flang/test/Semantics/c_loc01-relaxed.f90 b/flang/test/Semantics/c_loc01-relaxed.f90
index 59c51e17d7fc2..94bca594e8d4a 100644
--- a/flang/test/Semantics/c_loc01-relaxed.f90
+++ b/flang/test/Semantics/c_loc01-relaxed.f90
@@ -26,9 +26,9 @@ subroutine test(assumedType, poly, nclen, n)
     real :: arr2(purefun2(c_funloc(subr))) ! ok
     character(:), allocatable, target :: deferred
     character(n), pointer :: p2ch
-    !WARNING: C_LOC() argument must be a data pointer or target [-Wc-loc]
+    !WARNING: C_LOC() argument should be a data pointer or target [-Wc-loc]
     cp = c_loc(notATarget)
-    !WARNING: C_LOC() argument must be a data pointer or target [-Wc-loc]
+    !WARNING: C_LOC() argument should be a data pointer or target [-Wc-loc]
     cp = c_loc(pptr)
     !ERROR: C_LOC() argument must be contiguous
     cp = c_loc(arr(1:3:2))
@@ -89,9 +89,12 @@ subroutine test
     procedure(helper), pointer :: pptr
     cp = c_loc(modtarg)    ! ok
     cp = c_loc(localtarg)  ! ok
-    !WARNING: C_LOC() argument must be a data pointer or target [-Wc-loc]
+    !WARNING: C_LOC() argument should be a data pointer or target [-Wc-loc]
     cp = c_loc(notATarget)
-    !WARNING: C_LOC() argument must be a data pointer or target [-Wc-loc]
+    !WARNING: C_LOC() argument should be a data pointer or target [-Wc-loc]
     cp = c_loc(pptr)
+    !ERROR: C_LOC() argument must be a object or procedure
+    !ERROR: alternate return specification may not appear on function reference
+10  cp = c_loc(*10)
   end subroutine
 end module

>From 12039281e8fac1f5543d0087f98e3456b170040c Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 1 May 2026 09:00:41 -0700
Subject: [PATCH 4/9] clang format

---
 flang/lib/Evaluate/intrinsics.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 454905742ac79..87b041238071f 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -3557,7 +3557,8 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::HandleC_Loc(
       characteristics::DummyDataObject ddo{std::move(*typeAndShape)};
       ddo.intent = common::Intent::In;
       specificCall.specificIntrinsic.characteristics.value()
-          .dummyArguments.emplace_back(characteristics::DummyArgument{"x", std::move(ddo)});
+          .dummyArguments.emplace_back(
+              characteristics::DummyArgument{"x", std::move(ddo)});
       specificCall.arguments.emplace_back(std::move(arguments[0]));
       return specificCall;
     } else if (expr && IsProcedurePointer(*expr)) {

>From 69d9675ce65ea22e34c8fd09f4b5ba23668ba0f1 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 1 May 2026 09:40:38 -0700
Subject: [PATCH 5/9] remove pedantic

---
 flang/test/Semantics/c_loc01-relaxed.f90 | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/flang/test/Semantics/c_loc01-relaxed.f90 b/flang/test/Semantics/c_loc01-relaxed.f90
index 94bca594e8d4a..2fbf5539aa16e 100644
--- a/flang/test/Semantics/c_loc01-relaxed.f90
+++ b/flang/test/Semantics/c_loc01-relaxed.f90
@@ -1,4 +1,4 @@
-! RUN: %python %S/test_errors.py %s %flang_fc1 -frelaxed-c-loc -pedantic
+! RUN: %python %S/test_errors.py %s %flang_fc1 -frelaxed-c-loc
 module m
   use iso_c_binding
   type haslen(L)
@@ -14,7 +14,6 @@ subroutine test(assumedType, poly, nclen, n)
     type(c_ptr) cp
     type(c_funptr) cfp
     real notATarget
-    !PORTABILITY: Procedure pointer 'pptr' should not have an ELEMENTAL intrinsic as its interface [-Wportability]
     procedure(sin), pointer :: pptr
     real, target :: arr(3)
     type(hasLen(1)), target :: clen
@@ -41,7 +40,6 @@ subroutine test(assumedType, poly, nclen, n)
     cp = c_loc(nclen)
     !ERROR: C_LOC() argument may not be zero-length character
     cp = c_loc(ch(2:1))
-    !WARNING: C_LOC() argument has non-interoperable character length [-Wcharacter-interoperability]
     cp = c_loc(ch)
     !WARNING: C_LOC() argument has non-interoperable intrinsic type or kind [-Winteroperability]
     cp = c_loc(unicode)

>From f30b2c3e90bbb53f4b6eddc9ebd25ecec5dac71a Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 1 May 2026 16:43:28 -0700
Subject: [PATCH 6/9] addressing feedback

---
 flang/docs/Extensions.md                 |  6 ++++++
 flang/lib/Evaluate/intrinsics.cpp        | 27 ++++++++++++++----------
 flang/test/Semantics/c_loc01-relaxed.f90 |  7 +++---
 flang/test/Semantics/c_loc01.f90         |  2 ++
 4 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index 391fe99749fd6..566bbd00440b6 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -527,6 +527,12 @@ end program
 * Default exponent of zero, e.g. `3.14159E`, on a READ from a
   fixed-width input field.  Includes the case with only an
   exponent letter for compatibility with other compilers.
+* Relax some restriction to make `C_LOC` more like `LOC` for
+  compatibility with legacy code that needs updated. This is
+  is unsafe and can be used to create aliases that the compiler
+  does not know about. Loactions obtained this way should be
+  passed directly to C code. 
+  [-frelaxed-c-loc]
 
 ### Extensions and legacy features deliberately not supported
 
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 87b041238071f..901421dbf2818 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -3561,17 +3561,22 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::HandleC_Loc(
               characteristics::DummyArgument{"x", std::move(ddo)});
       specificCall.arguments.emplace_back(std::move(arguments[0]));
       return specificCall;
-    } else if (expr && IsProcedurePointer(*expr)) {
-      auto dummyArg{characteristics::DummyArgument::FromActual(
-          "x", *expr, context, /*forImplicitInterface=*/false)};
-      CHECK(dummyArg.has_value());
-      specificCall.specificIntrinsic.characteristics.value()
-          .dummyArguments.emplace_back(std::move(*dummyArg));
-      specificCall.arguments.emplace_back(std::move(arguments[0]));
-      return specificCall;
-    } else {
-      context.messages().Say(arguments[0]->sourceLocation(),
-          "C_LOC() argument must be a object or procedure"_err_en_US);
+      // C_LOC() argument is a procedure pointer
+    } else if (context.languageFeatures().IsEnabled(
+                   common::LanguageFeature::RelaxedCLoc)) {
+      if (!expr || !IsProcedurePointer(*expr)) {
+        // There are more specific errors as to why the expression doesn't exist
+        // or isn't characterizable as a data object or procedure.
+      } else if (auto proc{characteristics::Procedure::Characterize(
+                     *expr, context)}) {
+        characteristics::DummyProcedure dProc{std::move(*proc)};
+        dProc.intent = common::Intent::In;
+        specificCall.specificIntrinsic.characteristics.value()
+            .dummyArguments.emplace_back(
+                characteristics::DummyArgument{"x", std::move(dProc)});
+        specificCall.arguments.emplace_back(std::move(arguments[0]));
+        return specificCall;
+      }
     }
   }
   return std::nullopt;
diff --git a/flang/test/Semantics/c_loc01-relaxed.f90 b/flang/test/Semantics/c_loc01-relaxed.f90
index 2fbf5539aa16e..c958a95e0949b 100644
--- a/flang/test/Semantics/c_loc01-relaxed.f90
+++ b/flang/test/Semantics/c_loc01-relaxed.f90
@@ -46,6 +46,8 @@ subroutine test(assumedType, poly, nclen, n)
     cp = c_loc(ch(1:1)) ! ok
     cp = c_loc(deferred) ! ok
     cp = c_loc(p2ch) ! ok
+    !ERROR: alternate return specification may not appear on function reference
+666 cp = c_loc(*666)
     !ERROR: PRIVATE name '__address' is accessible only within module '__fortran_builtins'
     cp = c_ptr(0)
     !ERROR: PRIVATE name '__address' is accessible only within module '__fortran_builtins'
@@ -76,11 +78,13 @@ module m2
 
 module m3
   use iso_c_binding
+  implicit none
   real, target :: modtarg
  contains
   subroutine helper()
   end subroutine
   subroutine test
+    implicit none
     type(c_ptr) :: cp
     real :: notATarget
     real, target :: localtarg
@@ -91,8 +95,5 @@ subroutine test
     cp = c_loc(notATarget)
     !WARNING: C_LOC() argument should be a data pointer or target [-Wc-loc]
     cp = c_loc(pptr)
-    !ERROR: C_LOC() argument must be a object or procedure
-    !ERROR: alternate return specification may not appear on function reference
-10  cp = c_loc(*10)
   end subroutine
 end module
diff --git a/flang/test/Semantics/c_loc01.f90 b/flang/test/Semantics/c_loc01.f90
index 16f5618b6330f..2496027f4315b 100644
--- a/flang/test/Semantics/c_loc01.f90
+++ b/flang/test/Semantics/c_loc01.f90
@@ -48,6 +48,8 @@ subroutine test(assumedType, poly, nclen, n)
     cp = c_loc(ch(1:1)) ! ok
     cp = c_loc(deferred) ! ok
     cp = c_loc(p2ch) ! ok
+    !ERROR: alternate return specification may not appear on function reference
+666 cp = c_loc(*666)
     !ERROR: PRIVATE name '__address' is accessible only within module '__fortran_builtins'
     cp = c_ptr(0)
     !ERROR: PRIVATE name '__address' is accessible only within module '__fortran_builtins'

>From bfd63dda7d207bfb4bfa3057ec9aa5cf6817c073 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 1 May 2026 16:45:35 -0700
Subject: [PATCH 7/9] fix typos

---
 flang/docs/Extensions.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index 566bbd00440b6..05e0ef5358165 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -527,11 +527,11 @@ end program
 * Default exponent of zero, e.g. `3.14159E`, on a READ from a
   fixed-width input field.  Includes the case with only an
   exponent letter for compatibility with other compilers.
-* Relax some restriction to make `C_LOC` more like `LOC` for
-  compatibility with legacy code that needs updated. This is
+* Relax some restrictions to make `C_LOC` more like `LOC` for
+  compatibility with legacy code that should be fixed. This is
   is unsafe and can be used to create aliases that the compiler
-  does not know about. Loactions obtained this way should be
-  passed directly to C code. 
+  does not know about. Locations obtained this way should be
+  passed directly to C code. This could be removed at any time.
   [-frelaxed-c-loc]
 
 ### Extensions and legacy features deliberately not supported

>From 4d0a150a04de77f1658ce6dc9b255c426c88dde3 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 1 May 2026 16:48:12 -0700
Subject: [PATCH 8/9] remove useless comment

---
 flang/lib/Evaluate/intrinsics.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 901421dbf2818..310cde22a265c 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -3561,7 +3561,6 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::HandleC_Loc(
               characteristics::DummyArgument{"x", std::move(ddo)});
       specificCall.arguments.emplace_back(std::move(arguments[0]));
       return specificCall;
-      // C_LOC() argument is a procedure pointer
     } else if (context.languageFeatures().IsEnabled(
                    common::LanguageFeature::RelaxedCLoc)) {
       if (!expr || !IsProcedurePointer(*expr)) {

>From 89238a7a4f3ac1b71edea228edfa6012784ea515 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Mon, 4 May 2026 09:39:09 -0700
Subject: [PATCH 9/9] fix typo

---
 flang/docs/Extensions.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index 05e0ef5358165..bbacc61632339 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -529,7 +529,7 @@ end program
   exponent letter for compatibility with other compilers.
 * Relax some restrictions to make `C_LOC` more like `LOC` for
   compatibility with legacy code that should be fixed. This is
-  is unsafe and can be used to create aliases that the compiler
+  unsafe and can be used to create aliases that the compiler
   does not know about. Locations obtained this way should be
   passed directly to C code. This could be removed at any time.
   [-frelaxed-c-loc]



More information about the flang-commits mailing list