$(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 )

$(LNAME2 grammar, Function Declarations)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME FuncDeclaration): $(GLINK2 declaration, StorageClasses)$(OPT ) $(GLINK2 type, BasicType) $(GLINK FuncDeclarator) $(GLINK FunctionBody) $(GLINK AutoFuncDeclaration) $(DDOC_BLANKLINE ) $(GNAME AutoFuncDeclaration): $(GLINK2 declaration, StorageClasses) $(GLINK_LEX Identifier) $(GLINK FuncDeclaratorSuffix) $(GLINK FunctionBody) $(DDOC_BLANKLINE ) $(GNAME FuncDeclarator): $(GLINK2 type, TypeSuffixes)$(OPT ) $(GLINK_LEX Identifier) $(GLINK FuncDeclaratorSuffix) $(DDOC_BLANKLINE ) $(GNAME FuncDeclaratorSuffix): $(GLINK Parameters) $(GLINK MemberFunctionAttributes)$(OPT ) $(GLINK2 template, TemplateParameters) $(GLINK Parameters) $(GLINK MemberFunctionAttributes)$(OPT ) $(GLINK2 template, Constraint)$(OPT ) ) $(UL $(LI A FuncDeclaration with a $(GLINK FunctionBody) is called a function definition.) $(LI A FuncDeclaration with TemplateParameters defines a $(DDSUBLINK spec/template, function-templates, function template). $(DDOC_BLANKLINE ) ) )

Function Parameters

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME Parameters): $(D $(LPAREN)) $(GLINK ParameterList)$(OPT ) $(D $(RPAREN )) $(DDOC_BLANKLINE ) $(GNAME ParameterList): $(GLINK Parameter) $(GLINK Parameter) $(D ,) $(GSELF ParameterList)$(OPT ) $(GLINK VariadicArgumentsAttributes)$(OPT ) $(D ...) $(DDOC_BLANKLINE ) $(GNAME Parameter): $(GLINK ParameterAttributes)$(OPT ) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) $(GLINK ParameterAttributes)$(OPT ) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) $(D ...) $(GLINK ParameterAttributes)$(OPT ) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) $(D =) $(GLINK2 expression, AssignExpression) $(GLINK ParameterAttributes)$(OPT ) $(GLINK2 type, Type) $(GLINK ParameterAttributes)$(OPT ) $(GLINK2 type, Type) $(D ...) $(GLINK ParameterAttributes)$(OPT ) $(GLINK2 type, Type) $(D =) $(GLINK2 expression, AssignExpression) $(DDOC_BLANKLINE ) $(GNAME ParameterAttributes): $(GLINK ParameterStorageClass) $(GLINK2 attribute, UserDefinedAttribute) $(GSELF ParameterAttributes) $(GLINK ParameterStorageClass) $(GSELF ParameterAttributes) $(GLINK2 attribute, UserDefinedAttribute) $(DDOC_BLANKLINE ) $(GNAME ParameterStorageClass): $(D auto) $(GLINK2 type, TypeCtor) $(D final) $(D in) $(D lazy) $(D out) $(D ref) $(D return) $(D scope) $(DDOC_BLANKLINE ) $(GNAME VariadicArgumentsAttributes): $(GLINK VariadicArgumentsAttribute) $(GLINK VariadicArgumentsAttribute) $(GSELF VariadicArgumentsAttributes) $(DDOC_BLANKLINE ) $(GNAME VariadicArgumentsAttribute): $(D const) $(D immutable) $(D return) $(D scope) $(D shared) ) $(DDOC_BLANKLINE ) $(NOTE In D2, declaring a parameter final is a semantic error, but not a parse error.) $(DDOC_BLANKLINE ) $(P See also: $(RELATIVE_LINK2 param-storage, parameter storage classes).) $(DDOC_BLANKLINE )

Function Attributes

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME FunctionAttributes): $(GLINK FunctionAttribute) $(GLINK FunctionAttribute) $(GSELF FunctionAttributes) $(DDOC_BLANKLINE ) $(GNAME FunctionAttribute): $(GLINK2 attribute, FunctionAttributeKwd) $(GLINK2 attribute, Property) $(GLINK2 attribute, AtAttribute) $(DDOC_BLANKLINE ) $(GNAME MemberFunctionAttributes): $(GLINK MemberFunctionAttribute) $(GLINK MemberFunctionAttribute) $(GSELF MemberFunctionAttributes) $(DDOC_BLANKLINE ) $(GNAME MemberFunctionAttribute): $(D const) $(D immutable) $(D inout) $(D return) $(D scope) $(D shared) $(GLINK FunctionAttribute) ) $(DDOC_BLANKLINE )

