Ifort: move_allo c () для атрибута указателя, намерение (в) вызывает ошибку - PullRequest
4 голосов
/ 29 января 2020

Я хотел бы расширить выделяемый атрибут структуры, и MOVE_ALLOC() кажется самым чистым способом сделать это. Поэтому я создал подпрограмму, используя Pointer, Intent(in), указывающий на структуру в качестве аргумента, и попытался вызвать:

Type(STRUCT1), Pointer, Intent(in)  :: str1
...
call MOVE_ALLOC(TO= str1%arrayofint , FROM= temparray)

, где str1 - указатель на структуру, а arrayofint - атрибут, который должен быть расширен , Смотрите подпрограмму LOC_extendsecond() следующего примера кода. Компилятор ifort возвращает следующую ошибку:

Source1.f90(91): error #7999: The FROM or TO arguments of a MOVE_ALLOC reference must not be INTENT(IN).   [MOVE_ALLOC]

Как будто str1%arrayofint были Intent(in). Помня, что указатель str1 равен Intent(in), являются ли атрибуты остроконечной структуры str1%arrayofint, которые следует рассматривать как Intent(in)?

Чтобы исследовать проблему, я попытался не использовать MOVE_AllOC() и обнаружил, что освобождение или выделение str1%arrayofint в подпрограмме не вызывает никаких ошибок или предупреждений от ifort. Смотрите подпрограмму LOC_extendfirst() примера кода.

Мой коллега предложил обходной путь (спасибо Лу c!): Создана локальная копия указателя, и можно вызывать MOVE_AllOC(), используя эту локальную копию указателя, без вызова ifort ошибка. См. Подпрограмму LOC_extendthird() примера кода.

Type(STRUCT1), Pointer, Intent(in)  :: str1
Type(STRUCT1), Pointer  :: str2
str2=>str1
...
call MOVE_ALLOC(TO= str2%arrayofint , FROM= temparray)

Вот пример кода:

Module Source1

    Implicit None

    Public :: STRUCT1

  Private

  Type STRUCT1
      Integer, dimension(:), allocatable    :: arrayofint 

  End Type STRUCT1

    Contains

    Subroutine newstruct1(str1,ier,errmsg)

        Type(STRUCT1), Pointer, Intent(inout)  :: str1
        Integer, Intent(out) :: ier
        character(len=256), Intent(out) :: errmsg

        ier=0
        allocate(str1,stat=ier, errmsg=errmsg)
        if( ier>0) return

        allocate(str1%arrayofint(2),stat=ier, errmsg=errmsg)
        if( ier>0) return

    End Subroutine newstruct1

    Subroutine LOC_extendfirst(str1,targetsize,ier,errmsg)
        Type(STRUCT1), Pointer, Intent(in)  :: str1
        Integer, Intent(out)  :: ier
        character(len=256), Intent(out)  :: errmsg
        Integer, Intent(in)  :: targetsize 

        Integer,dimension(1) :: shp
        Integer :: newsize , formersize
        Integer, dimension(:), allocatable :: temparray

        ier=0

        shp=shape(str1%arrayofint)
        formersize=shp(1)
        if (targetsize .GT. formersize) then
            newsize=MAX(targetsize,2*formersize)
            allocate(temparray(newsize),stat=ier, errmsg=errmsg)
            if( ier>0) then; return ; endif
            temparray(1:formersize)=str1%arrayofint
            allocate(temparray(formersize),stat=ier, errmsg=errmsg)
            if( ier>0) then; return ; endif 
            temparray=str1%arrayofint
            if(allocated(str1%arrayofint)) deallocate(str1%arrayofint)
            allocate(str1%arrayofint(newsize),stat=ier, errmsg=errmsg)
            if( ier>0) then; return ; endif  
            str1%arrayofint(1:formersize)=temparray
            if(allocated(temparray)) deallocate(temparray)
        endif

  End Subroutine LOC_extendfirst

      Subroutine LOC_extendsecond(str1,targetsize,ier,errmsg)
          Type(STRUCT1), Pointer, Intent(in)  :: str1
          Integer, Intent(out)  :: ier
          character(len=256), Intent(out)  :: errmsg
          Integer, Intent(in)  :: targetsize 

          Integer,dimension(1) :: shp
          Integer :: newsize , formersize
          Integer, dimension(:), allocatable :: temparray

          ier=0

          shp=shape(str1%arrayofint)
          formersize=shp(1)
          if (targetsize .GT. formersize) then 
               newsize=MAX(targetsize,2*formersize)
               allocate(temparray(newsize),stat=ier, errmsg=errmsg)
               if( ier>0) then; return ; endif
               temparray(1:formersize)=str1%arrayofint
               ! TODO uncomment the following line to get error from ifort
               call MOVE_ALLOC(TO= str1%arrayofint , FROM= temparray)
          endif

  End Subroutine LOC_extendsecond

  Subroutine LOC_extendthird(str1,targetsize,ier,errmsg)
      Type(STRUCT1), Pointer, Intent(in)  :: str1
      Integer, Intent(out)  :: ier
      character(len=256), Intent(out)  :: errmsg
      Integer, Intent(in)  :: targetsize 

      Integer,dimension(1) :: shp
      Integer :: newsize , formersize
      Integer, dimension(:), allocatable :: temparray

      Type(STRUCT1), Pointer  :: str2
      ier=0

      str2=>str1
      shp=shape(str2%arrayofint)
      formersize=shp(1)
      if (targetsize .GT. formersize) then 
          newsize=MAX(targetsize,2*formersize)
          allocate(temparray(newsize),stat=ier, errmsg=errmsg)
          if( ier>0) then; return ; endif
          temparray(1:formersize)=str1%arrayofint
          call MOVE_ALLOC(TO= str2%arrayofint , FROM= temparray)

      endif

  End Subroutine LOC_extendthird

  End Module Source1

