Author:halw

Date:2009-05-14T00:20:57.000000Z


git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@215 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
halw
2009-05-14 00:20:57 +00:00
parent 491ac51b82
commit 7aa2ee9e67
8 changed files with 589 additions and 41 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1,3 @@
title=VoidSafeVEVI1
author=halw
path=content/voidsafevevi1

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,3 @@
title=VoidSafeVJAR1
author=halw
path=content/voidsafevjar1

View File

@@ -0,0 +1,242 @@
[[Property:title|Converting existing software to void-safety]]
[[Property:weight|6]]
[[Property:uuid|eb901272-d405-2277-005d-e37275b9baa4]]
{{underconstruction}}
If you have been using Eiffel for some time, you may be maintaining systems which were developed in the time before Eiffel became void-safe. If that's the case, then you will probably want to make those systems void-safe.
In this section we will use the experience of converting a set of simple (but not too simple) legacy Eiffel classes to show the types of issues that you may encounter, and how to deal with them.
The system we will use consists of these classes:
{| border="1"
|-
! Class name
! Description
|-
| APPLICATION
| Simple root class containing declarations of types NVP and NVP_LIST
|-
| NVP
| Class modeling name/value pairs of type STRING
|-
| NVP_LIST
| Class modeling a list of NVP's with specialized behavior
|}
=Conversion considerations=
==To redesign or not to redesign==
As you sift through your existing software during the void-safe conversion, you may not get very far before you see things that you wish had been done differently. This occurs often during reviews of existing systems, not just because of the introduction of void-safety. You will see some such cases in the routines of legacy classes below.
=Conversion process=
==Enable full class checking==
First make sure your project will compile correctly under the configuration of EiffelStudio that you intend to use to convert to void-safe.
Then set the project setting '''Full Class Checking''' to '''True'''. Do a ''clean'' compile of your system. To do this shut down and restart EiffelStudio. When the project selection dialog appears, select your project, then next to '''Action:''' select '''Compile''' in the drop-down, and check '''Clean'''.
Full class checking will analyze your classes to make sure that in cases of inheritance features of the parent classes are recheck for validity in the heirs.
Here's an example of the kind of error you might expect when compiling with full class checking:
[[Image:VGCC error]]
The situation here is that the feature <code>split</code> has been inherited by our class <code>NVP_LIST</code>. Feature <code>split</code> includes code to create and attach feature <code>sublist</code> which is typed <code>attached like Current</code> which in this case means <code>attached NVP_LIST</code>. To do this creation, <code>split</code> uses a creation procedure <code>make_sublist</code>.
Now here's the rub: <code>NVP_LIST</code> has not named <code>make_sublist</code> as a creation procedure:
<code>
create
make, make_from_string, make_from_file_named
</code>
So if we go to the <code>create</code> part of <code>NVP_LIST</code> and add <code>make_sublist</code> to its list of creation procedures, this will fix the problem:
<code>
create
make, make_from_string, make_from_file_named, make_sublist
</code>
So, fix any problems that arise out of turning on full class checking.
==Enable other project settings==
The second step in conversion of existing software is to change the values of the other void-safe related project settings and use the void-safe configurations for any delivered libraries and precompilations.
In the project settings for the target in which you are working:
# Set '''Void safe''' to either '''Complete void safety''' or '''On demand void safety'''.
# Set '''Are types attached by default?''' to '''True'''.
{{note|Remember that during a transitional period starting with V6.4, there will be multiple versions of the configuration files for Eiffel libraries and precompiles. For example, base.ecf (non-void-safe) and base-safe.ecf (void-safe). It is expected that in the future there will only be one configuration file (e.g., base.ecf) that will work with both void-safe and non-void-safe client software. }}
If necessary, remove Eiffel libraries and any precompiled library that your project uses and re-add them with their void-safe configuration files. Because you've set your target to void-safety, when you click '''Add Library''', you should see only void-safe configurations by default.
You will see a check box on the dialog that you can uncheck if you want to see all available library configurations:
[[Image:VoidSafeAddLibraryDialog]]
Now do a clean compile.
If you've replaced a precompiled library that you have not already built, EiffelStudio will offer to build it for you on the fly:
[[Image:VoidSafePrecompileOffer]]
Now you should see error messages representing any situation in your project in which the compiler determines that it cannot guarantee void safety.
This is what our legacy system produced:
[[Image:VoidSafeErrorList]]
==Fix the issues==
Next you fix the problems that the compiler discovered. The compiler errors concerning void-safety typically will be of three varieties.
# VEVI: violations of the '''Variable initialization rule'''. An attached variable is not '''properly set'''.
# VUTA: violations of the '''Target rule'''. The target of a feature call is not attached.
# VJAR (and other related codes): violations of attached status considered in conformance. The attachment status of the source of an assignment (or an argument to a feature call) is not compatible with that of the target of the assignment (or the formal argument).
Let's look at some specific cases and how fixing them unfolds.
[[Image:VoidSafeVEVI1]]
There are two errors like this in our legacy system. They are probably the most obvious and easiest cases to handle.
<code>
feature {NONE} -- Initialization
make
-- Run application.
do
...
end
feature -- Access
my_nvp: NVP
-- NVP for testing
my_nvp_list: NVP_LIST
-- NVP_LIST for testing
</code>
Here attribute declarations for <code>my_nvp</code> and <code>my_nvp_list</code> are made. These are assumed to be attached because of the project setting. But the create routine <code>make</code> fails to create objects and attach them. So by adding those creations, as shown below, the compiler is satisfied.
<code>
make
-- Run application.
do
create my_nvp.make ("Hello", "World")
create my_nvp_list.make
...
end
</code>
In second case, there is also an Initialization rule violation (VEVI), this time on <code>Result</code>, in this routine:
<code>
at_first (nm: STRING): NVP is
-- The first found NVP with name matching nm.
-- Or Void if not found
require
nm_valid: nm /= Void and then not nm.is_empty
local
tc: CURSOR
do
tc := cursor
start
name_search (nm)
if not exhausted then
Result := item
end
go_to (tc)
ensure
index_unchanged: index = old index
end
</code>
Here we cannot just ensure that <code>Result</code> is attached, because, as indicated by the header comment, <code>Result</code> is allowed to be void by design.
So the least impact to this routine will be to declare its type as <code>detachable</code>:
<code>
at_first (nm: STRING): detachable NVP is
-- The first found NVP with name matching nm.
-- Or Void if not found
</code>
The same change is made in other routines that can return void by design.
The change to <code>at_first</code> satisfies the VEVI issue in <code>at_first</code>, but it introduces a previously unseen conformance issue (VJAR) in a routine <code>value_at_first</code>:
[[Image:VoidSafeVJAR1]]
<code>value_at_first</code> looks like this:
<code>
value_at_first (nm: STRING): detachable STRING is
-- Value from first found NVP with name matching nm
-- Or Void of not found
require
nm_valid: nm /= Void and then not nm.is_empty
local
tn: NVP
do
tn := at_first (nm)
if tn /= Void then
Result := tn.value
end
end
</code>
The problem is that the local variable <code>tn</code> is declared as <code>attached</code>, but we know that now the result of <code>at_first</code> is detachable, making this assignment invalid:
<code>
tn := at_first (nm)
</code>
Here the '''attached syntax''' can fix the problem and streamline the routine:
<code>
value_at_first (nm: STRING): detachable STRING is
-- Value from first found NVP with name matching nm
-- Or Void of not found
require
nm_valid: nm /= Void and then not nm.is_empty
do
if attached at_first (nm) as tn then
Result := tn.value
end
end
</code>
In this version <code>tn</code> need not be declared as a local variable. Remember that the attached syntax provides a fresh local variable, if the expression is not void.
==Code patterns==
===CAPs===
===The '''attribute''' keyword===
===Using generic classes===
===Using '''check''' blocks===
==Transitioning steps==

View File

@@ -0,0 +1,73 @@
[[Property:title|Creating a new void-safe project]]
[[Property:weight|2]]
[[Property:uuid|92cea2e9-b094-6380-2c5d-1cd1eb3038b4]]
{{underconstruction}}
=Creating a new void-safe project=
Now that we've been introduced to the Eiffel void-safe facilities, let's look at what it takes to set up a new void-safe software project. Here we'll look at the void-safety related project settings and how the can be used. Then we'll look deeper into the use of some of the void-safe tools.
==Project settings for void-safe projects==
There are three project settings that are related to void-safety. These settings can be set with great granularity throughout your project to allow you maximum flexibility, particularly when including classes or libraries that are non-void-safe or that have been converted to void-safety, but must do double duty in the void-safe and non-void-safe worlds.
===The ''"Void-safe"'' setting===
The '''Void-safe''' setting determines whether and how the Eiffel compiler checks your project against the void-safe related validity rules.
This is the essential void-safe project setting. It can assume one of three values:
# '''No void safety''': No checking against any of the void-safety validity rules.
# '''On demand void safety''': Validity rules are selectively checked. Attachment status (VJAR/VBAR and related) is taken into account when performing conformance checks, but initialization rule (VEVI) and the target rule (VUTA) are checked only for attached entities and attached call targets -- i.e., detachable cases are not checked.
# '''Complete void safety''': Complete checking against all void-safety validity rules.
So, for a new void-safe project, you would want to set this option to either '''Complete void safety''' or '''On demand void safety'''.
===The ''"Are types attached by default?"'' setting===
It is this setting that tells the compiler how to treat declarations which specify neither the <code>detachable</code> keyword nor the <code>attached</code> keyword, for example:
<code>
x: T
</code>
A value of '''True''' will instruct the compiler to treat <code>x</code> as if it were declared:
<code>
x: attached T
</code>
A value of '''False''', and <code>x</code> will be viewed as if it were:
<code>
x: detachable T
</code>
In a new project, ideally all of your declarations would be of attached types. But of course there are some occasions, for various reasons, that you must or should use detachable types.
So, for a new void-safe project, it is recommended that a value of '''True''' is used.
===The ''"Full class checking"'' setting===
This setting instructs the compiler to recheck inherited features in descendant classes.
'''Full class checking''' should always be set to '''True''' when compiling void-safe code.
==Void-safe libraries==
As of EiffelStudio version 6.4, the majority of the libraries distributed with EiffelStudio are void-safe.
During a period of transition, there will be different Eiffel configuration files (.ecf's) for non-void-safe and void-safe projects. If you have set the '''Void-safe''' setting to check for void-safety, then when you add a library to your project in EiffelStudio, you will see the void-safe configurations by default. After the transition period, it is expected that there will be only one version of the configuration files for each library. The single configuration files will serve both non-void-safe and void-safe projects.
==Using non-void-safe libraries==
==Using the ''attribute'' keyword carefully==
The keyword <code>attribute</code> is should be used with some care. You might be tempted to think that it would be convenient or add an extra element of safety to use self-initializing attributes widely. And in a way, you would be correct. But you should also understand that there is a price to pay for using self-initializing attributes and stable attributes. It is that upon every access, an evaluation of the state of the attribute must be made. So, as a general rule, you should avoid using self-initializing attributes only for the purpose of lazy initialization.
==More about CAPs==
==More about the ''attached syntax''==
==Stable attributes==

View File

@@ -22,46 +22,5 @@ The result is that we must consider certain questions:
# What do I need to know to produce standard Eiffel software?
# What do I need to know to convert my existing systems to be standard?
Let's try to answer those questions.
==Background==
The primary focus of Eiffel is on software quality. Void-safety, like static typing, is another facility for improving software quality. Void-safe software is protected from run time errors caused by calls to void references, and therefore will be more reliable than software in which calls to void targets can occur. The analogy to static typing is a useful one. In fact, void-safe capability could be seen as an extension to the type system, or a step beyond static typing, because the mechanism for ensuring void-safety is integrated into the type system.
===Static typing===
You know that static typing eliminates a whole class of software failures. This is done by making an assurance at compile time about a feature call of the form:
<code>
x.f (a)
</code>
Such a feature call is judged acceptable at compile time only if the type of <code>x</code> has a feature <code>f</code> and that any arguments, represented here by <code>a</code>, number the same as the formal arguments of <code>f</code>, and are compatible with the types of those formal arguments.
In statically typed languages like Eiffel, the compiler guarantees that you cannot, at run time, have a situation in which feature <code>f</code> is not applicable to the object attached to <code>x</code>. If you've ever been a Smalltalk programmer, you are certainly familiar with this most common of errors that manifests itself as "Message not understood." It happens because Smalltalk is not statically typed.
===Non-void-safe software===
Static typing will ensure that there is some feature <code>f</code> that can be applied at run time to <code>x</code> in the example above. But it does not assure us that, in the case in which <code>x</code> is a reference, that there will always be an object attached to <code>x</code> at any time <code>x.f (a)</code> is executed.
This problem is not unique to Eiffel. Other environments that allow or mandate reference semantics also allow the possibility of non-void-safe run time errors. If you've worked in Java or .NET you may have seen the NullReferenceException. Sometimes you might have experienced this rather poetic sounding message: "Object reference not set to an instance of an object". In Eiffel you would see "Feature call on void target". All these are the hallmarks of run time errors resulting from non-void-safe software.
{{note|If you need a review of difference between reference types and expanded types in Eiffel, see [[ET: The Dynamic Structure: Execution Model|the chapter of the Eiffel Tutorial dedicated to the Eiffel execution model]]. }}
Of course this is not an issue with instances of expanded types, because these instances are indeed "expanded" within their parent objects. But we could not imagine a world with expanded types only. References are important for performance reasons and for modeling purposes. For example, consider that a car has an engine and a manufacturer. When we model cars in software, it might be appropriate for engines to be expanded types, as each car has one engine. But certainly the same is not true for manufacturer. Many cars can share, through a reference, a single manufacturer.
So, references are necessary, but we want them to be trouble free.
==Void-safe software==
Void-safe software, then, is software in which the compiler can give assurance, through a static analysis of the code, that at run time whenever a feature is applied to a reference, that the reference in question will have an object attached. This means that the feature call
<code>
x.f (a)
</code>
is valid only if we are assured that <code>x</code> will be attached to an object when the call executes.
{{info|This validity rule is called the '''Target rule''', validity code VUTA, and is the primary rule for void-safety. In the following discussion, you will see that other validity rules are involved, too. You can see the formal definition of all validity rules in the [http://www.ecma-international.org/publications/standards/Ecma-367.htm ISO/ECMA standard document] available online. }}
Once we have committed ourselves to this validity rule, we must have a strategy for complying with the rule.

View File

@@ -0,0 +1,268 @@
[[Property:title|Void-safety: Background, definition, and tools]]
[[Property:weight|0]]
[[Property:uuid|689f62b2-5675-5ab6-cd47-d891cf3d484d]]
{{underconstruction}}
=Background=
The primary focus of Eiffel is on software quality. Void-safety, like static typing, is another facility for improving software quality. Void-safe software is protected from run time errors caused by calls to void references, and therefore will be more reliable than software in which calls to void targets can occur. The analogy to static typing is a useful one. In fact, void-safe capability could be seen as an extension to the type system, or a step beyond static typing, because the mechanism for ensuring void-safety is integrated into the type system.
==Static typing==
You know that static typing eliminates a whole class of software failures. This is done by making an assurance at compile time about a feature call of the form:
<code>
x.f (a)
</code>
Such a feature call is judged acceptable at compile time only if the type of <code>x</code> has a feature <code>f</code> and that any arguments, represented here by <code>a</code>, number the same as the formal arguments of <code>f</code>, and are compatible with the types of those formal arguments.
In statically typed languages like Eiffel, the compiler guarantees that you cannot, at run time, have a situation in which feature <code>f</code> is not applicable to the object attached to <code>x</code>. If you've ever been a Smalltalk programmer, you are certainly familiar with this most common of errors that manifests itself as "Message not understood." It happens because Smalltalk is not statically typed.
==Non-void-safe software==
Static typing will ensure that there is some feature <code>f</code> that can be applied at run time to <code>x</code> in the example above. But it does not assure us that, in the case in which <code>x</code> is a reference, that there will always be an object attached to <code>x</code> at any time <code>x.f (a)</code> is executed.
This problem is not unique to Eiffel. Other environments that allow or mandate reference semantics also allow the possibility of non-void-safe run time errors. If you've worked in Java or .NET you may have seen the NullReferenceException. Sometimes you might have experienced this rather poetic sounding message: "Object reference not set to an instance of an object". In Eiffel you would see "Feature call on void target". All these are the hallmarks of run time errors resulting from non-void-safe software.
{{note|If you need a review of difference between reference types and expanded types in Eiffel, see [[ET: The Dynamic Structure: Execution Model|the chapter of the Eiffel Tutorial dedicated to the Eiffel execution model]]. }}
Of course this is not an issue with instances of expanded types, because these instances are indeed "expanded" within their parent objects. But we could not imagine a world with expanded types only. References are important for performance reasons and for modeling purposes. For example, consider that a car has an engine and a manufacturer. When we model cars in software, it might be appropriate for engines to be expanded types, as each car has one engine. But certainly the same is not true for manufacturer. Many cars can share, through a reference, a single manufacturer.
So, references are necessary, but we want them to be trouble free.
=Void-safe software=
Void-safe software, then, is software in which the compiler can give assurance, through a static analysis of the code, that at run time whenever a feature is applied to a reference, that the reference in question will have an object attached. This means that the feature call
<code>
x.f (a)
</code>
is valid only if we are assured that <code>x</code> will be attached to an object when the call executes.
{{info|This validity rule is called the '''Target rule''', validity code VUTA, and is the primary rule for void-safety. In the following discussion, you will see that other validity rules are involved, too. You can see the formal definition of all validity rules in the [http://www.ecma-international.org/publications/standards/Ecma-367.htm ISO/ECMA standard document] available online. }}
Once we have committed ourselves to this validity rule, we must have a strategy for complying with the rule.
==Elements of the void-safe strategy==
Here are the tools of void-safe trade. They will each be addressed in more detail throughout the documentation that follows. As you look at these elements it helps to try to think about things from the compiler's viewpoint ... after all, it is the compiler that we expect to give us the guarantee that our code is indeed void-safe.
First let's look at a couple of approaches that won't work.
It might occur to us that we could enforce compliance with the target rule by simply eliminating the concept of void references. But this would not be practical. Void is a valuable abstraction that is useful in many situations, such as providing void links in structures. So, we must keep void ... but we want to keep it under control.
Another thought might be that we could just have the compiler do all the work for us. But would be impossibly time consuming for the compiler to investigate every conceivable execution path available to a system to make certain that every possible feature call was made on an attached reference.
So, all of this boils down to the fact that we have to take some actions that help the compiler along. That's what the following are about.
===Certified Attachment Patterns (CAPs)===
We know that in the context of certain code patterns, it is clear that it would be impossible for a reference to be void. These patterns are identified and we call them CAPs, short for Certified Attachment Patterns. Here is a very straightforward example expressed in a syntax that should be familiar to all Eiffel developers:
<code>
if x /= Void then
-- ... Any other instructions here that do not assign to x
x.f (a)
end
</code>
Here a check is made to ensure <code>x</code> is not void. Then as long as no assignments to <code>x</code> are made in the interim, a feature <code>f</code> can be applied to <code>x</code> with the certainty that <code>x</code> will be attached at the time ... and importantly, this can be determined at compile time. So, we say that this code pattern is a CAP for <code>x</code>.
It is important to understand that in this example (and with other CAPs), <code>x</code> is allowed to be a local variable or formal argument only. That is, <code>x</code> may not be an attribute or general expression. Direct access to class attribute references cannot allowed via a CAP due to the fact that they could be set to void by a routine call in some execution path invoked by the intervening instructions or possibly even different process thread.
{{note|You will find a more detailed discussion of CAPs in [[Void-safe programming in Eiffel#More about CAPs|More about CAPs]]. The current list of CAPs appears in the [[Catalog of Certified Attachment Patterns]]. }}
===The ''attached syntax''===
For the purposes of void-safety, the '''attached syntax''' does double duty for us. It allows us to make certain that a reference is attached, and it provides us a safe way to access objects that are attached to class attributes.
We noted earlier that this code
<code>
if x /= Void then
-- ... Any other instructions here that do not assign to x
x.f (a)
end
</code>
creates a CAP for feature calls on <code>x</code>, but only if <code>x</code> is a local variable or a formal argument.
By using the '''attached syntax''', we can perform an '''object test''' on a variable. That is, the attached syntax is a <code>BOOLEAN</code> expression which provides an answer to the question "Is <code>x</code> attached to an object?" At the same time, if indeed <code>x</code> is attached to an object, the attached syntax will deliver to us a fresh local variable, also attached to <code>x</code>'s object, on which we can make feature calls.
<code>
if attached x as l_x then
l_x.f (a)
end
</code>
In the example above, <code>x</code> is tested to make certain that it is attached. If so, the local <code>l_x</code> is created and can be used safely even if <code>x</code> is a class attribute. So, the attached syntax, is really another CAP, because it provides a clearly verifiable place for the application of features to targets that are guaranteed not to be void.
{{note|The attached syntax has other syntax variations as well as other uses. These will be discussed later. }}
One way to make sure we comply with the target rule would be always use a CAP or the attached syntax every time we want to apply a feature to a referenced object. That might work, but it falls among the impractical approaches to the problem ... it would break a very high percentage of existing EIffel code, not to mention cluttering things up quite a bit.
===Types as "attached" or "detachable"===
Rather than trying to protect every feature call, Eiffel allows us to declare variables as being of '''attached types'''. This is an important extension to the Eiffel type system.
In Eiffel prior to the introduction of void-safe facilities, any reference variable could be set to <code>Void</code>. So, all variables were considered '"detachable"'.
The current standard Eiffel supports a mixture of '''attached''' and '''detachable''' types. When a variable is declared of an attached type, as in the following example, then the compiler will prevent it from being set to <code>Void</code> or set to anything that can be set to <code>Void</code>.
<code>
my_attached_string: attached STRING
</code>
It is easy to imagine that the more declarations are of attached types, the easier it will be to guarantee that a call to a void target cannot take place at run time. In fact, if every declaration was guaranteed to be of an attached type, then that would be all that was needed to satisfy the Target rule.
However, it wouldn't be workable to have only attached types, because sometimes it's important to allow references to have a value of <code>Void</code>.
When it is necessary to allow <code>Void</code> as a value, a declaration can use the ''detachable mark'' as in the following.
<code>
my_detachable_string: detachable STRING
</code>
This doesn't mean that on every declaration you must put either an ''attached mark'' or a ''detachable mark''. Declarations that are unmarked are allowed. Whether unmarked declarations are considered attached or detachable is determined by the value of an EiffelStudio project setting named '''Are types attached by default?''' This setting can be set differently in different parts of your project giving you fine-grained control, which is particularly useful while converting existing software or mixing libraries of differing void-safety levels.
In Eiffel then, all declarations will have types that are either '''attached''' or '''detachable'''.
This means that we need only use CAPs and the attached syntax with detachable types.
===Initialization rule===
If we have attached types, then we can assume variables declared of these types, once attached, will always be attached. But how do they get attached in the first place? That's what the initialization rule is all about.
The rule says that at any place in which a variable is accessed, it must be '''properly set'''. A variable's being properly set has a precise, but not particularly simple definition in the Eiffel standard.
{{info|You can find the formal definition of the '''Variable initialization rule''', validity code VEVI, and its related concepts such as '''properly set''' variables in the [http://www.ecma-international.org/publications/standards/Ecma-367.htm ISO/ECMA standard document]. }}
Still, it's not too hard to understand the basics of initializing variables of attached types:
* For the initialization of attributes of a class, we can apply a rule similar to that of the initial evaluation of class invariants ... that is, everything must be in order upon completion of a creation procedure. If a class attribute is of an attached type, then each of the class's creation procedures is responsible for making sure that the attribute is attached to an object upon its completion.
* A local variable is considered properly set if it is initialized at some point '''preceding''' its use in any execution path in which it is used. So immediately after its <code>create</code> instruction, the local variable would be considered properly set. But if the <code>create</code> occurred in the <code>then</code> part of an <code>if</code> instruction, the local variable would not be properly set in the <code>else</code> part of that same <code>if</code> instruction:
<code>
my_routine
-- Illustrate properly set local variable
local
l_my_string: STRING
do
if my_condition then
create l_my_string.make_empty
-- ... l_my_string is properly set here
else
-- ... l_my_string is not properly set here
end
end
</code>
* A variable is considered properly set if it is '''self-initializing'''. What it means to be self-initializing is explained [[Void-safe programming in Eiffel#Self-initializing variables|below]].
===Self-initializing attributes===
A self-initializing attribute is guaranteed to have a value when accessed at run time. Declarations of self-initializing attributes are characterized by the use of <code>attribute</code> keyword. The code that follows the <code>attribute</code> keyword is executed to initialize the attribute in the case that the attribute is accessed prior to being initialized in any other way. So, the code in the attribute part is a kind of last resort for initialization.
<code>
value: STRING
attribute
create Result.make_empty
end
</code>
In the example above, the attribute <code>value</code> will be attached to an object of type STRING, in fact, the empty string, if no other initialization occurs before the first access of <code>value</code>.
===Rule for conformance===
You will remember that the Eiffel type system dictates that an assignment instruction:
<code>
x := y
</code>
is valid only if the type of <code>y</code> is '''compatible''' with the type of <code>x</code>. Compatibility, in turn, means either '''conversion''' or '''conformance'''.
The fact that all types are either '''attached''' or '''detachable''' adds another dimension to rule for conformance:
*If x is of an attached type, then y must be of an attached type.
This prevents us from circumventing attached status at run time. If <code>x</code> is of a detachable type, then <code>y</code> could be either a detachable or attached type.
The same goes for routine calls. In a call:
<code>
z.r (y)
</code>
where <code>x</code> is the formal argument for <code>r</code>, then if x is of an attached type, then y must be of an attached type.
===Stable attributes===
Stable attributes are really stable detachable attributes, as adding the concept of stability is meaningful only for detachable attributes. Declaring a detachable attribute as stable, means that it behaves like a detachable attribute except that its assignment rules mimic those of attached attributes. In other words, a stable attribute can never be the target of an assignment in which the source is <code>Void</code> or a detachable type.
<code>
test: detachable TEST
note
options: stable
attribute
end
</code>
This means that even though stable attributes do not need to be initialized like attached attributes, once they are attached to an object, they can never be void again.
===Rule for generic parameters===
Generic classes provide another question. A generic class like
<code>
class
C [G]
...
</code>
allows us to create a type by providing a specific actual generic parameter for the formal parameter <code>G</code>.
So, two valid derivations are:
<code>
my_integer_derivation: C [INTEGER]
</code>
and
<code>
my_employee_derivation: C [EMPLOYEE]
</code>
If class C contains a declaration:
<code>
x: G
</code>
What do we know about the void-safety of <code>x</code> ?
In the case of the <code>INTEGER</code> derivation above, we know <code>x</code> is safe because <code>INTEGER</code> is an expanded type. But often types like <code>EMPLOYEE</code> will be reference types which could be void at run time.
So for a class like <code>C [G]</code> containing a declaration <code>x: G</code> :
* <code>G</code> is considered detachable and any class will work for an actual generic parameter
* Application of features to <code>x</code> must include verification of attachment (CAPs, attached syntax, etc)
Constrained genericity can be used to create generic classes in which the generic parameter represents an attached type. If class <code>C</code> had been defined as:
<code>
class C [G -> attached ANY]
...
</code>
then <code>x</code> in this class <code>G</code> represents an attached type. Consequently, the actual generic type in any derivation must be attached ... and feature calls on <code>x</code> are safe.
===Rule for ARRAYs===
The rule for generic parameters applies to all generic types ... except <code>ARRAYs</code>. In the typical creation of an <code>ARRAY</code>, we would provide a minimum and maximum index.
<code>
my_array: ARRAY [STRING]
...
create my_array.make (1, 100)
</code>
During creation, an area to store the appropriate number of entries is also created. And depending upon the actual generic parameter, these entries are either objects for expanded types or references for reference types.
In the case of an actual generic parameter of an attached reference type, all the elements must be attached to instances of type during the creation of the ARRAY. The <code>make</code> procedure would not do this. Creation of an <code>ARRAY</code> in which the actual generic parameter is attached must be done using the <code>make_filled</code> creation procedure.
<code>
create my_array.make_filled (1, 100, "")
</code>
The third argument is an object of the actual generic type, in this case an empty <code>STRING</code>. Every entry in the newly created <code>ARRAY</code> will be initialized to reference this object.