Fortran 90/95 遗留代码接口错误 (#6634)
Error with interfaces (#6634) with legacy code to Fortran 90/95
我使用一个用于机械分析的科学(学术)软件已经有一段时间了。现在,代码从八十年代 (Fortran 77) 开始,以 Fortran 90/95 的 mixed/hybrid 形式到达我这里。但是,由于需要添加 MKL 等强大工具,我决定从旧的 Intel Visual Fortran 11.072(使用 VS2008)迁移到 "recent" 14.0(在 ComposerXE 2013 中)。 F77 核心编译没有问题,但我在处理变量延迟定义的子例程中遇到一些接口问题。我没有烦扰大量的例程,而是让 MWE 能够重复这些问题。
小程序复制如下,大家可以自己动手:
program main
implicit none
print *, 'Start of the program'
call mainsub
print *, 'End of the program'
pause
end program
有一个定义尺寸的模块 problem.f:
module problem
implicit none
save
integer, parameter :: size1 = 6
integer, parameter :: size2 = 3
integer, parameter :: size3 = 18
end module problem
所以,有一个调用 "first level" 子程序的主子程序:
SUBROUTINE mainsub
use problem ! here there are the dimensions defined
implicit none
! Scalars (almost)
REAL*8 :: sca01, sca02(size2), sca03
! Vectors
REAL*8 :: arr01(size1)
REAL*8 :: arr02(size1)
REAL*8 :: arr03(size3)
REAL*8 :: arr04(size1)
! Matrices
REAL*8 :: mat01(size1,size1)
REAL*8 :: mat02(size3)
! trying to trick IFORT with interface (hiding dimension)
print *, 'Calling sub11'
CALL sub11(arr01)
print *, 'Calling sub11 - end'
pause
print *, 'Calling sub12'
CALL sub12(arr02,arr03,arr04)
print *, 'Calling sub12 - end'
pause
print *, 'Calling sub13'
CALL sub13(mat01,mat02)
print *, 'Calling sub13 - end'
pause
print *, 'Calling sub14'
CALL sub14(sca01,sca02,sca03)
print *, 'Calling sub14 - end'
pause
contains
subroutine sub11(arr01)
use problem
implicit none
REAL*8, DIMENSION(:) :: arr01
print *, 'This is sub11, size arr01: ', SIZE(arr01), SHAPE(arr01)
CALL sub21(arr01)
end subroutine
end subroutine
这些是 "first level" 子程序
SUBROUTINE sub12(arr02, arr03, arr04)
use problem
implicit none
REAL*8 :: arr02(*)
REAL*8 :: arr03(size3)
REAL*8 :: arr04(*)
REAL*8 :: dummy(600)
print *, 'sub 12'
call sub22(arr02, dummy, arr04)
END SUBROUTINE
SUBROUTINE sub13(mat01,mat02)
use problem
implicit none
REAL*8 :: mat01(size1,size1)
REAL*8 :: mat02(size3,*)
print *, 'sub 13'
call sub23(mat01, mat02)
END SUBROUTINE
SUBROUTINE sub14(sca01,sca02,sca03)
use problem
implicit none
REAL*8 :: sca01, sca02(*), sca03
REAL*8 :: dummy(600)
print *, 'sub 14'
call sub24(sca01, dummy, sca03)
END SUBROUTINE
最后是 "second level" 子例程:
SUBROUTINE sub21(arr01)
use problem
implicit none
REAL*8 :: arr01(size3,size1)
print *, 'This is sub21, size arr01: ', SIZE(arr01)
END SUBROUTINE
SUBROUTINE sub22(arr02, arr03, arr04)
use problem
implicit none
REAL*8 :: arr02(size3)
REAL*8 :: arr03(size3)
REAL*8 :: arr04(size2,size3)
print *, 'sub22'
print *, SIZE(arr02)
print *, SIZE(arr03)
print *, SIZE(arr04)
END SUBROUTINE
SUBROUTINE sub23(mat01,mat02)
use problem
implicit none
REAL*8 :: mat01(size1,size2)
REAL*8 :: mat02(size1,size2,size3)
print *, 'sub 23'
print *, SHAPE(mat01), SIZE(mat01)
print *, SHAPE(mat02), SIZE(mat02)
end subroutine
SUBROUTINE sub24(sca01,sca02,sca03)
use problem
implicit none
REAL*8 :: sca01, sca02(*), sca03
print *, 'sub 24'
print *, SHAPE(sca01), SHAPE(sca03)
end subroutine
这段代码在我的 Intel Fortran 14 机器上编译正确。现在,让我们考虑可能出现的一系列情况。
常见变量不匹配
如果我将一个实际变量定义为 Real*8,而在子例程中相应的虚拟变量是 Real*4,或者混合使用 Real*8 --> Integer*8,编译器会识别出不匹配并给出错误。同样,如果我定义一个标量变量 Real sca01 并在子例程中定义它 Real sca01(*) 或 Real sca01(size1),编译器会再次识别一个是数组而另一个不是,因此它会抛出一个错误。 (错误 #6633:实际参数的类型与伪参数的类型不同。)
数组大小不匹配
如果您将一个数组定义为 arr02(size1) 并且在调用的子程序 arr02(size2) 中,只有当运行时检查错误处于活动状态并且整数 size1、size2 被声明为参数(如在模块 problem.f).
但是,我在两个定义中间放置了一个中间子程序,就像上面的 MWE 一样:
sub11 -- sub21
/
mainsub --- sub12 -- sub22
\
sub13 -- sub23
与 CONTAINS 语句(mainsub 到 sub11)的接口检查实际变量和虚拟变量的大小和维度是否一致.但是,它不会检查对 sub21 的下一次调用是否会在从接口子例程中退出时丢失对大小的跟踪。
在sub12中,通过使用假设的形状数组(*)定义,我可以根据需要更改形状和大小。当然,我有时会遇到分段错误,但即使我进行了所有运行时检查和恒定大小,也不会抛出任何错误或警告。
最后,对于 sub12,这个技巧也适用于多个维度,有时即使它不是形状假定数组,例如 mat02(这很奇怪。 ..).
因此,我有几个问题:
接口定义中带(*)和(:)的区别是什么?
if ( * ) 就像数组大小的延迟定义,为什么它不适用于标量? (假设标量是数组 1x1)
为什么它不通过子程序检查大小?
在 Intel Fortran 11 中,许多数组大小检查未完成。现在,对于 Intel Fortran 14,我遇到了很多#6633、#6634 和#8284 错误。它改变了什么?
给定这个混合 Fortran77/90/95 全景图,我应该考虑保留哪些定义,哪些不应该保留? (显然在上面用到的里面并没有进入面向对象,因为是过程式程序)
Fortran 中变量定义的哲学(主要实际原因)是什么?如果我可以 "trick" 带有程序程序的编译器(并且大小不变,没有可分配的),我想我错过了一些东西。
对于问题的长度,我深表歉意,但我是单独学习 Fortran 的,没有 CS 背景,我觉得我错过了问题的要点...
谢谢!
对于虚拟参数,dummy(*)
声明了一个假定大小的数组,dummy(:)
声明了一个假定的形状数组[并且为了完整性,dummy(some_expression)
声明了一个显式形状数组].
对于假定大小的数组:
实参必须是可以产生数组元素序列的东西,但实参的维数不需要与虚参的维数相同。
在带有伪参数的过程中,实际参数的大小不会自动知道,并且您不能调用需要该大小的伪参数的操作(例如 SIZE(dummy)
不是允许。如果过程内部由于某种原因需要实参指定的数组元素序列的大小,则需要用户单独传递。
在调用过程的作用域中不需要显式接口。 (假设大小是 Fortran 77 的一个概念,在该语言具有显式接口的概念之前。)
对于假定的形状数组:
实际参数必须与虚拟参数在等级上匹配。
在过程中,实际参数的大小和形状通过虚拟对象自动可用。
在调用该过程的任何作用域中都需要该过程的显式接口。
显式大小数组类似于假定大小数组,但在过程中数组的大小是已知的(因为它是显式声明的!)。实参指定的数组元素序列的大小必须等于或大于显式指定的大小。
作为标量变量名称的实参(不是 CHARACTER 类型,而是默认或 C_CHAR 类型)不指定数组元素序列。作为一般原则,标量不是数组。
改进了编译器查找程序错误的能力。此外,一些与错误检查相关的编译器选项可能已更改默认值。 (注意,不同的编译器在这方面有不同的诊断能力。)
哪种类型的数组声明(除了这里讨论的那些之外还有其他的)最好取决于你想做什么,但作为新代码的指南,如果对假定形状的限制不要引起问题,然后对数组使用假定的形状。
不能保证编译器会捕获所有编程错误。不同的声明是为了不同的目的。
我使用一个用于机械分析的科学(学术)软件已经有一段时间了。现在,代码从八十年代 (Fortran 77) 开始,以 Fortran 90/95 的 mixed/hybrid 形式到达我这里。但是,由于需要添加 MKL 等强大工具,我决定从旧的 Intel Visual Fortran 11.072(使用 VS2008)迁移到 "recent" 14.0(在 ComposerXE 2013 中)。 F77 核心编译没有问题,但我在处理变量延迟定义的子例程中遇到一些接口问题。我没有烦扰大量的例程,而是让 MWE 能够重复这些问题。
小程序复制如下,大家可以自己动手:
program main
implicit none
print *, 'Start of the program'
call mainsub
print *, 'End of the program'
pause
end program
有一个定义尺寸的模块 problem.f:
module problem
implicit none
save
integer, parameter :: size1 = 6
integer, parameter :: size2 = 3
integer, parameter :: size3 = 18
end module problem
所以,有一个调用 "first level" 子程序的主子程序:
SUBROUTINE mainsub
use problem ! here there are the dimensions defined
implicit none
! Scalars (almost)
REAL*8 :: sca01, sca02(size2), sca03
! Vectors
REAL*8 :: arr01(size1)
REAL*8 :: arr02(size1)
REAL*8 :: arr03(size3)
REAL*8 :: arr04(size1)
! Matrices
REAL*8 :: mat01(size1,size1)
REAL*8 :: mat02(size3)
! trying to trick IFORT with interface (hiding dimension)
print *, 'Calling sub11'
CALL sub11(arr01)
print *, 'Calling sub11 - end'
pause
print *, 'Calling sub12'
CALL sub12(arr02,arr03,arr04)
print *, 'Calling sub12 - end'
pause
print *, 'Calling sub13'
CALL sub13(mat01,mat02)
print *, 'Calling sub13 - end'
pause
print *, 'Calling sub14'
CALL sub14(sca01,sca02,sca03)
print *, 'Calling sub14 - end'
pause
contains
subroutine sub11(arr01)
use problem
implicit none
REAL*8, DIMENSION(:) :: arr01
print *, 'This is sub11, size arr01: ', SIZE(arr01), SHAPE(arr01)
CALL sub21(arr01)
end subroutine
end subroutine
这些是 "first level" 子程序
SUBROUTINE sub12(arr02, arr03, arr04)
use problem
implicit none
REAL*8 :: arr02(*)
REAL*8 :: arr03(size3)
REAL*8 :: arr04(*)
REAL*8 :: dummy(600)
print *, 'sub 12'
call sub22(arr02, dummy, arr04)
END SUBROUTINE
SUBROUTINE sub13(mat01,mat02)
use problem
implicit none
REAL*8 :: mat01(size1,size1)
REAL*8 :: mat02(size3,*)
print *, 'sub 13'
call sub23(mat01, mat02)
END SUBROUTINE
SUBROUTINE sub14(sca01,sca02,sca03)
use problem
implicit none
REAL*8 :: sca01, sca02(*), sca03
REAL*8 :: dummy(600)
print *, 'sub 14'
call sub24(sca01, dummy, sca03)
END SUBROUTINE
最后是 "second level" 子例程:
SUBROUTINE sub21(arr01)
use problem
implicit none
REAL*8 :: arr01(size3,size1)
print *, 'This is sub21, size arr01: ', SIZE(arr01)
END SUBROUTINE
SUBROUTINE sub22(arr02, arr03, arr04)
use problem
implicit none
REAL*8 :: arr02(size3)
REAL*8 :: arr03(size3)
REAL*8 :: arr04(size2,size3)
print *, 'sub22'
print *, SIZE(arr02)
print *, SIZE(arr03)
print *, SIZE(arr04)
END SUBROUTINE
SUBROUTINE sub23(mat01,mat02)
use problem
implicit none
REAL*8 :: mat01(size1,size2)
REAL*8 :: mat02(size1,size2,size3)
print *, 'sub 23'
print *, SHAPE(mat01), SIZE(mat01)
print *, SHAPE(mat02), SIZE(mat02)
end subroutine
SUBROUTINE sub24(sca01,sca02,sca03)
use problem
implicit none
REAL*8 :: sca01, sca02(*), sca03
print *, 'sub 24'
print *, SHAPE(sca01), SHAPE(sca03)
end subroutine
这段代码在我的 Intel Fortran 14 机器上编译正确。现在,让我们考虑可能出现的一系列情况。
常见变量不匹配 如果我将一个实际变量定义为 Real*8,而在子例程中相应的虚拟变量是 Real*4,或者混合使用 Real*8 --> Integer*8,编译器会识别出不匹配并给出错误。同样,如果我定义一个标量变量 Real sca01 并在子例程中定义它 Real sca01(*) 或 Real sca01(size1),编译器会再次识别一个是数组而另一个不是,因此它会抛出一个错误。 (错误 #6633:实际参数的类型与伪参数的类型不同。)
数组大小不匹配 如果您将一个数组定义为 arr02(size1) 并且在调用的子程序 arr02(size2) 中,只有当运行时检查错误处于活动状态并且整数 size1、size2 被声明为参数(如在模块 problem.f).
但是,我在两个定义中间放置了一个中间子程序,就像上面的 MWE 一样:
sub11 -- sub21
/
mainsub --- sub12 -- sub22
\
sub13 -- sub23
与 CONTAINS 语句(mainsub 到 sub11)的接口检查实际变量和虚拟变量的大小和维度是否一致.但是,它不会检查对 sub21 的下一次调用是否会在从接口子例程中退出时丢失对大小的跟踪。
在sub12中,通过使用假设的形状数组(*)定义,我可以根据需要更改形状和大小。当然,我有时会遇到分段错误,但即使我进行了所有运行时检查和恒定大小,也不会抛出任何错误或警告。
最后,对于 sub12,这个技巧也适用于多个维度,有时即使它不是形状假定数组,例如 mat02(这很奇怪。 ..).
因此,我有几个问题:
接口定义中带(*)和(:)的区别是什么?
if ( * ) 就像数组大小的延迟定义,为什么它不适用于标量? (假设标量是数组 1x1) 为什么它不通过子程序检查大小?
在 Intel Fortran 11 中,许多数组大小检查未完成。现在,对于 Intel Fortran 14,我遇到了很多#6633、#6634 和#8284 错误。它改变了什么?
给定这个混合 Fortran77/90/95 全景图,我应该考虑保留哪些定义,哪些不应该保留? (显然在上面用到的里面并没有进入面向对象,因为是过程式程序)
Fortran 中变量定义的哲学(主要实际原因)是什么?如果我可以 "trick" 带有程序程序的编译器(并且大小不变,没有可分配的),我想我错过了一些东西。
对于问题的长度,我深表歉意,但我是单独学习 Fortran 的,没有 CS 背景,我觉得我错过了问题的要点...
谢谢!
对于虚拟参数,
dummy(*)
声明了一个假定大小的数组,dummy(:)
声明了一个假定的形状数组[并且为了完整性,dummy(some_expression)
声明了一个显式形状数组].对于假定大小的数组:
实参必须是可以产生数组元素序列的东西,但实参的维数不需要与虚参的维数相同。
在带有伪参数的过程中,实际参数的大小不会自动知道,并且您不能调用需要该大小的伪参数的操作(例如
SIZE(dummy)
不是允许。如果过程内部由于某种原因需要实参指定的数组元素序列的大小,则需要用户单独传递。在调用过程的作用域中不需要显式接口。 (假设大小是 Fortran 77 的一个概念,在该语言具有显式接口的概念之前。)
对于假定的形状数组:
实际参数必须与虚拟参数在等级上匹配。
在过程中,实际参数的大小和形状通过虚拟对象自动可用。
在调用该过程的任何作用域中都需要该过程的显式接口。
显式大小数组类似于假定大小数组,但在过程中数组的大小是已知的(因为它是显式声明的!)。实参指定的数组元素序列的大小必须等于或大于显式指定的大小。
作为标量变量名称的实参(不是 CHARACTER 类型,而是默认或 C_CHAR 类型)不指定数组元素序列。作为一般原则,标量不是数组。
改进了编译器查找程序错误的能力。此外,一些与错误检查相关的编译器选项可能已更改默认值。 (注意,不同的编译器在这方面有不同的诊断能力。)
哪种类型的数组声明(除了这里讨论的那些之外还有其他的)最好取决于你想做什么,但作为新代码的指南,如果对假定形状的限制不要引起问题,然后对数组使用假定的形状。
不能保证编译器会捕获所有编程错误。不同的声明是为了不同的目的。