Вызов ifort 19.0.2.190 IA32 на windows с использованием ifort /nologo /debug:full /Od /debug-parameters:all /warn:unused /warn:truncated_source /warn:uncalled /warn:interfaces /Qsave /traceback /check:pointer /check:bounds /check:uninit /libs:static /threads /dbglibs /c /Qm32 "Source1.f90" производит ошибка.

Наоборот, использование gfortran из g cc 6.3.0 на Debian с использованием gfortran -c Source1.f90 -Wall не приводит к ошибке: отображает только предупреждения о функции not используется.

Я довольно новичок ie относительно Фортрана. Я знаю, что указатели Fortran обертывают гораздо больше данных, чем указатель C, поэтому мне интересно, корректно ли изменение атрибута str1%arrayofint, поскольку str1 является Pointer, Intent(in), точно так же, как изменение str1-> arrayofint в функции правильно Указатель str1 передается по значению.

Как устранить разницу в поведении между ифортом и гфортраном? Является ли ifort правильным, поскольку он сообщает об ошибке в данной ситуации? Почему ifort считает str1%arrayofint Intent(in), как если бы str1 было Type(STRUCT1), Intent(in), а не Type(STRUCT1), Pointer, Intent(in)? Является ли введение локальной копии указателя, как показано в LOC_extendthird(), наиболее подходящим способом смягчения проблемы?

1 Ответ

4 голосов
/ 30 января 2020

Для фиктивного аргумента указателя атрибут intent(in) означает, что указатель не должен появляться в так называемом контексте ассоциации указателей . В общем, это означает, что вам не разрешено (потенциально) изменять указатель ассоциации фиктивного аргумента. Вам разрешено изменить значение цели этого указателя.

Для фиктивного аргумента указателя атрибут intent(in) не "каскадно" относится к подобъектам аргумента (например, в этом случае компоненту arrayofint): компонент arrayofint цели str1 не имеет атрибута intent(in).

В ссылке, подобной str1%arrayofint с указателем str1, это ссылка на компонент arrayofint цели из str1. Даже если атрибут intent(in) действительно применяется к подобъектам фиктивного аргумента указателя, объект, на который ссылается str1%arrayofint, не является подобъектом str1.

ifort неверно думать, что такой объект имеет intent(in) атрибут. Вы нашли правильный способ обойти такие недостатки в ifort. Вы должны сообщить об этой уязвимости в Intel.

Наконец, могут быть более эффективные способы решения проблемы изменения размера компонента без использования фиктивных аргументов указателя, но я не буду рассматривать их в этом ответе.

...