$(LNAME2 function-bodies, Function Bodies)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME FunctionBody): $(GLINK SpecifiedFunctionBody) $(GLINK ShortenedFunctionBody) $(GLINK MissingFunctionBody) $(DDOC_BLANKLINE ) $(GNAME SpecifiedFunctionBody): $(D do)$(OPT ) $(GLINK2 statement, BlockStatement) $(GLINK FunctionContracts)$(OPT ) $(GLINK InOutContractExpression) $(D do)$(OPT ) $(GLINK2 statement, BlockStatement) $(GLINK FunctionContracts)$(OPT ) $(GLINK InOutStatement) $(D do) $(GLINK2 statement, BlockStatement) $(DDOC_BLANKLINE ) $(GNAME ShortenedFunctionBody): $(GLINK InOutContractExpressions)$(OPT ) $(D =>) $(GLINK2 expression, AssignExpression) $(D ;) ) $(DDOC_BLANKLINE ) $(P Examples:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) hasSpecifiedBody() { $(D_KEYWORD return) 1; } $(D_KEYWORD int) hasShortenedBody() => 1; $(D_COMMENT // equivalent )) $(DDOC_BLANKLINE ) $(LEGACY_LNAME2 FunctionLiteralBody) $(P The ShortenedFunctionBody form implies a $(DDSUBLINK spec/statement, ReturnStatement, return statement). This syntax also applies for $(DDSUBLINK spec/expression, function_literals, function literals).) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 function-declarations, Functions without Bodies)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME MissingFunctionBody): $(D ;) $(GLINK FunctionContracts)$(OPT ) $(GLINK InOutContractExpression) $(D ;) $(GLINK FunctionContracts)$(OPT ) $(GLINK InOutStatement) ) $(DDOC_BLANKLINE ) $(P Functions without bodies:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) foo(); ) $(DDOC_BLANKLINE ) $(P that are not declared as $(D abstract) are expected to have their implementations elsewhere, and that implementation will be provided at the link step. This enables an implementation of a function to be completely hidden from the user of it, and the implementation may be in another language such as C, assembler, etc. ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 contracts, Function Contracts)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME FunctionContracts): $(GLINK FunctionContract) $(GLINK FunctionContract) $(GSELF FunctionContracts) $(DDOC_BLANKLINE ) $(GNAME FunctionContract): $(GLINK InOutContractExpression) $(GLINK InOutStatement) $(DDOC_BLANKLINE ) $(GNAME InOutContractExpressions): $(GLINK InOutContractExpression) $(GLINK InOutContractExpression) $(GSELF InOutContractExpressions) $(DDOC_BLANKLINE ) $(GNAME InOutContractExpression): $(GLINK InContractExpression) $(GLINK OutContractExpression) $(DDOC_BLANKLINE ) $(GNAME InOutStatement): $(GLINK InStatement) $(GLINK OutStatement) $(DDOC_BLANKLINE ) $(GNAME InContractExpression): $(D in $(LPAREN)) $(GLINK2 expression, AssertArguments) $(D $(RPAREN )) $(DDOC_BLANKLINE ) $(GNAME OutContractExpression): $(D out $(LPAREN) ;) $(GLINK2 expression, AssertArguments) $(D $(RPAREN )) $(D out $(LPAREN)) $(GLINK_LEX Identifier) $(D ;) $(GLINK2 expression, AssertArguments) $(D $(RPAREN )) $(DDOC_BLANKLINE ) $(GNAME InStatement): $(D in) $(GLINK2 statement, BlockStatement) $(DDOC_BLANKLINE ) $(GNAME OutStatement): $(D out) $(GLINK2 statement, BlockStatement) $(D out) $(D $(LPAREN)) $(GLINK_LEX Identifier) $(D $(RPAREN )) $(GLINK2 statement, BlockStatement) ) $(DDOC_BLANKLINE ) $(P Function Contracts specify the preconditions and postconditions of a function. They are used in $(LINK2 contracts.html, Contract Programming). ) $(DDOC_BLANKLINE ) $(P Preconditions and postconditions do not affect the type of the function.) $(DDOC_BLANKLINE )

$(LNAME2 preconditions, Preconditions)

$(DDOC_BLANKLINE ) $(P An $(GLINK InContractExpression) is a precondition.) $(DDOC_BLANKLINE ) $(P The first $(GLINK2 expression, AssignExpression) of the $(GLINK2 expression, AssertArguments) must evaluate to true. If it does not, the precondition has failed.) $(DDOC_BLANKLINE ) $(P The second $(I AssignExpression), if present, must be implicitly convertible to type 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 )

$(LNAME2 postconditions, Postconditions)

$(DDOC_BLANKLINE ) $(P An $(GLINK OutContractExpression) is a postcondition.) $(DDOC_BLANKLINE ) $(P The first $(GLINK2 expression, AssignExpression) of the $(GLINK2 expression, AssertArguments) must evaluate to true. If it does not, the postcondition has failed.) $(DDOC_BLANKLINE ) $(P The second $(I AssignExpression), if present, must be implicitly convertible to type 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 )

Example

$(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) fun($(D_KEYWORD ref) $(D_KEYWORD int) a, $(D_KEYWORD int) b) $(D_KEYWORD in) (a > 0) $(D_KEYWORD in) (b >= 0, $(D_STRING "b cannot be negative!")) $(D_KEYWORD out) (r; r > 0, $(D_STRING "return must be positive")) $(D_KEYWORD out) (; a != 0) { $(D_COMMENT // function body )} ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) fun($(D_KEYWORD ref) $(D_KEYWORD int) a, $(D_KEYWORD int) b) $(D_KEYWORD in) { $(D_KEYWORD assert)(a > 0); $(D_KEYWORD assert)(b >= 0, $(D_STRING "b cannot be negative!")); } $(D_KEYWORD out) (r) { $(D_KEYWORD assert)(r > 0, $(D_STRING "return must be positive")); $(D_KEYWORD assert)(a != 0); } $(D_KEYWORD do) { $(D_COMMENT // function body )} ) $(DDOC_BLANKLINE ) $(P The two functions are identical semantically.) $(DDOC_BLANKLINE )

$(LNAME2 in_out_inheritance, In, Out and Inheritance)

$(DDOC_BLANKLINE ) $(P If a function in a derived class overrides a function from its super class, then only the preconditions of one of the function and its overridden functions must be satisfied. Overriding functions then becomes a process of $(I loosening) the preconditions. ) $(DDOC_BLANKLINE ) $(P A function without preconditions means its precondition is always satisfied. Therefore if any function in an inheritance hierarchy has no preconditions, then any preconditions on functions overriding it have no meaningful effect. ) $(DDOC_BLANKLINE ) $(P Conversely, all of the postconditions of the function and its overridden functions must to be satisfied. Adding overriding functions then becomes a processes of $(I tightening) the postconditions. ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 function-return-values, Function Return Values)

$(DDOC_BLANKLINE ) $(P At least one $(DDSUBLINK spec/statement, return-statement, return statement) is required if the function specifies a return type that is not void, unless:) $(UL $(LI the function executes an infinite loop) $(LI the function executes an 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 )

$(LNAME2 pure-functions, Pure Functions)

$(DDOC_BLANKLINE ) $(P Pure functions are annotated with the 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 )

$(LNAME2 weak-purity, Strong vs Weak Purity)

$(DDOC_BLANKLINE ) $(P A $(I weakly pure function) has parameters with mutable indirections. Program state can be modified transitively through the matching argument. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD pure) size_t foo($(D_KEYWORD int)[] arr) { arr[] += 1; $(D_KEYWORD return) arr.length; } $(D_KEYWORD int)[] a = [1, 2, 3]; foo(a); $(D_KEYWORD assert)(a == [2, 3, 4]); ) ) $(DDOC_BLANKLINE ) $(P A $(I strongly pure function) has no parameters with mutable indirections and cannot modify any program state external to the function. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD double) x; } $(D_KEYWORD pure) size_t foo($(D_KEYWORD immutable)($(D_KEYWORD int))[] arr, $(D_KEYWORD int) num, S val) { $(D_COMMENT //arr[num] = 1; // compile error ) num = 2; $(D_COMMENT // has no side effect on the caller side ) val.x = 3.14; $(D_COMMENT // ditto ) $(D_KEYWORD return) arr.length; } ) ) $(DDOC_BLANKLINE ) $(P A strongly pure function can call a weakly pure function.) $(DDOC_BLANKLINE )

$(LNAME2 pure-special-cases, Special Cases)

$(DDOC_BLANKLINE ) $(P A pure function can:) $(DDOC_BLANKLINE ) $(UL $(LI read and write the floating point exception flags) $(LI read and write the floating point mode flags, as long as those flags are restored to their initial state upon function entry) ) $(DDOC_BLANKLINE ) $(UNDEFINED_BEHAVIOR occurs if these flags are not restored to their initial state upon function exit. It is the programmer's responsibility to ensure this. Setting these flags is not allowed in @safe code.) $(DDOC_BLANKLINE )

$(LNAME2 pure-debug, Debugging)

$(DDOC_BLANKLINE ) $(P A pure function can perform impure operations in statements that are in a $(GLINK2 version, ConditionalStatement) controlled by a $(GLINK2 version, DebugCondition). ) $(DDOC_BLANKLINE ) $(BEST_PRACTICE this relaxation of purity checks in DebugConditions is intended solely to make debugging programs easier.) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD pure) $(D_KEYWORD int) foo($(D_KEYWORD int) i) { $(D_KEYWORD debug) writeln($(D_STRING "i = "), i); $(D_COMMENT // ok, impure code allowed in debug statement ) ... } ) $(DDOC_BLANKLINE )

$(LNAME2 pure-nested, Nested Functions)

$(DDOC_BLANKLINE ) $(P $(RELATIVE_LINK2 nested, Nested functions) inside a pure function are implicitly marked as pure.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD pure) $(D_KEYWORD int) foo($(D_KEYWORD int) x, $(D_KEYWORD immutable) $(D_KEYWORD int) y) { $(D_KEYWORD int) bar() $(D_COMMENT // implicitly marked as pure, to be "weakly pure" ) $(D_COMMENT // since hidden context pointer to foo stack context is mutable ) { x = 10; $(D_COMMENT // can access states in enclosing scope ) $(D_COMMENT // through the mutable context pointer ) $(D_KEYWORD return) x; } $(D_KEYWORD pragma)(msg, $(D_KEYWORD typeof)(&bar)); $(D_COMMENT // int delegate$(LPAREN)$(RPAREN ) pure ) $(D_KEYWORD int) baz() $(D_KEYWORD immutable) $(D_COMMENT // qualifies hidden context pointer with immutable, ) $(D_COMMENT // and has no other parameters, therefore "strongly pure" ) { $(D_COMMENT //return x; // error, cannot access mutable data ) $(D_COMMENT // through the immutable context pointer ) $(D_KEYWORD return) y; $(D_COMMENT // ok ) } $(D_COMMENT // can call pure nested functions ) $(D_KEYWORD return) bar() + baz(); } ) ) $(DDOC_BLANKLINE )

$(LNAME2 pure-factory-functions, Pure Factory Functions)

$(DDOC_BLANKLINE ) $(P A $(I pure factory function) is a strongly pure function that returns a result that has only mutable indirections. All mutable memory returned by the call cannot be referenced by any other part of the program, i.e. it is newly allocated by the function. The mutable references of the result similarly cannot refer to any object that existed before the function call. This allows the result to be implicitly cast from anything to 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 )

$(LNAME2 pure-optimization, Optimization)

$(DDOC_BLANKLINE ) $(IMPLEMENTATION_DEFINED An implementation may assume that a strongly pure function called with arguments that have only immutable indirections (or none) that returns a result without mutable indirections will have the same effect for all invocations with equivalent arguments. It is allowed to memoize the result of the function under the assumption that equivalent arguments always produce equivalent results.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int) a($(D_KEYWORD int)) $(D_KEYWORD pure); $(D_COMMENT // no mutable indirections )$(D_KEYWORD int) b($(D_KEYWORD const) Object) $(D_KEYWORD pure); $(D_COMMENT // depends on argument passed )$(D_KEYWORD immutable)(Object) c($(D_KEYWORD immutable) Object) $(D_KEYWORD pure); $(D_COMMENT // always memoizable ) $(D_KEYWORD void) g(); $(D_KEYWORD void) f($(D_KEYWORD int) n, $(D_KEYWORD const) Object co, $(D_KEYWORD immutable) Object io) { $(D_KEYWORD const) $(D_KEYWORD int) x = a(n); g(); $(D_COMMENT // `n` does not change between calls to `a` ) $(D_KEYWORD int) i = a(n); $(D_COMMENT // same as `i = x` ) $(D_KEYWORD const) $(D_KEYWORD int) y = b(co); $(D_COMMENT // `co` may have mutable indirection ) g(); $(D_COMMENT // may change fields of `co` through another reference ) i = b(co); $(D_COMMENT // call is not memoizable, result may differ ) $(D_KEYWORD const) $(D_KEYWORD int) z = b(io); i = b(io); $(D_COMMENT // same as `i = z` )} ) ) $(DDOC_BLANKLINE ) $(P Such a function may still have behavior inconsistent with memoization by e.g. using casts 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 )

$(LNAME2 nothrow-functions, Nothrow Functions)

$(DDOC_BLANKLINE ) $(P Nothrow functions can only throw exceptions derived from $(LINK2 https://dlang.org/phobos/object.html#.Error, $(D class Error)). ) $(DDOC_BLANKLINE ) $(P Nothrow functions are covariant with throwing ones.) $(DDOC_BLANKLINE )

$(LNAME2 ref-functions, Ref Functions)

$(DDOC_BLANKLINE ) $(P A 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 )

$(LNAME2 auto-functions, Auto Functions)

$(DDOC_BLANKLINE ) $(P Auto functions have their return type inferred from any $(GLINK2 statement, ReturnStatement)s in the function body. ) $(DDOC_BLANKLINE ) $(P An auto function is declared without a return type. Auto functions can use any valid $(GLINK2 declaration, StorageClass), not just 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 )

$(LNAME2 auto-ref-functions, Auto Ref Functions)

$(DDOC_BLANKLINE ) $(P Auto ref functions can infer their return type just as $(RELATIVE_LINK2 auto-functions, auto functions) do. In addition, they become $(RELATIVE_LINK2 ref-functions, ref functions) if all of these apply:) $(UL $(LI All expressions returned from the function are lvalues) $(LI No local variables are returned) $(LI Any parameters returned are reference parameters) $(LI Each returned expression must implicitly convert to an lvalue of the deduced return type $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD auto) $(D_KEYWORD ref) f1($(D_KEYWORD int) x) { $(D_KEYWORD return) x; } $(D_COMMENT // value return )$(D_KEYWORD auto) $(D_KEYWORD ref) f2() { $(D_KEYWORD return) 3; } $(D_COMMENT // value return )$(D_KEYWORD auto) $(D_KEYWORD ref) f3($(D_KEYWORD ref) $(D_KEYWORD int) x) { $(D_KEYWORD return) x; } $(D_COMMENT // ref return )$(D_KEYWORD auto) $(D_KEYWORD ref) f4($(D_KEYWORD out) $(D_KEYWORD int) x) { $(D_KEYWORD return) x; } $(D_COMMENT // ref return )$(D_KEYWORD auto) $(D_KEYWORD ref) f5() { $(D_KEYWORD static) $(D_KEYWORD int) x; $(D_KEYWORD return) x; $(D_COMMENT // ref return )} ) ) ) ) $(P The ref-ness of a function is determined from all $(GLINK2 statement, ReturnStatement)s in the function body:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD auto) $(D_KEYWORD ref) f1($(D_KEYWORD ref) $(D_KEYWORD int) x) { $(D_KEYWORD return) 3; $(D_KEYWORD return) x; } $(D_COMMENT // ok, value return )$(D_KEYWORD auto) $(D_KEYWORD ref) f2($(D_KEYWORD ref) $(D_KEYWORD int) x) { $(D_KEYWORD return) x; $(D_KEYWORD return) 3; } $(D_COMMENT // ok, value return )$(D_KEYWORD auto) $(D_KEYWORD ref) f3($(D_KEYWORD ref) $(D_KEYWORD int) x, $(D_KEYWORD ref) $(D_KEYWORD double) y) { $(D_KEYWORD return) x; $(D_KEYWORD return) y; $(D_COMMENT // The return type is deduced to be double, but cast$(LPAREN)double$(RPAREN )x is not an lvalue, ) $(D_COMMENT // so f3 has a value return. )} ) ) $(DDOC_BLANKLINE ) $(P Auto ref functions can have an explicit return type.) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD auto) $(D_KEYWORD ref) $(D_KEYWORD int) bar($(D_KEYWORD ref) $(D_KEYWORD int) x) { $(D_KEYWORD return) x; } $(D_COMMENT // ok, ref return )$(D_KEYWORD auto) $(D_KEYWORD ref) $(D_KEYWORD int) foo($(D_KEYWORD double) x) { $(D_KEYWORD return) x; } $(D_COMMENT // error, cannot convert double to int )) $(DDOC_BLANKLINE )

$(LNAME2 inout-functions, Inout Functions)

$(DDOC_BLANKLINE ) $(P For extensive information see $(DDSUBLINK spec/const3, inout, $(D inout) type qualifier).) $(DDOC_BLANKLINE )

$(LNAME2 optional-parenthesis, Optional Parentheses)

$(DDOC_BLANKLINE ) $(P If a function call passes no explicit argument, i.e. it would syntactically use $(D ()), then these parentheses may be omitted, similar to a getter invocation of a $(RELATIVE_LINK2 property-functions, property function). A $(RELATIVE_LINK2 pseudo-member, UFCS) call can also omit empty parentheses. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) foo() {} $(D_COMMENT // no arguments )$(D_KEYWORD void) fun($(D_KEYWORD int) x = 10) {} $(D_KEYWORD void) bar($(D_KEYWORD int)[] arr) {} $(D_KEYWORD void) main() { foo(); $(D_COMMENT // OK ) foo; $(D_COMMENT // also OK ) fun; $(D_COMMENT // OK ) $(D_KEYWORD int)[] arr; arr.bar(); $(D_COMMENT // UFCS call ) arr.bar; $(D_COMMENT // also OK )} ) ) $(DDOC_BLANKLINE ) $(P Due to ambiguity, parentheses are required to call a delegate or a function pointer:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD void) main() { $(D_KEYWORD int) $(D_KEYWORD function)() fp; $(D_KEYWORD assert)(fp == 6); $(D_COMMENT // Error, incompatible types int function$(LPAREN)$(RPAREN ) and int ) $(D_KEYWORD assert)(*fp == 6); $(D_COMMENT // Error, incompatible types int$(LPAREN)$(RPAREN ) and int ) $(D_KEYWORD int) $(D_KEYWORD delegate)() dg; $(D_KEYWORD assert)(dg == 6); $(D_COMMENT // Error, incompatible types int delegate$(LPAREN)$(RPAREN ) and int )} ) $(DDOC_BLANKLINE ) $(P If a function returns a delegate or a function pointer, any parentheses apply first to the function call, not the result. Two sets of parentheses are required to call the result directly: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int) getNum() { $(D_KEYWORD return) 6; } $(D_KEYWORD int) $(D_KEYWORD function)() getFunc() { $(D_KEYWORD return) &getNum; } $(D_KEYWORD void) main() { $(D_KEYWORD int) $(D_KEYWORD function)() fp; fp = getFunc; $(D_COMMENT // implicit call ) $(D_KEYWORD assert)(fp() == 6); fp = getFunc(); $(D_COMMENT // explicit call ) $(D_KEYWORD assert)(fp() == 6); $(D_KEYWORD int) x = getFunc()(); $(D_KEYWORD assert)(x == 6); } ) ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) getNum() { $(D_KEYWORD return) 6; } $(D_KEYWORD int) $(D_KEYWORD delegate)() getDel() $(D_KEYWORD return) { $(D_KEYWORD return) &getNum; } } $(D_KEYWORD void) main() { S s; $(D_KEYWORD int) $(D_KEYWORD delegate)() dg; dg = s.getDel; $(D_COMMENT // implicit call ) $(D_KEYWORD assert)(dg() == 6); dg = s.getDel(); $(D_COMMENT // explicit call ) $(D_KEYWORD assert)(dg() == 6); $(D_KEYWORD int) y = s.getDel()(); $(D_KEYWORD assert)(y == 6); } ) ) $(DDOC_BLANKLINE )

$(LNAME2 property-functions, Property Functions)

$(DDOC_BLANKLINE ) $(P WARNING: The definition and usefulness of property functions is being reviewed, and the implementation is currently incomplete. Using property functions is not recommended until the definition is more certain and implementation more mature.) $(DDOC_BLANKLINE ) $(P Properties are functions that can be syntactically treated as if they were fields or variables. Properties can be read from or written to. A property is read by calling a method or function with no arguments; a property is written by calling a method or function with its argument being the value it is set to. ) $(DDOC_BLANKLINE ) $(P Simple getter and setter properties can be written using $(RELATIVE_LINK2 pseudo-member, UFCS). These can be enhanced with the additon of the $(D @property) attribute to the function, which adds the following behaviors: ) $(DDOC_BLANKLINE ) $(UL $(LI $(D @property) functions cannot be overloaded with non-$(D @property) functions with the same name.) $(LI $(D @property) functions can only have zero, one or two parameters.) $(LI $(D @property) functions cannot have variadic parameters.) $(LI For the expression $(D typeof(exp)) where $(D exp) is an $(D @property) function, the type is the return type of the function, rather than the type of the function.) $(LI For the expression $(D __traits(compiles, exp)) where $(D exp) is an $(D @property) function, a further check is made to see if the function can be called.) $(LI $(D @property) are mangled differently, meaning that $(D @property) must be consistently used across different compilation units.) $(LI The ObjectiveC interface recognizes $(D @property) setter functions as special and modifies them accordingly.) ) $(DDOC_BLANKLINE ) $(P A simple property would be:) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) Foo { @property $(D_KEYWORD int) data() { $(D_KEYWORD return) m_data; } $(D_COMMENT // read property ) @property $(D_KEYWORD int) data($(D_KEYWORD int) value) { $(D_KEYWORD return) m_data = value; } $(D_COMMENT // write property ) $(D_KEYWORD private): $(D_KEYWORD int) m_data; } ) ) $(DDOC_BLANKLINE ) $(P To use it:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) test() { Foo f; f.data = 3; $(D_COMMENT // same as f.data$(LPAREN)3$(RPAREN ); ) $(D_KEYWORD return) f.data + 3; $(D_COMMENT // same as return f.data$(LPAREN)$(RPAREN ) + 3; )} ) $(DDOC_BLANKLINE ) $(P The absence of a read method means that the property is write-only. The absence of a write method means that the property is read-only. Multiple write methods can exist; the correct one is selected using the usual function overloading rules. ) $(DDOC_BLANKLINE ) $(P In all the other respects, these methods are like any other methods. They can be static, have different linkages, have their address taken, etc. ) $(DDOC_BLANKLINE ) $(P The built in properties $(D .sizeof), $(D .alignof), and $(D .mangleof) may not be declared as fields or methods in structs, unions, classes or enums. ) $(DDOC_BLANKLINE ) $(P If a property function has no parameters, it works as a getter. If has exactly one parameter, it works as a setter. ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 virtual-functions, Virtual Functions)

$(DDOC_BLANKLINE ) $(P Virtual functions are class member functions that are called indirectly through a function pointer table, called a 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 )

$(LNAME2 covariance, Covariance)

$(DDOC_BLANKLINE ) $(P An overriding function may be covariant with the overridden function. A covariant function has a type that is implicitly convertible to the type of the overridden function. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD class) A { } $(D_KEYWORD class) B : A { } $(D_KEYWORD class) Foo { A test() { $(D_KEYWORD return) $(D_KEYWORD null); } } $(D_KEYWORD class) Bar : Foo { $(D_COMMENT // overrides and is covariant with Foo.test$(LPAREN)$(RPAREN ) ) $(D_KEYWORD override) B test() { $(D_KEYWORD return) $(D_KEYWORD null); } } ) ) $(DDOC_BLANKLINE )

$(LNAME2 base-methods, Calling Base Class Methods)

$(DDOC_BLANKLINE ) $(P To directly call a member function of a base class 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 )

$(LNAME2 function-inheritance, Overload Sets and Overriding)

$(DDOC_BLANKLINE ) $(P When doing overload resolution, the functions in the base class are not considered, as they are not in the same $(RELATIVE_LINK2 overload-sets, Overload Set): ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD class) A { $(D_KEYWORD int) foo($(D_KEYWORD int) x) { ... } $(D_KEYWORD int) foo($(D_KEYWORD long) y) { ... } } $(D_KEYWORD class) B : A { $(D_KEYWORD override) $(D_KEYWORD int) foo($(D_KEYWORD long) x) { ... } } $(D_KEYWORD void) test() { B b = $(D_KEYWORD new) B(); b.foo(1); $(D_COMMENT // calls B.foo$(LPAREN)long$(RPAREN ), since A.foo$(LPAREN)int$(RPAREN ) is not considered ) A a = b; a.foo(1); $(D_COMMENT // issues runtime error $(LPAREN)instead of calling A.foo$(LPAREN)int$(RPAREN )$(RPAREN ) )} ) $(DDOC_BLANKLINE ) $(P To include the base class's functions in the overload resolution process, use an $(GLINK2 declaration, AliasDeclaration): ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD class) A { $(D_KEYWORD int) foo($(D_KEYWORD int) x) { ... } $(D_KEYWORD int) foo($(D_KEYWORD long) y) { ... } } $(D_KEYWORD class) B : A { $(CODE_HIGHLIGHT $(D_KEYWORD alias) foo = A.foo;) $(D_KEYWORD override) $(D_KEYWORD int) foo($(D_KEYWORD long) x) { ... } } $(D_KEYWORD void) test() { A a = $(D_KEYWORD new) B(); a.foo(1); $(D_COMMENT // calls A.foo$(LPAREN)int$(RPAREN ) ) B b = $(D_KEYWORD new) B(); b.foo(1); $(D_COMMENT // calls A.foo$(LPAREN)int$(RPAREN ) )} ) $(DDOC_BLANKLINE ) $(P If such an $(I AliasDeclaration) is not used, the derived class's functions completely override all the functions of the same name in the base class, even if the types of the parameters in the base class functions are different. It is illegal if, through implicit conversions to the base class, those other functions do get called: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD class) A { $(D_KEYWORD void) $(CODE_HIGHLIGHT set)($(D_KEYWORD long) i) { } $(D_KEYWORD void) set($(D_KEYWORD int) i) { } } $(D_KEYWORD class) B : A { $(D_KEYWORD override) $(D_KEYWORD void) set($(D_KEYWORD long) i) { } } $(D_KEYWORD void) test() { A a = $(D_KEYWORD new) B; a.set(3); $(D_COMMENT // error, use of A.set$(LPAREN)int$(RPAREN ) is hidden by B ) $(D_COMMENT // use 'alias set = A.set;' to introduce base class overload set )} ) ) $(DDOC_BLANKLINE )

$(LNAME2 override-defaults, Default Values)

$(DDOC_BLANKLINE ) $(P A function parameter's default value is not inherited:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD class) A { $(D_KEYWORD void) $(CODE_HIGHLIGHT foo)($(D_KEYWORD int) x = 5) { ... } } $(D_KEYWORD class) B : A { $(D_KEYWORD void) foo($(D_KEYWORD int) $(CODE_HIGHLIGHT x = 7)) { ... } } $(D_KEYWORD class) C : B { $(D_KEYWORD void) foo($(D_KEYWORD int) $(CODE_HIGHLIGHT x)) { ... } } $(D_KEYWORD void) test() { A a = $(D_KEYWORD new) A(); a.foo(); $(D_COMMENT // calls A.foo$(LPAREN)5$(RPAREN ) ) B b = $(D_KEYWORD new) B(); b.foo(); $(D_COMMENT // calls B.foo$(LPAREN)7$(RPAREN ) ) C c = $(D_KEYWORD new) C(); c.foo(); $(D_COMMENT // error, need an argument for C.foo )} ) $(DDOC_BLANKLINE )

$(LNAME2 inheriting-attributes, Inherited Attributes)

$(DDOC_BLANKLINE ) $(P An overriding function inherits any unspecified $(GLINK FunctionAttributes) from the attributes of the overridden function.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD class) B { $(D_KEYWORD void) foo() $(D_KEYWORD pure) $(D_KEYWORD nothrow) @safe {} } $(D_KEYWORD class) D : B { $(D_KEYWORD override) $(D_KEYWORD void) foo() {} } $(D_KEYWORD void) main() { $(D_KEYWORD auto) d = $(D_KEYWORD new) D(); $(D_KEYWORD pragma)(msg, $(D_KEYWORD typeof)(&d.foo)); $(D_COMMENT // prints "void delegate$(LPAREN)$(RPAREN ) pure nothrow @safe" in compile time )} ) ) $(DDOC_BLANKLINE )

$(LNAME2 override-restrictions, Restrictions)

$(DDOC_BLANKLINE ) $(P The attributes $(LINK2 attribute.html#disable, $(D @disable)) and $(LINK2 attribute.html#deprecated, $(D deprecated)) are not allowed on overriding functions. ) $(DDOC_BLANKLINE ) $(RATIONALE To stop the compilation or to output the deprecation message, the implementation must be able to determine the target of the call, which can't be guaranteed when it is virtual. ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD class) B { $(D_KEYWORD void) foo() {} } $(D_KEYWORD class) D : B { @disable $(D_KEYWORD override) $(D_KEYWORD void) foo() {} $(D_COMMENT // error, can't apply @disable to overriding function )} ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 inline-functions, Inline Functions)

$(DDOC_BLANKLINE ) $(P The compiler makes the decision whether to inline a function or not. This decision may be controlled by $(LINK2 pragma.html#inline, 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 )

$(LNAME2 function-overloading, Function Overloading)

$(DDOC_BLANKLINE ) $(P $(I Function overloading) occurs when two or more functions in the same scope have the same name. The function selected is the one that is the $(I best match) to the arguments. The matching levels are: ) $(DDOC_BLANKLINE ) $(OL $(LI no match) $(LI match with implicit conversions) $(LI match with qualifier conversion (if the argument type is $(GLOSSARY qualifier-convertible) to the parameter type)) $(LI exact match) ) $(DDOC_BLANKLINE ) $(P Each argument (including any $(CODE this) reference) is compared against the function's corresponding parameter to determine the match level for that argument. The match level for a function is the $(I worst) match level of each of its arguments.) $(DDOC_BLANKLINE ) $(P Literals do not match $(CODE ref) or $(CODE out) parameters.) $(DDOC_BLANKLINE ) $(P $(CODE scope) parameter storage class does not affect function overloading.) $(DDOC_BLANKLINE ) $(P If two or more functions have the same match level, then $(LNAME2 partial-ordering, $(I partial ordering)) is used to disambiguate to find the best match. Partial ordering finds the most specialized function. If neither function is more specialized than the other, then it is an ambiguity error. Partial ordering is determined for functions $(I f) and $(I g) by taking the parameter types of $(I f), constructing a list of arguments by taking the default values of those types, and attempting to match them against $(I g). If it succeeds, then $(I g) is at least as specialized as $(I f). For example: ) $(D_CODE $(D_KEYWORD class) A { } $(D_KEYWORD class) B : A { } $(D_KEYWORD class) C : B { } $(D_KEYWORD void) foo(A); $(D_KEYWORD void) foo(B); $(D_KEYWORD void) test() { C c; $(D_COMMENT /* Both foo$(LPAREN)A$(RPAREN ) and foo$(LPAREN)B$(RPAREN ) match with implicit conversions $(LPAREN)level 2$(RPAREN ). * Applying partial ordering rules, * foo$(LPAREN)B$(RPAREN ) cannot be called with an A, and foo$(LPAREN)A$(RPAREN ) can be called * with a B. Therefore, foo$(LPAREN)B$(RPAREN ) is more specialized, and is selected. */) foo(c); $(D_COMMENT // calls foo$(LPAREN)B$(RPAREN ) )} ) $(P A function with a variadic argument is considered less specialized than a function without. ) $(DDOC_BLANKLINE ) $(P A static member function can be overloaded with a member function. The struct, class or union of the static member function is inferred from the type of the 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 )

$(LNAME2 overload-sets, Overload Sets)

$(DDOC_BLANKLINE ) $(P Functions declared at the same scope overload against each other, and are called an $(I Overload Set). An example of an overload set are functions defined at module level: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD module) A; $(D_KEYWORD void) foo() { } $(D_KEYWORD void) foo($(D_KEYWORD long) i) { } ) ) $(DDOC_BLANKLINE ) $(P $(CODE A.foo()) and $(CODE A.foo(long)) form an overload set. A different module can also define another overload set of functions with the same name: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD module) B; $(D_KEYWORD class) C { } $(D_KEYWORD void) foo(C) { } $(D_KEYWORD void) foo($(D_KEYWORD int) i) { } ) ) $(DDOC_BLANKLINE ) $(P and A and B can be imported by a third module, C. Both overload sets, the $(CODE A.foo) overload set and the $(CODE B.foo) overload set, are found when searching for symbol 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 )

$(LNAME2 parameters, Function Parameters)

$(DDOC_BLANKLINE )

$(LNAME2 param-storage, Parameter Storage Classes)

$(DDOC_BLANKLINE ) $(P Parameter storage classes are $(D in), $(D out), $(D ref), $(D lazy), 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 )

$(LNAME2 in-params, In Parameters)

$(DDOC_BLANKLINE ) $(B Note: The following requires the $(D -preview=in) switch, available in $(LINK2 $(ROOT_DIR )changelog/2.094.0.html#preview-in, v2.094.0) or higher. When not in use, 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 )

$(LNAME2 ref-params, Ref and Out Parameters)

$(DDOC_BLANKLINE ) $(P By default, parameters take rvalue arguments. A 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 )

$(LNAME2 lazy-params, Lazy Parameters)

$(DDOC_BLANKLINE ) $(P An argument to a $(D lazy) parameter is not evaluated before the function is called. The argument is only evaluated if/when the parameter is evaluated within the function. Hence, a $(D lazy) argument can be executed 0 or more times. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD import) std.stdio : writeln; $(D_KEYWORD void) main() { $(D_KEYWORD int) x; 3.times(writeln(x++)); writeln($(D_STRING "-")); writeln(x); } $(D_KEYWORD void) times($(D_KEYWORD int) n, $(D_KEYWORD lazy) $(D_KEYWORD void) exp) { $(D_KEYWORD while) (n--) exp(); } ) ) $(DDOC_BLANKLINE ) $(P prints to the console:) $(DDOC_BLANKLINE ) $(CONSOLE 0 1 2 $(MINUS ) 3 ) $(DDOC_BLANKLINE ) $(P A $(D lazy) parameter cannot be an lvalue.) $(DDOC_BLANKLINE ) $(P The underlying delegate of the $(D lazy) parameter may be extracted by using the $(D &) operator:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) test($(D_KEYWORD lazy) $(D_KEYWORD int) dg) { $(D_KEYWORD int) $(D_KEYWORD delegate)() dg_ = &dg; $(D_KEYWORD assert)(dg_() == 7); $(D_KEYWORD assert)(dg == dg_()); } $(D_KEYWORD void) main() { $(D_KEYWORD int) a = 7; test(a); } ) ) $(DDOC_BLANKLINE ) $(P A $(D lazy) parameter of type $(D void) can accept an argument of any type.) $(DDOC_BLANKLINE ) $(P See Also: $(RELATIVE_LINK2 lazy_variadic_functions, Lazy Variadic Functions)) $(DDOC_BLANKLINE )

$(LNAME2 function-default-args, Default Arguments)

$(DDOC_BLANKLINE ) $(P Function parameter declarations can have default values:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD void) foo($(D_KEYWORD int) x, $(D_KEYWORD int) y = 3) { ... } ... foo(4); $(D_COMMENT // same as foo$(LPAREN)4, 3$(RPAREN ); )) $(DDOC_BLANKLINE ) $(P Default parameters are resolved and semantically checked in the context of the function declaration.) $(D_CODE $(D_KEYWORD module) m; $(D_KEYWORD private) $(D_KEYWORD immutable) $(D_KEYWORD int) b; $(D_KEYWORD pure) $(D_KEYWORD void) g($(D_KEYWORD int) a = b) {} ) $(D_CODE $(D_KEYWORD import) m; $(D_KEYWORD int) b; $(D_KEYWORD pure) $(D_KEYWORD void) f() { g(); $(D_COMMENT // ok, uses m.b )} ) $(DDOC_BLANKLINE ) $(P The attributes of the $(GLINK2 expression, AssignExpression) are applied where the default expression is used.) $(D_CODE $(D_KEYWORD module) m; $(D_KEYWORD int) b; $(D_KEYWORD pure) $(D_KEYWORD void) g($(D_KEYWORD int) a = b) {} ) $(D_CODE $(D_KEYWORD import) m; $(D_KEYWORD enum) $(D_KEYWORD int) b = 3; $(D_KEYWORD pure) $(D_KEYWORD void) f() { g(); $(D_COMMENT // error, cannot access mutable global `m.b` in pure function )} ) $(DDOC_BLANKLINE ) $(P If the default value for a parameter is given, all following parameters must also have default values. ) $(DDOC_BLANKLINE ) $(P See also: function type aliases $(DDSUBLINK spec/declaration, alias-function, with default values).) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 return-ref-parameters, Return Ref Parameters)

$(DDOC_BLANKLINE ) $(P Return ref parameters are used with $(RELATIVE_LINK2 ref-functions, ref functions) to ensure that the returned reference will not outlive the matching argument's lifetime. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD ref) $(D_KEYWORD int) identity($(D_KEYWORD return) $(D_KEYWORD ref) $(D_KEYWORD int) x) { $(D_KEYWORD return) x; $(D_COMMENT // pass-through function that does nothing )} $(D_KEYWORD ref) $(D_KEYWORD int) fun() { $(D_KEYWORD int) x; $(D_KEYWORD return) identity(x); $(D_COMMENT // Error: escaping reference to local variable x )} $(D_KEYWORD ref) $(D_KEYWORD int) gun($(D_KEYWORD return) $(D_KEYWORD ref) $(D_KEYWORD int) x) { $(D_KEYWORD return) identity(x); $(D_COMMENT // OK )} ) ) $(P Returning the address of a 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 )

$(LNAME2 struct-return-methods, Struct Return Methods)

$(DDOC_BLANKLINE ) $(P Struct non-static methods can be marked with the 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 )

$(LNAME2 scope-parameters, Scope Parameters)

$(DDOC_BLANKLINE ) $(P A 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 )

$(LNAME2 return-scope-parameters, Return Scope Parameters)

$(DDOC_BLANKLINE ) $(P Parameters marked as 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 )

$(LNAME2 ref-return-scope-parameters, Ref Return Scope Parameters)

$(DDOC_BLANKLINE ) $(P It is not possible to have both 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 )

$(LNAME2 pure-scope-inference, Inferred scope parameters in pure functions)

$(DDOC_BLANKLINE ) When a parameter is not marked or inferred 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 )

$(LNAME2 udas-parameters, User-Defined Attributes for Parameters)

$(DDOC_BLANKLINE ) See also: $(GLINK2_ALTTEXT attribute, UserDefinedAttribute, User-Defined Attributes) $(DDOC_BLANKLINE )

$(LNAME2 variadic, Variadic Functions)

$(DDOC_BLANKLINE ) $(P $(I Variadic Functions) take a variable number of arguments. There are three forms:) $(DDOC_BLANKLINE ) $(OL $(LI $(RELATIVE_LINK2 c_style_variadic_functions, C-style variadic functions)) $(LI $(RELATIVE_LINK2 d_style_variadic_functions, Variadic functions with type info)) $(LI $(RELATIVE_LINK2 typesafe_variadic_functions, Typesafe variadic functions)) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 c_style_variadic_functions, C-style Variadic Functions)

$(DDOC_BLANKLINE ) $(P A C-style variadic function is declared with a parameter ... 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 )

$(LNAME2 d_style_variadic_functions, D-style Variadic Functions)

$(DDOC_BLANKLINE ) $(P D-style variadic functions have D linkage and ... 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 )

$(LNAME2 typesafe_variadic_functions, Typesafe Variadic Functions)

$(DDOC_BLANKLINE ) $(P A typesafe variadic function has D linkage and a variadic parameter declared as either an array or a class. The array or class is constructed from the arguments, and is passed as an array or class object. ) $(DDOC_BLANKLINE ) $(P For dynamic arrays:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int) sum($(D_KEYWORD int)[] ar ...) $(D_COMMENT // typesafe variadic function ){ $(D_KEYWORD int) s; $(D_KEYWORD foreach) ($(D_KEYWORD int) x; ar) s += x; $(D_KEYWORD return) s; } $(D_KEYWORD import) std.stdio; $(D_KEYWORD void) main() { writeln(stan()); $(D_COMMENT // 6 ) writeln(ollie()); $(D_COMMENT // 15 )} $(D_KEYWORD int) stan() { $(D_KEYWORD return) sum(1, 2, 3) + sum(); $(D_COMMENT // returns 6+0 )} $(D_KEYWORD int) ollie() { $(D_KEYWORD int)[3] ii = [4, 5, 6]; $(D_KEYWORD return) sum(ii); $(D_COMMENT // returns 15 )} ) ) $(DDOC_BLANKLINE ) $(P For static arrays, the number of arguments must match the array dimension.) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) sum($(D_KEYWORD int)[3] ar ...) $(D_COMMENT // typesafe variadic function ){ $(D_KEYWORD int) s; $(D_KEYWORD foreach) ($(D_KEYWORD int) x; ar) s += x; $(D_KEYWORD return) s; } $(D_KEYWORD int) frank() { $(D_KEYWORD return) sum(2, 3); $(D_COMMENT // error, need 3 values for array ) $(D_KEYWORD return) sum(1, 2, 3); $(D_COMMENT // returns 6 )} $(D_KEYWORD int) dave() { $(D_KEYWORD int)[3] ii = [4, 5, 6]; $(D_KEYWORD int)[] jj = ii; $(D_KEYWORD return) sum(ii); $(D_COMMENT // returns 15 ) $(D_KEYWORD return) sum(jj); $(D_COMMENT // error, type mismatch )} ) $(DDOC_BLANKLINE ) $(P For class objects:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) tesla($(D_KEYWORD int) x, C c ...) { $(D_KEYWORD return) x + c.x; } $(D_KEYWORD class) C { $(D_KEYWORD int) x; string s; $(D_KEYWORD this)($(D_KEYWORD int) x, string s) { $(D_KEYWORD this).x = x; $(D_KEYWORD this).s = s; } } $(D_KEYWORD void) edison() { C g = $(D_KEYWORD new) C(3, $(D_STRING "abc")); tesla(1, c); $(D_COMMENT // ok, since c is an instance of C ) tesla(1, 4, $(D_STRING "def")); $(D_COMMENT // ok ) tesla(1, 5); $(D_COMMENT // error, no matching constructor for C )} ) $(DDOC_BLANKLINE ) $(P The lifetime of the variadic class object or array instance ends at the end of the function. ) $(DDOC_BLANKLINE ) $(D_CODE C orville(C c ...) { $(D_KEYWORD return) c; $(D_COMMENT // error, c instance contents invalid after return )} $(D_KEYWORD int)[] wilbur($(D_KEYWORD int)[] a ...) { $(D_KEYWORD return) a; $(D_COMMENT // error, array contents invalid after return ) $(D_KEYWORD return) a[0..1]; $(D_COMMENT // error, array contents invalid after return ) $(D_KEYWORD return) a.dup; $(D_COMMENT // ok, since copy is made )} ) $(DDOC_BLANKLINE ) $(IMPLEMENTATION_DEFINED the variadic object or array instance may be constructed on the stack.) $(DDOC_BLANKLINE ) $(P For other types, the argument is passed by value.) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) neil($(D_KEYWORD int) i ...) { $(D_KEYWORD return) i; } $(D_KEYWORD void) buzz() { neil(3); $(D_COMMENT // returns 3 ) neil(3, 4); $(D_COMMENT // error, too many arguments ) $(D_KEYWORD int)[] x; neil(x); $(D_COMMENT // error, type mismatch )} ) $(DDOC_BLANKLINE )

$(LNAME2 lazy_variadic_functions, Lazy Variadic Functions)

$(DDOC_BLANKLINE ) $(P If the variadic parameter of a function is an array of delegates with no parameters, then each of the arguments whose type does not match that of the delegate is converted to a delegate of that type. ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD void) hal($(D_KEYWORD scope) $(D_KEYWORD int) $(D_KEYWORD delegate)()[] dgs ...); $(D_KEYWORD void) dave() { $(D_KEYWORD int) $(D_KEYWORD delegate)() dg; hal(1, 3+x, dg, $(D_KEYWORD cast)($(D_KEYWORD int) $(D_KEYWORD delegate)())$(D_KEYWORD null)); $(D_COMMENT // $(LPAREN)1$(RPAREN ) ) hal( { $(D_KEYWORD return) 1; }, { $(D_KEYWORD return) 3+x; }, dg, $(D_KEYWORD null) ); $(D_COMMENT // same as $(LPAREN)1$(RPAREN ) )} ) $(DDOC_BLANKLINE ) $(P The variadic delegate array differs from using a lazy variadic array. With the former each array element access would evaluate every array element. With the latter, only the element being accessed would be evaluated.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio; $(D_KEYWORD void) main() { $(D_KEYWORD int) x; ming(++x, ++x); $(D_KEYWORD int) y; flash(++y, ++y); } $(D_COMMENT // lazy variadic array )$(D_KEYWORD void) ming($(D_KEYWORD lazy) $(D_KEYWORD int)[] arr...) { writeln(arr[0]); $(D_COMMENT // 1 ) writeln(arr[1]); $(D_COMMENT // 4 )} $(D_COMMENT // variadic delegate array )$(D_KEYWORD void) flash($(D_KEYWORD scope) $(D_KEYWORD int) $(D_KEYWORD delegate)()[] arr ...) { writeln(arr[0]()); $(D_COMMENT // 1 ) writeln(arr[1]()); $(D_COMMENT // 2 )} ) ) $(DDOC_BLANKLINE ) $(BEST_PRACTICE Use 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)

$(LNAME2 hidden-parameters, Hidden Parameters)

$(DDOC_BLANKLINE ) $(UL $(LI Non-static member functions all have a hidden parameter called the $(DDSUBLINK spec/expression, this, 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 )

$(LNAME2 refscopereturn, Ref Scope Return Cases)

$(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 rsr_definitions, Definitions)

$(DDOC_BLANKLINE ) $(TABLE2 Definitions, Term, Description $(TROW I, type that contains no indirections) $(TROW P, type that contains indirections) $(TROW X, type that may or may not contain indirections) $(TROW p, parameter of type P) $(TROW i, parameter of type I) $(TROW ref, 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 )

$(LNAME2 rsr_classification, Classification)

$(DDOC_BLANKLINE ) $(P A parameter must be in one of the following states:) $(DDOC_BLANKLINE ) $(TABLE2 Classification, Term, Description $(TROW None, 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 )

$(LNAME2 rsr_mapping, Mapping Syntax Onto Classification)

$(DDOC_BLANKLINE ) $(P The juxtaposition of 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 )

$(LNAME2 rsr_memberfunctions, Member Functions)

$(DDOC_BLANKLINE ) $(P Member functions are rewritten as if the 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 )

$(LNAME2 rsr_PandRef, P and ref)

$(DDOC_BLANKLINE ) $(P The rules account for switching between 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 )

$(LNAME2 rsr_covariance, Covariance)

$(DDOC_BLANKLINE ) $(P Covariance means a parameter with restrictions can be converted to a parameter with fewer restrictions. This is deducible from the description of each state. ) $(DDOC_BLANKLINE ) $(NOTE 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 )

$(LEGACY_LNAME2 Local Variables, local-variables, Local Variables)

$(DDOC_BLANKLINE ) $(P Local variables are declared within the scope of a function. Function parameters are included.) $(DDOC_BLANKLINE ) $(P A local variable cannot be read without first assigning it a value.) $(DDOC_BLANKLINE ) $(IMPLEMENTATION_DEFINED The implementation may not always be able to detect these cases. ) $(DDOC_BLANKLINE ) $(P The address of or reference to a local non-static variable cannot be returned from the function. ) $(DDOC_BLANKLINE ) $(P A local variable and a label in the same function cannot have the same name. ) $(DDOC_BLANKLINE ) $(P A local variable cannot hide another local variable in the same function. ) $(DDOC_BLANKLINE ) $(RATIONALE whenever this is done it often is a bug or at least looks like a bug. ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD ref) $(D_KEYWORD double) func($(D_KEYWORD int) x) { $(D_KEYWORD int) x; $(D_COMMENT // error, hides previous definition of x ) $(D_KEYWORD double) y; { $(D_KEYWORD char) y; $(D_COMMENT // error, hides previous definition of y ) $(D_KEYWORD int) z; } { $(D_KEYWORD wchar) z; $(D_COMMENT // Ok, previous z is out of scope ) } z: $(D_COMMENT // error, z is a local variable and a label ) $(D_KEYWORD return) y; $(D_COMMENT // error, returning ref to local )} ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 Local Static Variables, local-static-variables, Local Static Variables)

$(DDOC_BLANKLINE ) $(P Local variables in functions declared as 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 )

$(LNAME2 nested, Nested Functions)

$(DDOC_BLANKLINE ) $(P Functions may be nested within other functions:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int) bar($(D_KEYWORD int) a) { $(D_KEYWORD int) foo($(D_KEYWORD int) b) { $(D_KEYWORD int) abc() { $(D_KEYWORD return) 1; } $(D_KEYWORD return) b + abc(); } $(D_KEYWORD return) foo(a); } $(D_KEYWORD void) test() { $(D_KEYWORD int) i = bar(3); $(D_COMMENT // i is assigned 4 )} ) ) $(DDOC_BLANKLINE ) $(P Nested functions can be accessed only if the name is in scope.) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD void) foo() { $(D_KEYWORD void) A() { B(); $(D_COMMENT // error, B$(LPAREN)$(RPAREN ) is forward referenced ) C(); $(D_COMMENT // error, C undefined ) } $(D_KEYWORD void) B() { A(); $(D_COMMENT // ok, in scope ) $(D_KEYWORD void) C() { $(D_KEYWORD void) D() { A(); $(D_COMMENT // ok ) B(); $(D_COMMENT // ok ) C(); $(D_COMMENT // ok ) D(); $(D_COMMENT // ok ) } } } A(); $(D_COMMENT // ok ) B(); $(D_COMMENT // ok ) C(); $(D_COMMENT // error, C undefined )} ) $(DDOC_BLANKLINE ) and: $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) bar($(D_KEYWORD int) a) { $(D_KEYWORD int) foo($(D_KEYWORD int) b) { $(D_KEYWORD return) b + 1; } $(D_KEYWORD int) abc($(D_KEYWORD int) b) { $(D_KEYWORD return) foo(b); } $(D_COMMENT // ok ) $(D_KEYWORD return) foo(a); } $(D_KEYWORD void) test() { $(D_KEYWORD int) i = bar(3); $(D_COMMENT // ok ) $(D_KEYWORD int) j = bar.foo(3); $(D_COMMENT // error, bar.foo not visible )} ) $(DDOC_BLANKLINE ) $(P Nested functions have access to the variables and other symbols defined by the lexically enclosing function. This access includes both the ability to read and write them. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int) bar($(D_KEYWORD int) a) { $(D_KEYWORD int) c = 3; $(D_KEYWORD int) foo($(D_KEYWORD int) b) { b += c; $(D_COMMENT // 4 is added to b ) c++; $(D_COMMENT // bar.c is now 5 ) $(D_KEYWORD return) b + c; $(D_COMMENT // 12 is returned ) } c = 4; $(D_KEYWORD int) i = foo(a); $(D_COMMENT // i is set to 12 ) $(D_KEYWORD return) i + c; $(D_COMMENT // returns 17 )} $(D_KEYWORD void) test() { $(D_KEYWORD int) i = bar(3); $(D_COMMENT // i is assigned 17 )} ) ) $(DDOC_BLANKLINE ) $(P This access can span multiple nesting levels:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int) bar($(D_KEYWORD int) a) { $(D_KEYWORD int) c = 3; $(D_KEYWORD int) foo($(D_KEYWORD int) b) { $(D_KEYWORD int) abc() { $(D_KEYWORD return) c; $(D_COMMENT // access bar.c ) } $(D_KEYWORD return) b + c + abc(); } $(D_KEYWORD return) foo(3); } ) ) $(DDOC_BLANKLINE ) $(P Static nested functions cannot access any stack variables of any lexically enclosing function, but can access static variables. This is analogous to how static member functions behave. ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) bar($(D_KEYWORD int) a) { $(D_KEYWORD int) c; $(D_KEYWORD static) $(D_KEYWORD int) d; $(D_KEYWORD static) $(D_KEYWORD int) foo($(D_KEYWORD int) b) { b = d; $(D_COMMENT // ok ) b = c; $(D_COMMENT // error, foo$(LPAREN)$(RPAREN ) cannot access frame of bar$(LPAREN)$(RPAREN ) ) $(D_KEYWORD return) b + 1; } $(D_KEYWORD return) foo(a); } ) $(DDOC_BLANKLINE ) $(P Functions can be nested within member functions:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) Foo { $(D_KEYWORD int) a; $(D_KEYWORD int) bar() { $(D_KEYWORD int) c; $(D_KEYWORD int) foo() { $(D_KEYWORD return) c + a; } $(D_KEYWORD return) 0; } } ) ) $(DDOC_BLANKLINE ) $(P Nested functions always have the D function linkage type. ) $(DDOC_BLANKLINE )

$(LNAME2 nested-declaration-order, Declaration Order)

$(DDOC_BLANKLINE ) $(P Unlike module level declarations, declarations within function scope are processed in order. This means that two nested functions cannot mutually call each other: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD void) test() { $(D_KEYWORD void) foo() { bar(); } $(D_COMMENT // error, bar not defined ) $(D_KEYWORD void) bar() { foo(); } $(D_COMMENT // ok )} ) $(DDOC_BLANKLINE ) $(P There are several workarounds for this limitation:) $(DDOC_BLANKLINE ) $(UL $(DDOC_BLANKLINE ) $(LI Declare the functions to be static members of a nested struct:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD void) test() { $(D_KEYWORD static) $(D_KEYWORD struct) S { $(D_KEYWORD static) $(D_KEYWORD void) foo() { bar(); } $(D_COMMENT // ok ) $(D_KEYWORD static) $(D_KEYWORD void) bar() { foo(); } $(D_COMMENT // ok ) } S.foo(); $(D_COMMENT // compiles $(LPAREN)but note the infinite runtime loop$(RPAREN ) )} ) $(DDOC_BLANKLINE ) $(LI Declare one or more of the functions to be function templates even if they take no specific template arguments:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) test() { $(D_KEYWORD void) foo()() { bar(); } $(D_COMMENT // ok $(LPAREN)foo is a function template$(RPAREN ) ) $(D_KEYWORD void) bar() { foo(); } $(D_COMMENT // ok )} ) ) $(DDOC_BLANKLINE ) $(LI Declare the functions inside of a mixin template:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD mixin) $(D_KEYWORD template) T() { $(D_KEYWORD void) foo() { bar(); } $(D_COMMENT // ok ) $(D_KEYWORD void) bar() { foo(); } $(D_COMMENT // ok )} $(D_KEYWORD void) main() { $(D_KEYWORD mixin) T!(); } ) ) $(DDOC_BLANKLINE ) $(LI Use a delegate:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) test() { $(D_KEYWORD void) $(D_KEYWORD delegate)() fp; $(D_KEYWORD void) foo() { fp(); } $(D_KEYWORD void) bar() { foo(); } fp = &bar; } ) ) $(DDOC_BLANKLINE ) ) $(DDOC_BLANKLINE ) $(P Nested functions cannot be overloaded.) $(DDOC_BLANKLINE )

$(LNAME2 function-pointers-delegates, Function Pointers, Delegates and Closures)

$(DDOC_BLANKLINE )

$(LNAME2 function-pointers, Function Pointers)

$(DDOC_BLANKLINE ) $(P A function pointer is declared with the 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 )

$(LNAME2 closures, Delegates & Closures)

$(DDOC_BLANKLINE ) $(P A delegate can be set to a non-static nested function:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int) $(D_KEYWORD delegate)() dg; $(D_KEYWORD void) test() { $(D_KEYWORD int) a = 7; $(D_KEYWORD int) foo() { $(D_KEYWORD return) a + 3; } dg = &foo; $(D_KEYWORD int) i = dg(); $(D_COMMENT // i is set to 10 )} $(D_KEYWORD void) main() { test(); $(D_KEYWORD int) i = dg(); $(D_COMMENT // ok, test.a is in a closure and still exists ) $(D_KEYWORD assert)(i == 10); } ) ) $(DDOC_BLANKLINE ) $(P The stack variables referenced by a nested function are still valid even after the function exits (NOTE this is different from D 1.0). This combining of the environment and the function is called a $(I dynamic closure). ) $(DDOC_BLANKLINE ) $(P Those referenced stack variables that make up the closure are allocated on the GC heap, unless:) $(UL $(LI The closure is passed to a 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 )

$(LNAME2 method-delegates, Method Delegates)

$(DDOC_BLANKLINE ) $(P Delegates to non-static nested functions contain two pieces of data: the pointer to the stack frame of the lexically enclosing function (called the $(I context pointer)) and the address of the function. This is analogous to struct/class non-static member function delegates consisting of a $(I this) pointer and the address of the member function. Both forms of delegates are indistinguishable, and are the same type. ) $(P A delegate can be set to a particular object and method using &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 )

$(LNAME2 function-delegate-init, Initialization)

$(DDOC_BLANKLINE ) $(P Function pointers are zero-initialized by default. They can be initialized to the address of any function (including a function literal). Initialization with the address of a function that requires a context pointer is not allowed in @safe functions. ) $(DDOC_BLANKLINE ) $(UNDEFINED_BEHAVIOR Calling a function pointer that was set to point to a function that requires a context pointer. ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD static) $(D_KEYWORD int) sfunc(); $(D_KEYWORD int) member(); $(D_COMMENT // has hidden `this` reference parameter )} @safe $(D_KEYWORD void) sun() { $(D_KEYWORD int) $(D_KEYWORD function)() fp = &S.sfunc; fp(); $(D_COMMENT // Ok ) fp = &S.member; $(D_COMMENT // error )} @system $(D_KEYWORD void) moon() { $(D_KEYWORD int) $(D_KEYWORD function)() fp = &S.member; $(D_COMMENT // Ok because @system ) fp(); $(D_COMMENT // undefined behavior )} ) $(DDOC_BLANKLINE ) $(P Delegates are zero-initialized by default. They can be initialized by taking the address of a non-static member function, but a context pointer must be supplied. They can be initialized by taking the address of a non-static nested function or function literal, where the context pointer will be set to point to the stack frame, closure, or 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 )

$(LNAME2 anonymous, Anonymous Functions and Anonymous Delegates)

$(DDOC_BLANKLINE ) $(P See $(GLINK2 expression, FunctionLiteral)s. ) $(DDOC_BLANKLINE )

$(LNAME2 main, $(D main()) Function)

$(DDOC_BLANKLINE ) $(P For console programs, $(D main()) serves as the entry point. It gets called after all the $(DDSUBLINK spec/module, staticorder, module initializers) are run, and after any $(DDLINK spec/unittest, Unit Tests, unittests) are run. After it returns, all the module destructors are run. $(D main()) must be declared as follows: ) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME MainFunction): $(GLINK MainReturnDecl) $(D main$(LPAREN)$(RPAREN )) $(GLINK2 statement, MainFunctionBody) $(GLINK MainReturnDecl) $(D main$(LPAREN)string[]) $(GLINK_LEX Identifier)$(D $(RPAREN )) $(GLINK2 statement, MainFunctionBody) $(DDOC_BLANKLINE ) $(GNAME MainReturnDecl): $(D void) $(D int) $(GLINK2 type, noreturn) $(RELATIVE_LINK2 auto-functions, $(D auto)) $(DDOC_BLANKLINE ) $(GNAME MainFunctionBody): $(GLINK ShortenedFunctionBody) $(GLINK SpecifiedFunctionBody) ) $(DDOC_BLANKLINE ) $(UL $(LI If 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 )

$(LNAME2 betterc-main, $(D extern(C) main()) Function)

$(DDOC_BLANKLINE ) $(P Programs may define an $(D extern(C) main) function as an alternative to the standard $(RELATIVE_LINK2 main, entry point). This form is required for $(DDLINK spec/betterc, Better C, $(B BetterC)) programs.) $(DDOC_BLANKLINE ) $(P A C $(D main) function must be declared as follows:) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME CMainFunction): $(D extern (C)) $(GLINK MainReturnDecl) $(D main$(LPAREN)$(GLINK CmainParameters)$(OPT )$(RPAREN )) $(GLINK2 statement, BlockStatement) $(DDOC_BLANKLINE ) $(GNAME CmainParameters): $(D int) $(GLINK_LEX Identifier), $(D char**) $(GLINK_LEX Identifier) $(D int) $(GLINK_LEX Identifier), $(D char**) $(GLINK_LEX Identifier), $(D char**) $(GLINK_LEX Identifier) ) $(DDOC_BLANKLINE ) $(P When defined, the first two parameters denote a C-style array (length + pointer) that holds the arguments passed to the program by the OS. The third parameter is a POSIX extension called $(D environ) and holds information about the current environment variables.) $(DDOC_BLANKLINE ) $(NOTE The exemption for storage classes / $(D enum)'s defined for a D $(D main) function also applies to C $(D main) functions.) $(DDOC_BLANKLINE ) $(P This function takes the place of the C main function and is executed immediately without any setup or teardown associated with a D $(D main) function. Programs reliant on module constructors, module destructors, or unittests need to manually perform (de)initialization using the appropriate $(DDSUBLINK phobos/core_runtime, Runtime, runtime functions).) $(DDOC_BLANKLINE ) $(IMPLEMENTATION_DEFINED Other system-specific entry points may exist, such as 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 )

$(LNAME2 function-templates, Function Templates)

$(DDOC_BLANKLINE ) $(P Functions can have compile time arguments in the form of a template. See $(DDSUBLINK spec/template, function-templates, function templates).) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 interpretation, Compile Time Function Execution (CTFE))

$(DDOC_BLANKLINE ) $(P In contexts where a compile time value is required, functions can be used to compute those values. This is called $(I Compile Time Function Execution), or $(I CTFE).) $(DDOC_BLANKLINE ) $(P These contexts are:) $(DDOC_BLANKLINE ) $(UL $(LI initialization of a static variable or a $(DDSUBLINK spec/enum, manifest_constants, manifest constant)) $(LI static initializers of struct/class members) $(LI dimension of a $(DDSUBLINK spec/arrays, static-arrays, static array)) $(LI argument for a $(DDSUBLINK spec/template, template_value_parameter, template value parameter)) $(LI $(DDSUBLINK spec/version, staticif, 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 )

$(LNAME2 string-mixins, String Mixins and Compile Time Function Execution)

$(DDOC_BLANKLINE ) $(P All functions that execute in CTFE must also be executable at run time. The compile time evaluation of a function does the equivalent of running the function at run time. The semantics of a function cannot depend on compile time values of the function. For example:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) foo(string s) { $(D_KEYWORD return) $(D_KEYWORD mixin)(s); } $(D_KEYWORD const) $(D_KEYWORD int) x = foo($(D_STRING "1")); ) $(DDOC_BLANKLINE ) $(COMMENT Intentionally not a $(P ...) so that it doesn't get a paragraph number, because this continues the paragraph above.) $(DDOC_BLANKLINE ) is illegal, because the runtime code for 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 )

$(LNAME2 nogc-functions, No-GC Functions)

$(DDOC_BLANKLINE ) $(P No-GC functions are functions marked with the $(D @nogc) attribute. Those functions do not allocate memory on the GC heap. These operations are not allowed in No-GC functions: ) $(DDOC_BLANKLINE ) $(OL $(LI $(DDSUBLINK spec/expression, ArrayLiteral, constructing an array) on the heap) $(LI resizing an array by writing to its $(D .length) property) $(LI $(DDSUBLINK spec/expression, CatExpression, array concatenation)) $(LI $(DDSUBLINK spec/expression, simple_assignment_expressions, array appending)) $(LI $(DDSUBLINK spec/expression, AssocArrayLiteral, constructing an associative array)) $(LI $(DDSUBLINK spec/expression, IndexOperation, indexing) an associative array $(NOTE because it may throw $(D RangeError) if the specified key is not present)) $(LI $(DDSUBLINK spec/expression, NewExpression, allocating an object with 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 )

$(LNAME2 function-safety, Function Safety)

$(DDOC_BLANKLINE )

$(LNAME2 safe-functions, Safe Functions)

$(DDOC_BLANKLINE ) $(P Safe functions are marked with the $(CODE @safe) attribute. @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 )

Safe External Functions

$(DDOC_BLANKLINE ) $(P External functions don't have a function body visible to the compiler: ) $(D_CODE @safe $(D_KEYWORD extern) (C) $(D_KEYWORD void) play(); ) and so safety cannot be verified automatically. $(DDOC_BLANKLINE ) $(BEST_PRACTICE Explicitly set an attribute for external functions rather than relying on default settings.) $(DDOC_BLANKLINE )

$(LNAME2 trusted-functions, Trusted Functions)

$(DDOC_BLANKLINE ) $(P Trusted functions are marked with the $(CODE @trusted) attribute.) $(DDOC_BLANKLINE ) $(P Like $(RELATIVE_LINK2 safe-functions, safe functions), trusted functions have $(RELATIVE_LINK2 safe-interfaces, safe interfaces). Unlike safe functions, this is not enforced by restrictions on the function body. Instead, it is the responsibility of the programmer to ensure that the interface of a trusted function is safe.) $(DDOC_BLANKLINE ) $(P Example:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD immutable)($(D_KEYWORD int))* f($(D_KEYWORD int)* p) @trusted { $(D_KEYWORD version) (none) p[2] = 13; $(D_COMMENT // Invalid. p[2] is out of bounds. This line would exhibit undefined ) $(D_COMMENT // behavior. ) $(D_KEYWORD version) (none) p[1] = 13; $(D_COMMENT // Invalid. In this program, p[1] happens to be in-bounds, so the ) $(D_COMMENT // line would not exhibit undefined behavior, but a trusted function ) $(D_COMMENT // is not allowed to rely on this. ) $(D_KEYWORD version) (none) $(D_KEYWORD return) $(D_KEYWORD cast)($(D_KEYWORD immutable)) p; $(D_COMMENT // Invalid. @safe code still has mutable access and could trigger ) $(D_COMMENT // undefined behavior by overwriting the value later on. ) $(D_KEYWORD int)* p2 = $(D_KEYWORD new) $(D_KEYWORD int); *p2 = 42; $(D_KEYWORD return) $(D_KEYWORD cast)($(D_KEYWORD immutable)) p2; $(D_COMMENT // Valid. After f returns, no mutable aliases of p2 can exist. )} $(D_KEYWORD void) main() @safe { $(D_KEYWORD int)[2] a = [10, 20]; $(D_KEYWORD int)* mp = &a[0]; $(D_KEYWORD immutable)($(D_KEYWORD int))* ip = f(mp); $(D_KEYWORD assert)(a[1] == 20); $(D_COMMENT // Guaranteed. f cannot access a[1]. ) $(D_KEYWORD assert)(ip !$(D_KEYWORD is) mp); $(D_COMMENT // Guaranteed. f cannot introduce unsafe aliasing. )} ) $(DDOC_BLANKLINE ) $(P Trusted functions may call safe, trusted, or system functions. ) $(DDOC_BLANKLINE ) $(P Trusted functions are covariant with safe or system functions.) $(DDOC_BLANKLINE ) $(BEST_PRACTICE Trusted functions should be kept small so that they are easier to manually verify. ) $(DDOC_BLANKLINE )

$(LNAME2 system-functions, System Functions)

$(DDOC_BLANKLINE ) $(P System functions are functions not marked with $(CODE @safe) or $(CODE @trusted) and are not nested inside $(CODE @safe) functions. System functions may be marked with the $(CODE @system) attribute. A function being system does not mean it actually is unsafe, it just means that its safety must be manually verified. ) $(DDOC_BLANKLINE ) $(P System functions are $(B not) covariant with trusted or safe functions. ) $(DDOC_BLANKLINE ) $(P System functions can call safe and trusted functions.) $(DDOC_BLANKLINE ) $(BEST_PRACTICE When in doubt, mark 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 )

$(LNAME2 safe-interfaces, Safe Interfaces)

$(DDOC_BLANKLINE ) $(P When it is only called with $(RELATIVE_LINK2 safe-values, safe values) and $(RELATIVE_LINK2 safe-aliasing, safe aliasing), a function has a safe interface when:) $(OL $(LI it cannot exhibit $(DDSUBLINK spec/glossary, undefined_behavior, undefined behavior), and) $(LI it cannot create unsafe values that are accessible from other parts of the program (e.g., via return values, global variables, or 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 )

$(LNAME2 safe-values, Safe Values)

$(DDOC_BLANKLINE ) $(P For a 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 )

$(LNAME2 safe-aliasing, Safe Aliasing)

$(DDOC_BLANKLINE ) $(P When one memory location is accessible with two different types, that aliasing is considered safe if:) $(OL $(LI both types are 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 )

$(LNAME2 function-attribute-inference, Function Attribute Inference)

$(DDOC_BLANKLINE ) $(P $(GLINK2 expression, FunctionLiteral)s, $(RELATIVE_LINK2 auto-functions, Auto Functions), $(RELATIVE_LINK2 auto-ref-functions, Auto Ref Functions), $(RELATIVE_LINK2 nested, nested functions) and $(DDSUBLINK spec/template, function-templates, function templates), since their function bodies are always present, infer the following attributes unless specifically overridden: ) $(UL $(LI $(RELATIVE_LINK2 pure-functions, $(D pure))) $(LI $(RELATIVE_LINK2 nothrow-functions, $(D nothrow))) $(LI $(RELATIVE_LINK2 safe-functions, $(D @safe))) $(LI $(RELATIVE_LINK2 nogc-functions, $(D @nogc))) $(LI $(RELATIVE_LINK2 return-ref-parameters, return ref parameters)) $(LI $(RELATIVE_LINK2 scope-parameters, scope parameters)) $(LI $(RELATIVE_LINK2 return-scope-parameters, return scope parameters)) $(LI $(RELATIVE_LINK2 ref-return-scope-parameters, ref return scope parameters) ) ) $(P Attribute inference is not done for other functions, even if the function body is present. ) $(DDOC_BLANKLINE ) $(P The inference is done by determining if the function body follows the rules of the particular attribute. ) $(DDOC_BLANKLINE ) $(P Cyclic functions (i.e. functions that wind up directly or indirectly calling themselves) are inferred as being impure, throwing, and @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 )

$(LNAME2 pseudo-member, Uniform Function Call Syntax (UFCS))

$(DDOC_BLANKLINE ) $(P A free function can be called like a member function when both:) $(UL $(LI The member function does not (or cannot) exist for the object expression) $(LI The free function's first parameter type matches the object expression) ) $(P The object expression can be any type. This is called a $(I UFCS function call).) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD void) sun(T, $(D_KEYWORD int)); $(D_KEYWORD void) moon(T t) { t.sun(1); $(D_COMMENT // If `T` does not have a member function `sun`, ) $(D_COMMENT // `t.sun$(LPAREN)1$(RPAREN )` is interpreted as if it were written `sun$(LPAREN)t, 1$(RPAREN )` )} ) $(DDOC_BLANKLINE ) $(RATIONALE This provides a way to add external functions to a class as if they were public $(RELATIVE_LINK2 final, 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) ) )