Bug 45424 - [F08] Add IS_CONTIGUOUS intrinsic
Summary: [F08] Add IS_CONTIGUOUS intrinsic
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 4.6.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on: 53298 36825
Blocks: F2008 53320
  Show dependency treegraph
 
Reported: 2010-08-27 07:48 UTC by Tobias Burnus
Modified: 2019-01-07 19:41 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2013-12-20 00:00:00


Attachments
Draft patch: is_contiguous.diff (3.23 KB, text/plain)
2012-05-09 15:36 UTC, Tobias Burnus
Details
Update of Tobias' patch to 9-trunk (except for ChangeLog) (2.80 KB, text/plain)
2018-12-27 22:51 UTC, Harald Anlauf
Details
Extended Update of Tobias' patch to 9-trunk (except for ChangeLog) (5.06 KB, patch)
2019-01-02 23:12 UTC, Harald Anlauf
Details | Diff
Corrected update of Tobias' patch to 9-trunk (except for ChangeLog) (5.57 KB, patch)
2019-01-03 21:20 UTC, Harald Anlauf
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tobias Burnus 2010-08-27 07:48:09 UTC
For compile-time checking that can simply use the the gfc_is_simply_contiguous check. For run time, it should use the flag in the descriptor (if existing) or some library check, cf. pack checks.

Because of the potential descriptor element, mark dependent on PR 36825.


From Fortran 2008:

13.7.86 IS_CONTIGUOUS (ARRAY)

Description.  Test contiguity of an array (5.3.7).
Class.  Inquiry function.
Argument.  ARRAY may be of any type. It shall be an array. If it is a pointer it shall be associated.
Result Characteristics.  Default logical scalar.
Result Value.  The result has the value true if ARRAY is contiguous, and false otherwise.
Example.  After the pointer assignment AP => TARGET (1:10:2), IS_CONTIGUOUS (AP) has the value false.
Comment 1 Tobias Burnus 2012-05-09 15:36:40 UTC
Created attachment 27359 [details]
Draft patch: is_contiguous.diff

Attached is a mostly ready patch for IS_CONTIGUOUS.

The main TODO is to understand when IS_CONTIGUOUS should print .TRUE. and when .FALSE. The standard is not very clear about it, cf. 5.3.7 of F2008.


a) Array element with nonzero strides (which is still a single array element): "a(1:1:1)" vs. "a(1:1:5)". With the attached patch but also with ifort and crayftn, the result is .true. and .false. The attached patch only checks the "stride", ignoring the number of elements.

b) The same - but instead of a single element, using zero-sized arrays: a(2:1:1) vs. a(2:1:2).

c) The following example is mishandled as we have a stride and not a stride multiplied (sm):

type t
  integer :: i, j
end type t
type(t) :: x(5)
print *, is_contiguous(x(:)) ! Shall be (and is) true
print *, is_contiguous(x(:)%i) ! Shall be false - but prints "true".
end

The fortran-devel solution would be to use: sm == TYPE_SIZE(a%i). The trunk solution is to compare the array spec's object "a" with the the type-size of "a%i" - and if it differs to abort.

d) The gfc_is_simply_contiguous function needs to be refined and extended - and a "gfc_is_simply_noncontiguous" has to be added.

e) More tests are needed - including polymorphic arrays and compile-time checks for the code in simplify.c. [Which is related to (d).]

f) For TS29113, one needs to handle assumed-rank arrays, including rank-1 ones. Again the question is whether the dummy argument associated with a scalar is then contiguous or not.
Comment 2 Tobias Burnus 2012-05-09 16:02:18 UTC
(In reply to comment #1)
> a) Array element with nonzero strides

I meant: A an array containing a single element and having a nonunity stride.


> type t
>   integer :: i, j
> end type t
> print *, is_contiguous(x(:)%i) ! Shall be false - but prints "true".

Those kind of arguments can get really tricky! Assume as component a:
    integer :: i(1) ! or "i(2)
or
    class(t), allocatable :: a
or
    integer, allocatable :: a(:)
which is accessed as "is_contiguous(x(:)%a(1))".

Intel and Cray handle it simply: by always returning "F" - even for "type t; integer :: i; end type", which should have no padding and be contiguous in memory. We could do the same - or we rule out BT_CLASS, arrays and coarrays and do then a size check: whole derived type == expr->ts.type size.


