簡體   English   中英

Ada 2005,訪問類型和局部變量轉義分析

[英]Ada 2005, access types, and local variable escape analysis

因此,當在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來輕松解決。 通過使用池訪問,我告訴編譯器指針不能指向堆棧對象。 很好

但是,然后我嘗試使用命名的通用訪問權限:

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 ?)

盡管一般而言 ,常規訪問類型可以指向非堆對象(全局對象和堆棧對象),但是有些規則會阻止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,則不能接受比級別“更深”(嵌套更多)的訪問值。每個匿名訪問都聲明一個新類型,其類型通常取決於聲明的位置。 在使用匿名訪問類型的示例中, 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中的復雜性與匿名訪問參數,匿名訪問判別以及其他一些與示例無關的特殊情況有關。

一種解決方案是稍微重組代碼。 使用擴展收益可以為您做到:

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;

盡管可訪問性檢查可能會令人沮喪,但我對它們有一個總體良好的看法:我們希望懸掛指針。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM