【问题标题】:Ada 2005, access types, and local variable escape analysisAda 2005,访问类型和局部变量转义分析
【发布时间】:2014-04-19 11:22:10
【问题描述】:

因此,当在 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;

这两条带星号的行会产生编译错误,因为 Ada 2005 的分析认为我可能会泄漏本地指针。 (我知道 Ada 2012 有更好的分析并会接受这一点。但是,我没有 Ada 2012 编译器。)

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

不过,后来我尝试使用命名的 general 访问:

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

function reverselist(input: List) return access Node is
   result: List := null;
   this: List := input;
   next: List := null;
begin
   while this /= null loop
      next := this.next;
      this.next := result;
      result := this;
      this := next;
   end loop;
   return result;
end;

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

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

【问题讨论】:

  • 只是想知道:什么 Ada 编译器?获取 Ada 2012 编译器很容易,只需从 AdaCore Libre 站点下载 GNAT GPL 2013。
  • 我实际上在使用ideone.com 的在线代码sn-p 执行环境,所以我无法控制它使用的编译器。我知道自己买一个很容易,但现在,就我所做的而言,在线的更容易。

标签: pointers ada


【解决方案1】:

虽然一般一般访问类型可以指向非堆对象(全局和堆栈),但有一些规则会阻止List 指向堆栈。在这种情况下,由于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;

由于规则“确保”List 永远不能指向堆栈变量,因此编译器无需拒绝任何其他语句。 (我将“确保”放在引号中,因为您可以使用 'Unchecked_Access 绕过它们,以及使用 Unchecked_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 很难理解,并且容易让那些试图阅读它的人产生偏头痛和边缘精神错乱。)

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

【讨论】:

  • 啊 - 因为两个等效的命名访问类型是不同的且不兼容的,所以编译器能够进行比匿名访问类型更强大的分析,它无法区分?
  • (顺便说一句,您的代码 sn-p 中有错字 - 您分配给 List,这是一个类型名称。而且 Ada RM 对我没有任何恐惧:我曾经尝试阅读C++ 规范!他们告诉我脑损伤有一天会修复......)
  • @DavidGiven 我不确定您的第一条评论是什么意思。查看我的编辑。
  • 是的,这是有道理的(尽管我还是有点失望)。我想我现在可以解决这个问题了 --- 谢谢。
【解决方案2】:

一种解决方案是稍微重构一下代码;使用扩展返回可能会为您做到这一点:

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

function reverse_list(input: access Node) return access Node is
  this: access Node := input;
  next: access Node := null;
begin
  Return result: access Node := null do
    while this /= null loop
      next := this.next;
      this.next := result;
      result := this;
      this := next;
    end loop;
  end return;
end reverse_list;

虽然可访问性检查可能会令人沮丧,但我对它们的总体看法是好的:我们确实想要悬空指针。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多