Another issue are substrings. For those one needs to check that the substring range matches the string length.
Comment 3 Sean Santos 2013-04-19 21:12:06 UTC
A naive interpretation of the standard suggests that in the case "a)", is_contiguous should return .true., because the standard doesn't say anything about strides here, only about the ordering of the elements. (Of course strides matter for "simply contiguous" things, but that's different.)

Case "b)" seems to be the same.

In case "f)", since scalars can be associated with assumed-rank entities that have the contiguous attribute, it wouldn't make much sense to return ".false." in this case.
Comment 4 janus 2013-04-20 17:41:22 UTC
(In reply to comment #1)
> d) The gfc_is_simply_contiguous function needs to be refined and extended - and
> a "gfc_is_simply_noncontiguous" has to be added.

Another application for 'gfc_is_simply_noncontiguous' would be to reject invalid code like

  integer, dimension(1:5,1:5), target :: zzz
  type(c_ptr) :: ptr
  ptr = c_loc (zzz(4:,4:))

cf. http://gcc.gnu.org/ml/fortran/2013-04/msg00181.html and follow-ups.
Comment 5 Harald Anlauf 2018-12-27 20:00:49 UTC
Looking at the F2k18 draft, I see some updates and clarifications:

16.9.105 IS_CONTIGUOUS (ARRAY)
Description. Array contiguity test (8.5.7).
Class. Inquiry function.
Argument. ARRAY may be of any type. It shall be assumed-rank or an array. If it is a pointer it shall be associated.
Result Characteristics. Default logical scalar.
Result Value. The result has the value true if ARRAY has rank zero or is contiguous, and false otherwise.

[Example elided]

The clarification about rank zero is certainly helpful.

Furthermore:

8.5.7 CONTIGUOUS attribute

NOTE 1
If a derived type has only one component that is not zero-sized, it is
processor dependent whether a structure component of a contiguous array
of that type is contiguous. That is, the derived type might contain padding
on some processors.

This gives some freedom for a "conservative" implementation of the
intrinsic.
Comment 6 Harald Anlauf 2018-12-27 22:51:35 UTC
Created attachment 45292 [details]
Update of Tobias' patch to 9-trunk (except for ChangeLog)

I've tried to update Tobias' patch so that it compiles with 9-trunk
and adjusted his testcase so that it uses STOP instead of call abort ().

There's one problem left and one question.

The problem is that I'm getting one failure with the following test:

  implicit none
  real, allocatable :: a(:), b(:,:)
  integer :: k = 0

  allocate(a(5), b(10,10))
  call test (a, .true.)
  call test (b, .true.)
  call test (b(::1,::1), .true.)
  call test (b(::2,::1), .false.)
  call test (b(::1,::2), .false.)  ! This test fails currently
contains
  subroutine test (x, res)
!   type(*), dimension(..), intent(in) :: x  ! Should this be allowed?
    real,    dimension(..), intent(in) :: x
    logical,                intent(in) :: res
    k = k + 1
    if (is_contiguous (x) .eqv. res) return
    print *, "Failure of test", k
    stop "FAIL"
  end subroutine test
end program

Maybe something else needs to be updated.

Furthermore, should type(*), dimension(..) be allowed?  Opinions?
I tend to think so.  Does this need explicit coding as for e.g. SIZEOF,
or are there better ways?
Comment 7 Harald Anlauf 2019-01-02 23:12:41 UTC
Created attachment 45322 [details]
Extended Update of Tobias' patch to 9-trunk (except for ChangeLog)

I've updated Tobias' patch, adding a runtime library function to handle
assumed rank arrays (not sure if this is the right way).  This now works
for the following testcase:

program is_contiguous_2
  implicit none
  real, allocatable :: b(:,:)
  allocate(b(10,10))
  if (fail (b,          .true.) ) stop 1
  if (fail (b(::1,::1), .true.) ) stop 2
  if (fail (b(::2,::1), .false.)) stop 3
  if (fail (b(::1,::2), .false.)) stop 4
  if (fail (b(:10,:10), .true. )) stop 5
  if (fail (b(: 9,:10), .false.)) stop 6
contains
  pure logical function fail (x, expect)
!   type(*), dimension(..), intent(in) :: x  ! This should work, too
    real,    dimension(..), intent(in) :: x
    logical,                intent(in) :: expect
    fail = is_contiguous (x) .neqv. expect
  end function fail
end program

