AttributeSpecifier:Attribute:AttributeDeclarationBlockAttribute:AlignAttributeAtAttributeDeprecatedAttributeFunctionAttributeKwdLinkageAttributePragmaVisibilityAttributeabstractautoconstfinal__gsharedexternimmutableinoutoverriderefreturn__rvaluescopesharedstaticsynchronizedFunctionAttributeKwd:nothrowpureAtAttribute:@disable@__future@nogc@liveProperty@safe@system@trustedUserDefinedAttributeProperty:@propertyDeclarationBlock:DeclDef{DeclDefsopt}
Attributes are a way to modify one or more declarations. The general forms are:
attribute declaration;// affects the declarationattribute:// affects all declarations until the end of// the current scope declaration; declaration; ...attribute// affects all declarations in the block{ declaration; declaration; ...}
Function attributes@nogc,nothrow andpure do not propagate inside aggregate declarations.
constpure @safe{int i;// function attributes ignored for non-functionsvoid f();// const attribute ignored for free functionstruct S {int i;void f();// pure ignored inside struct }}staticassert(is(typeof(i) ==constint));staticassert(is(typeof(&f) ==voidfunction()pure @safe));staticassert(is(typeof(S.i) ==constint));staticassert(is(typeof(&S().f) ==voiddelegate()const @safe));
See also:core.attribute for other compiler-recognized attributes.
LinkageAttribute:extern(LinkageType)extern(C++,)extern(C++,QualifiedIdentifier)extern(C++,NamespaceList)extern(C++,class)extern(C++,struct)LinkageType:CC++DWindowsSystemObjective-CNamespaceList:ConditionalExpressionConditionalExpression,ConditionalExpression,NamespaceList
D provides an easy way to call C functions and operating system API functions, as compatibility with both is essential. TheLinkageType is case sensitive, and is meant to be extensible by the implementation (they are not keywords).C andD must be supplied, the others are what makes sense for the implementation.C++ offers limited compatibility with C++, see theInterfacing to C++ documentation for more information.Objective-C offers compatibility with Objective-C, see theInterfacing to Objective-C documentation for more information.System is the same asWindows on Windows platforms, andC on other platforms.
Implementation Note: for Win32 platforms,Windows should exist.
C function calling conventions are specified by:
extern (C):int foo();// call foo() with C conventionsNote thatextern(C) can be provided for all types of declarations, includingstruct orclass, even though there is no corresponding match on theC side. In that case, the attribute is ignored. This behavior applies for nested functions and nested variables as well. However, forstatic member methods andstatic nested functions, addingextern(C) will change the calling convention, but not the mangling.
D conventions are:
extern (D):Windows API conventions are:
extern (Windows):void *VirtualAlloc(void *lpAddress,uint dwSize,uint flAllocationType,uint flProtect );
The Windows convention is distinct from the C convention only on Win32 platforms, where it is equivalent to thestdcall convention.
Note that aloneextern keyword is used as a storage class.
The linkage formextern (C++,QualifiedIdentifier) creates C++ declarations that reside in C++ namespaces. TheQualifiedIdentifier specifies the namespaces.
extern (C++, N) {void foo(); }
refers to the C++ declaration:
namespace N { void foo(); }and can be referred to with or without qualification:
foo();N.foo();
Namespaces create a new named scope that is imported into its enclosing scope.
extern (C++, N) {void foo();void bar(); }extern (C++, M) {void foo(); }void main(){ bar();// ok//foo(); // error - N.foo() or M.foo() ? M.foo();// ok}
Multiple dotted identifiers in theQualifiedIdentifier create nested namespaces:
extern (C++, N.M) {extern (C++) {extern (C++, R) {void foo(); } } }N.M.R.foo();
refers to the C++ declaration:
namespace N { namespace M { namespace R { void foo(); } } }AlignAttribute:align ( default )alignalign(AssignExpression)
Specifies the alignment of:
align(default) (re)sets it to the default, which matches the default member alignment of the companion C compiler.align by itself is the same asalign(default).
struct S{align(default):// same as `align:`byte a;// placed at offset 0int b;// placed at offset 4long c;// placed at offset 8}staticassert(S.alignof == 8);staticassert(S.c.offsetof == 8);staticassert(S.sizeof == 16);
TheAssignExpression specifies the alignment which matches the behavior of the companion C compiler when non-default alignments are used. It must be a non-negative power of 2.
A value of 1 means that no alignment is done; fields are packed together.
struct S{align (1):byte a;// placed at offset 0int b;// placed at offset 1long c;// placed at offset 5}staticassert(S.alignof == 1);staticassert(S.c.offsetof == 5);staticassert(S.sizeof == 13);
The natural alignment of an aggregate is the maximum alignment of its fields. It can be overridden by setting the alignment outside of the aggregate.
align (2)struct S{align (1):byte a;// placed at offset 0int b;// placed at offset 1long c;// placed at offset 5}staticassert(S.alignof == 2);staticassert(S.c.offsetof == 5);staticassert(S.sizeof == 14);
Setting the alignment of a field aligns it to that power of 2, regardless of the size of the field.
struct S{byte a;// placed at offset 0align (4)byte b;// placed at offset 4align (16)short c;// placed at offset 16}staticassert(S.alignof == 16);staticassert(S.c.offsetof == 16);staticassert(S.sizeof == 32);
TheAlignAttribute is reset to the default when entering a function scope or a non-anonymous struct, union, class, and restored when exiting that scope. It is not inherited from a base class.
See also:Struct Layout.
Do not align references or pointers that were allocated usingNewExpression on boundaries that are not a multiple ofsize_t. The garbage collector assumes that pointers and references to GC allocated objects will be onsize_t byte boundaries.
struct S{align(1):byte b;int* p;}staticassert(S.p.offsetof == 1);@safevoid main(){ S s; s.p =newint;// error: can't modify misaligned pointer in @safe code}
DeprecatedAttribute:deprecateddeprecated (AssignExpression)
It is often necessary to deprecate a feature in a library, yet retain it for backwards compatibility. Such declarations can be marked asdeprecated, which means that the compiler can be instructed to produce an error if any code refers to deprecated declarations:
deprecated{void oldFoo();}oldFoo();// Deprecated: function test.oldFoo is deprecated
Optionally a string literal or manifest constant can be used to provide additional information in the deprecation message.
deprecated("Don't use bar")void oldBar();oldBar();// Deprecated: function test.oldBar is deprecated - Don't use bar
Calling CTFE-able functions or using manifest constants is also possible.
import std.format;enum message = format("%s and all its members are obsolete", Foobar.stringof);deprecated(message)class Foobar {}deprecated(format("%s is also obsolete","This class"))class BarFoo {}void main(){auto fb =new Foobar();// Deprecated: class test.Foobar is deprecated - Foobar// and all its members are obsoleteauto bf =new BarFoo();// Deprecated: class test.BarFoo is deprecated - This// class is also obsolete}
Implementation Note: The compiler should have a switch specifying ifdeprecated should be ignored, cause a warning, or cause an error during compilation.
The@__future attribute is used to mark a new symbol which could cause a name clash in another module. The other module using a symbol with the same name will continue to compile as before but will show a deprecation message. The message indicates that the new symbol will break the existing code in future, and the other module should be updated.
See DIP1007 for details.
VisibilityAttribute:exportpackagepackage(QualifiedIdentifier)privateprotectedpublic
Visibility is an attribute that is one ofprivate,package,protected,public, orexport. They may be referred to as protectionattributes in documents predatingDIP22.
Visibility participates insymbol name lookup.
export means that a symbol can be accessed from outside the executable, shared library, or DLL. The symbol is said to be exported from where it is defined in an executable, shared library, or DLL, and imported by another executable, shared library, or DLL.
export applied to the definition of a symbol will export it.export applied to a declaration of a symbol will import it. A variable is a definition unlessextern is applied to it.
exportint x = 3;// definition, exporting `x`exportint y;// definition, exporting `y`exportexternint z;// declaration, importing `z`export__gshared h = 3;// definition, exporting `h`export__gshared i;// definition, exporting `i`exportextern__gsharedint j;// declaration, importing `j`
A function with a body is a definition, without a body is a declaration.
exportvoid f() { }// definition, exporting `f`exportvoid g();// declaration, importing `g`
In Windows terminology,dllexport means exporting a symbol from a DLL, anddllimport means a DLL or executable is importing a symbol from a DLL.
package extendsprivate so that package members can be accessed from code in other modules that are in the same package. If no identifier is provided, this applies to the innermost package only, or defaults toprivate if a module is not nested in a package.
package may have an optional parameter in the form of a dot-separated identifier list which is resolved as the qualified package name. The package must be either the module's parent package or one of its ancestors. If this parameter is present, the symbol will be visible in the specified package and all of its descendants.
Symbols withprivate visibility can only be accessed from within the same module. Private member functions are implicitlyfinal and cannot be overridden.
protected only applies inside classes (and templates as they can be mixed in) and means that a symbol can only be seen by members of the same module, or by a derived class. If accessing a protected instance member through a derived class member function, that member can only be accessed for the object instance which can be implicitly cast to the same type as ‘this’.protected module members are illegal.
public means that any code within the executable can see the member. It is the default visibility attribute.
Theconst type qualifier changes the type of the declared symbol fromT toconst(T), whereT is the type specified (or inferred) for the introduced symbol in the absence ofconst.
constint foo = 7;staticassert(is(typeof(foo) ==const(int)));constdouble bar = foo + 6;staticassert(is(typeof(bar) ==const(double)));
class C{constvoid foo();const {void bar(); }void baz()const;}pragma(msg,typeof(C.foo));// const void()pragma(msg,typeof(C.bar));// const void()pragma(msg,typeof(C.baz));// const void()staticassert(is(typeof(C.foo) ==typeof(C.bar)) &&is(typeof(C.bar) ==typeof(C.baz)));
See also:Methods Returning a Qualified Type.
Theimmutable attribute modifies the type fromT toimmutable(T), the same way asconst does. See:
Theinout attribute modifies the type fromT toinout(T), the same way asconst does.
Seeshared.
By default, non-immutable global declarations reside in thread local storage. When a global variable is marked with the__gshared attribute, its value is shared across all threads.
int foo;// Each thread has its own exclusive copy of foo.__gsharedint bar;// bar is shared by all threads.
__gshared may also be applied to member variables and local variables. In these cases,__gshared is equivalent tostatic, except that the variable is shared by all threads rather than being thread local.
class Foo{__gsharedint bar;}int foo(){__gsharedint bar = 0;return bar++;// Not thread safe.}
Warning: Unlike theshared attribute,__gshared provides no safeguards against data races or other multi-threaded synchronization issues. It is the responsibility of the programmer to ensure that access to variables marked__gshared is synchronized correctly.
__gshared is disallowed in@safe code.
A reference to a declaration marked with the@disable attributecauses a compile time error. This can be used to explicitly disallow certainoperations or overloads at compile time rather than relying on generating aruntime error.
@disableint x;@disablevoid foo();void main(){ x++;// error, x is disabled foo();// error, foo is disabled}
@disable this(); inside a struct disallows default construction.
Disabling a struct copy constructor makes the struct not copyable.
SeeFunction Safety.
Variables marked@system cannot be accessed from@safe code.
@systemint* p;struct S{ @systemint i;}void main() @safe{int x = *p;// error with `-preview=systemVariables`, deprecation otherwise S s; s.i = 0;// ditto}
SeeNo-GC Functions.
SeePure Functions.
Thestatic attribute applies to types, functions and data.static is ignored when applied to other declarations.
Inside an aggregate type, astatic declaration does not apply to a particular instance of an object, but to the type of the object. In other words, it means there is nothis reference.
class Foo{staticint x;staticint bar() {return x; }int foobar() {return 7; }}Foo.x = 6;// no instance neededassert(Foo.bar() == 6);//Foo.foobar(); // error, no instance of FooFoo f =new Foo;assert(f.bar() == 6);assert(f.foobar() == 7);
Static methods are nevervirtual.
Static data has one instance per thread, not one per object.
A staticnested functionortype cannotaccess variables in the parent scope.
Inside a function, astatic local variable persists after the function returns.
Static does not have the additional C meaning of being local to a file. Use theprivate attribute in D to achieve that. For example:
module foo;int x = 3;// x is globalprivateint y = 4;// y is local to module foo
Theauto attribute is used when there are no other attributes andtype inference is desired.
auto i = 6.8;// declare i as a double
For functions, theauto attribute means return type inference. SeeAuto Functions.
Thescope attribute signifies a variable's pointer values will not escape the scope that the variable is declared in.
If the variable has a type that does not contain any indirections, thescope attribute is ignored.
When applied to a global variable,scope is also ignored, since there is no scope larger than global scope that pointers could escape to.
scopeint* x;// scope ignored, global variablevoid main(){// scope static int* x; // cannot be both scope and staticscopefloat y;// scope ignored, no indirectionsscopeint[2] z;// scope ignored, static array is value typescopeint[] w;// scope dynamic array}
When applied to a local variable with a type that has indirections, its value may not be assigned to a variable with longer lifetime:
Other operations implicitly assigning them to variables with longer lifetime are also disallowed:
Thescope attribute is part of the variable declaration, not the type, and it only applies to the first level of indirection. For example, it is impossible to declare a variable as a dynamic array of scope pointers, becausescope only applies to the.ptr of the array itself, not its elements.scope affects various types as follows:
| Type of local variable | Whatscope applies to |
|---|---|
| AnyBasic Data Type | nothing |
| PointerT* | the pointer value |
| Dynamic ArrayT[] | the.ptr to the elements |
| Static ArrayT[n] | each elementT |
| Associative ArrayK[V] | the pointer to the implementation defined structure |
| struct orunion | each of its member variables |
| function pointer | the pointer value |
| delegate | both the.funcptr and.ptr (closure context) pointer values |
| class orinterface | the class reference |
| enum | the base type |
struct S{ string str;// note: string = immutable(char)[] string* strPtr;}string escape(scope S s,scope S* sPtr,scope string[2] sarray,scope string[] darray){return s.str;// invalid, scope applies to struct membersreturn *s.strPtr;// valid, scope struct member is dereferencedreturn sPtr.str;// valid, struct pointer is dereferencedreturn *sPtr.strPtr;// valid, two pointers are dereferencedreturn sarray[0];// invalid, scope applies to static array elementsreturn sarray[1];// invalid, dittoreturn darray[0];// valid, scope applies to array pointer, not elements}
A "scope value" is the value of ascope variable, or a generated value pointing to stack allocated memory. Such values are generated byslicing a static array or creatinga pointer to a variable that is (or may be) allocated on the stack:
The variadic parameter fromTypesafe Variadic Functions is also a scope value, since the arguments are passed on the stack.
When a local variable is assigned ascope value, it is inferredscope, even when the variable has an explicit type and does not use theauto keyword.
@safe:refint[2] identity(returnrefint[2] x) {return x;}int* escape(int[2] y,scopeint* z){int x;auto xPtr = &x;// inferred `scope int*`int[] yArr = identity(y)[];// inferred `scope int[]`int* zCopy = z;// inferred `scope int*`return zCopy;// error}void variadic(int[] a...){int[] x = a;// inferred `scope int[]`}void main(){ variadic(1, 2, 3);}
struct S{int x;// this method may be called on a stack-allocated instance of Svoid f() @safe {int* p = &x;// inferred `scope int* p`int* q = &this.x;// equivalent }}
Scope Parameters are treated the same as scope local variables, except that returning them is allowed when the function hasFunction Attribute Inference. In that case, they are inferred asReturn Scope Parameters.
When used to allocate a class instance directly, ascope variable signifies the RAII (Resource Acquisition Is Initialization) protocol. This means that the destructor for an object is automatically called when the reference to it goes out of scope. The destructor is called even if the scope is exited via a thrown exception, thusscope is used to guarantee cleanup.
When a class is constructed withnew and assigned to a localscope variable, it may be allocated on the stack and permitted in a@nogc context.
If there is more than onescope class variable going out of scope at the same point, then the destructors are called in the reverse order that the variables were constructed.
Assignment to ascope variable with class type, other than initialization, is not allowed, because that would complicate proper destruction of the variable.
import core.stdc.stdio : puts;class C{ ~this() @nogc { puts(__FUNCTION__); }}void main() @nogc{ {scope c0 =new C();// allocated on the stackscope c1 =new C();//c1 = c0; // Error: cannot rebind scope variables// destructor of `c1` and `c0` are called here in that order } puts("bye");}
Anabstract class must be overridden by a derived class. Declaring an abstract member function makes the class abstract.
The@mustuse attribute is a compiler-recognized UDA defined in the D runtime modulecore.attribute.
An expression is considered to be discarded if and only if either of the following is true:
It is a compile-time error to discard an expression if all of the following are true:
"Assignment expression" means either a simple assignment expression or anassignment operator expression.
"Increment expression" means aUnaryExpression orPostfixExpression whose operator is++.
"Decrement expression" means aUnaryExpression orPostfixExpression whose operator is--.
It is a compile-time error to attach@mustuse to a function declaration or to any aggregate declaration other than astruct orunion declaration. The purpose of this rule is to reserve such usage for possible future expansion.
UserDefinedAttribute:@ (TemplateArgumentList)@TemplateSingleArgument@Identifier(NamedArgumentListopt)@TemplateInstance@TemplateInstance(NamedArgumentListopt)
User-Defined Attributes (UDA) are compile-time annotations that can be attached to a declaration. These attributes can then be queried, extracted, and manipulated at compile time. There is no runtime component to them.
A user-defined attribute is defined using:@3int a;// value attribute@("string", 7)int b;// multiple values// using compile-time constantenum val = 3;@valint a2;// has same attribute as `a`enum Foo;@Fooint c;// type name attributestruct Bar{int x;}@Bar()int d;// type instance attribute@Bar(3)int e;// type instance attribute using initializer
Fore, the attribute is an instance of structBar which isstatically initialized using its argument.
If there are multiple UDAs in scope for a declaration, they are concatenated:
@(1){ @(2)int a;// has UDAs (1, 2) @("string")int b;// has UDAs (1, "string")}A function parameter can have a UDA. Parameters do not inherit UDAs from outside the parameter list.
@2void f(@3int p){pragma(msg,__traits(getAttributes, p));// prints AliasSeq!(3)}
UDAs can be extracted into acompile-time sequence using__traits:
@('c') string s;pragma(msg,__traits(getAttributes, s));// prints AliasSeq!('c')If there are no user-defined attributes for the symbol, an empty sequence is returned. The result can be used just like any compile-time sequence - it can be indexed, passed as template parameters, etc.
enum e = 7;@("hello")struct SSS { }@(3){ @(4) @e @SSSint foo;}alias TP =__traits(getAttributes, foo);pragma(msg, TP);// prints AliasSeq!(3, 4, 7, (SSS))pragma(msg, TP[2]);// prints 7
Any types in the sequence can be used to declare things:
TP[3] a;// a is declared as an SSSThe attribute of the type name is not the same as the attribute of the variable:
pragma(msg,__traits(getAttributes, a));// prints AliasSeq!()pragma(msg,__traits(getAttributes,typeof(a)));// prints AliasSeq!("hello")
Of course, the real value of UDAs is to be able to create user-defined types with specific values. Having attribute values of basic types does not scale.
Whether the attributes are values or symbols is up to the user, and whether later attributes accumulate or override earlier ones is also up to how the user interprets them.
If a UDA is attached to a template declaration, then it will be automatically attached to all direct members of instances of that template. If any of those members are templates themselves, this rule applies recursively:
@("foo")template Outer(T){struct S {int x; }int y;void fun() {} @("bar")template Inner(U) {int z; }}pragma(msg,__traits(getAttributes, Outer!int.S));// prints AliasSeq!("foo")pragma(msg,__traits(getAttributes, Outer!int.S.x));// prints AliasSeq!()pragma(msg,__traits(getAttributes, Outer!int.y));// prints AliasSeq!("foo")pragma(msg,__traits(getAttributes, Outer!int.fun));// prints AliasSeq!("foo")pragma(msg,__traits(getAttributes, Outer!int.Inner));// prints AliasSeq!("foo", "bar")pragma(msg,__traits(getAttributes, Outer!int.Inner!int.z));// prints AliasSeq!("foo", "bar")
UDAs cannot be attached to template parameters.