Skip to content

Commit 0899863

Browse files
authored
Merge pull request #3284 from dkorpel/scope-attribute
Improve spec of `scope` attribute
2 parents 5ad3364 + 7c61da7 commit 0899863

File tree

1 file changed

+168
-10
lines changed

1 file changed

+168
-10
lines changed

spec/attribute.dd

Lines changed: 168 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -645,26 +645,185 @@ auto i = 6.8; // declare i as a double
645645
$(H2 $(LNAME2 scope, $(D scope) Attribute))
646646

647647
$(P
648-
The $(D scope) attribute is used for local variables and implements the
649-
RAII (Resource Acquisition Is Initialization) protocol.
648+
The `scope` attribute signifies a variable's pointer values will not escape the scope that the variable is declared in.
649+
)
650+
651+
$(P
652+
If the variable has a type that does not contain any indirections, the `scope` attribute is ignored.
653+
)
654+
655+
$(P
656+
When applied to a global variable, `scope` is also ignored, since there is no scope larger than global scope that pointers could escape to.
657+
)
658+
659+
$(SPEC_RUNNABLE_EXAMPLE_COMPILE
660+
---
661+
scope int* x; // scope ignored, global variable
662+
663+
void main()
664+
{
665+
static int* x; // scope ignored, global variable
666+
scope float y; // scope ignored, no indirections
667+
scope int[2] z; // scope ignored, static array is value type
668+
scope int[] w; // scope dynamic array
669+
}
670+
---
671+
)
672+
673+
$(P
674+
When applied to a local variable with a type that has indirections,
675+
its value may not be assigned to a variable with longer lifetime:
676+
677+
$(UL
678+
$(LI Variables outside the $(DDSUBLINK spec/statement, scope-statement, scope statement) that the variable is declared in)
679+
$(LI Variables declared before the `scope` variable$(COMMA) since local variables are destructed in the reverse order that they are declared in.)
680+
$(LI `__gshared` or `static` variables)
681+
)
682+
683+
Other operations implicitly assigning them to variables with longer lifetime are also disallowed:
684+
685+
$(UL
686+
$(LI Returning a `scope` variable from a function)
687+
$(LI Assigning a `scope` variable to a non-scope parameter by calling a function)
688+
$(LI Putting a `scope` variable in an array literal)
689+
)
690+
)
691+
692+
$(P
693+
The `scope` attribute is part of the variable declaration, not of the type, and it only applies to the first level of indirection.
694+
For example, it is impossible to declare a variable as a dynamic array of scope pointers, because `scope` only applies to the `.ptr`
695+
of the array itself, not its elements. `scope` affects various types as follows:
696+
697+
$(TABLE_2COLS ,
698+
$(THEAD Type of local variable, What `scope` applies to)
699+
700+
$(TROW Any $(DDSUBLINK spec/type, basic-data-types, Basic Data Type), nothing)
701+
$(TROW $(DDSUBLINK spec/arrays, pointers, Pointer) `T*`, the pointer value)
702+
$(TROW $(DDSUBLINK spec/arrays, dynamic-arrays, Dynamic Array) `T[]`, the `.ptr` to the elements)
703+
$(TROW $(DDSUBLINK spec/arrays, static-arrays, Static Array) `T[n]`, each element `T`)
704+
$(TROW $(GLINK2 hash-map, Associative Array) `K[V]`, the pointer to the implementation defined structure)
705+
$(TROW `struct` or `union`, each of its member variables)
706+
$(TROW `function` pointer, the pointer value)
707+
$(TROW $(DDSUBLINK spec/function, closures, delegate), both the `.funcptr` and `.ptr` (closure context) pointer values)
708+
$(TROW `class` or `interface`, the class reference)
709+
$(TROW $(GLINK2 enum, enum), the base type)
710+
)
711+
712+
Example:
713+
)
714+
715+
---
716+
struct S
717+
{
718+
string str; // note: string = immutable(char)[]
719+
string* strPtr;
720+
}
721+
722+
string escape(scope S s, scope S* sPtr, string[2] sarray, scope string[] darray)
723+
{
724+
return s.str; // invalid, scope applies to struct members
725+
return *s.strPtr; // valid, scope struct member is dereferenced
726+
return sPtr.str; // valid, struct pointer is dereferenced
727+
return *sPtr.strPtr; // valid, two pointers are dereferenced
728+
return sarray[0]; // invalid, scope applies to static array elements
729+
return sarray[1]; // invalid, ditto
730+
return darray[0]; // valid, scope applies to array pointer, not elements
731+
}
732+
---
733+
734+
$(P
735+
A "`scope` value" is the value of a `scope` variable, or any generated value that is (or could be) allocated on the stack:
736+
$(UL
737+
$(LI The address of a function local variable / parameter)
738+
$(LI A struct member accessed through the implicit `this` parameter)
739+
$(LI The return value of a $(DDSUBLINK spec/function, ref-functions, Ref Function))
740+
$(LI The variadic parameter from $(DDSUBLINK spec/function, typesafe_variadic_functions, Typesafe Variadic Functions))
741+
)
742+
743+
When a local variable is assigned a `scope` value, it is inferred `scope`, even when the variable has an explicit type and does not use the `auto` keyword.
744+
)
745+
746+
---
747+
int* escape(ref int x, int y, scope int* z)
748+
{
749+
scope int* w;
750+
751+
auto xPtr = &x; // inferred `scope int*`
752+
int* yPtr = &y; // also inferred `scope int*`
753+
int* zCopy = z; // inferred `scope int*`
754+
int* wCopy = w; // inferred `scope int*`
755+
756+
return yPtr; // error
757+
}
758+
759+
void variadic(int[] a...)
760+
{
761+
int[] x = a; // inferred `scope int[]`
762+
}
763+
764+
struct S
765+
{
766+
int x;
767+
768+
// this method may be called on a stack-allocated instance of S
769+
void f()
770+
{
771+
int* p = &x; // inferred `scope int* p`
772+
int* q = &this.x; // equivalent
773+
}
774+
}
775+
---
776+
777+
$(P
778+
$(DDSUBLINK spec/function, scope-parameters, scope parameters) are treated the same as scope local variables,
779+
except that returning them is allowed when the function has $(DDSUBLINK spec/function, function-attribute-inference, Function Attribute Inference).
780+
In that case, they are inferred as $(DDSUBLINK spec/function, return-scope-parameters, return scope parameters).
781+
)
782+
783+
$(H3 $(LNAME2 scope-class-var, $(D scope) variables with `class` type))
784+
$(P
785+
When used on variables with a `class` type, `scope` signifies the RAII
786+
(Resource Acquisition Is Initialization) protocol.
650787
This means that the destructor for an object is automatically called when the
651788
reference to it goes out of scope. The destructor is called even
652789
if the scope is exited via a thrown exception, thus $(D scope)
653790
is used to guarantee cleanup.
654791
)
655792
$(P
656-
If there is more than one $(D scope) variable going out of scope
793+
When a class is constructed with `new` and assigned to a local `scope` variable,
794+
it may be allocated on the stack and permitted in a `@nogc` context.
795+
)
796+
$(P
797+
If there is more than one `scope` class variable going out of scope
657798
at the same point, then the destructors are called in the reverse
658799
order that the variables were constructed.
659800
)
660801
$(P
661-
$(D scope) cannot be applied to globals, statics, data members, ref
662-
or out parameters. Arrays of $(D scope)s are not allowed, and $(D scope)
663-
function return values are not allowed. Assignment to a $(D scope),
664-
other than initialization, is not allowed.
665-
$(D Rationale:) These restrictions may get relaxed in the future
666-
if a compelling reason to appears.
802+
If there is more than one `scope` class variable going out of scope
803+
at the same point, then the destructors are called in the reverse
804+
order that the variables were constructed.
667805
)
806+
$(P
807+
Assignment to a $(D scope) variable with class type,
808+
other than initialization, is not allowed, because that would complicate
809+
proper destruction of the variable.
810+
)
811+
812+
---
813+
class C {}
814+
815+
void main() @nogc
816+
{
817+
{
818+
scope c0 = new C(); // allocated on the stack
819+
scope c1 = new C();
820+
821+
c1 = c0; // not allowed
822+
823+
// destructor of `c1` and `c0` are called here in that order
824+
}
825+
}
826+
---
668827

669828
$(H2 $(LNAME2 abstract, $(D abstract) Attribute))
670829

@@ -720,7 +879,6 @@ $(H2 $(LNAME2 mustuse-attribute, `@mustuse` Attribute))
720879
An expression is considered to be discarded if and only if either of the
721880
following is true:
722881
)
723-
724882
$(UL
725883
$(LI
726884
it is the top-level $(GLINK2 expression, Expression) in an $(GLINK2

0 commit comments

Comments
 (0)