Skip to content

Commit 2612ebe

Browse files
authored
Merge pull request dlang#3611 from WalterBright/article-ref-return-scope
add article on ref, return, and scope annotations Signed-off-by: Petar Kirov <PetarKirov@users.noreply.github.com> Merged-on-behalf-of: Petar Kirov <PetarKirov@users.noreply.github.com>
2 parents 578b55f + 06800d6 commit 2612ebe

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed

articles/RefReturnScope.dd

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
Ddoc
2+
3+
$(D_S $(TITLE),
4+
5+
$(P A wild pointer is a pointer that escapes its corral, or in other words,
6+
escapes its scope. A pointer within its scope is valid, and outside
7+
its scope is not valid. Attempting to read or write using an out-of-scope pointer
8+
will produce undefined behavior. Undefined behavior can lead to crashes, corruption,
9+
malware, and other costly problems. D uses `ref`, `return` and `scope` keywords to
10+
prevent pointer escapes.)
11+
12+
13+
$(H3 What is a Scope?)
14+
15+
$(P The scope of a declaration is closely related to its lifetime.
16+
Thread local variables have a lifetime that's the life of the thread they are
17+
in. Global variables have a lifetime from the program start to its finish.
18+
A local variable has a lifetime from its initialization to its closing curly brace.)
19+
20+
$(P Note on examples: `@safe` annotation is assumed)
21+
22+
---
23+
int x; // thread local lifetime
24+
__gshared int y; // global lifetime
25+
26+
void mars(int i /* lifetime of i is from function call to function return */)
27+
{
28+
{ // open new scope
29+
int* q = &i; // lifetime starts after q is set to the address of i
30+
*q = 3; // sets i to 3
31+
} // q's lifetime ends with the end of the scope
32+
*q = 4; // oops, can't use q here
33+
int* p = &i; // lifetime starts after p is set to the address of i
34+
*p = 5; // sets i to 5
35+
} // lifetimes of i and p end when function returns
36+
---
37+
38+
39+
$(H3 What is an Escaping Pointer?)
40+
41+
$(P A pointer escapes when its value becomes available outside the scope of the pointer.
42+
An example of an escaping pointer:)
43+
44+
---
45+
int* escape()
46+
{
47+
int i;
48+
int* p = &i; // create pointer to local variable
49+
return p; // return pointer to a local variable that is no longer live
50+
}
51+
52+
void crash()
53+
{
54+
int* q = escape();
55+
*q = 5; // unleash the Hounds of Hell
56+
}
57+
---
58+
59+
$(P Pointer escapes can occur in many, often not-so-obvious, cases. The compiler is
60+
the perfect tool to detect all the cases and report them as errors. Even if a
61+
particular pointer escape is benign, if a function interface makes clear that
62+
a function arguments cannot escape, it improves user understanding of the function.)
63+
64+
65+
$(H3 The Role of `scope`)
66+
67+
$(P `scope` is a storage class. When it is applied to a pointer variable, then the pointer's
68+
value is mechanically (i.e. enforced by the compiler) prevented from outlasting the scope
69+
of the pointer variable. The previous example is modified with the addition of `scope`:)
70+
71+
---
72+
int* escape()
73+
{
74+
int i;
75+
scope int* p = &i; // p is a scoped pointer
76+
return p; // error: scoped pointer p is escaping
77+
}
78+
---
79+
80+
The compiler, for this case, helpfully goes one better:
81+
82+
---
83+
int* escape()
84+
{
85+
int i;
86+
int* p = &i; // p is inferred to be a scoped pointer
87+
return p; // error: scoped pointer p is escaping
88+
}
89+
---
90+
91+
$(P I.e. if a pointer variable is set to be the address of a local variable, or to the contents of
92+
scope pointer, then that pointer variable is automatically set to be a scope pointer.
93+
The compiler is pretty good at inferring scope, thus relieving the programmer of adding
94+
a lot of annotations.)
95+
96+
$(P Note that as `scope` is a storage class, not a type constructor, it is not possible to specify
97+
a scope pointer to a scope pointer. It surprisingly turns out to not be necessary to support that.)
98+
99+
100+
$(H3 The Role of `return scope`)
101+
102+
$(P Consider the following:)
103+
104+
---
105+
void f()
106+
{
107+
int i;
108+
*process(&i) = 4;
109+
}
110+
111+
int* process(scope int* p) { return p; }
112+
---
113+
114+
$(P This is perfectly legitimate code, there is no pointer escaping bug. But it won't compile.
115+
Without `scope` on the parameter `p`, the call to `process(&i)` would be disallowed.
116+
But with `scope` on `p`, the `return p;` is disallowed.)
117+
118+
$(P The solution is adding a `return` annotation:)
119+
120+
---
121+
int* process(return scope int* p) { return p; }
122+
---
123+
124+
$(P which allows the scope pointer value to be returned by the function.)
125+
126+
$(P If a function returns `void`, `return scope` also allows returning the scope value
127+
through the first parameter:)
128+
129+
---
130+
void mun(ref int* v, return scope int* p)
131+
{
132+
v = p; // ok
133+
}
134+
---
135+
136+
$(P And that's it for pointers. Remember that pointer scoping is concerned with the value
137+
of the pointer variable.)
138+
139+
140+
$(H3 The Role of `ref`)
141+
142+
$(P A `ref` is a reference to a value, a fancy way of representing a pointer to a value.
143+
It is distinguished from a pointer:)
144+
145+
$(OL
146+
$(LI by not allowing arithmetic on the address)
147+
$(LI whenever the `ref` variable is used an automatic dereference is performed)
148+
$(LI a ref cannot escape from a function)
149+
)
150+
151+
---
152+
ref int fin(ref int i)
153+
{
154+
return i; // error, cannot return ref variable i by ref
155+
}
156+
---
157+
158+
159+
$(H3 The role of `return ref`)
160+
161+
$(P But it will be allowed if `return` is applied:)
162+
163+
---
164+
ref int fin(return ref int i)
165+
{
166+
return i; // ok
167+
}
168+
---
169+
170+
171+
$(H3 The role of `ref scope`)
172+
173+
$(P The storage classes `ref` and `scope` together are orthogonal, they do not affect each other.
174+
`ref` refers to the address of the variable, `scope` refers to the contents of the variable.)
175+
176+
177+
$(H3 The role of `return ref scope`)
178+
179+
$(P The `return` here applies to the `ref`, not the `scope`.)
180+
181+
$(P Let's try tricking the compiler:)
182+
183+
---
184+
ref int* fin(return ref scope int* p) { return p; } // ok
185+
186+
int* tricky()
187+
{
188+
int i;
189+
int* p = &i; // p is now inferred to be scope
190+
auto q = fin(p); // q now contains the address of i, and so scope is also inferred
191+
return q; // error: scope variable `q` may not be returned
192+
}
193+
---
194+
195+
$(P Curses! Foiled again!)
196+
197+
$(P The operational idea here is, while compiling `@safe` code, it is not be possible to escape
198+
a scoped value, no matter how twisty the code is.)
199+
200+
)
201+
202+
Macros:
203+
TITLE=Coralling Wild Pointers With $(D ref return scope)
204+
ITEMR=$(LI $(RELATIVE_LINK2 $1, $+))
205+
ITEM=<hr>$(H3 <a name="$1">$+</a>)
206+
SUBNAV=$(SUBNAV_ARTICLES)
207+

articles/index.dd

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,17 @@ $(D_S Articles,
185185
)
186186
)
187187
)
188+
$(DIVC row,
189+
$(DIVC item,
190+
$(H4 $(LINK2 $(ROOT_DIR)articles/RefReturnScope.html,
191+
Coralling Wild Pointers with `ref return scope`))
192+
$(P
193+
How to use the `ref`, `return` and `scope` annotations
194+
to prevent the escape of pointers. Escaping pointers can
195+
cause memory corruption and other problems.
196+
)
197+
)
198+
)
188199
)
189200
)
190201

posix.mak

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ ARTICLE_FILES=$(addprefix articles/, index builtin code_coverage const-faq \
338338
migrate-to-shared mixin pretod rationale regular-expression \
339339
safed templates-revisited variadic-function-templates warnings \
340340
cppcontracts template-comparison dll-linux \
341+
RefReturnScope \
341342
)
342343

343344
# Website root filenames. They have extension .dd in the source

0 commit comments

Comments
 (0)