$(DDOC $(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(SPEC_S Functions,
$(DDOC_BLANKLINE )
$(HEADERNAV_TOC $(HEADERNAV_SUBITEMS grammar, Function Declarations,
$(HEADERNAV_ITEM function-bodies, Function Bodies)
$(HEADERNAV_ITEM function-declarations, Functions without Bodies)
)
$(HEADERNAV_SUBITEMS contracts, Function Contracts,
$(HEADERNAV_ITEM preconditions, Preconditions)
$(HEADERNAV_ITEM postconditions, Postconditions)
$(HEADERNAV_ITEM in_out_inheritance, In, Out and Inheritance)
)
$(HEADERNAV_ITEM function-return-values, Function Return Values)
$(HEADERNAV_SUBITEMS pure-functions, Pure Functions,
$(HEADERNAV_ITEM weak-purity, Strong vs Weak Purity)
$(HEADERNAV_ITEM pure-special-cases, Special Cases)
$(HEADERNAV_ITEM pure-factory-functions, Pure Factory Functions)
$(HEADERNAV_ITEM pure-optimization, Optimization)
)
$(HEADERNAV_ITEM nothrow-functions, Nothrow Functions)
$(HEADERNAV_ITEM ref-functions, Ref Functions)
$(HEADERNAV_ITEM auto-functions, Auto Functions)
$(HEADERNAV_ITEM auto-ref-functions, Auto Ref Functions)
$(HEADERNAV_ITEM inout-functions, Inout Functions)
$(HEADERNAV_ITEM optional-parenthesis, Optional Parentheses)
$(HEADERNAV_ITEM property-functions, Property Functions)
$(HEADERNAV_SUBITEMS virtual-functions, Virtual Functions,
$(HEADERNAV_ITEM covariance, Covariance)
$(HEADERNAV_ITEM base-methods, Calling Base Class Methods)
$(HEADERNAV_ITEM function-inheritance, Overload Sets and Overriding)
$(HEADERNAV_ITEM override-defaults, Default Values)
$(HEADERNAV_ITEM inheriting-attributes, Inherited Attributes)
$(HEADERNAV_ITEM override-restrictions, Restrictions)
)
$(HEADERNAV_ITEM inline-functions, Inline Functions)
$(HEADERNAV_SUBITEMS function-overloading, Function Overloading,
$(HEADERNAV_ITEM overload-sets, Overload Sets)
)
$(HEADERNAV_SUBITEMS parameters, Function Parameters,
$(HEADERNAV_ITEM param-storage, Parameter Storage Classes)
$(HEADERNAV_ITEM in-params, In Parameters)
$(HEADERNAV_ITEM ref-params, Ref and Out Parameters)
$(HEADERNAV_ITEM lazy-params, Lazy Parameters)
$(HEADERNAV_ITEM function-default-args, Default Arguments)
$(HEADERNAV_ITEM return-ref-parameters, Return Ref Parameters)
$(HEADERNAV_ITEM scope-parameters, Scope Parameters)
$(HEADERNAV_ITEM return-scope-parameters, Return Scope Parameters)
$(HEADERNAV_ITEM ref-return-scope-parameters, Ref Return Scope Parameters)
$(HEADERNAV_ITEM pure-scope-inference, Inferred scope
parameters in pure
functions)
$(HEADERNAV_ITEM udas-parameters, User-Defined Attributes for Parameters)
$(HEADERNAV_ITEM variadic, Variadic Functions)
$(HEADERNAV_ITEM hidden-parameters, Hidden Parameters)
)
$(HEADERNAV_SUBITEMS refscopereturn, Ref Scope Return Cases,
$(HEADERNAV_ITEM rsr_definitions, Definitions)
$(HEADERNAV_ITEM rsr_classification, Classification)
$(HEADERNAV_ITEM rsr_mapping, Mapping Syntax Onto Classification)
$(HEADERNAV_ITEM rsr_memberfunctions, Member Functions)
$(HEADERNAV_ITEM rsr_PandRef, P and ref)
$(HEADERNAV_ITEM rsr_covariance, Covariance)
)
$(HEADERNAV_SUBITEMS local-variables, Local Variables,
$(HEADERNAV_ITEM local-static-variables, Local Static Variables)
)
$(HEADERNAV_SUBITEMS nested, Nested Functions,
$(HEADERNAV_ITEM nested-declaration-order, Declaration Order)
)
$(HEADERNAV_SUBITEMS function-pointers-delegates, Function Pointers, Delegates and Closures,
$(HEADERNAV_ITEM function-pointers, Function Pointers)
$(HEADERNAV_ITEM closures, Delegates & Closures)
$(HEADERNAV_ITEM function-delegate-init, Initialization)
$(HEADERNAV_ITEM anonymous, Anonymous Functions and Anonymous Delegates)
)
$(HEADERNAV_SUBITEMS main, $(D main()) Function,
$(HEADERNAV_ITEM betterc-main, $(D extern(C) main()) Function)
)
$(HEADERNAV_ITEM function-templates, Function Templates)
$(HEADERNAV_SUBITEMS interpretation, Compile Time Function Execution (CTFE),
$(HEADERNAV_ITEM string-mixins, String Mixins and Compile Time Function Execution)
)
$(HEADERNAV_ITEM nogc-functions, No-GC Functions)
$(HEADERNAV_SUBITEMS function-safety, Function Safety,
$(HEADERNAV_ITEM safe-functions, Safe Functions)
$(HEADERNAV_ITEM trusted-functions, Trusted Functions)
$(HEADERNAV_ITEM system-functions, System Functions)
$(HEADERNAV_ITEM safe-interfaces, Safe Interfaces)
$(HEADERNAV_ITEM safe-values, Safe Values)
$(HEADERNAV_ITEM safe-aliasing, Safe Aliasing)
)
$(HEADERNAV_ITEM function-attribute-inference, Function Attribute Inference)
$(HEADERNAV_ITEM pseudo-member, Uniform Function Call Syntax (UFCS))
)
$(DDOC_BLANKLINE )
final
is a semantic error, but not a parse error.)
$(DDOC_BLANKLINE )
$(P See also: $(RELATIVE_LINK2 param-storage, parameter storage classes).)
$(DDOC_BLANKLINE )
const(char)[]
.
)
$(DDOC_BLANKLINE )
$(P An $(GLINK InStatement) is also a precondition. Any $(GLINK2 expression, AssertExpression) appearing
in an $(I InStatement) will be an $(I InContractExpression).
)
$(DDOC_BLANKLINE )
$(P Preconditions must semantically be satisfied before the function starts executing.
If it is not, the program enters an $(I Invalid State).
)
$(DDOC_BLANKLINE )
$(IMPLEMENTATION_DEFINED Whether the preconditions are actually run or not is implementation defined.
This is usually selectable with a compiler switch.
Its behavior upon precondition failure is also usually selectable with a compiler switch.
One option is to throw an AssertError
with a message consisting of the optional second
$(I AssignExpression).
)
$(DDOC_BLANKLINE )
$(BEST_PRACTICE Use preconditions to validate that input arguments have values that are
expected by the function.)
$(DDOC_BLANKLINE )
$(BEST_PRACTICE Since preconditions may or may not be actually checked at runtime, avoid
using preconditions that have side effects.)
$(DDOC_BLANKLINE )
$(P The expression form is:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD in) (expression)
$(D_KEYWORD in) (expression, $(D_STRING "failure string"))
{
...$(D_KEYWORD function) body...
}
)
$(DDOC_BLANKLINE )
$(P The block statement form is:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD in)
{
...contract preconditions...
}
$(D_KEYWORD do)
{
...$(D_KEYWORD function) body...
}
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
const(char)[]
.
)
$(DDOC_BLANKLINE )
$(P An $(GLINK OutStatement) is also a postcondition. Any $(GLINK2 expression, AssertExpression) appearing
in an $(I OutStatement) will be an $(I OutContractExpression).
)
$(DDOC_BLANKLINE )
$(P Postconditions must semantically be satisfied after the function finishes executing.
If it is not, the program enters an $(I Invalid State).
)
$(DDOC_BLANKLINE )
$(IMPLEMENTATION_DEFINED Whether the postconditions are actually run or not is implementation defined.
This is usually selectable with a compiler switch.
Its behavior upon postcondition failure is also usually selectable with a compiler switch.
One option is to throw an AssertError
with a message consisting of the optional second
$(I AssignExpression).
)
$(DDOC_BLANKLINE )
$(BEST_PRACTICE Use postconditions to validate that the input arguments and return value have values that are
expected by the function.)
$(DDOC_BLANKLINE )
$(BEST_PRACTICE Since postconditions may or may not be actually checked at runtime, avoid
using postconditions that have side effects.)
$(DDOC_BLANKLINE )
$(P The expression form is:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD out) (identifier; expression)
$(D_KEYWORD out) (identifier; expression, $(D_STRING "failure string"))
$(D_KEYWORD out) (; expression)
$(D_KEYWORD out) (; expression, $(D_STRING "failure string"))
{
...$(D_KEYWORD function) body...
}
)
$(DDOC_BLANKLINE )
$(P The block statement form is:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD out)
{
...contract postconditions...
}
$(D_KEYWORD out) (identifier)
{
...contract postconditions...
}
$(D_KEYWORD do)
{
...$(D_KEYWORD function) body...
}
)
$(DDOC_BLANKLINE )
$(P The optional identifier in either type of postcondition is set to the return value
of the function, and can be accessed from within the postcondition.)
$(DDOC_BLANKLINE )
assert(0)
statement)
$(LI the function evaluates an expression of type
$(DDSUBLINK spec/type, noreturn, noreturn
))
$(LI the function contains inline assembler code)
)
$(DDOC_BLANKLINE )
$(P Function return values not marked as $(RELATIVE_LINK2 ref-functions, ref
)
are considered to be rvalues.
This means they cannot be passed by reference to other functions.
)
$(DDOC_BLANKLINE )
pure
attribute.
Pure functions cannot directly access global or static
mutable state.
Pure functions can only call pure functions.)
$(DDOC_BLANKLINE )
$(P Pure functions can:)
$(UL $(LI Modify the local state of the function.)
$(LI Throw exceptions.)
)
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int) x;
$(D_KEYWORD immutable) $(D_KEYWORD int) y;
$(D_KEYWORD pure) $(D_KEYWORD int) foo($(D_KEYWORD int) i)
{
i++; $(D_COMMENT // ok, modifying local state
) $(D_COMMENT //x = i; // error, modifying global state
) $(D_COMMENT //i = x; // error, reading mutable global state
) i = y; $(D_COMMENT // ok, reading immutable global state
) $(D_KEYWORD throw) $(D_KEYWORD new) Exception($(D_STRING "failed")); $(D_COMMENT // ok
)}
)
)
$(DDOC_BLANKLINE )
$(P A pure function can override an impure function,
but cannot be overridden by an impure function.
I.e. it is covariant with an impure function.
)
$(DDOC_BLANKLINE )
@safe
code.)
$(DDOC_BLANKLINE )
immutable
or const shared
,
and from shared
and const shared
to (unshared) const
.
For example:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) List { $(D_KEYWORD int) payload; List* next; }
$(D_KEYWORD pure) List* make($(D_KEYWORD int) a, $(D_KEYWORD int) b)
{
$(D_KEYWORD auto) result = $(D_KEYWORD new) List(a, $(D_KEYWORD null));
result.next = $(D_KEYWORD new) List(b, $(D_KEYWORD null));
$(D_KEYWORD return) result;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD auto) list = make(1, 2);
$(D_KEYWORD pragma)(msg, $(D_KEYWORD typeof)(list)); $(D_COMMENT // List*
)
$(D_KEYWORD immutable) ilist = make(1, 2);
$(D_KEYWORD pragma)(msg, $(D_KEYWORD typeof)(ilist)); $(D_COMMENT // immutable List*
) $(D_KEYWORD pragma)(msg, $(D_KEYWORD typeof)(ilist.next)); $(D_COMMENT // immutable List*
)}
)
)
$(DDOC_BLANKLINE )
$(P All references in make
's result refer to List
objects created by make
, and no other part of the program refers to
any of these objects. Hence the result can initialize an immutable
variable.)
$(DDOC_BLANKLINE )
$(P This does not affect any Exception or Error thrown from the function.
)
$(DDOC_BLANKLINE )
cast
s or by changing behavior
depending on the address of its parameters. An implementation is currently
not required to enforce validity of memoization in all cases.
)
$(P If a function throws an Exception or an Error, the
assumptions related to memoization do not carry to the thrown
exception.)
$(DDOC_BLANKLINE )
$(P Pure destructors do not benefit of special elision.)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
ref
function returns by reference (instead of by value).
The return value of a ref
function must be an lvalue
(whereas the return value of a non-ref
function can be an rvalue, too).
An expression formed by calling a ref
function is an lvalue
(whereas an expression formed by calling a non-ref
function is an rvalue).
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int) *p;
$(D_KEYWORD ref) $(D_KEYWORD int) foo()
{
p = $(D_KEYWORD new) $(D_KEYWORD int)(2);
$(D_KEYWORD return) *p;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD int) i = foo();
$(D_KEYWORD assert)(i == 2);
foo() = 3; $(D_COMMENT // reference returns can be lvalues
) $(D_KEYWORD assert)(*p == 3);
}
)
)
$(DDOC_BLANKLINE )
$(P Returning a reference to an expired function context is not allowed.
This includes local variables, temporaries and parameters that are part
of an expired function context.
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD ref) $(D_KEYWORD int) sun()
{
$(D_KEYWORD int) i;
$(D_KEYWORD return) i; $(D_COMMENT // error, escaping a reference to local variable i
)}
)
$(DDOC_BLANKLINE )
$(P A ref
parameter may not be returned by ref
, unless it is
$(RELATIVE_LINK2 return-ref-parameters, return ref
).)
$(D_CODE $(D_KEYWORD ref) $(D_KEYWORD int) moon($(D_KEYWORD ref) $(D_KEYWORD int) i)
{
$(D_KEYWORD return) i; $(D_COMMENT // error
)}
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
auto
.
)
$(DDOC_BLANKLINE )
$(P If there are multiple $(I ReturnStatement)s, the types
of them must be implicitly convertible to a common type.
If there are no $(I ReturnStatement)s, the return type is inferred
to be $(D_KEYWORD void).)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD auto) foo($(D_KEYWORD int) x) { $(D_KEYWORD return) x + 3; } $(D_COMMENT // inferred to be int
)$(D_KEYWORD pure) bar($(D_KEYWORD int) x) { $(D_KEYWORD return) x; $(D_KEYWORD return) 2.5; } $(D_COMMENT // inferred to be double
))
)
$(DDOC_BLANKLINE )
$(NOTE Return type inference also triggers
$(RELATIVE_LINK2 function-attribute-inference, attribute inference).)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
vtbl[]
, rather than directly.
Member functions that are virtual can be overridden in a derived class:
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD class) A
{
$(D_KEYWORD void) foo($(D_KEYWORD int) x) {}
}
$(D_KEYWORD class) B : A
{
$(D_KEYWORD override) $(D_KEYWORD void) foo($(D_KEYWORD int) x) {}
$(D_COMMENT //override void foo$(LPAREN)$(RPAREN ) {} // error, no foo$(LPAREN)$(RPAREN ) in A
)}
$(D_KEYWORD void) test()
{
A a = $(D_KEYWORD new) B();
a.foo(1); $(D_COMMENT // calls B.foo$(LPAREN)int$(RPAREN )
)}
)
)
$(DDOC_BLANKLINE )
$(P The override
attribute is required when overriding a function.
This is useful for catching errors when a base class's member function
has its parameters changed, and all derived classes need to have
their overriding functions updated.)
$(DDOC_BLANKLINE )
$(P The $(LNAME2 final, final
) method attribute
prevents a subclass from overriding the method.)
$(DDOC_BLANKLINE )
$(P The following are not virtual:)
$(UL $(LI Struct and union member functions)
$(LI final
member functions)
$(LI $(DDSUBLINK spec/attribute, static, static
) member functions)
$(LI Member functions which are $(D private) or $(D package))
$(LI Member template functions)
)
$(DDOC_BLANKLINE )
$(P $(B Example:))
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD class) A
{
$(D_KEYWORD int) def() { ... }
$(D_KEYWORD final) $(D_KEYWORD int) foo() { ... }
$(D_KEYWORD final) $(D_KEYWORD private) $(D_KEYWORD int) bar() { ... }
$(D_KEYWORD private) $(D_KEYWORD int) abc() { ... }
}
$(D_KEYWORD class) B : A
{
$(D_KEYWORD override) $(D_KEYWORD int) def() { ... } $(D_COMMENT // ok, overrides A.def
) $(D_KEYWORD override) $(D_KEYWORD int) foo() { ... } $(D_COMMENT // error, A.foo is final
) $(D_KEYWORD int) bar() { ... } $(D_COMMENT // ok, A.bar is final private, but not virtual
) $(D_KEYWORD int) abc() { ... } $(D_COMMENT // ok, A.abc is not virtual, B.abc is virtual
)}
$(D_KEYWORD void) test()
{
A a = $(D_KEYWORD new) B;
a.def(); $(D_COMMENT // calls B.def
) a.foo(); $(D_COMMENT // calls A.foo
) a.bar(); $(D_COMMENT // calls A.bar
) a.abc(); $(D_COMMENT // calls A.abc
)}
)
$(DDOC_BLANKLINE )
$(P Member functions with Objective-C
linkage are virtual even if marked
with final
or static
, and can be overridden.
)
$(DDOC_BLANKLINE )
Base
,
write Base.
before the function name.
This avoids dynamic dispatch through a function pointer. For
example:
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD class) B
{
$(D_KEYWORD int) foo() { $(D_KEYWORD return) 1; }
}
$(D_KEYWORD class) C : B
{
$(D_KEYWORD override) $(D_KEYWORD int) foo() { $(D_KEYWORD return) 2; }
$(D_KEYWORD void) test()
{
$(D_KEYWORD assert)(B.foo() == 1); $(D_COMMENT // translated to this.B.foo$(LPAREN)$(RPAREN ), and
) $(D_COMMENT // calls B.foo statically.
) $(D_KEYWORD assert)(C.foo() == 2); $(D_COMMENT // calls C.foo statically, even if
) $(D_COMMENT // the actual instance of 'this' is D.
) }
}
$(D_KEYWORD class) D : C
{
$(D_KEYWORD override) $(D_KEYWORD int) foo() { $(D_KEYWORD return) 3; }
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD auto) d = $(D_KEYWORD new) D();
$(D_KEYWORD assert)(d.foo() == 3); $(D_COMMENT // calls D.foo
) $(D_KEYWORD assert)(d.B.foo() == 1); $(D_COMMENT // calls B.foo
) $(D_KEYWORD assert)(d.C.foo() == 2); $(D_COMMENT // calls C.foo
) d.test();
}
)
)
$(P Base class methods can also be called through the
$(DDSUBLINK spec/expression, super, super
) reference.)
$(DDOC_BLANKLINE )
$(IMPLEMENTATION_DEFINED Normally calling a virtual function implies getting the
address of the function at runtime by indexing into the class's vtbl[]
.
If the implementation can determine that the called virtual function will be statically
known, such as if it is final
, it can use a direct call instead.
)
$(DDOC_BLANKLINE )
pragma(inline)
).)
$(DDOC_BLANKLINE )
$(IMPLEMENTATION_DEFINED Whether a function is inlined or not is implementation defined, though
any $(GLINK2 expression, FunctionLiteral) should be inlined
when used in its declaration scope.
)
$(DDOC_BLANKLINE )
this
argument.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD struct) S {
$(D_KEYWORD void) eggs($(D_KEYWORD int));
$(D_KEYWORD static) $(D_KEYWORD void) eggs($(D_KEYWORD long));
}
S s;
s.eggs(0); $(D_COMMENT // calls void eggs$(LPAREN)int$(RPAREN );
)S.eggs(0); $(D_COMMENT // error: need `this`
)s.eggs(0L); $(D_COMMENT // calls static void eggs$(LPAREN)long$(RPAREN );
)S.eggs(0L); $(D_COMMENT // calls static void eggs$(LPAREN)long$(RPAREN );
)
$(D_KEYWORD struct) T {
$(D_KEYWORD void) bacon($(D_KEYWORD int));
$(D_KEYWORD static) $(D_KEYWORD void) bacon($(D_KEYWORD int));
}
T t;
t.bacon(0); $(D_COMMENT // error: ambiguous
)T.bacon(0); $(D_COMMENT // error: ambiguous
))
$(DDOC_BLANKLINE )
$(RATIONALE A static member function that doesn't need
the this
parameter does not need to pass it.)
$(DDOC_BLANKLINE )
foo
.
An instance of $(CODE foo) is selected
based on it matching in exactly one overload set:
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) A;
$(D_KEYWORD import) B;
$(D_KEYWORD void) bar(C c , $(D_KEYWORD long) i)
{
foo(); $(D_COMMENT // calls A.foo$(LPAREN)$(RPAREN )
) foo(i); $(D_COMMENT // calls A.foo$(LPAREN)long$(RPAREN )
) foo(c); $(D_COMMENT // calls B.foo$(LPAREN)C$(RPAREN )
) foo(1,2); $(D_COMMENT // error, does not match any foo
) foo(1); $(D_COMMENT // error, matches A.foo$(LPAREN)long$(RPAREN ) and B.foo$(LPAREN)int$(RPAREN )
) A.foo(1); $(D_COMMENT // calls A.foo$(LPAREN)long$(RPAREN )
)}
)
$(DDOC_BLANKLINE )
$(P Even though $(CODE B.foo(int)) is a better match than $(CODE A.foo(long)) for $(CODE foo(1)),
it is an error because the two matches are in
different overload sets.
)
$(DDOC_BLANKLINE )
$(P Overload sets can be merged with an alias declaration:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) A;
$(D_KEYWORD import) B;
$(D_KEYWORD alias) foo = A.foo;
$(D_KEYWORD alias) foo = B.foo;
$(D_KEYWORD void) bar(C c)
{
foo(); $(D_COMMENT // calls A.foo$(LPAREN)$(RPAREN )
) foo(1L); $(D_COMMENT // calls A.foo$(LPAREN)long$(RPAREN )
) foo(c); $(D_COMMENT // calls B.foo$(LPAREN)C$(RPAREN )
) foo(1,2); $(D_COMMENT // error, does not match any foo
) foo(1); $(D_COMMENT // calls B.foo$(LPAREN)int$(RPAREN )
) A.foo(1); $(D_COMMENT // calls A.foo$(LPAREN)long$(RPAREN )
)}
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
return
and $(D scope).
Parameters can also take the type constructors $(D const), $(D immutable), $(D shared) and inout
.
)
$(DDOC_BLANKLINE )
$(P $(D in), $(D out), $(D ref) and $(D lazy) are mutually exclusive. The first three are used to
denote input, output and input/output parameters, respectively.
For example:
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD int) read($(D_KEYWORD in) $(D_KEYWORD char)[] input, $(D_KEYWORD ref) size_t count, $(D_KEYWORD out) $(D_KEYWORD int) errno);
$(D_KEYWORD void) main()
{
size_t a = 42;
$(D_KEYWORD int) b;
$(D_KEYWORD int) r = read($(D_STRING "Hello World"), a, b);
}
)
$(DDOC_BLANKLINE )
$(P read
has three parameters. $(D input) will only be read and no reference to it will be retained.
$(D count) may be read and written to, and $(D errno) will be set to a value from
within the function.)
$(DDOC_BLANKLINE )
$(P The argument $(D "Hello World") gets bound to parameter $(D input),
$(D a) gets bound to $(D count) and $(D b) to $(D errno).
)
$(DDOC_BLANKLINE )
$(TABLE_2COLS Parameter Storage Class and Type Constructor Overview,
Storage Class, Description
$(DDOC_BLANKLINE )
$(TROW $(I none), The parameter will be a mutable copy of its argument.)
$(DDOC_BLANKLINE )
$(TROW $(D in), The parameter is an input to the function.)
$(DDOC_BLANKLINE )
$(TROW $(D out), The argument must be an lvalue, which will be passed by reference and initialized
upon function entry with the default value (T.init
) of its type.
)
$(DDOC_BLANKLINE )
$(TROW $(D ref), The parameter is an $(I input/output) parameter, passed by reference.
)
$(DDOC_BLANKLINE )
$(TROW $(D scope), $(ARGS The parameter must not escape the function call
(e.g. by being assigned to a global variable).
Ignored for any parameter that is not a reference type.
))
$(DDOC_BLANKLINE )
$(TROW $(D return), $(ARGS Parameter may be returned or copied to the first parameter,
but otherwise does not escape from the function.
Such copies are required not to outlive the argument(s) they were derived from.
Ignored for parameters with no references.
See $(DDSUBLINK spec/memory-safe-d, scope-return-params, Scope Parameters).))
$(DDOC_BLANKLINE )
$(TROW $(D lazy), argument is evaluated by the called function and not by the caller)
$(DDOC_BLANKLINE )
Type Constructor, Description
$(DDOC_BLANKLINE )
$(TROW $(D const), argument is implicitly converted to a const type)
$(TROW $(D immutable), argument is implicitly converted to an immutable type)
$(TROW $(D shared), argument is implicitly converted to a shared type)
$(TROW $(D inout), argument is implicitly converted to an inout type)
)
$(DDOC_BLANKLINE )
in
is equivalent to const
.)
$(P The parameter is an input to the function. Input parameters behave as if they have
the $(D const scope) storage classes. Input parameters may also be passed by reference by the compiler.)
$(P Unlike $(D ref) parameters, $(D in) parameters can bind to both lvalues and rvalues
(such as literals).)
$(P Types that would trigger a side effect if passed by value (such as types with copy constructor,
postblit, or destructor), and types which cannot be copied
(e.g. if their copy constructor is marked as $(D @disable)) will always be passed by reference.
Dynamic arrays, classes, associative arrays, function pointers, and delegates
will always be passed by value.)
$(IMPLEMENTATION_DEFINED If the type of the parameter does not fall in one of those categories,
whether or not it is passed by reference is implementation defined, and the backend is free
to choose the method that will best fit the ABI of the platform.
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
ref
parameter takes an lvalue argument, so changes to its value will operate
on the caller's argument.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD void) inc($(D_KEYWORD ref) $(D_KEYWORD int) x)
{
x += 1;
}
$(D_KEYWORD void) seattle()
{
$(D_KEYWORD int) z = 3;
inc(z);
$(D_KEYWORD assert)(z == 4);
}
)
)
$(DDOC_BLANKLINE )
$(P A ref
parameter can also be returned by reference, see
$(RELATIVE_LINK2 return-ref-parameters, Return Ref Parameters.))
$(DDOC_BLANKLINE )
$(P An out
parameter is similar to a ref
parameter, except it is initialized
with x.init
upon function invocation.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD void) zero($(D_KEYWORD out) $(D_KEYWORD int) x)
{
$(D_KEYWORD assert)(x == 0);
}
$(D_KEYWORD void) two($(D_KEYWORD out) $(D_KEYWORD int) x)
{
x = 2;
}
$(D_KEYWORD void) tacoma()
{
$(D_KEYWORD int) a = 3;
zero(a);
$(D_KEYWORD assert)(a == 0);
$(D_KEYWORD int) y = 3;
two(y);
$(D_KEYWORD assert)(y == 2);
}
)
)
$(DDOC_BLANKLINE )
$(P For dynamic array and class object parameters, which are always passed
by reference, out
and ref
apply only to the reference and not the contents.
)
$(DDOC_BLANKLINE )
ref
variable is also checked in @safe
code.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD int)* pluto($(D_KEYWORD ref) $(D_KEYWORD int) i) @safe
{
$(D_KEYWORD return) &i; $(D_COMMENT // error: returning &i escapes a reference to parameter i
)}
$(D_KEYWORD int)* mars($(D_KEYWORD return) $(D_KEYWORD ref) $(D_KEYWORD int) i) @safe
{
$(D_KEYWORD return) &i; $(D_COMMENT // OK with -preview=dip1000
)}
)
)
$(DDOC_BLANKLINE )
$(P If a function returns void
, and the first parameter is ref
or out
, then
all subsequent return ref
parameters are considered as being assigned to
the first parameter for lifetime checking.
The this
reference parameter to a struct non-static member function is
considered the first parameter.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD struct) S
{
$(D_KEYWORD private) $(D_KEYWORD int)* p;
$(D_KEYWORD void) f($(D_KEYWORD return) $(D_KEYWORD ref) $(D_KEYWORD int) i) $(D_KEYWORD scope) @safe
{
p = &i;
}
}
$(D_KEYWORD void) main() @safe
{
$(D_KEYWORD int) i;
S s;
s.f(i); $(D_COMMENT // OK with -preview=dip1000, lifetime of `s` is shorter than `i`
) *s.p = 2;
$(D_KEYWORD assert)(i == 2);
}
)
$(DDOC_BLANKLINE )
$(P If there are multiple return ref
parameters, the lifetime of the return
value is the smallest lifetime of the corresponding arguments.)
$(DDOC_BLANKLINE )
$(P Neither the type of the return ref
parameter(s) nor the type of the return
value is considered when determining the lifetime of the return value.)
$(DDOC_BLANKLINE )
$(P It is not an error if the return type does not contain any indirections.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD int) mercury($(D_KEYWORD return) $(D_KEYWORD ref) $(D_KEYWORD int) i)
{
$(D_KEYWORD return) i; $(D_COMMENT // ok
)}
)
$(DDOC_BLANKLINE )
$(P Template functions, auto functions, nested functions and $(DDSUBLINK spec/expression, function_literals, lambdas)
can deduce the return
attribute.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE @safe:
$(D_KEYWORD ref) $(D_KEYWORD int) templateFunction()($(D_KEYWORD ref) $(D_KEYWORD int) i)
{
$(D_KEYWORD return) i; $(D_COMMENT // ok
)}
$(D_KEYWORD ref) $(D_KEYWORD auto) autoFunction($(D_KEYWORD ref) $(D_KEYWORD int) i)
{
$(D_KEYWORD return) i; $(D_COMMENT // ok
)}
$(D_KEYWORD void) uranus()
{
$(D_KEYWORD ref) $(D_KEYWORD int) nestedFunction($(D_KEYWORD ref) $(D_KEYWORD int) i)
{
$(D_KEYWORD return) i; $(D_COMMENT // ok
) }
$(D_KEYWORD auto) lambdaFunction =
($(D_KEYWORD ref) $(D_KEYWORD int) i)
{
$(D_KEYWORD return) &i; $(D_COMMENT // ok
) };
}
)
)
$(DDOC_BLANKLINE )
return
attribute to ensure a returned
reference will not outlive the struct instance.
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S
{
$(D_KEYWORD private) $(D_KEYWORD int) x;
$(D_KEYWORD ref) $(D_KEYWORD int) get() $(D_KEYWORD return) { $(D_KEYWORD return) x; }
}
$(D_KEYWORD ref) $(D_KEYWORD int) escape()
{
S s;
$(D_KEYWORD return) s.get(); $(D_COMMENT // Error: escaping reference to local variable s
)}
)
)
$(P The hidden this
ref-parameter then becomes return ref
.)
$(DDOC_BLANKLINE )
$(P The return
attribute can also be used to limit
the lifetime of the returned value, even when the method is not ref
:
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD struct) S
{
$(D_KEYWORD private) $(D_KEYWORD int) i;
$(D_KEYWORD int)* get() $(D_KEYWORD return) @safe => &i;
}
$(D_KEYWORD void) f() @safe
{
$(D_KEYWORD int)* p;
{
S s;
$(D_KEYWORD int) *q = s.get(); $(D_COMMENT // OK, q has shorter lifetime than s
) p = s.get(); $(D_COMMENT // error, p has longer lifetime
) p = ($(D_KEYWORD new) S).get(); $(D_COMMENT // OK, heap allocated S
) }
}
)
$(DDOC_BLANKLINE )
scope
parameter of reference type must not escape the function call
(e.g. by being assigned to a global variable). It has no effect for non-reference types.
scope
escape analysis is only done for @safe
functions. For other functions scope
semantics must be manually enforced.)
$(DDOC_BLANKLINE )
$(D_CODE @safe:
$(D_KEYWORD int)* gp;
$(D_KEYWORD void) thorin($(D_KEYWORD scope) $(D_KEYWORD int)*);
$(D_KEYWORD void) gloin($(D_KEYWORD int)*);
$(D_KEYWORD int)* balin($(D_KEYWORD scope) $(D_KEYWORD int)* q, $(D_KEYWORD int)* r)
{
gp = q; $(D_COMMENT // error, q escapes to global gp
) gp = r; $(D_COMMENT // ok
)
thorin(q); $(D_COMMENT // ok, q does not escape thorin$(LPAREN)$(RPAREN )
) thorin(r); $(D_COMMENT // ok
)
gloin(q); $(D_COMMENT // error, gloin$(LPAREN)$(RPAREN ) escapes q
) gloin(r); $(D_COMMENT // ok that gloin$(LPAREN)$(RPAREN ) escapes r
)
$(D_KEYWORD return) q; $(D_COMMENT // error, cannot return 'scope' q
) $(D_KEYWORD return) r; $(D_COMMENT // ok
)}
)
$(DDOC_BLANKLINE )
$(P As a scope
parameter must not escape, the compiler can potentially avoid heap-allocating a
unique argument to a scope
parameter. Due to this, passing an array literal, delegate
literal or a $(GLINK2 expression, NewExpression) to a scope parameter may be allowed in a
@nogc
context, depending on the compiler implementation.)
$(DDOC_BLANKLINE )
return scope
that contain indirections
can only escape those indirections via the function's return value.)
$(DDOC_BLANKLINE )
$(D_CODE @safe:
$(D_KEYWORD int)* gp;
$(D_KEYWORD void) thorin($(D_KEYWORD scope) $(D_KEYWORD int)*);
$(D_KEYWORD void) gloin($(D_KEYWORD int)*);
$(D_KEYWORD int)* balin($(D_KEYWORD return) $(D_KEYWORD scope) $(D_KEYWORD int)* p)
{
gp = p; $(D_COMMENT // error, p escapes to global gp
) thorin(p); $(D_COMMENT // ok, p does not escape thorin$(LPAREN)$(RPAREN )
) gloin(p); $(D_COMMENT // error, gloin$(LPAREN)$(RPAREN ) escapes p
) $(D_KEYWORD return) p; $(D_COMMENT // ok
)}
)
$(DDOC_BLANKLINE )
$(P Class references are considered pointers that are subject to scope
.)
$(DDOC_BLANKLINE )
$(D_CODE @safe:
$(D_KEYWORD class) C { }
C gp;
$(D_KEYWORD void) thorin($(D_KEYWORD scope) C);
$(D_KEYWORD void) gloin(C);
C balin($(D_KEYWORD return) $(D_KEYWORD scope) C p, $(D_KEYWORD scope) C q, C r)
{
gp = p; $(D_COMMENT // error, p escapes to global gp
) gp = q; $(D_COMMENT // error, q escapes to global gp
) gp = r; $(D_COMMENT // ok
)
thorin(p); $(D_COMMENT // ok, p does not escape thorin$(LPAREN)$(RPAREN )
) thorin(q); $(D_COMMENT // ok
) thorin(r); $(D_COMMENT // ok
)
gloin(p); $(D_COMMENT // error, gloin$(LPAREN)$(RPAREN ) escapes p
) gloin(q); $(D_COMMENT // error, gloin$(LPAREN)$(RPAREN ) escapes q
) gloin(r); $(D_COMMENT // ok that gloin$(LPAREN)$(RPAREN ) escapes r
)
$(D_KEYWORD return) p; $(D_COMMENT // ok
) $(D_KEYWORD return) q; $(D_COMMENT // error, cannot return 'scope' q
) $(D_KEYWORD return) r; $(D_COMMENT // ok
)}
)
$(DDOC_BLANKLINE )
$(P return scope
can be applied to the this
of class and interface member functions.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD class) C
{
C bofur() $(D_KEYWORD return) $(D_KEYWORD scope) { $(D_KEYWORD return) $(D_KEYWORD this); }
}
)
$(DDOC_BLANKLINE )
$(P Template functions, auto functions, nested functions and
$(DDSUBLINK spec/expression, function_literals, lambdas) can deduce
the return scope
attribute.)
$(DDOC_BLANKLINE )
return ref
and return scope
semantics
for the same parameter.
When a parameter is passed by ref
and has both the return
and scope
storage classes,
it gets $(LINK2 #return-scope-parameters, return scope
) semantics if and only if the return
and scope
keywords appear adjacent to each other, in that order.
Specifying a return ref
and scope
parameter enables returning a reference to a scope pointer.
In all other cases, the parameter has $(LINK2 #return-ref-parameters, return ref
) semantics
and regular $(LINK2 #scope-parameters, scope
) semantics.)
$(DDOC_BLANKLINE )
$(D_CODE U xerxes( $(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) V v) $(D_COMMENT // $(LPAREN)1$(RPAREN ) ref and return scope
)U sargon($(D_KEYWORD return) $(D_KEYWORD ref) $(D_KEYWORD scope) V v) $(D_COMMENT // $(LPAREN)2$(RPAREN ) return ref and scope
)
$(D_KEYWORD struct) S
{
$(D_COMMENT // note: in struct member functions, the implicit `this` parameter
) $(D_COMMENT // is passed by `ref`
)
U xerxes() $(D_KEYWORD return) $(D_KEYWORD scope); $(D_COMMENT // return scope
) U sargon() $(D_KEYWORD scope) $(D_KEYWORD return); $(D_COMMENT // return ref, `return` comes after `scope`
) U xerxes() $(D_KEYWORD return) $(D_KEYWORD const) $(D_KEYWORD scope); $(D_COMMENT // return ref, `return` and `scope` are not adjacent
)}
)
$(DDOC_BLANKLINE )
$(P Example of combinations of return scope
, return ref
, and scope
semantics:)
$(D_CODE @safe:
$(D_KEYWORD int)* globalPtr;
$(D_KEYWORD struct) S
{
$(D_KEYWORD int) val;
$(D_KEYWORD int)* ptr;
$(D_KEYWORD this)($(D_KEYWORD return) $(D_KEYWORD scope) $(D_KEYWORD ref) $(D_KEYWORD int)* p) { ptr = p; }
$(D_COMMENT // note: `this` is passed by `ref` in structs
)
$(D_KEYWORD int)* retRefA() $(D_KEYWORD scope) $(D_KEYWORD return) $(D_COMMENT // return-ref, scope
) {
globalPtr = $(D_KEYWORD this).ptr; $(D_COMMENT // disallowed, `this` is `scope`
) $(D_KEYWORD return) &$(D_KEYWORD this).val; $(D_COMMENT // allowed, `return` means `return ref`
) }
$(D_KEYWORD ref) $(D_KEYWORD int) retRefB() $(D_KEYWORD scope) $(D_KEYWORD return) $(D_COMMENT // return-ref, scope
) {
globalPtr = $(D_KEYWORD this).ptr; $(D_COMMENT // disallowed, `this` is `scope`
) $(D_KEYWORD return) $(D_KEYWORD this).val; $(D_COMMENT // allowed, `return` means `return ref`
) }
$(D_KEYWORD int)* retScopeA() $(D_KEYWORD return) $(D_KEYWORD scope) $(D_COMMENT // ref, return-scope
) {
$(D_KEYWORD return) &$(D_KEYWORD this).val; $(D_COMMENT // disallowed, escaping a reference to `this`
) $(D_KEYWORD return) $(D_KEYWORD this).ptr; $(D_COMMENT // allowed, returning a `return scope` pointer
) }
$(D_KEYWORD ref) $(D_KEYWORD int) retScopeB() $(D_KEYWORD return) $(D_KEYWORD scope) $(D_COMMENT // ref, return-scope
) {
$(D_KEYWORD return) $(D_KEYWORD this).val; $(D_COMMENT // disallowed, escaping a reference to `this`
) $(D_KEYWORD return) *$(D_KEYWORD this).ptr; $(D_COMMENT // allowed, returning a `return scope` pointer
) }
$(D_KEYWORD ref) $(D_KEYWORD int)* retRefScopeC() $(D_KEYWORD scope) $(D_KEYWORD return) $(D_COMMENT // return-ref, scope
) {
$(D_KEYWORD return) $(D_KEYWORD this).ptr; $(D_COMMENT // allowed, returning a reference to a scope pointer
) }
}
$(D_KEYWORD int)* retRefA($(D_KEYWORD return) $(D_KEYWORD ref) $(D_KEYWORD scope) S s)
{
globalPtr = s.ptr; $(D_COMMENT // disallowed, `s` is `scope`
) $(D_KEYWORD return) &s.val; $(D_COMMENT // allowed, returning a reference to `return ref s`
)}
$(D_KEYWORD ref) $(D_KEYWORD int) retRefB($(D_KEYWORD return) $(D_KEYWORD ref) $(D_KEYWORD scope) S s)
{
globalPtr = s.ptr; $(D_COMMENT // disallowed, `s` is `scope`
) $(D_KEYWORD return) s.val;
}
$(D_KEYWORD int)* retScopeA($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) S s)
{
$(D_KEYWORD return) &s.val; $(D_COMMENT // disallowed, escaping a reference to `s`
) $(D_KEYWORD return) s.ptr; $(D_COMMENT // allowed, returning a `return scope` pointer
)}
$(D_KEYWORD ref) $(D_KEYWORD int) retScopeB($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) S s)
{
$(D_KEYWORD return) s.val; $(D_COMMENT // disallowed, escaping a reference to `s`
) $(D_KEYWORD return) *s.ptr; $(D_COMMENT // allowed, returning a `return scope` pointer
)}
$(D_KEYWORD ref) $(D_KEYWORD int)* retRefScopeC($(D_KEYWORD return) $(D_KEYWORD ref) $(D_KEYWORD scope) $(D_KEYWORD int)* p)
{
$(D_KEYWORD return) p; $(D_COMMENT // allowed, returning a reference to a scope pointer
)}
)
$(DDOC_BLANKLINE )
scope
parameters in pure
functions)scope
, it may still be @safe
to assign it a scope
pointer in a function call.
The following conditions need to be met:
$(DDOC_BLANKLINE )
$(UL $(LI The function is $(RELATIVE_LINK2 pure-functions, pure
), hence the argument cannot be assigned to a global variable)
$(LI The function is $(RELATIVE_LINK2 nothrow-functions, nothrow
), hence the argument cannot be assigned to a thrown Exception
object)
$(LI None of the other parameters have mutable indirections, hence the argument cannot be assigned to a longer-lived variable)
)
$(DDOC_BLANKLINE )
Then, the parameter is still treated as scope
or return scope
depending on the return type of the function:
$(UL $(LI If the function returns by ref
or has a return type that contains pointers, the argument could be returned, so it is treated as return scope
)
$(LI Otherwise, the argument cannot escape the function, so it is treated as scope
)
)
$(DDOC_BLANKLINE )
$(D_CODE @safe:
$(D_KEYWORD int) dereference($(D_KEYWORD int)* x) $(D_KEYWORD pure) $(D_KEYWORD nothrow);
$(D_KEYWORD int)* identity($(D_KEYWORD int)* x) $(D_KEYWORD pure) $(D_KEYWORD nothrow);
$(D_KEYWORD int)* identityThrow($(D_KEYWORD int)* x) $(D_KEYWORD pure);
$(D_KEYWORD void) assignToRef($(D_KEYWORD int)* x, $(D_KEYWORD ref) $(D_KEYWORD int)* escapeHatch) $(D_KEYWORD pure) $(D_KEYWORD nothrow);
$(D_KEYWORD void) assignToPtr($(D_KEYWORD int)* x, $(D_KEYWORD int)** escapeHatch) $(D_KEYWORD pure) $(D_KEYWORD nothrow);
$(D_KEYWORD void) cannotAssignTo($(D_KEYWORD int)* x, $(D_KEYWORD const) $(D_KEYWORD ref) $(D_KEYWORD int)* noEscapeHatch) $(D_KEYWORD pure) $(D_KEYWORD nothrow);
$(D_KEYWORD int)* globalPtr;
$(D_KEYWORD int)* test($(D_KEYWORD scope) $(D_KEYWORD int)* ptr)
{
$(D_KEYWORD int) result = dereference(ptr); $(D_COMMENT // allowed, treated as `scope`
) $(D_KEYWORD int)* ptr2 = identity(ptr); $(D_COMMENT // allowed, treated as `return scope`
) $(D_KEYWORD int)* ptr3 = identityThrow(ptr); $(D_COMMENT // not allowed, can throw an `Exception`
) assignToRef(ptr, globalPtr); $(D_COMMENT // not allowed, mutable second parameter
) assignToPtr(ptr, &globalPtr); $(D_COMMENT // not allowed, mutable second parameter
) cannotAssignTo(ptr, globalPtr); $(D_COMMENT // allowed
)
$(D_KEYWORD return) ptr2; $(D_COMMENT // not allowed, ptr2 is inferred `scope` now
)}
)
$(DDOC_BLANKLINE )
...
as the last function parameter.
It has non-D linkage, such as $(D extern (C)).)
$(DDOC_BLANKLINE )
$(P To access the variadic arguments,
import the standard library
module $(LINK2 $(ROOT_DIR )phobos/core_stdc_stdarg.html, $(D core.stdc.stdarg)).
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) core.stdc.stdarg;
$(D_KEYWORD extern) (C) $(D_KEYWORD void) dry($(D_KEYWORD int) x, $(D_KEYWORD int) y, ...); $(D_COMMENT // C-style Variadic Function
)
$(D_KEYWORD void) spin()
{
dry(3, 4); $(D_COMMENT // ok, no variadic arguments
) dry(3, 4, 6.8); $(D_COMMENT // ok, one variadic argument
) dry(2); $(D_COMMENT // error, no argument for parameter y
)}
)
$(DDOC_BLANKLINE )
$(P There must be at least one non-variadic parameter declared.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD extern) (C) $(D_KEYWORD int) def(...); $(D_COMMENT // error, must have at least one parameter
))
$(DDOC_BLANKLINE )
$(P C-style variadic functions match the C calling convention for
variadic functions, and can call C Standard library
functions like $(D printf).
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD extern) (C) $(D_KEYWORD int) printf($(D_KEYWORD const)($(D_KEYWORD char))*, ...);
$(D_KEYWORD void) main()
{
printf($(D_STRING "hello world\n"));
}
)
$(DDOC_BLANKLINE )
$(P C-style variadic functions cannot be marked as $(D @safe).)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD void) wash()
{
rinse(3, 4, 5); $(D_COMMENT // first variadic argument is 5
)}
$(D_KEYWORD import) core.stdc.stdarg;
$(D_KEYWORD extern) (C) $(D_KEYWORD void) rinse($(D_KEYWORD int) x, $(D_KEYWORD int) y, ...)
{
va_list args;
va_start(args, y); $(D_COMMENT // y is the last named parameter
) $(D_KEYWORD int) z;
va_arg(args, z); $(D_COMMENT // z is set to 5
) va_end(args);
}
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
...
as the last
parameter.)
$(DDOC_BLANKLINE )
$(P ...
can be the only parameter.)
$(DDOC_BLANKLINE )
$(P If there are parameters preceding the ...
parameter, there
must be a comma separating them from the ...
.)
$(DDOC_BLANKLINE )
$(NOTE If the comma is ommitted, it is a $(RELATIVE_LINK2 variadic, TypeSafe Variadic Function).)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int) abc($(D_KEYWORD char) c, ...); $(D_COMMENT // one required parameter: c
)$(D_KEYWORD int) def(...); $(D_COMMENT // no required parameters
)$(D_KEYWORD int) ghi($(D_KEYWORD int) i ...); $(D_COMMENT // a typesafe variadic function
)$(D_COMMENT //int boo$(LPAREN), ...$(RPAREN ); // error
))
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(P Two hidden arguments are passed to the function:)
$(DDOC_BLANKLINE )
$(UL $(LI void* _argptr
)
$(LI TypeInfo[] _arguments
)
)
$(DDOC_BLANKLINE )
$(P $(D _argptr) is a
reference to the first of the variadic
arguments. To access the variadic arguments,
import $(LINK2 $(ROOT_DIR )phobos/core_vararg.html, $(D core.vararg)).
Use $(D _argptr) in conjunction with $(D core.va_arg):)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) core.vararg;
$(D_KEYWORD void) test()
{
foo(3, 4, 5); $(D_COMMENT // first variadic argument is 5
)}
@system $(D_KEYWORD void) foo($(D_KEYWORD int) x, $(D_KEYWORD int) y, ...)
{
$(D_KEYWORD int) z = va_arg!$(D_KEYWORD int)(_argptr); $(D_COMMENT // z is set to 5 and _argptr is advanced
) $(D_COMMENT // to the next argument
)}
)
$(DDOC_BLANKLINE )
$(P $(D _arguments) gives the number of arguments and the typeid
of each, enabling type safety to be checked at run time.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD import) std.stdio;
$(D_KEYWORD void) main()
{
Foo f = $(D_KEYWORD new) Foo();
Bar b = $(D_KEYWORD new) Bar();
writefln($(D_STRING "%s"), f);
printargs(1, 2, 3L, 4.5, f, b);
}
$(D_KEYWORD class) Foo { $(D_KEYWORD int) x = 3; }
$(D_KEYWORD class) Bar { $(D_KEYWORD long) y = 4; }
$(D_KEYWORD import) core.vararg;
@system $(D_KEYWORD void) printargs($(D_KEYWORD int) x, ...)
{
writefln($(D_STRING "%d arguments"), _arguments.length);
$(D_KEYWORD for) ($(D_KEYWORD int) i = 0; i < _arguments.length; i++)
{
writeln(_arguments[i]);
$(D_KEYWORD if) (_arguments[i] == $(D_KEYWORD typeid)($(D_KEYWORD int)))
{
$(D_KEYWORD int) j = va_arg!($(D_KEYWORD int))(_argptr);
writefln($(D_STRING "\t%d"), j);
}
$(D_KEYWORD else) $(D_KEYWORD if) (_arguments[i] == $(D_KEYWORD typeid)($(D_KEYWORD long)))
{
$(D_KEYWORD long) j = va_arg!($(D_KEYWORD long))(_argptr);
writefln($(D_STRING "\t%d"), j);
}
$(D_KEYWORD else) $(D_KEYWORD if) (_arguments[i] == $(D_KEYWORD typeid)($(D_KEYWORD double)))
{
$(D_KEYWORD double) d = va_arg!($(D_KEYWORD double))(_argptr);
writefln($(D_STRING "\t%g"), d);
}
$(D_KEYWORD else) $(D_KEYWORD if) (_arguments[i] == $(D_KEYWORD typeid)(Foo))
{
Foo f = va_arg!(Foo)(_argptr);
writefln($(D_STRING "\t%s"), f);
}
$(D_KEYWORD else) $(D_KEYWORD if) (_arguments[i] == $(D_KEYWORD typeid)(Bar))
{
Bar b = va_arg!(Bar)(_argptr);
writefln($(D_STRING "\t%s"), b);
}
$(D_KEYWORD else)
$(D_KEYWORD assert)(0);
}
}
)
)
$(DDOC_BLANKLINE )
which prints:
$(DDOC_BLANKLINE )
$(D_CODE 0x00870FE0
5 arguments
$(D_KEYWORD int)
2
$(D_KEYWORD long)
3
$(D_KEYWORD double)
4.5
Foo
0x00870FE0
Bar
0x00870FD0
)
$(DDOC_BLANKLINE )
$(P D-style variadic functions cannot be marked as $(D @safe).)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
scope
when declaring the array of delegates
parameter. This will prevent a closure being generated for the delegate,
as scope
means the delegate will not escape the function.)
$(DDOC_BLANKLINE )
$(LEGACY_LNAME2 this-reference)
this
reference), which refers to the object for which
the function is called.
)
$(LI D-style variadic functions have
$(RELATIVE_LINK2 d_style_variadic_functions, hidden parameters).)
$(LI Functions with Objective-C
linkage have an additional hidden,
unnamed, parameter which is the selector it was called with.
)
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
ref
or out
parameter)
$(DDOC_BLANKLINE )
$(TROW returned, returned via the return
statement)
$(TROW escaped, stored in a global or other memory not in the function$(RSQUO )s stack frame)
)
$(DDOC_BLANKLINE )
p
may be returned or escaped)
$(DDOC_BLANKLINE )
$(TROW ReturnScope,
p
may be returned but not escaped)
$(DDOC_BLANKLINE )
$(TROW Scope,
p
may be neither returned nor escaped)
$(DDOC_BLANKLINE )
$(TROW Ref,
p
may be returned or escaped,
ref
may not be returned nor escaped)
$(DDOC_BLANKLINE )
$(TROW ReturnRef,
p
may be returned or escaped,
ref
may be returned but not escaped)
$(DDOC_BLANKLINE )
$(TROW RefScope,
p
may be neither returned nor escaped,
ref
may not be returned nor escaped)
$(DDOC_BLANKLINE )
$(TROW ReturnRef-Scope,
p
may be neither returned nor escaped,
ref
may be returned but not escaped)
$(DDOC_BLANKLINE )
$(TROW Ref-ReturnScope,
p
may be returned but not escaped,
ref
may not be returned nor escaped)
$(DDOC_BLANKLINE )
$(TROW ReturnRef-ReturnScope,
p
may be returned but not escaped,
ref
may be returned but not escaped.
This isn't expressible with the current syntax
and so is not allowed.)
)
$(DDOC_BLANKLINE )
return
immediately preceding scope
means ReturnScope.
Otherwise, return
and ref
in any position means ReturnRef.)
$(DDOC_BLANKLINE )
$(TABLE2 Mapping,
Example, Classification, Comments
$(TROW X foo(P p)
,
None,)
$(DDOC_BLANKLINE )
$(TROW X foo(scope P p)
,
Scope,)
$(DDOC_BLANKLINE )
$(TROW P foo(return scope P p)
,
ReturnScope,)
$(DDOC_BLANKLINE )
$(TROW I foo(return scope P p)
,
Scope,
The return
is dropped because the return type I
contains no pointers.)
$(DDOC_BLANKLINE )
$(TROW P foo(return P p)
,
ReturnScope,
Makes no sense to have return
without scope
.)
$(DDOC_BLANKLINE )
$(TROW I foo(return P p)
,
Scope,
The return
is dropped because the return type I
contains no pointers.)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(TROW X foo(ref P p)
,
Ref,)
$(DDOC_BLANKLINE )
$(TROW X foo(ref scope P p)
,
RefScope,)
$(DDOC_BLANKLINE )
$(TROW P foo(ref return scope P p)
,
Ref-ReturnScope,)
$(DDOC_BLANKLINE )
$(TROW P foo(return ref scope P p)
,
ReturnRef-Scope,)
$(DDOC_BLANKLINE )
$(TROW I foo(ref return scope P p)
,
RefScope,)
$(DDOC_BLANKLINE )
$(TROW P foo(ref return P p)
,
ReturnRef,)
$(DDOC_BLANKLINE )
$(TROW I foo(ref return P p)
,
Ref,)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(TROW ref X foo(P p)
,
None,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(scope P p)
,
Scope,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(return scope P p)
,
ReturnScope,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(return P p)
,
ReturnScope,
Makes no sense to have return
without scope
.)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(TROW ref X foo(ref P p)
,
Ref,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(ref scope P p)
,
RefScope,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(ref return scope P p)
,
Ref-ReturnScope,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(return ref scope P p)
,
ReturnRef-Scope,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(ref return P p)
,
ReturnRef,)
$(DDOC_BLANKLINE )
$(TROW X foo(I i)
,
None,)
$(DDOC_BLANKLINE )
$(TROW X foo(scope I i)
,
None,)
$(DDOC_BLANKLINE )
$(TROW X foo(return scope I i)
,
None,)
$(DDOC_BLANKLINE )
$(TROW X foo(return I i)
,
None,)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(TROW X foo(ref I i)
,
Ref,)
$(DDOC_BLANKLINE )
$(TROW X foo(ref scope I i)
,
Ref,)
$(DDOC_BLANKLINE )
$(TROW X foo(ref return scope I i)
,
Ref,)
$(DDOC_BLANKLINE )
$(TROW P foo(ref return I i)
,
ReturnRef,)
$(DDOC_BLANKLINE )
$(TROW I foo(ref return I i)
,
Ref,)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(TROW ref X foo(I i)
,
None,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(scope I i)
,
None,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(return scope I i)
,
None,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(return I i)
,
None,)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(TROW ref X foo(ref I i)
,
Ref,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(ref scope I i)
,
Ref,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(ref return scope I i)
,
ReturnRef,)
$(DDOC_BLANKLINE )
$(TROW ref X foo(ref return I i)
,
ReturnRef,)
)
$(DDOC_BLANKLINE )
this
parameter is the first
parameter of a non-member function,
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD struct) S {
X foo();
}
)
$(DDOC_BLANKLINE )
$(P is treated as:)
$(DDOC_BLANKLINE )
$(D_CODE X foo($(D_KEYWORD ref) S);
)
$(DDOC_BLANKLINE )
$(P and:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD class) C {
X foo()
}
)
$(DDOC_BLANKLINE )
$(P is treated as:)
$(DDOC_BLANKLINE )
$(D_CODE X foo(P)
)
$(DDOC_BLANKLINE )
ref
and P, such as:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD int)* foo($(D_KEYWORD return) $(D_KEYWORD ref) $(D_KEYWORD int) i) { $(D_KEYWORD return) &i; }
$(D_KEYWORD ref) $(D_KEYWORD int) foo($(D_KEYWORD int)* p) { $(D_KEYWORD return) *p; }
)
$(DDOC_BLANKLINE )
ref
is not covariant with non-ref
, so those entries are omitted from the
table for simplicity.
)
$(DDOC_BLANKLINE )
$(TABLE2 Covariance,
From\To, None, ReturnScope, Scope
$(TROW None, ✔, , )
$(TROW ReturnScope, ✔, ✔ , )
$(TROW Scope, ✔, ✔ , ✔)
)
$(DDOC_BLANKLINE )
$(TABLE2 Ref Covariance,
From\To, Ref , ReturnRef, RefScope, ReturnRef-Scope, Ref-ReturnScope
$(TROW Ref, ✔, ✔ , , , )
$(TROW ReturnRef, , ✔ , , , )
$(TROW RefScope, ✔, ✔ , ✔, ✔ , ✔)
$(TROW ReturnRef-Scope, , ✔ , , ✔ , )
$(TROW Ref-ReturnScope, ✔, ✔ , , , ✔)
)
$(DDOC_BLANKLINE )
$(P For example, scope
matches all non-ref parameters, and ref scope
matches all
ref parameters.)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
static
, shared static
or $(D __gshared) are statically allocated
rather than being allocated on the stack.
The lifetime of __gshared
and shared static
variables begins
when the function is first executed and ends when the program ends.
The lifetime of static
variables begins when the function is first
executed within the thread and ends when that thread terminates.
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD import) std.stdio : writeln;
$(D_KEYWORD void) foo()
{
$(D_KEYWORD static) $(D_KEYWORD int) n;
$(D_KEYWORD if) (++n == 100)
writeln($(D_STRING "called 100 times"));
}
)
)
$(DDOC_BLANKLINE )
$(P The initializer for a static variable must be evaluatable at
compile time.
There are no static constructors or static destructors
for static local variables.
)
$(DDOC_BLANKLINE )
$(P Although static variable name visibility follows the usual scoping
rules, the names of them must be unique within a particular function.
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD void) main()
{
{ $(D_KEYWORD static) $(D_KEYWORD int) x; }
{ $(D_KEYWORD static) $(D_KEYWORD int) x; } $(D_COMMENT // error
) { $(D_KEYWORD int) i; }
{ $(D_KEYWORD int) i; } $(D_COMMENT // ok
)}
)
$(DDOC_BLANKLINE )
function
keyword:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) f($(D_KEYWORD int));
$(D_KEYWORD void) $(D_KEYWORD function)($(D_KEYWORD int)) fp = &f; $(D_COMMENT // fp is a pointer to a function taking an int
))
)
$(P A function pointer can point to a static nested function:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int) $(D_KEYWORD function)() fp; $(D_COMMENT // fp is a pointer to a function returning an int
)
$(D_KEYWORD void) test()
{
$(D_KEYWORD static) $(D_KEYWORD int) a = 7;
$(D_KEYWORD static) $(D_KEYWORD int) foo() { $(D_KEYWORD return) a + 3; }
fp = &foo;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD assert)(!fp);
test();
$(D_KEYWORD int) i = fp();
$(D_KEYWORD assert)(i == 10);
}
)
)
$(DDOC_BLANKLINE )
$(IMPLEMENTATION_DEFINED Two functions with identical bodies, or two functions
that compile to identical assembly code, are not guaranteed to have
distinct function pointer values. The implementation may merge
functions bodies into one if they compile to identical code.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int) abc($(D_KEYWORD int) x) { $(D_KEYWORD return) x + 1; }
$(D_KEYWORD uint) def($(D_KEYWORD uint) y) { $(D_KEYWORD return) y + 1; }
$(D_KEYWORD int) $(D_KEYWORD function)($(D_KEYWORD int)) fp1 = &abc;
$(D_KEYWORD uint) $(D_KEYWORD function)($(D_KEYWORD uint)) fp2 = &def;
$(D_COMMENT // Do not rely on fp1 and fp2 being different values; the compiler may merge
)$(D_COMMENT // them.
))
)
$(DDOC_BLANKLINE )
scope
parameter.)
$(LI The closure is an initializer for a scope
variable.)
$(LI The closure is assigned to a scope
variable.
)
)
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE @nogc:
$(D_KEYWORD void) f($(D_KEYWORD scope) $(D_KEYWORD int) $(D_KEYWORD delegate)());
$(D_KEYWORD void) g($(D_KEYWORD int) $(D_KEYWORD delegate)());
$(D_KEYWORD void) main()
{
$(D_KEYWORD int) i;
$(D_KEYWORD int) h() { $(D_KEYWORD return) i; }
h(); $(D_COMMENT // OK
) $(D_KEYWORD scope) x = &h; $(D_COMMENT // OK
) x(); $(D_COMMENT // OK
) $(D_COMMENT //auto y = &h; // error, can't allocate closure in @nogc function
) f(&h); $(D_COMMENT // OK
) $(D_COMMENT //g$(LPAREN)&h$(RPAREN ); // error
)
$(D_COMMENT // delegate literals
) f(() => i); $(D_COMMENT // OK
) $(D_KEYWORD scope) d = () => i; $(D_COMMENT // OK
) d = () => i + 1; $(D_COMMENT // OK
) f(d);
$(D_COMMENT //g$(LPAREN)$(LPAREN)$(RPAREN ) => i$(RPAREN ); // error, can't allocate closure in @nogc function
)}
)
)
$(DDOC_BLANKLINE )
$(NOTE Returning addresses of stack variables, however, is not
a closure and is an error.
)
$(DDOC_BLANKLINE )
&obj.method
:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) Foo
{
$(D_KEYWORD int) a;
$(D_KEYWORD int) get() { $(D_KEYWORD return) a; }
}
$(D_KEYWORD int) add1($(D_KEYWORD int) $(D_KEYWORD delegate)() dg)
{
$(D_KEYWORD return) dg() + 1;
}
$(D_KEYWORD void) main()
{
Foo f = {7};
$(D_KEYWORD int) $(D_KEYWORD delegate)() dg = &f.get; $(D_COMMENT // bind to an instance of Foo and a method
) $(D_KEYWORD assert)(dg.ptr == &f);
$(D_KEYWORD assert)(dg.funcptr == &Foo.get);
$(D_KEYWORD int) i = add1(dg);
$(D_KEYWORD assert)(i == 8);
$(D_KEYWORD int) x = 27;
$(D_KEYWORD int) abc() { $(D_KEYWORD return) x; }
i = add1(&abc);
$(D_KEYWORD assert)(i == 28);
}
)
)
$(DDOC_BLANKLINE )
$(P The $(D .ptr) property of a delegate will return the
$(I context pointer) value as a $(D void*).
)
$(DDOC_BLANKLINE )
$(P The $(D .funcptr) property of a delegate will return the
$(I function pointer) value as a function type.
)
$(DDOC_BLANKLINE )
null
.
)
$(DDOC_BLANKLINE )
$(P Delegates cannot be initialized by taking the address of a global function,
a static member function, or a static nested function.
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S
{
$(D_KEYWORD static) $(D_KEYWORD int) sfunc();
$(D_KEYWORD int) member() { $(D_KEYWORD return) 1; }
}
$(D_KEYWORD void) main()
{
S s;
$(D_KEYWORD int) $(D_KEYWORD delegate)() dg = &s.member; $(D_COMMENT // Ok, s supplies context pointer
) $(D_KEYWORD assert)(dg() == 1);
$(D_COMMENT //dg = &S.sfunc; // error
) $(D_COMMENT //dg = &S.member; // error
)
$(D_KEYWORD int) moon() { $(D_KEYWORD return) 2; }
dg = &moon; $(D_COMMENT // Ok
) $(D_KEYWORD assert)(dg() == 2);
$(D_KEYWORD static) $(D_KEYWORD int) mars() { $(D_KEYWORD return) 3; }
$(D_COMMENT //dg = &mars; // error
)
dg = () { $(D_KEYWORD return) 4; }; $(D_COMMENT // Ok
) $(D_KEYWORD assert)(dg() == 4);
}
)
)
$(P The last assignment uses a $(GLINK2 expression, FunctionLiteral), which
$(DDSUBLINK spec/expression, lambda-type-inference, is inferred)
as a delegate.)
$(DDOC_BLANKLINE )
$(NOTE Function pointers can be passed to functions taking a delegate argument by passing
them through the $(REF toDelegate, std,functional) template, which converts any callable
to a delegate with a null
context pointer.
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
main
returns void
, the OS will receive a zero value on success.)
$(LI If main
returns void
or noreturn
, the OS will receive a non-zero
value on abnormal termination, such as an uncaught exception.)
$(LI If main
is declared as auto
, the inferred return type must be
one of void
, int
and noreturn
.)
)
$(DDOC_BLANKLINE )
$(P If the $(D string[]) parameter is declared, the parameter will hold
arguments passed to the program by the OS. The first argument is typically
the executable name, followed by any command-line arguments.)
$(DDOC_BLANKLINE )
$(NOTE The runtime can remove any arguments prefixed --DRT-
.)
$(DDOC_BLANKLINE )
$(NOTE The aforementioned return / parameter types may be annotated with $(D const),
$(D immutable). They may also be replaced by $(D enum)'s with matching base types.)
$(DDOC_BLANKLINE )
$(P The main function must have D linkage.)
$(DDOC_BLANKLINE )
$(P Attributes may be added as needed, e.g. @safe
, @nogc
, nothrow
, etc.)
$(DDOC_BLANKLINE )
WinMain
and DllMain
on Windows systems.
)
$(DDOC_BLANKLINE )
$(NOTE Programs targeting platforms which require a different signature for $(D main) can use
a function with $(DDSUBLINK spec/pragma, mangle, explicit mangling):
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD pragma)(mangle, $(D_STRING "main"))
$(D_KEYWORD int) myMain($(D_KEYWORD int) a, $(D_KEYWORD int) b, $(D_KEYWORD int) c)
{
$(D_KEYWORD return) 0;
}
)
$(DDOC_BLANKLINE )
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
static if
))
$(LI $(DDSUBLINK spec/version, staticforeach, static foreach
))
$(LI $(DDSUBLINK spec/version, static-assert, static assert
))
$(LI $(DDSUBLINK spec/statement, mixin-statement,
mixin
statement))
$(LI $(DDLINK spec/pragma, Pragmas, pragma
argument))
$(LI $(DDLINK spec/traits, Traits, __traits
argument))
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD enum) eval($(D_KEYWORD alias) arg) = arg;
$(D_KEYWORD int) square($(D_KEYWORD int) i)
{
$(D_KEYWORD return) i * i;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD import) std.stdio;
$(D_KEYWORD static) j = square(3); $(D_COMMENT // CTFE
) writeln(j);
$(D_KEYWORD assert)(square(4) == 16); $(D_COMMENT // run time
) $(D_KEYWORD static) $(D_KEYWORD assert)(square(3) == 9); $(D_COMMENT // CTFE
) writeln(eval!(square(5))); $(D_COMMENT // CTFE
)}
)
)
$(DDOC_BLANKLINE )
$(P The function must have a $(GLINK SpecifiedFunctionBody).)
$(DDOC_BLANKLINE )
$(P CTFE is subject to the following restrictions:)
$(DDOC_BLANKLINE )
$(OL $(LI Expressions may not reference any global or local
static variables.)
$(DDOC_BLANKLINE )
$(LI $(DDSUBLINK spec/iasm, asmstatements, AsmStatements) are not permitted)
$(DDOC_BLANKLINE )
$(LI Non-portable casts (eg, from $(D int[]) to $(D float[])), including
casts which depend on endianness, are not permitted.
Casts between signed and unsigned types are permitted.)
$(DDOC_BLANKLINE )
$(LI Reinterpretation of overlapped fields in a union is not permitted.)
)
$(DDOC_BLANKLINE )
$(P Pointers are permitted in CTFE, provided they are used safely:)
$(DDOC_BLANKLINE )
$(UL $(LI Pointer arithmetic is permitted only on pointers which point to static
or dynamic array elements.
A pointer may also point to the first element past the array, although
such pointers cannot be dereferenced.
Pointer arithmetic on pointers which are null,
or which point to a non-array, is not allowed.
)
$(DDOC_BLANKLINE )
$(LI Ordered comparison ($(D <), $(D <)$(D =), $(D >), $(D >=)) between two pointers is permitted
when both pointers point to the same array, or when at least one
pointer is $(D null).
)
$(DDOC_BLANKLINE )
$(LI Pointer comparisons between discontiguous memory blocks are illegal,
unless two such comparisons are combined
using $(D &&) or $(CODE_PIPE )$(CODE_PIPE ) to yield a result which is independent of the
ordering of memory blocks. Each comparison must consist of two pointer
expressions compared with $(D <), $(D <)$(D =), $(D >),
or $(D >)$(D =), and may optionally be
negated with $(D !).
For example, the expression $(D (p1 > q1 && p2 <= q2))
is permitted when $(D p1), $(D p2) are expressions yielding pointers
to memory block $(I P), and $(D q1), $(D q2) are expressions yielding
pointers to memory block $(I Q), even when $(I P) and $(I Q) are
unrelated memory blocks.
It returns true if $(D [p1..p2]) lies inside $(D [q1..q2]), and false otherwise.
Similarly, the expression $(D (p1 < q1 || p2 > q2)) is true if
$(D [p1..p2]) lies outside $(D [q1..q2]), and false otherwise.
)
$(DDOC_BLANKLINE )
$(LI Equality comparisons (==, !=, $(D_KEYWORD is), $(D_KEYWORD !is)) are
permitted between all pointers, without restriction.
)
$(DDOC_BLANKLINE )
$(LI Any pointer may be cast to $(D void*) and from $(D void*) back to
its original type. Casting between pointer and non-pointer types is
illegal.
)
)
$(DDOC_BLANKLINE )
$(P The above restrictions apply only to expressions which are
actually executed. For example:
)
$(D_CODE $(D_KEYWORD static) $(D_KEYWORD int) y = 0;
$(D_KEYWORD int) countTen($(D_KEYWORD int) x)
{
$(D_KEYWORD if) (x > 10)
++y; $(D_COMMENT // access static variable
) $(D_KEYWORD return) x;
}
$(D_KEYWORD static) $(D_KEYWORD assert)(countTen(6) == 6); $(D_COMMENT // OK
)$(D_KEYWORD static) $(D_KEYWORD assert)(countTen(12) == 12); $(D_COMMENT // invalid, modifies y.
))
$(P The $(D __ctfe) boolean pseudo-variable evaluates to $(D_KEYWORD true)
during CTFE but $(D_KEYWORD false) otherwise.
)
$(DDOC_BLANKLINE )
$(NOTE __ctfe
can be used to provide
an alternative execution path to avoid operations which are forbidden
in CTFE. Every usage of $(D __ctfe) is statically evaluated
and has no run-time cost.
)
$(DDOC_BLANKLINE )
$(P Non-recoverable errors (such as $(D_KEYWORD assert) failures) are illegal.
)
$(DDOC_BLANKLINE )
$(IMPLEMENTATION_DEFINED Executing functions via CTFE can take considerably
longer than executing it at run time.
If the function goes into an infinite loop, it may cause the compiler to hang.
)
$(DDOC_BLANKLINE )
$(IMPLEMENTATION_DEFINED Functions executed via CTFE can give different results
from run time when implementation-defined or undefined-behavior occurs.
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
foo
cannot be
generated.
$(DDOC_BLANKLINE )
$(BEST_PRACTICE A function template, where s
is a template argument,
would be the appropriate
method to implement this sort of thing.
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
new
) on the heap
$(NOTE new
declarations of $(D class types) in function scopes are compatible with
$(D @nogc) if used for $(D scope) variables, as they result in allocations on the stack))
$(LI calling functions that are not @nogc
, unless the call is
in a $(GLINK2 version, ConditionalStatement)
controlled by a $(GLINK2 version, DebugCondition))
)
$(DDOC_BLANKLINE )
$(D_CODE @nogc $(D_KEYWORD void) foo()
{
$(D_KEYWORD auto) a = ['a']; $(D_COMMENT // $(LPAREN)1$(RPAREN ) error, allocates
) a.length = 1; $(D_COMMENT // $(LPAREN)2$(RPAREN ) error, array resizing allocates
) a = a ~ a; $(D_COMMENT // $(LPAREN)3$(RPAREN ) error, arrays concatenation allocates
) a ~= 'c'; $(D_COMMENT // $(LPAREN)4$(RPAREN ) error, appending to arrays allocates
)
$(D_KEYWORD auto) aa = [$(D_STRING "x"):1]; $(D_COMMENT // $(LPAREN)5$(RPAREN ) error, allocates
) aa[$(D_STRING "abc")]; $(D_COMMENT // $(LPAREN)6$(RPAREN ) error, indexing may allocate and throws
)
$(D_KEYWORD auto) p = $(D_KEYWORD new) $(D_KEYWORD int); $(D_COMMENT // $(LPAREN)7$(RPAREN ) error, operator new allocates
) $(D_KEYWORD scope) $(D_KEYWORD auto) p = $(D_KEYWORD new) GenericClass(); $(D_COMMENT // $(LPAREN)7$(RPAREN ) Ok
) bar(); $(D_COMMENT // $(LPAREN)8$(RPAREN ) error, bar$(LPAREN)$(RPAREN ) may allocate
) $(D_KEYWORD debug) bar(); $(D_COMMENT // $(LPAREN)8$(RPAREN ) Ok
)}
$(D_KEYWORD void) bar() { }
)
$(DDOC_BLANKLINE )
$(P No-GC functions can only use a closure if it is scope
-
see $(RELATIVE_LINK2 closures, Delegates & Closures).
)
$(DDOC_BLANKLINE )
$(D_CODE @nogc $(D_KEYWORD int) $(D_KEYWORD delegate)() foo()
{
$(D_KEYWORD int) n; $(D_COMMENT // error, variable n cannot be allocated on heap
) $(D_KEYWORD return) (){ $(D_KEYWORD return) n; } $(D_COMMENT // since `n` escapes `foo$(LPAREN)$(RPAREN )`, a closure is required
)}
)
$(DDOC_BLANKLINE )
$(P $(D @nogc) affects the type of the function. A $(D @nogc)
function is covariant with a non-$(D @nogc) function.
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD void) $(D_KEYWORD function)() fp;
$(D_KEYWORD void) $(D_KEYWORD function)() @nogc gp; $(D_COMMENT // pointer to @nogc function
)
$(D_KEYWORD void) foo();
@nogc $(D_KEYWORD void) bar();
$(D_KEYWORD void) test()
{
fp = &foo; $(D_COMMENT // ok
) fp = &bar; $(D_COMMENT // ok, it's covariant
) gp = &foo; $(D_COMMENT // error, not contravariant
) gp = &bar; $(D_COMMENT // ok
)}
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
@safe
can be inferred,
see $(RELATIVE_LINK2 function-attribute-inference, Function Attribute Inference).)
$(DDOC_BLANKLINE )
$(P Safe functions have $(RELATIVE_LINK2 safe-interfaces, safe
interfaces). An implementation must enforce this by restricting the
function's body to operations that are known safe.)
$(DDOC_BLANKLINE )
$(P The following operations are not allowed in safe
functions:)
$(DDOC_BLANKLINE )
$(UL $(LI No casting from a pointer type to any type with pointers other than $(CODE void*).)
$(LI No casting from any non-pointer type to a pointer type.)
$(LI No pointer arithmetic (including pointer indexing).)
$(LI Cannot access unions that have pointers or references overlapping
with other types.)
$(LI Cannot access unions that have fields with invariants overlapping
with other types.)
$(LI Calling any $(RELATIVE_LINK2 system-functions, System Functions).)
$(LI No catching of exceptions that are not derived from
$(LINK2 https://dlang.org/phobos/object.html#.Exception, $(D class Exception)).)
$(LI No inline assembler.)
$(LI No explicit casting of mutable objects to immutable.)
$(LI No explicit casting of immutable objects to mutable.)
$(LI No explicit casting of thread local objects to shared.)
$(LI No explicit casting of shared objects to thread local.)
$(LI Cannot access $(D __gshared) variables.)
$(LI Cannot use $(D void) initializers for pointers.)
$(LI Cannot use $(D void) initializers for class or interface references.)
$(LI Cannot use $(D void) initializers for types that have invariants.)
)
$(DDOC_BLANKLINE )
$(P When indexing or slicing an array, an out of bounds access
will cause a runtime error.
)
$(DDOC_BLANKLINE )
$(P Functions nested inside safe functions default to being
safe functions.
)
$(DDOC_BLANKLINE )
$(P Safe functions are covariant with trusted or system functions.)
$(DDOC_BLANKLINE )
$(BEST_PRACTICE Mark as many functions @safe
as practical.)
$(DDOC_BLANKLINE )
extern (C)
and extern (C++)
functions as
@system
when their implementations are not in D, as the D compiler will be
unable to check them. Most of them are @safe
, but will need to be manually
checked.)
$(DDOC_BLANKLINE )
$(BEST_PRACTICE The number and size of system functions should be minimized.
This minimizes the work necessary to manually check for safety.)
$(DDOC_BLANKLINE )
ref
parameters), and)
$(LI it cannot introduce unsafe aliasing that is accessible from
other parts of the program.)
)
$(DDOC_BLANKLINE )
$(P Functions that meet these requirements may be
$(RELATIVE_LINK2 safe-functions, @safe
) or
$(RELATIVE_LINK2 trusted-functions, @trusted
). Function that do not
meet these requirements can only be
$(RELATIVE_LINK2 system-functions, @system
).)
$(DDOC_BLANKLINE )
$(P Examples:)
$(DDOC_BLANKLINE )
$(UL $(LI C's free
does not have a safe interface:
$(D_CODE $(D_KEYWORD extern) (C) @system $(D_KEYWORD void) free($(D_KEYWORD void)* ptr);
)
because free(p)
invalidates p
, making its value unsafe.
free
can only be @system
.
)
$(LI C's strlen
and memcpy
do not have safe interfaces:
$(D_CODE $(D_KEYWORD extern) (C) @system size_t strlen($(D_KEYWORD char)* s);
$(D_KEYWORD extern) (C) @system $(D_KEYWORD void)* memcpy($(D_KEYWORD void)* dst, $(D_KEYWORD void)* src, size_t nbytes);
)
because they iterate pointers based on unverified assumptions
(strlen
assumes that s
is zero-terminated; memcpy
assumes
that the memory objects pointed to by dst
and src
are at least nbytes
big). Any function
that traverses a C string passed as an argument can only be
@system
. Any function that trusts a separate parameter for
array bounds can only be @system
.
)
$(LI C's malloc
does have a safe interface:
$(D_CODE $(D_KEYWORD extern) (C) @trusted $(D_KEYWORD void)* malloc(size_t sz);
)
It does not exhibit undefined behavior for any input. It returns
either a valid pointer, which is safe, or null
which is also
safe. It returns a pointer to a fresh allocation, so it cannot
introduce any unsafe aliasing.
$(NOTE The implementation of malloc
is most likely @system code.)
)
$(LI A D version of memcpy
can have a safe interface:
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE @safe $(D_KEYWORD void) memcpy(E)(E[] src, E[] dst)
{
$(D_KEYWORD import) std.math : min;
$(D_KEYWORD foreach) (i; 0 .. min(src.length, dst.length))
{
dst[i] = src[i];
}
}
)
)
because the rules for $(RELATIVE_LINK2 safe-values, safe
values) ensure that the lengths of the arrays are correct.
)
)
$(DDOC_BLANKLINE )
bool
, only 0 and 1 are safe values.)
$(DDOC_BLANKLINE )
$(P For all other $(DDSUBLINK spec/type, basic-data-types, basic data types), all
possible bit patterns are safe.)
$(DDOC_BLANKLINE )
$(P A pointer is a safe value when it is one of:)
$(OL $(LI null
)
$(LI it points to a memory object that is live and
the pointed to value in that memory object is safe.)
)
$(P Examples:)
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)* n = $(D_KEYWORD null); $(D_COMMENT /* n is safe because dereferencing null is a well-defined
crash. */)
$(D_KEYWORD int)* x = $(D_KEYWORD cast)($(D_KEYWORD int)*) 0xDEADBEEF; $(D_COMMENT /* x is $(LPAREN)most likely$(RPAREN ) unsafe because it
is not a valid pointer and cannot be dereferenced. */)
$(D_KEYWORD import) core.stdc.stdlib: malloc, free;
$(D_KEYWORD int)* p1 = $(D_KEYWORD cast)($(D_KEYWORD int)*) malloc($(D_KEYWORD int).sizeof); $(D_COMMENT /* p1 is safe because the
pointer is valid and *p1 is safe regardless of its actual value. */)
free(p1); $(D_COMMENT /* This makes p1 unsafe. */)
$(D_KEYWORD int)** p2 = &p1; $(D_COMMENT /* While it can be dereferenced, p2 is unsafe because p1
is unsafe. */)
p1 = $(D_KEYWORD null); $(D_COMMENT /* This makes p1 and p2 safe. */)
)
)
$(DDOC_BLANKLINE )
$(P A dynamic array is safe when:)
$(OL $(LI its pointer is safe, and)
$(LI its length is in-bounds with the corresponding memory object,
and)
$(LI all its elements are safe.)
)
$(DDOC_BLANKLINE )
$(P Examples:)
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int)[] f() @system
{
$(D_KEYWORD bool) b = $(D_KEYWORD true); $(D_COMMENT /* b is initialized safe */)
*($(D_KEYWORD cast)($(D_KEYWORD ubyte)*) &b) = 0xAA; $(D_COMMENT /* b is now unsafe because it's not 0 or 1 */)
$(D_KEYWORD int)[3] a;
$(D_KEYWORD int)[] d1 = a[0 .. 2]; $(D_COMMENT /* d1 is safe. */)
$(D_KEYWORD int)[] d2 = a.ptr[0 .. 3]; $(D_COMMENT /* d2 is unsafe because it goes beyond a's
bounds. */)
$(D_KEYWORD int)*[] d3 = [$(D_KEYWORD cast)($(D_KEYWORD int)*) 0xDEADBEEF]; $(D_COMMENT /* d3 is unsafe because the
element is unsafe. */)
$(D_KEYWORD return) d1; $(D_COMMENT /* Up to here, d1 was safe, but its pointer becomes
invalid when the function returns, so the returned dynamic array
is unsafe. */)
}
)
)
$(DDOC_BLANKLINE )
$(P A static array is safe when all its elements are safe. Regardless
of the element type, a static array with length zero is always safe.)
$(DDOC_BLANKLINE )
$(P An associative array is safe when all its keys and elements are
safe.)
$(DDOC_BLANKLINE )
$(P A struct/union instance is safe when:)
$(OL $(LI the values of its accessible fields are safe, and)
$(LI it does not introduce $(RELATIVE_LINK2 safe-aliasing, unsafe
aliasing) with unions.)
)
$(DDOC_BLANKLINE )
$(P Examples:)
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE
$(D_KEYWORD void) fun()
{
$(D_KEYWORD struct) S { $(D_KEYWORD int)* p; }
S s1 = S($(D_KEYWORD new) $(D_KEYWORD int)); $(D_COMMENT /* s1 is safe. */)
S s2 = S($(D_KEYWORD cast)($(D_KEYWORD int)*) 0xDEADBEEF); $(D_COMMENT /* s2 is unsafe, because s2.p is
unsafe. */)
$(D_KEYWORD union) U { $(D_KEYWORD int)* p; size_t x; }
U u = U($(D_KEYWORD new) $(D_KEYWORD int)); $(D_COMMENT /* Even though both u.p and u.x are safe, u is unsafe
because of unsafe aliasing. */)
}
)
)
$(DDOC_BLANKLINE )
$(P A class reference is safe when it is null
or:)
$(OL $(LI it refers to a valid class instance of the class type or a
type derived from the class type, and)
$(LI the values of the instance's accessible fields are safe, and)
$(LI it does not introduce unsafe aliasing with unions.)
)
$(DDOC_BLANKLINE )
$(P A function pointer is safe when it is null
or it refers to a valid
function that has the same or a covariant signature.)
$(DDOC_BLANKLINE )
$(P A delegate
is safe when:)
$(OL $(LI its .funcptr
property is null
or refers to a function that matches
or is covariant with the delegate type, and)
$(LI its .ptr
property is null
or refers to a memory object that is in a form
expected by the function.)
)
$(DDOC_BLANKLINE )
const
or immutable
; or)
$(LI one of the types is mutable while the other is a const
-qualified
$(DDSUBLINK spec/type, basic-data-types, basic data type); or)
$(LI both types are mutable basic data types; or)
$(LI one of the types is a static array type with length zero; or)
$(LI one of the types is a static array type with non-zero length, and
aliasing of the array's element type and the other type is safe; or)
$(LI both types are pointer types, and aliasing of the target types is
safe, and the target types have the same size.)
)
$(DDOC_BLANKLINE )
$(P All other cases of aliasing are considered unsafe.)
$(DDOC_BLANKLINE )
$(NOTE Safe aliasing may be exposed to functions with
$(RELATIVE_LINK2 safe-interfaces, safe interfaces) without affecting their
guaranteed safety. Unsafe aliasing does not guarantee safety.)
$(DDOC_BLANKLINE )
$(NOTE Safe aliasing does not imply that all aliased
views of the data have $(RELATIVE_LINK2 safe-values, safe values).
Those must examined separately for safety.)
$(DDOC_BLANKLINE )
$(P Examples:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) f1($(D_KEYWORD ref) $(D_KEYWORD ubyte) x, $(D_KEYWORD ref) $(D_KEYWORD float) y) @safe { x = 0; y = $(D_KEYWORD float).init; }
$(D_KEYWORD union) U1 { $(D_KEYWORD ubyte) x; $(D_KEYWORD float) y; } $(D_COMMENT // safe aliasing
)
$(D_KEYWORD void) test1()
{
U1 u1;
f1(u1.x, u1.y); $(D_COMMENT // Ok
)}
$(D_KEYWORD void) f2($(D_KEYWORD ref) $(D_KEYWORD int)* x, $(D_KEYWORD ref) $(D_KEYWORD int) y) @trusted { x = $(D_KEYWORD new) $(D_KEYWORD int); y = 0xDEADBEEF; }
$(D_KEYWORD union) U2 { $(D_KEYWORD int)* x; $(D_KEYWORD int) y; } $(D_COMMENT // unsafe aliasing
)
$(D_KEYWORD void) test2()
{
U2 u2;
$(D_KEYWORD version) (none) f1(u2.x, u2.y); $(D_COMMENT // not safe
)}
)
)
$(DDOC_BLANKLINE )
@system
.
)
$(DDOC_BLANKLINE )
$(P If a function attempts to test itself for those attributes, then
the function is inferred as not having those attributes.
)
$(DDOC_BLANKLINE )
$(RATIONALE Function attribute inference greatly reduces the need for the user to add attributes
to functions, especially for templates.)
$(DDOC_BLANKLINE )
final
) member functions.
This enables minimizing the number of functions in a class to only the essentials that
are needed to take care of the object's private state, without the temptation to add
a kitchen-sink's worth of member functions.
It also enables
$(HTTP www.drdobbs.com/architecture-and-design/component-programming-in-d/240008321,
function chaining and component programming).
)
$(DDOC_BLANKLINE )
$(P A more complex example:)
$(DDOC_BLANKLINE )
$(D_CODE stdin.byLine(KeepTerminator.yes)
.map!(a => a.idup)
.array
.sort
.copy(stdout.lockingTextWriter());
)
$(DDOC_BLANKLINE )
$(P is the equivalent of:)
$(DDOC_BLANKLINE )
$(D_CODE copy(sort(array(map!(a => a.idup)(byLine(stdin, KeepTerminator.yes)))), lockingTextWriter(stdout));
)
$(DDOC_BLANKLINE )
$(P UFCS works with $(D @property) functions:)
$(DDOC_BLANKLINE )
$(D_CODE @property prop(X thisObj);
@property prop(X thisObj, $(D_KEYWORD int) value);
X obj;
obj.prop; $(D_COMMENT // if X does not have member prop, reinterpret as prop$(LPAREN)obj$(RPAREN );
)obj.prop = 1; $(D_COMMENT // similarly, reinterpret as prop$(LPAREN)obj, 1$(RPAREN );
))
$(DDOC_BLANKLINE )
$(P Functions declared in a local scope are not found when searching for a matching
UFCS function. Neither are other local symbols, although local imports are searched:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD module) a;
$(D_KEYWORD void) foo(X);
$(D_KEYWORD alias) boo = foo;
$(D_KEYWORD void) main()
{
$(D_KEYWORD void) bar(X); $(D_COMMENT // bar declared in local scope
) $(D_KEYWORD import) b : baz; $(D_COMMENT // void baz$(LPAREN)X$(RPAREN );
)
X obj;
obj.foo(); $(D_COMMENT // OK, calls a.foo;
) $(D_COMMENT //obj.bar$(LPAREN)$(RPAREN ); // NG, UFCS does not see nested functions
) obj.baz(); $(D_COMMENT // OK, calls b.baz, because it is declared at the
) $(D_COMMENT // top level scope of module b
)
$(D_KEYWORD import) b : boo = baz;
obj.boo(); $(D_COMMENT // OK, calls aliased b.baz instead of a.boo $(LPAREN)== a.foo$(RPAREN ),
) $(D_COMMENT // because the declared alias name 'boo' in local scope
) $(D_COMMENT // overrides module scope name
)}
)
$(DDOC_BLANKLINE )
$(P Member functions are not found when searching for a matching
UFCS function.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD class) C
{
$(D_KEYWORD void) mfoo(X); $(D_COMMENT // member function
) $(D_KEYWORD static) $(D_KEYWORD void) sbar(X); $(D_COMMENT // static member function
) $(D_KEYWORD import) b : ibaz = baz; $(D_COMMENT // void baz$(LPAREN)X$(RPAREN );
)
$(D_KEYWORD void) test()
{
X obj;
$(D_COMMENT //obj.mfoo$(LPAREN)$(RPAREN ); // NG, UFCS does not see member functions
) $(D_COMMENT //obj.sbar$(LPAREN)$(RPAREN ); // NG, UFCS does not see static member functions
) obj.ibaz(); $(D_COMMENT // OK, ibaz is an alias of baz which is declared at
) $(D_COMMENT // the top level scope of module b
) }
}
)
$(DDOC_BLANKLINE )
$(P Otherwise, UFCS function lookup proceeds normally.)
$(DDOC_BLANKLINE )
$(RATIONALE Local function symbols are not considered by UFCS
to avoid unexpected name conflicts. See below for problematic examples.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD int) front($(D_KEYWORD int)[] arr) { $(D_KEYWORD return) arr[0]; }
$(D_KEYWORD void) main()
{
$(D_KEYWORD int)[] a = [1,2,3];
$(D_KEYWORD auto) x = a.front(); $(D_COMMENT // call .front by UFCS
)
$(D_KEYWORD auto) front = x; $(D_COMMENT // front is now a variable
) $(D_KEYWORD auto) y = a.front(); $(D_COMMENT // Error, front is not a function
)}
$(D_KEYWORD class) C
{
$(D_KEYWORD int)[] arr;
$(D_KEYWORD int) front()
{
$(D_KEYWORD return) arr.front(); $(D_COMMENT // Error, C.front is not callable
) $(D_COMMENT // using argument types $(LPAREN)int[]$(RPAREN )
) }
}
)
$(DDOC_BLANKLINE )
$(SPEC_SUBNAV_PREV_NEXT const3, Type Qualifiers, operatoroverloading, Operator Overloading)
)
)