[英]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
是合法的,因為result
與next
字段一樣淺,並且一開始不允許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.