Pointers Ada 2005,访问类型和局部变量转义分析

Pointers Ada 2005,访问类型和局部变量转义分析,pointers,ada,Pointers,Ada,因此,当在Ada编译器(结果是Ada 2005)上使用访问类型时,我尝试以下经典示例: type Node is record next: access Node; end record; function reverselist(input: access Node) return access Node is result: access Node := null; this: access Node := input; next: access Node := null;

因此,当在Ada编译器(结果是Ada 2005)上使用访问类型时,我尝试以下经典示例:

type Node is record
  next: access Node;
end record;

function reverselist(input: access Node) return access Node is
  result: access Node := null;
  this: access Node := input;
  next: access Node := null;
begin
  while this /= null loop
    next := this.next;
    this.next := result; -- [*]
    result := this;
    this := next;
  end loop;
  return result; -- [*]
end;
这两行星号会产生编译错误,因为Ada2005的分析认为我可能泄漏了一个本地指针。(我知道Ada 2012有更好的分析,我会接受这一点。但是,我没有Ada 2012编译器。)

好的,这可以通过使用命名池访问而不是匿名
访问节点
轻松解决;通过使用池访问,我告诉编译器指针不能引用堆栈对象。那很好

但是,我尝试使用命名的常规访问:

一般访问类型可以指向堆栈对象和堆对象;那么,为什么编译器拒绝第二个示例的原因与拒绝第一个示例的原因相同呢

(我也有点惊讶,匿名
access integer
变量似乎以一般访问语义结尾。为什么我不使用
access all integer
进行匿名一般访问?

尽管一般访问类型可以指向非堆对象(全局和堆栈),有一些规则会阻止
列表
指向堆栈。在这种情况下,由于未在任何子程序中声明
List
,因此无法使其指向堆栈变量:

function reverselist(input: List) return access Node is
   result: List := null;
   this: List := input;
   next: List := null;
   Dummy : aliased Node;
begin
   result := Dummy'Access;  -- a compile-time error would occur at this point
   while this /= null loop
      next := this.next;
      this.next := result;
      result := this;
      this := next;
   end loop;
   return result;
end;
由于规则“确保”列表永远不能指向堆栈变量,因此编译器无需拒绝任何其他语句。(我在引号中加上“确保”,因为您可以使用
'Unchecked\u Access
绕过它们,以及
Unchecked\u Conversion
或其他一些绕过类型系统的方法。)

如果在子程序内声明了访问类型,则它可以指向该子程序内的堆栈变量。这是因为该访问类型的对象不能在子程序之外声明,因此在子程序完成后,该类型的任何对象都不可能存在(除非使用未检查的技巧)。它还可以指向全局对象或堆对象。但它不能指向嵌套子程序中的堆栈变量

procedure Proc is
    type Node is ... 
    type Acc is access all Node;
    N1 : aliased Node;

    procedure Nested_Proc is
        N2 : aliased Node;
        X : Acc;
    begin
        X := N1'Access;   -- legal
        X := N2'Access;   -- illegal.  N2 is too deep for this access type.
    end;
因此,在使用
'Access
时,编译器确保不能创建对不再存在的变量的悬空引用

所有这些都受可访问性规则的约束,这些规则在RM 3.10.2中有明确说明。(我在开玩笑。3.10.2很难理解,在那些试图阅读它的人中,它往往会引起偏头痛和边缘精神错乱。)

编辑:继续你的评论:基本上,我不认为编译器在做任何类型的“健壮分析”。大多数规则实际上比我说的要简单。在Ada2005中,大多数访问类型(包括大多数匿名类型)都有一个“级别”,该级别取决于类型的声明位置。如果访问对象的类型是级别L,则它不能接受级别比L“更深”(嵌套更多)的访问值。每个匿名访问都会声明一个新类型,其级别通常取决于声明的位置。在使用匿名访问类型的示例中,
next
记录字段的类型是库级别的,因为它在任何过程之外;
结果的类型比这个类型更深,因为它在一个过程中
this.next:=result
是非法的,因为
result
越深,可能会指向堆栈变量,而
this.next
的级别越浅,因此这是不允许的。对于命名的访问类型案例,
this.next:=result
是合法的,因为
result
next
字段一样浅,并且
result
首先不允许指向堆栈变量。在Ada 2012中,我认为这是不同的,因为匿名访问对象必须在运行时跟踪它们指向的对象的级别。所以
this.next:=result
是合法的,但是(我认为)如果
result
指向堆栈变量,则会在运行时引发异常。在Ada 2012中,这些访问对象将需要一些额外的数据来允许它们进行此检查;在ADA2005中,它们可能都只是简单的指针。(对于2012年《美国残疾人法案》的一些修改,我可能是错的。)但希望这不会太复杂。3.10.2中的复杂情况与匿名访问参数、匿名访问判别式以及您的示例所不涉及的其他一些特殊情况有关。

尽管一般的访问类型可以指向非堆对象(全局和堆栈),有一些规则会阻止
列表
指向堆栈。在这种情况下,由于未在任何子程序中声明
List
,因此无法使其指向堆栈变量:

function reverselist(input: List) return access Node is
   result: List := null;
   this: List := input;
   next: List := null;
   Dummy : aliased Node;
begin
   result := Dummy'Access;  -- a compile-time error would occur at this point
   while this /= null loop
      next := this.next;
      this.next := result;
      result := this;
      this := next;
   end loop;
   return result;
end;
由于规则“确保”列表永远不能指向堆栈变量,因此编译器无需拒绝任何其他语句。(我在引号中加上“确保”,因为您可以使用
'Unchecked\u Access
绕过它们,以及
Unchecked\u Conversion
或其他一些绕过类型系统的方法。)

如果在子程序内声明了访问类型,则它可以指向该子程序内的堆栈变量。这是因为该访问类型的对象不能在子程序之外声明,因此在子程序完成后,该类型的任何对象都不可能存在(除非使用未检查的技巧)。它还可以指向全局对象或堆对象。但它不能指向嵌套子程序中的堆栈变量

procedure Proc is
    type Node is ... 
    type Acc is access all Node;
    N1 : aliased Node;

    procedure Nested_Proc is
        N2 : aliased Node;
        X : Acc;
    begin
        X := N1'Access;   -- legal
        X := N2'Access;   -- illegal.  N2 is too deep for this access type.
    end;
因此,在使用
'Access
时,编译器确保不能创建对不再存在的变量的悬空引用

所有这些都受可访问性规则的约束,这些规则在RM 3.10.2中有明确说明。(我很高兴