I don't know how to handle assumed type; I need help by somebody else
who is interested in pursuing this.

Thanks to whoever will pick this up.
Comment 8 Harald Anlauf 2019-01-03 21:20:40 UTC
Created attachment 45332 [details]
Corrected update of Tobias' patch to 9-trunk (except for ChangeLog)

This version corrects the previous attempt which broke the original patch.
It comes with an additional testcase that checks assumed rank/assumed type.
Comment 9 Thomas Koenig 2019-01-07 19:31:31 UTC
Author: tkoenig
Date: Mon Jan  7 19:30:28 2019
New Revision: 267657

URL: https://gcc.gnu.org/viewcvs?rev=267657&root=gcc&view=rev
Log:
2019-01-07  Thomas Koenig  <tkoenig@gcc.gnu.org>
	Harald Anlauf <anlauf@gmx.de>
	Tobias Burnus <burnus@gcc.gnu.org>

	PR fortran/45424
	* check.c (gfc_check_is_contiguous): New function.
	* expr.c (gfc_is_not_contiguous): New function.
	* gfortran.h (gfc_isym_id): Add GFC_ISYM_IS_CONTIGUOUS.
	Add prototype for gfc_is_not_contiguous.
	* intrinsic.c (do_ts29113_check): Add GFC_ISYM_IS_CONTIGUOUS.
	(add_function): Add is_contiguous.
	* intrinsic.h: Add prototypes for gfc_check_is_contiguous,
	gfc_simplify_is_contiguous and gfc_resolve_is_contiguous.
	* intrinsic.texi: Add IS_CONTIGUOUS.
	* iresolve.c (gfc_resolve_is_contiguous): New function.
	* simplify.c (gfc_simplify_is_contiguous): New function.
	* trans-decl.c (gfor_fncecl_is_contiguous0): New variable.
	(gfc_build_intrinsic_function_decl): Add it.
	* trans-intrinsic.c (gfc_conv_intrinsic_is_contiguous): New
	function.
	(gfc_conv_intrinsic_function): Handle GFC_ISYM_IS_CONTIGUOUS.

2019-01-07  Thomas Koenig  <tkoenig@gcc.gnu.org>
	Harald Anlauf <anlauf@gmx.de>
	Tobias Burnus <burnus@gcc.gnu.org>

	PR fortran/45424
	* Makefile.am: Add intrinsics/is_contiguous.c.
	* Makefile.in: Regenerated.
	* gfortran.map: Add _gfortran_is_contiguous0.
	* intrinsics/is_contiguous.c: New file.
	* libgfortran.h: Add prototype for is_contiguous0.

2019-01-07  Thomas Koenig  <tkoenig@gcc.gnu.org>
	Harald Anlauf <anlauf@gmx.de>
	Tobias Burnus <burnus@gcc.gnu.org>

	* gfortran.dg/is_contiguous_1.f90: New test.
	* gfortran.dg/is_contiguous_2.f90: New test.
	* gfortran.dg/is_contiguous_3.f90: New test.


Added:
    trunk/gcc/testsuite/gfortran.dg/is_contiguous_1.f90
    trunk/gcc/testsuite/gfortran.dg/is_contiguous_2.f90
    trunk/gcc/testsuite/gfortran.dg/is_contiguous_3.f90
    trunk/libgfortran/intrinsics/is_contiguous.c
Modified:
    trunk/gcc/fortran/ChangeLog
    trunk/gcc/fortran/check.c
    trunk/gcc/fortran/expr.c
    trunk/gcc/fortran/gfortran.h
    trunk/gcc/fortran/intrinsic.c
    trunk/gcc/fortran/intrinsic.h
    trunk/gcc/fortran/intrinsic.texi
    trunk/gcc/fortran/iresolve.c
    trunk/gcc/fortran/simplify.c
    trunk/gcc/fortran/trans-decl.c
    trunk/gcc/fortran/trans-intrinsic.c
    trunk/gcc/fortran/trans.h
    trunk/gcc/testsuite/ChangeLog
    trunk/libgfortran/ChangeLog
    trunk/libgfortran/Makefile.am
    trunk/libgfortran/Makefile.in
    trunk/libgfortran/gfortran.map
    trunk/libgfortran/libgfortran.h
Comment 10 Thomas Koenig 2019-01-07 19:41:54 UTC
Implemented, closing.

Harald, thanks a lot for picking this up again and for preparing
the patch.