diff --git a/bugstats.php.dd b/bugstats.php.dd index 06a9fafd7e..bb74b191c8 100644 --- a/bugstats.php.dd +++ b/bugstats.php.dd @@ -36,7 +36,7 @@ $(DISPLAY All open, y_axis_field=bug_severity&query_format=report-table&product= $(DISPLAY All closed, y_axis_field=bug_severity&query_format=report-table&product=D&bug_severity=normal&bug_severity=minor&bug_severity=trivial&bug_severity=regression&bug_severity=blocker&bug_severity=critical&bug_severity=major&bug_severity=enhancement&bug_status=RESOLVED&bug_status=VERIFIED&bug_status=CLOSED) ) -$(P
$(LINK2 https://issues.dlang.org/reports.cgi?product=D&datasets=NEW%3A&datasets=ASSIGNED%3A&datasets=REOPENED%3A&datasets=RESOLVED%3A, )
) +$(P
$(LINK2 https://issues.dlang.org/reports.cgi?product=D&datasets=NEW&datasets=ASSIGNED&datasets=REOPENED&datasets=RESOLVED, ) ) diff --git a/changelog.dd b/changelog.dd index 5a0a13da91..d1d7610fa5 100644 --- a/changelog.dd +++ b/changelog.dd @@ -5,14 +5,27 @@ $(D_S D Change Log, $(VERSION 066, ??? ??, 2014, =================================================, $(BUGSTITLE Compiler Changes, -$(LI $(RELATIVE_LINK2 boundscheck, $(D -noboundscheck) has been deprecated in favor of -$(D boundscheck=[on|safeonly|off]).)) +$(LI $(RELATIVE_LINK2 warn_unused_retval, $(D -w) now warns about an unused return value of a strongly pure nothrow function call.)) +$(LI $(RELATIVE_LINK2 boundscheck, $(D -noboundscheck) has been deprecated in favor of $(D boundscheck=[on|safeonly|off]).)) +$(LI $(RELATIVE_LINK2 vgc_switch, $(D -vgc) was added to list GC allocation code positions in the code.)) +$(LI $(RELATIVE_LINK2 vcolumns_switch, $(D -vcolumns) was added to display column numbers in error messages.)) +$(LI $(RELATIVE_LINK2 color_switch, $(D -color) was added to make console output colored.)) ) $(BUGSTITLE Language Changes, +$(LI $(RELATIVE_LINK2 nogc_attribute, $(D @nogc) attribute was added.)) +$(LI $(RELATIVE_LINK2 extern_cpp_nspace, $(D extern $(LPAREN)C++,) $(I namespace)$(D $(RPAREN)) was added.)) +$(LI $(RELATIVE_LINK2 opover_multidim_slicing, Operator overloading for multi-dimensional slicing was added.)) +$(LI $(RELATIVE_LINK2 traits_getfunctionattributes, $(D __traits$(LPAREN)getFunctionAttributes$(RPAREN)) was added.)) +$(LI $(RELATIVE_LINK2 narrowing_conversion_in_ifti, Support template parameter deduction for arguments with a narrowing conversion.)) +$(LI $(RELATIVE_LINK2 deprecate_rmw, Read-Modify-Write operations on shared variables are now deprecated.)) +$(LI $(RELATIVE_LINK2 uniform_scalar_construction, Support uniform construction syntax for built-in scalar types.)) ) $(BUGSTITLE Library Changes, +$(LI $(RELATIVE_LINK2 duration_split, Duration.get and its wrappers have been deprecated in favor of the new Duration.split.)) +$(LI $(RELATIVE_LINK2 array_and_aa_changes, Some built-in type properties have been replaced with library functions.)) +$(LI $(RELATIVE_LINK2 aa_key_requirement, Associative array keys now require equality instead of order comparison.)) ) $(BUGSTITLE Linker Changes, @@ -24,6 +37,24 @@ $(HR) $(BUGSTITLE Compiler Changes, +$(LI $(LNAME2 warn_unused_retval, $(D -w) now warns about an unused return value of a strongly pure nothrow function call:) + + $(P A discarded return value from a strongly pure nothrow function call now generates a warning. + + --------- + int foo() pure nothrow { return 1; } + void main() + { + foo(); // the result of foo() is unused + } + --------- + + With the $(D -w) switch, the compiler will complain: + + $(CONSOLE Warning: calling foo without side effects discards return value of type int, prepend a cast(void) if intentional) + ) +) + $(LI $(LNAME2 boundscheck, $(D -noboundscheck) has been deprecated in favor of $(D boundscheck=[on|safeonly|off]):) $(P Confusion over what the $(D -noboundscheck) command line option did led to the creation of @@ -50,14 +81,400 @@ $(LI $(LNAME2 boundscheck, $(D -noboundscheck) has been deprecated in favor of $ -release) does.) ) +$(LI $(LNAME2 vgc_switch, $(D -vgc) was added to list GC allocation code positions in the code): + + $(P Prints all GC-allocation points. Analysis will follow the semantics of + the new $(D @nogc) attribute. + ) +) + +$(LI $(LNAME2 vcolumns_switch, $(D -vcolumns) was added to display column numbers in error messages): + + $(P Diagnostic messages will print the character number from each line head. + + --------- + int x = missing_name; + --------- + + Without $(D -vcolumns): + + --------- + test.d(1): Error: undefined identifier missing_name + --------- + + With $(D -vcolumns): + + --------- + test.d(1,9): Error: undefined identifier missing_name + --------- + ) +) + +$(LI $(LNAME2 color_switch, $(D -color) was added to make console output colored:) + + $(P Errors, deprecation, and warning messages will be colored. + ) +) + ) $(BUGSTITLE Language Changes, +$(LI $(LNAME2 nogc_attribute, $(D @nogc) attribute was added:) + + $(P @nogc attribute disallows GC-heap allocation. + + --------- + class C {} + void foo() @nogc + { + auto c = new C(); // GC-allocation is disallowed + } + --------- + ) +) + +$(LI $(LNAME2 extern_cpp_nspace, $(D extern $(LPAREN)C++,) $(I namespace)$(D $(RPAREN)) was added): + + $(P To represent a C++ namespace, $(D extern $(LPAREN)C++$(RPAREN)) now takes optional dot-chained identifiers. + + --------- + extern (C++, a.b.c) int foo(); + --------- + + is equivalent with: + +$(CPPCODE +namespace a { + namespace b { + namespace c { + int foo(); + } + } +} +) + ) +) + +$(LI $(LNAME2 opover_multidim_slicing, Operator overloading for multi-dimensional slicing was added:) + + $(P Documentation is $(LINK2 operatoroverloading.html#ArrayOps, here).) + + $(P Example code: + + --------- + struct MyContainer(E) + { + E[][] payload; + + this(size_t w, size_t h) + { + payload = new E[][](h, w); + } + + size_t opDollar(size_t dim)() + { + return payload[dim].length; + } + + auto opSlice(size_t dim)(size_t lwr, size_t upr) + { + import std.typecons; + return tuple(lwr, upr); + } + + void opIndexAssign(A...)(E val, A indices) + { + assert(A.length == payload.length); + + foreach (dim, x; indices) + { + static if (is(typeof(x) : size_t)) + { + // this[..., x, ...] + payload[dim][x] = val; + } + else + { + // this[..., x[0] .. x[1], ...] + payload[dim][x[0] .. x[1]] = val; + } + } + } + } + void main() + { + import std.stdio; + + auto c = MyContainer!int(4, 3); + writefln("[%([%(%d%| %)]%|\n %)]", c.payload); + // [[0 0 0 0] + // [0 0 0 0] + // [0 0 0 0]] + + c[1 .. 3, + 2, + 0 .. $] = 1; + /* + Rewritten as: + c.opIndexAssign(c.opSlice!0(1, 3), + 2, + c.opSlice!2(0, c.opDollar!2())); + */ + + writefln("[%([%(%d%| %)]%|\n %)]", c.payload); + // [[0 1 1 0] + // [0 0 1 0] + // [1 1 1 1]] + } + --------- + ) +) + +$(LI $(LNAME2 traits_getfunctionattributes, $(D __traits$(LPAREN)getFunctionAttributes$(RPAREN)) was added): + + $(P This can take one argument, either a function symbol, function type, function + pointer type, or delegate type. + + Examples: + + --------- + void foo() pure nothrow @safe; + static assert([__traits(getFunctionAttributes, foo)] == ["pure", "nothrow", "@safe"]); + + ref int bar(int) @property @trusted; + static assert([__traits(getFunctionAttributes, typeof(&bar))] == ["@property", "ref", "@trusted"]); + --------- + ) +) + +$(LI $(LNAME2 narrowing_conversion_in_ifti, Support template parameter deduction for arguments with a narrowing conversion): + + $(P Implicit Function Template Instantiation will now consider a narrowing conversion + of function arguments when deducing the template instance parameter types. + + --------- + void foo(T)(T[] arr, T elem) { ... } + void main() + { + short[] a; + foo(a, 1); + } + --------- + + In 2.065 and earlier, calling $(D foo(a, 1)) was not allowed. From 2.066, + $(D T) is deduced as $(D short) by considering a narrowing conversion + of the second function argument `1` from $(D int) to $(D short). + ) +) + +$(LI $(LNAME2 deprecate_rmw, Read-Modify-Write operations on shared variables are now deprecated): + + $(P Examples: + + --------- + shared int global; + void main() + { + global++; // deprecated + global *= 2; // deprecated + } + --------- + + Instead you should use $(D atomicOp) from $(D core.atomic): + + --------- + shared int global; + void main() + { + import core.atomic; + atomicOp!"+="(global, 1); + atomicOp!"*="(global, 2); + } + --------- + ) +) + +$(LI $(LNAME2 uniform_scalar_construction, Support uniform construction syntax for built-in scalar types): + + $(P Examples: + + --------- + short n1 = 1; + auto n2 = short(1); // equivalent with n1, typeof(n2) is short + + auto p1 = new long(1); // typeof(p1) is long* + auto p2 = new immutable double(3.14); // typeof(p2) is immutable(double)* + --------- + ) + + $(P The constructor argument should be implicitly convertible to the constructed type. + + --------- + auto n1 = short(32767); // OK + auto n2 = short(32768); // Not allowed, out of bounds of signed short -32768 to 32767 + --------- + ) +) + ) $(BUGSTITLE Library Changes, +$(LI $(LNAME2 duration_split, Duration.get and its wrappers have been deprecated in favor of the new Duration.split:) + + $(P $(CXREF time, Duration.get) and its wrappers, $(CXREF time, Duration.weeks), + $(CXREF time, Duration.days), $(CXREF time, Duration.hours), and + $(CXREF time, Duration.seconds), as well as $(CXREF time, Duration.fracSec) (which + served a similar purpose as $(CXREF time, Duration.get) for the fractional second units) + have proven to be too easily confused with $(CXREF time, Duration.total), causing + subtle bugs. So, they have been deprecated. In their place, $(CXREF time, Duration.split) + has been added - and it's not only very useful, but it does a great job of showing off + what D can do. + + Whereas $(CXREF time, Duration.get) split out all of the units of a + $(CXREF time, Duration) and then returned only one of them, + $(CXREF time, Duration.split) splits out a $(CXREF time, Duration) into the units + that it's told to (which could be one unit or all of them) and returns all of + them. It has two overloads, both which take template arguments that indicate which + of the units are to be split out. The difference is in how the result is returned. + + As with most of the templates in core.time and std.datetime which take strings + to represent units, $(CXREF time, Duration.split) accepts $(D "weeks"), $(D "days"), + $(D "hours"), $(D "minutes"), $(D "seconds"), $(D "msecs"), $(D "usecs"), + $(D "hnsecs"), and $(D "nsecs"). + + The first overload returns the split out units as $(D out) parameters. + + --------- + auto d = weeks(5) + days(4) + hours(17) + seconds(2) + hnsecs(12_007); + short days; + long seconds; + int msecs; + d.split!("days", "seconds", "msecs")(days, seconds, msecs); + assert(days == 39); + assert(seconds == 61_202); + assert(msecs == 1); + --------- + + The arguments can be any integral type (though no protection is given against integer + overflow, so unless it's known that the values are going to be small, it's unwise to + use a small integral type for any of the arguments). + + The second overload returns a struct with the unit names as its fields. Only the + requested units are present as fields. All of the struct's fields are $(D long)s. + + --------- + auto d = weeks(5) + days(4) + hours(17) + seconds(2) + hnsecs(12_007); + auto result = d.split!("days", "seconds", "msecs")(); + assert(result.days == 39); + assert(result.seconds == 61_202); + assert(result.msecs == 1); + --------- + + Or if no units are given to the second overload, then it will return a struct with + all of the units save for $(D nsecs) (since $(D nsecs) would always be $(D 0) when + $(D hnsecs) is one of the units as $(CXREF time, Duration) has hnsec precision). + + --------- + auto d = weeks(5) + days(4) + hours(17) + seconds(2) + hnsecs(12_007); + auto result = d.split(); + assert(result.weeks == 5); + assert(result.days == 4); + assert(result.hours == 17); + assert(result.minutes == 0); + assert(result.seconds == 2); + assert(result.msecs == 1); + assert(result.usecs == 200); + assert(result.hnsecs == 7); + --------- + + Calling $(CXREF time, Duration.get) or its wrappers for each of the units would be + equivalent to that example, only less efficient when more than one unit is + requested, as the calculations would have to be done more than once. The exception + is $(CXREF time, Duration.fracSec) which would have given the total of the + fractional seconds as the requested units rather than splitting them out. + + --------- + // Equivalent to previous example + auto d = weeks(5) + days(4) + hours(17) + seconds(2) + hnsecs(12_007); + assert(d.weeks == 5); + assert(d.days == 4); + assert(d.hours == 17); + assert(d.minutes == 0); + assert(d.seconds == 2); + assert(d.fracSec.msecs == 1); + assert(d.fracSec.usecs == 1200); + assert(d.fracSec.hnsecs == 12_007); + --------- + + It is hoped that $(CXREF time, Duration.split) will be less confusing and thus + result in fewer bugs, but it's definitely the case that it's more powerful. It's + also a great example of D's metaprogramming capabilities given how it splits out + only the requested units and even is able to return a struct with fields with the + same names as the requested units. This on top of being able to handle a variety + of integral types as arguments. And its implemenation isn't even very complicated. + ) + +) + +$(LI $(LNAME2 array_and_aa_changes, Some built-in type properties have been replaced with library functions:) + + $(P Built-in array properties $(D dup) and $(D idup) were replaced with + (module-scoped) free functions in the $(D object) module, thanks to D's support of + $(LINK2 function.html#pseudo-member, Uniform Function Call Syntax). + ) + + $(P Built-in associative array properties $(D rehash), $(D dup), $(D byKey), + $(D byValue), $(D keys), $(D values), and $(D get) were also replaced with free + functions in the $(D object) module. + ) +) + +$(LI $(LNAME2 aa_key_requirement, Associative array keys now require equality rather than ordering:) + + $(P Until 2.065, opCmp was used to customize the comparison of AA struct keys. + + --------- + void main() + { + int[MyKey] aa; + } + + struct MyKey + { + int x; + int y; // want to be ignored for AA key comparison + + int opCmp(ref const MyKey rhs) const + { + if (this.x == rhs.x) + return 0; + + // defined order was merely unused for AA keys. + return this.x > rhs.x ? 1 : -1; + } + } + --------- + + From 2.066, the AA implementation has been changed to use the equality operator ($(D ==)) + for the key comparison. So the $(D MyKey) struct should be modified to: + + --------- + struct MyKey + { + int x; + int y; // want to be ignored for AA key comparison + + int opEquals(ref const MyKey rhs) const + { + return this.x == rhs.x; + } + } + --------- + ) +) + ) $(BUGSTITLE Linker Changes, @@ -176,6 +593,7 @@ $(LI $(RELATIVE_LINK2 deprecate_unorderd_compares, Deprecate unordered floating $(LI $(RELATIVE_LINK2 deprecate_floating_properties, Deprecate $(D .min) property for floating-point types.)) $(LI $(RELATIVE_LINK2 ctfe_overlapped_field, CTFE can handle overlapped union fields.)) $(LI $(RELATIVE_LINK2 get_alias_this, Add a new trait getAliasThis.)) +$(LI $(RELATIVE_LINK2 mixing-struct-opcall, Mixing struct constructors and static opCall is no longer allowed.)) ) $(BUGSTITLE Library Changes, @@ -450,6 +868,29 @@ $(LI $(LNAME2 get_alias_this, Add a new trait getAliasThis:) ) ) +$(LI $(LNAME2 mixing-struct-opcall, Mixing struct constructors and static opCall is no longer allowed): + $(P This was not implemented correctly and caused ambiguities.) + $(P + Example: + --- + struct S + { + this(int i) {} + + static S opCall() // disallowed due to constructor + { + return S.init; + } + } + --- + ) + $(P Note: $(D static opCall) can be used to simulate struct + constructors with no arguments, but this is not recommended + practice. Instead, the preferred solution is to use a factory + function to create struct instances. + ) +) + ) $(BUGSTITLE Library Changes, @@ -8867,3 +9308,4 @@ Macros: STDMODREF = $(D $2) XREF = $(D $2) + CXREF = $(D $2) diff --git a/dcompiler.dd b/dcompiler.dd index 7fb3083029..251ac56d89 100644 --- a/dcompiler.dd +++ b/dcompiler.dd @@ -386,6 +386,14 @@ $(WINDOWS compile only, do not link ) + $(SWITCH $(B -color)$(I =on|off), + Show colored console output. The default depends on terminal capabilities. + $(UL + $(LI $(B on): always use colored output. Same as $(B -color)) + $(LI $(B off): never use colored output.) + ) + ) + $(SWITCH2 -cov, $(P Perform $(LINK2 code_coverage.html, code coverage analysis) and generate .lst file with report.) diff --git a/doc.ddoc b/doc.ddoc index fdef927d06..ba336fb55e 100644 --- a/doc.ddoc +++ b/doc.ddoc @@ -188,10 +188,8 @@ $(NAVBLOCK_HEADER $(TOCHEADERL index.html, D Programming Language, D $(LATEST)), $(TOCENTRYT http://digitalmars.com/d/1.0/index.html, D Programming Language 1.0, D1 Home) ) $(NAVBLOCK_HEADER $(TOCHEADER Documentation), - $(TOCENTRY http://qznc.github.io/d-tut/, Tutorial) - $(LI $(AMAZONLINK 0321635361, Book)) - $(TOCENTRY http://www.informit.com/articles/article.aspx?p=1381876,  31. Tutorial) - $(TOCENTRY http://www.informit.com/articles/article.aspx?p=1609144,  13. Concurrency) + $(TOCENTRY http://ddili.org/ders/d.en/index.html, Online Book (free)) + $(TOCENTRYT http://wiki.dlang.org/Books, D Programming Language Books, More Books) $(TOCENTRYH language-reference.html, Language Reference, $(CATEGORY_SPEC $(SUBNAV_SPEC))) $(TOCENTRY phobos/index.html, Library Reference) @@ -395,7 +393,6 @@ SCINI=
$(NOTRANSLATE $0)
CONSOLE=
$(NOTRANSLATE $0)
MODDEFFILE=
$(NOTRANSLATE $0)
CODE_HIGHLIGHT=$(B $(I $0)) - MDASH= —  NEWS=http://digitalmars.com/drn-bin/wwwnews?$1/$+ diff --git a/download.dd b/download.dd index f8b25ec2fe..5f7b0f9ea0 100644 --- a/download.dd +++ b/download.dd @@ -119,7 +119,7 @@ $(TABLEDL GDC - D Programming Language for GCC, ) $(TR - $(TDL $(LINK2 gdcproject.org/downloads/sources/gdc-4.9-src.tar.xz, gdc-4.9-src.tar.xz) $(LINK2 http://wiki.dlang.org/GDC/Installation, (instructions))) + $(TDL $(LINK2 http://gdcproject.org/downloads/sources/gdc-4.9-src.tar.xz, gdc-4.9-src.tar.xz) $(LINK2 http://wiki.dlang.org/GDC/Installation, (instructions))) $(TD 4.9.0 (2.064.2)) $(TD i386, x86_64, armel, armhf, mips, mipsel, powerpc, ia64, s390, sparc) $(TD $(WIN32) $(LINUX) $(OSX) $(FREEBSD)) @@ -127,7 +127,7 @@ $(TABLEDL GDC - D Programming Language for GCC, ) $(TR - $(TDL $(LINK2 gdcproject.org/downloads/sources/gdc-4.8-src.tar.xz, gdc-4.8-src.tar.xz) $(LINK2 http://wiki.dlang.org/GDC/Installation, (instructions))) + $(TDL $(LINK2 http://gdcproject.org/downloads/sources/gdc-4.8-src.tar.xz, gdc-4.8-src.tar.xz) $(LINK2 http://wiki.dlang.org/GDC/Installation, (instructions))) $(TD 4.8.2 (2.064.2) $(TD i386, x86_64, armel, armhf, mips, mipsel, powerpc, ia64, s390, sparc) $(TD $(WIN32) $(LINUX) $(OSX) $(FREEBSD)) diff --git a/expression.dd b/expression.dd index c713272c4c..7821c86edf 100644 --- a/expression.dd +++ b/expression.dd @@ -1327,11 +1327,11 @@ $(GNAME ParameterAttributes): And: ------------- - int abc(int delegate(long i)); + int abc(int delegate(int i)); void test() { int b = 3; - int foo(long c) { return 6 + b; } + int foo(int c) { return 6 + b; } abc(&foo); } @@ -1340,12 +1340,12 @@ $(GNAME ParameterAttributes): is exactly equivalent to: ------------- - int abc(int delegate(long i)); + int abc(int delegate(int i)); void test() { int b = 3; - abc( delegate int(long c) { return 6 + b; } ); + abc( delegate int(int c) { return 6 + b; } ); } ------------- @@ -1353,15 +1353,15 @@ $(GNAME ParameterAttributes): $(D function)/$(D delegate) are inferred:) ------------- - int abc(int delegate(long i)); - int def(int function(long s)); + int abc(int delegate(int i)); + int def(int function(int s)); void test() { int b = 3; - abc( (long c) { return 6 + b; } ); // inferred to delegate - def( (long c) { return c * 2; } ); // inferred to function - //def( (long c) { return c * b; } ); // error! + abc( (int c) { return 6 + b; } ); // inferred to delegate + def( (int c) { return c * 2; } ); // inferred to function + //def( (int c) { return c * b; } ); // error! // Because the FunctionBody accesses b, then the function literal type // is inferred to delegate. But def cannot receive delegate. } diff --git a/faq.dd b/faq.dd index f8f52fc6fe..3c731a0ef2 100644 --- a/faq.dd +++ b/faq.dd @@ -189,7 +189,7 @@ $(ITEM q1_1, Could you change the name? D is kind of hard to search for on searc should yield substantially better search results. ) - $(P Most publically available D code has "// Written in the D programming + $(P Most publicly available D code has "// Written in the D programming language" as its first comment. ) diff --git a/hash-map.dd b/hash-map.dd index 7b1b94ae17..309d914dfc 100644 --- a/hash-map.dd +++ b/hash-map.dd @@ -13,7 +13,7 @@ $(SPEC_S Associative Arrays, --------- int[string] b; // associative array b of ints that are // indexed by an array of characters. - // The $(CODE_HIGHLIGHT KeyType) is string + // The KeyType is string b["hello"] = 3; // set value associated with key "hello" to 3 func(b["hello"]); // pass 3 as parameter to func() --------- @@ -58,14 +58,12 @@ $(H3 Using Classes as the KeyType) $(UL $(LI $(D hash_t toHash())) - - $(LI $(D bool opEquals(Object))) - $(LI $(D int opCmp(Object))) + $(LI $(D bool opEquals(Object))) ) $(P $(D hash_t) is an alias to an integral type.) - $(P Note that the parameter to $(D opCmp) and $(D opEquals) is + $(P Note that the parameter to $(D opEquals) is of type $(D Object), not the type of the class in which it is defined.) @@ -81,22 +79,20 @@ $(H3 Using Classes as the KeyType) { Foo foo = cast(Foo) o; return foo && a == foo.a && b == foo.b; } - - int $(CODE_HIGHLIGHT opCmp)(Object o) - { Foo foo = cast(Foo) o; - if (!foo) - return -1; - if (a == foo.a) - return b - foo.b; - return a - foo.a; - } } --- - $(P The implementation may use either $(D opEquals) or $(D opCmp) or - both. Care should be taken so that the results of - $(D opEquals) and $(D opCmp) are consistent with each other when - the class objects are the same or not.) + $(P Care should be taken that $(D toHash) should consistently be the + same value when $(D opEquals) returns true. In other words, two objects + that are considered equal should always have the same hash value. If + this is not the case, the associative array will not function properly. + Also note that $(D opCmp) is not used to check for equality by the + associative array. However, since the actual $(D opEquals) or $(D + opCmp) called is not decided until runtime, the compiler cannot always + detect mismatched functions. Because of legacy issues, the compiler may + reject an associative array key type that overrides $(D opCmp) but not + $(D opEquals). This restriction may be removed in future versions of + D.) $(H3 Using Structs or Unions as the KeyType) @@ -110,7 +106,6 @@ $(H3 Using Structs or Unions as the KeyType) --------- const hash_t $(CODE_HIGHLIGHT toHash)(); const bool $(CODE_HIGHLIGHT opEquals)(ref const KeyType s); - const int $(CODE_HIGHLIGHT opCmp)(ref const KeyType s); --------- $(P For example:) @@ -133,17 +128,18 @@ $(H3 Using Structs or Unions as the KeyType) return std.string.cmp(this.str, s.str) == 0; } - const int $(CODE_HIGHLIGHT opCmp)(ref const MyString s) - { - return std.string.cmp(this.str, s.str); - } } --------- - $(P The implementation may use either $(D opEquals) or $(D opCmp) or - both. Care should be taken so that the results of - $(D opEquals) and $(D opCmp) are consistent with each other when - the struct/union objects are the same or not.) + $(P Care should be taken that $(D toHash) should consistently be the + same value when $(D opEquals) returns true. In other words, two structs + that are considered equal should always have the same hash value. If + this is not the case, the associative array will not function properly. + Also note that $(D opCmp) is not used to check for equality by the + associative array. For this reason, and for legacy reasons, an + associative array key is not allowed to define a specialized $(D + opCmp), but omit a specialized $(D opEquals). This restriction may be + removed in future versions of D.) $(H3 Construction or assignment on setting AA entries) diff --git a/index.dd b/index.dd index 6201cb96e6..447c52082c 100644 --- a/index.dd +++ b/index.dd @@ -232,10 +232,10 @@ void main() ---- )) -$(LI The $(D @safe), $(D @trusted), and $(D @system) modular +$(LI The $(D @safe), $(D @trusted), and $(D @system) function attributes allow the programmer to best decide the safety-efficiency tradeoffs of an application, and have the compiler check for -consistency. $(LINK2 safed.html, Read more.)) +consistency. $(LINK2 memory-safe-d.html, Read more.)) ) diff --git a/memory-safe-d.dd b/memory-safe-d.dd index 4ede430249..dc5b028cdc 100644 --- a/memory-safe-d.dd +++ b/memory-safe-d.dd @@ -4,48 +4,39 @@ $(SPEC_S Memory Safety, $(P $(I Memory Safety) for a program is defined as it being impossible for the program to corrupt memory. - Therefore, the Safe D consists only of programming language - features that are guaranteed to never result in memory - corruption. + Therefore, the safe subset of D consists only of programming + language features that are guaranteed to never result in memory + corruption. See $(LINK2 safed.html, this article) for a rationale. ) - $(P Safe D is enabled on a per-module basis by compiling with - the $(B -safe) compiler switch. + $(P Memory-safe code $(DDSUBLINK function, function-safety, cannot + use certain language features), such as: + $(UL + $(LI Casts that break the type system.) + $(LI Modification of pointer values.) + $(LI Taking the address of a local variable or function parameter.) + ) ) -$(H3 Proscribed Forms) +$(H3 Usage) -$(UL - $(LI $(DDLINK iasm, Inline Assembler, Inline assembler). + $(P Memory safety can be enabled on a per-function basis using + the $(DDSUBLINK function, safe-functions, $(D @safe) attribute). + This can be inferred when the compiler has the function body + available. The $(DDSUBLINK function, trusted-functions, $(D + @trusted) attribute) can be used when a function has a safe + interface, but uses unsafe code internally. These functions can + be called from $(D @safe) code. ) - - $(LI Casting away const or immutable attributes. - ) - - $(LI Casting away shared attributes. - ) - - $(LI Casting from one pointer type to another pointer type, - except for: - $(UL - $(LI casting to $(CODE void*) is allowed) - $(LI casting from a pointer to an arithmetic type to - a pointer to another arithmetic type of the same or - smaller size is allowed) - ) - ) - - $(LI Casting from a non-pointer type to a pointer type. - ) -) - - $(P A safe module can import and use the public interface of - a system module. + + $(P Array bounds checks are necessary to enforce memory safety, so + these are enabled (by default) for $(D @safe) code even in $(B + -release) mode. ) $(H3 Limitations) - $(P Safe D does not imply that code is portable, uses only + $(P Memory safety does not imply that code is portable, uses only sound programming practices, is free of byte order dependencies, or other bugs. It is focussed only on eliminating memory corruption possibilities. diff --git a/operatoroverloading.dd b/operatoroverloading.dd index 8d64da9640..9fda6a3da9 100644 --- a/operatoroverloading.dd +++ b/operatoroverloading.dd @@ -11,15 +11,23 @@ $(SPEC_S Operator Overloading, $(LI $(RELATIVE_LINK2 Unary, Unary Operator Overloading)) $(LI $(RELATIVE_LINK2 Cast, Cast Operator Overloading)) $(LI $(RELATIVE_LINK2 Binary, Binary Operator Overloading)) - $(LI $(RELATIVE_LINK2 equals, Overloading $(D ==) and $(D !=))) - $(LI $(RELATIVE_LINK2 compare, Overloading $(D <), $(D <)$(D =), - $(D <), and $(D <)$(D =))) + $(LI $(RELATIVE_LINK2 eqcmp, Overloading the Comparison Operators) + $(UL + $(LI $(RELATIVE_LINK2 equals, Overloading $(D ==) and $(D !=))) + $(LI $(RELATIVE_LINK2 compare, Overloading $(D <), $(D <=), + $(D >), and $(D >=))) + ) + ) $(LI $(RELATIVE_LINK2 FunctionCall, Function Call Operator Overloading)) $(LI $(RELATIVE_LINK2 Assignment, Assignment Operator Overloading)) $(LI $(RELATIVE_LINK2 OpAssign, Op Assignment Operator Overloading)) - $(LI $(RELATIVE_LINK2 Array, Index Operator Overloading)) - $(LI $(RELATIVE_LINK2 Slice, Slice Operator Overloading)) - $(LI $(RELATIVE_LINK2 Dollar, Dollar Operator Overloading)) + $(LI $(RELATIVE_LINK2 ArrayOps, Array Indexing and Slicing Operators Overloading) + $(UL + $(LI $(RELATIVE_LINK2 Array, Index Operator Overloading)) + $(LI $(RELATIVE_LINK2 Slice, Slice Operator Overloading)) + $(LI $(RELATIVE_LINK2 Dollar, Dollar Operator Overloading)) + ) + ) $(LI $(RELATIVE_LINK2 Dispatch, Forwarding)) ) @@ -56,7 +64,7 @@ $(H2 $(LNAME2 Unary, Unary Operator Overloading)) ) ) - $(P For example, in order to overload the - (negation) operator for struct S, and + $(P For example, in order to overload the $(D -) (negation) operator for struct S, and no other operator:) --- @@ -124,65 +132,70 @@ $(H3 Overloading Slice Unary Operators) $(THEAD $(I op), $(I rewrite)) $(TROW $(D -)$(I a)$(D [)$(SLICE)$(D ]), - $(I a)$(D .opSliceUnary!("-")$(LPAREN))$(SLICE2)$(D $(RPAREN)) + $(I a)$(D .opIndexUnary!("-")$(LPAREN))$(I a)$(D .opSlice$(LPAREN))$(SLICE2)$(D $(RPAREN)$(RPAREN)) ) $(TROW $(D +)$(I a)$(D [)$(SLICE)$(D ]), - $(I a)$(D .opSliceUnary!("+")$(LPAREN))$(SLICE2)$(D $(RPAREN)) + $(I a)$(D .opIndexUnary!("+")$(LPAREN))$(I a)$(D .opSlice$(LPAREN))$(SLICE2)$(D $(RPAREN)$(RPAREN)) ) $(TROW $(D ~)$(I a)$(D [)$(SLICE)$(D ]), - $(I a)$(D .opSliceUnary!("~")$(LPAREN))$(SLICE2)$(D $(RPAREN)) + $(I a)$(D .opIndexUnary!("~")$(LPAREN))$(I a)$(D .opSlice$(LPAREN))$(SLICE2)$(D $(RPAREN)$(RPAREN)) ) $(TROW $(D *)$(I a)$(D [)$(SLICE)$(D ]), - $(I a)$(D .opSliceUnary!("*")$(LPAREN))$(SLICE2)$(D $(RPAREN)) + $(I a)$(D .opIndexUnary!("*")$(LPAREN))$(I a)$(D .opSlice$(LPAREN))$(SLICE2)$(D $(RPAREN)$(RPAREN)) ) $(TROW $(D ++)$(I a)$(D [)$(SLICE)$(D ]), - $(I a)$(D .opSliceUnary!("++")$(LPAREN))$(SLICE2)$(D $(RPAREN)) + $(I a)$(D .opIndexUnary!("++")$(LPAREN))$(I a)$(D .opSlice$(LPAREN))$(SLICE2)$(D $(RPAREN)$(RPAREN)) ) $(TROW $(D --)$(I a)$(D [)$(SLICE)$(D ]), - $(I a)$(D .opSliceUnary!("--")$(LPAREN))$(SLICE2)$(D $(RPAREN)) + $(I a)$(D .opIndexUnary!("--")$(LPAREN))$(I a)$(D .opSlice$(LPAREN))$(SLICE2)$(D $(RPAREN)$(RPAREN)) ) $(TROW $(D -)$(I a)$(D [ ]), - $(I a)$(D .opSliceUnary!("-")()) + $(I a)$(D .opIndexUnary!("-")()) ) $(TROW $(D +)$(I a)$(D [ ]), - $(I a)$(D .opSliceUnary!("+")()) + $(I a)$(D .opIndexUnary!("+")()) ) $(TROW $(D ~)$(I a)$(D [ ]), - $(I a)$(D .opSliceUnary!("~")()) + $(I a)$(D .opIndexUnary!("~")()) ) $(TROW $(D *)$(I a)$(D [ ]), - $(I a)$(D .opSliceUnary!("*")()) + $(I a)$(D .opIndexUnary!("*")()) ) $(TROW $(D ++)$(I a)$(D [ ]), - $(I a)$(D .opSliceUnary!("++")()) + $(I a)$(D .opIndexUnary!("++")()) ) $(TROW $(D --)$(I a)$(D [ ]), - $(I a)$(D .opSliceUnary!("--")()) + $(I a)$(D .opIndexUnary!("--")()) ) ) + $(P For backward compatibility, if the above rewrites fail and + $(D opSliceUnary) is defined, then the rewrites + $(D a.opSliceUnary!(op)(a, i, j)) and + $(D a.opSliceUnary!(op)) are tried instead, respectively.) + $(H2 $(LNAME2 Cast, Cast Operator Overloading)) $(TABLE2 Cast Operators, @@ -260,7 +273,45 @@ T opBinary(string op)(T rhs) { --- -$(H2 $(LNAME2 equals, Overloading $(D ==) and $(D !=))) +$(H2 $(LNAME2 eqcmp, Overloading the Comparison Operators)) + + $(P D allows overloading of the comparison operators $(D ==), $(D !=), + $(D <), $(D <=), $(D >=), $(D >) via two functions, $(D opEquals) and + $(D opCmp).) + + $(P The equality and inequality operators are treated separately + because while practically all user-defined types can be compared for + equality, only a subset of types have a meaningful ordering. For + example, while it makes sense to determine if two RGB color vectors are + equal, it is not meaningful to say that one color is greater than + another, because colors do not have an ordering. Thus, one would define + $(D opEquals) for a $(D Color) type, but not $(D opCmp).) + + $(P Furthermore, even with orderable types, the order relation may not + be linear. For example, one may define an ordering on sets via the + subset relation, such that $(D x < y) is true if $(D x) is a (strict) + subset of $(D y). If $(D x) and $(D y) are disjoint sets, then neither + $(D x < y) nor $(D y < x) holds, but that does not imply that $(D x == + y). Thus, it is insufficient to determine equality purely based on $(D + opCmp) alone. For this reason, $(D opCmp) is only used for the + inequality operators $(D <), $(D <=), $(D >=), and $(D >). The equality + operators $(D ==) and $(D !=) always employ $(D opEquals) instead.) + + $(P Therefore, it is the programmer's responsibility to ensure that $(D + opCmp) and $(D opEquals) are consistent with each other. If $(D + opEquals) is not specified, the compiler provides a default version + that does member-wise comparison. If this suffices, one may define only + $(D opCmp) to customize the behaviour of the inequality operators. But + if not, then a custom version of $(D opEquals) should be defined as + well, in order to preserve consistent semantics between the two kinds + of comparison operators.) + + $(P Finally, if the user-defined type is to be used as a key in the + built-in associative arrays, then the programmer must ensure that the + semantics of $(D opEquals) and $(D toHash) are consistent. If not, the + associative array may not work in the expected manner.) + +$(H3 $(LNAME2 equals, Overloading $(D ==) and $(D !=))) $(P Expressions of the form $(CODE a != b) are rewritten as $(CODE !(a == b)).) @@ -282,24 +333,25 @@ $(OL --- ) $(LI Otherwise the expressions $(CODE a.opEquals(b)) and - $(CODE b.opEquals(a)) are tried. If both resolve to the same opEquals - function, then the expression is rewritten to be $(CODE a.opEquals(b)). + $(CODE b.opEquals(a)) are tried. If both resolve to the same $(D + opEquals) function, then the expression is rewritten to be $(CODE + a.opEquals(b)). ) $(LI If one is a better match than the other, or one compiles and the other does not, the first is selected.) $(LI Otherwise, an error results.) ) - $(P If overridding Object.opEquals() for classes, the class member function - signature should look like:) + $(P If overridding $(D Object.opEquals()) for classes, the class member + function signature should look like:) --- class C { override bool opEquals(Object o) { ... } } --- - $(P If structs declare an opEquals member function for the identity comparison, - it could have several forms, such as:) + $(P If structs declare an $(D opEquals) member function for the + identity comparison, it could have several forms, such as:) --- struct S { // lhs should be mutable object @@ -311,7 +363,7 @@ $(OL } --- - $(P Alternatively, you can declare a single templated opEquals + $(P Alternatively, you can declare a single templated $(D opEquals) function with an $(XLINK2 template.html#auto-ref-parameters, auto ref) parameter:) --- @@ -323,7 +375,7 @@ $(OL --- -$(H2 $(LNAME2 compare, Overloading $(D <), $(D <)$(D =), $(D >), and $(D >)$(D =))) +$(H3 $(LNAME2 compare, Overloading $(D <), $(D <)$(D =), $(D >), and $(D >)$(D =))) $(P Comparison operations are rewritten as follows:) @@ -340,31 +392,38 @@ $(H2 $(LNAME2 compare, Overloading $(D <), $(D <)$(D =), $(D >), and $(D >)$(D = ) $(P Both rewrites are tried. If only one compiles, that one is taken. - If they both resolve to the same function, the first - rewrite is done. If they resolve to different functions, the best matching one - is used. If they both match the same, but are different functions, an ambiguity - error results. - ) + If they both resolve to the same function, the first rewrite is done. + If they resolve to different functions, the best matching one is used. + If they both match the same, but are different functions, an ambiguity + error results.) - $(P If overriding Object.opCmp() for classes, the class member function - signature should look like:) + $(P If overriding $(D Object.opCmp()) for classes, the class member + function signature should look like:) --- class C { override int opCmp(Object o) { ... } } --- - $(P If structs declare an opCmp member function, it should follow the following - form:) + $(P If structs declare an $(D opCmp) member function, it should have + the following form:) --- struct S { int opCmp(ref const S s) const { ... } } --- + $(P Note that $(D opCmp) is only used for the inequality operators; + expressions like $(D a == b) always uses $(D opEquals). If $(D opCmp) + is defined but $(D opEquals) isn't, the compiler will supply a default + version of $(D opEquals) that performs member-wise comparison. If this + member-wise comparison is not consistent with the user-defined $(D + opCmp), then it is up to the programmer to supply an appropriate + version of $(D opEquals). Otherwise, inequalities like $(D a <= b) + will behave inconsistently with equalities like $(D a == b).) $(H2 $(LNAME2 FunctionCall, Function Call Operator Overloading $(D f()))) - $(P The function call operator, (), can be overloaded by + $(P The function call operator, $(D ()), can be overloaded by declaring a function named $(CODE opCall): ) @@ -387,23 +446,9 @@ $(H2 $(LNAME2 FunctionCall, Function Call Operator Overloading $(D f()))) were a function. ) - $(P $(CODE static opCall) also works as expected for function call operator with - type names. - ) - - ------- - struct Double { - $(CODE_HIGHLIGHT static) int $(CODE_HIGHLIGHT opCall)(int x) { return x * 2; } - } - void test() { - int i = Double(2); - assert(i == 4); - } - ------- - $(P Note that merely declaring $(D opCall) automatically disables $(XLINK2 struct.html#StructLiteral, struct literal) syntax. - To avoid the limitation, you need to also declare $(XLINK2 Struct-Constructor, + To avoid the limitation, you need to also declare a $(XLINK2 struct.html#Struct-Constructor, constructor) so that it takes priority over $(D opCall) in $(D Type(...)) syntax. ) @@ -419,6 +464,41 @@ $(H2 $(LNAME2 FunctionCall, Function Call Operator Overloading $(D f()))) assert(result == 50); } ------- + +$(H3 $(LNAME2 static-opcall, Static opCall)) + + $(P $(CODE static opCall) also works as expected for a function call operator with + type names. + ) + + ------- + struct Double { + $(CODE_HIGHLIGHT static) int $(CODE_HIGHLIGHT opCall)(int x) { return x * 2; } + } + void test() { + int i = Double(2); + assert(i == 4); + } + ------- + + $(P Mixing struct constructors and $(D static opCall) is not allowed.) + + --- + struct S + { + this(int i) {} + static S opCall() // disallowed due to constructor + { + return S.init; + } + } + --- + + $(P Note: $(D static opCall) can be used to simulate struct + constructors with no arguments, but this is not recommended + practice. Instead, the preferred solution is to use a factory + function to create struct instances. + ) $(H2 $(LNAME2 Assignment, Assignment Operator Overloading)) @@ -469,7 +549,7 @@ $(H3 Index Assignment Operator Overloading) $(P If the left hand side of an assignment is an index operation on a struct or class instance, - it can be overloaded by providing an opIndexAssign member function. + it can be overloaded by providing an $(D opIndexAssign) member function. Expressions of the form $(D a[$(ARGUMENTS)] = c) are rewritten as $(D a.opIndexAssign$(LPAREN)c,$(ARGUMENTS)$(RPAREN)). ) @@ -487,29 +567,41 @@ void test() { $(H3 Slice Assignment Operator Overloading) - $(P If the left hand side of an assignment is a slice operation - on a struct or class instance, - it can be overloaded by providing an opSliceAssign member function. - Expressions of the form $(CODE a[)$(SLICE)$(D ] = c) are rewritten - as $(CODE a.opSliceAssign$(LPAREN)c,) $(SLICE2)$(D $(RPAREN)), and - $(CODE a[] = c) as $(CODE a.opSliceAssign(c)). + $(P If the left hand side of an assignment is a slice operation on a + struct or class instance, it can be overloaded by implementing an $(D + opIndexAssign) member function that takes the return value of the $(D + opSlice) function as parameter(s). + Expressions of the form $(CODE a[)$(SLICE)$(D ] = c) are rewritten as + $(CODE a.opIndexAssign$(LPAREN)c,) $(D a.opSlice$(LPAREN))$(SLICE2)$(D $(RPAREN)$(RPAREN)), + and $(CODE a[] = c) as $(CODE a.opIndexAssign(c)). + ) + + $(P See $(RELATIVE_LINK2 ArrayOps, Array + Indexing and Slicing Operators Overloading) for more details. ) ------- struct A { - int $(CODE_HIGHLIGHT opSliceAssign)(int v); // overloads a[] = v - int $(CODE_HIGHLIGHT opSliceAssign)(int v, size_t x, size_t y); // overloads a[i .. j] = v + int $(CODE_HIGHLIGHT opIndexAssign)(int v); // overloads a[] = v + int $(CODE_HIGHLIGHT opIndexAssign)(int v, size_t[2] x); // overloads a[i .. j] = v + int[2] $(CODE_HIGHLIGHT opSlice)(size_t x, size_t y); // overloads i .. j } void test() { A a; int v; - a$(CODE_HIGHLIGHT []) = v; // same as a.opSliceAssign(v); - a$(CODE_HIGHLIGHT [)3..4$(CODE_HIGHLIGHT ]) = v; // same as a.opSliceAssign(v,3,4); + a$(CODE_HIGHLIGHT []) = v; // same as a.opIndexAssign(v); + a$(CODE_HIGHLIGHT [)3..4$(CODE_HIGHLIGHT ]) = v; // same as a.opIndexAssign(v, a.opSlice(3,4)); } ------- + $(P For backward compatibility, if rewriting $(D a[)$(SLICE)$(D ]) as + $(D a.opIndexAssign$(LPAREN)a.opSlice$(LPAREN))$(SLICE2)$(D $(RPAREN)$(RPAREN)) + fails to compile, the legacy rewrite + $(D opSliceAssign$(LPAREN)c,) $(SLICE2)$(D $(RPAREN)) is used instead. + ) + $(H2 $(LNAME2 OpAssign, Op Assignment Operator Overloading)) $(P The following op assignment operators are overloadable:) @@ -534,7 +626,7 @@ a.opOpAssign!("$(METACODE op)")(b) $(H3 Index Op Assignment Operator Overloading) $(P If the left hand side of an $(I op)= is an index expression on - a struct or class instance and opIndexOpAssign is a member:) + a struct or class instance and $(D opIndexOpAssign) is a member:) --- a[$(METACODE $(ARGUMENTS))] $(METACODE op)= c @@ -549,7 +641,7 @@ a.opIndexOpAssign!("$(METACODE op)")(c, $(METACODE $(ARGUMENTS))) $(H3 Slice Op Assignment Operator Overloading) $(P If the left hand side of an $(I op)= is a slice expression on - a struct or class instance and opSliceOpAssign is a member:) + a struct or class instance and $(D opIndexOpAssign) is a member:) --- a[$(METACODE $(SLICE))] $(METACODE op)= c @@ -558,7 +650,7 @@ a[$(METACODE $(SLICE))] $(METACODE op)= c $(P it is rewritten as:) --- -a.opSliceOpAssign!("$(METACODE op)")(c, $(METACODE $(SLICE2))) +a.opIndexOpAssign!("$(METACODE op)")(c, a.opSlice($(METACODE $(SLICE2)))) --- $(P and) @@ -570,14 +662,27 @@ a[] $(METACODE op)= c $(P it is rewritten as:) --- -a.opSliceOpAssign!("$(METACODE op)")(c) +a.opIndexOpAssign!("$(METACODE op)")(c) --- -$(H2 $(LNAME2 Array, Index Operator Overloading)) + $(P For backward compatibility, if the above rewrites fail and $(D + opSliceOpAssign) is defined, then the rewrites + $(D a.opSliceOpAssign(c, i, j)) and $(D a.opSliceOpAssign(c)) are + tried, respectively. + ) - $(P The array index operator, $(D a[)$(ARGUMENTS)$(D ]), can be overloaded by - declaring a function named $(D opIndex) with one - or more parameters. + +$(H2 $(LNAME2 ArrayOps, Array Indexing and Slicing Operators Overloading)) + + $(P The array indexing and slicing operators are overloaded by + implementing the $(D opIndex), $(D opSlice), and $(D opDollar) methods. + These may be combined to implement multidimensional arrays. + ) + +$(H3 $(LNAME2 Array, Index Operator Overloading)) + + $(P Expressions of the form $(D arr[)$(ARGUMENTS)$(D ]) are translated + into $(D arr.opIndex$(LPAREN))$(ARGUMENTS)$(D $(RPAREN)). For example: ) ------- @@ -596,42 +701,139 @@ void test() { were an array. ) - $(P If an index expression can be rewritten using $(D opIndexAssign) or $(D opIndexOpAssign), - those are preferred over opIndex. + $(P If an index expression can be rewritten using $(D opIndexAssign) or + $(D opIndexOpAssign), those are preferred over $(D opIndex). ) -$(H2 $(LNAME2 Slice, Slice Operator Overloading)) +$(H3 $(LNAME2 Slice, Slice Operator Overloading)) $(P Overloading the slicing operator means overloading expressions - like $(CODE a[]) and $(D a[)$(SLICE)$(D ]). - This can be done by declaring a member function named $(CODE opSlice). + like $(D a[]) or $(D a[)$(SLICE)$(D ]), where the expressions inside + the square brackets contain slice expressions of the form $(SLICE). ) -------- -class A { - int $(CODE_HIGHLIGHT opSlice)(); // overloads a[] - int $(CODE_HIGHLIGHT opSlice)(size_t x, size_t y); // overloads a[i .. j] -} + $(P To overload $(D a[]), simply define $(D opIndex) with no parameters: + ) +----- +struct S { + int[] impl; + int[] opIndex() { + return impl[]; + } +} void test() { - A a = new A(); - int i; - int v; - - i = a$(CODE_HIGHLIGHT []); // same as i = a.opSlice(); - i = a$(CODE_HIGHLIGHT [)3..4$(CODE_HIGHLIGHT ]); // same as i = a.opSlice(3,4); + auto s = S([1,2,3]); + auto t = s[]; // calls s.opIndex() + assert(t == [1,2,3]); } -------- +----- + + $(P To overload array indexing of the form $(D a[)$(SLICE)$(D ,) ...$(D ]), + two steps are needed. First, the expressions of the form $(SLICE) are + translated via $(D opSlice) into user-defined objects that encapsulate + the endpoints $(I i) and $(I j). Then these user-defined objects are + passed to $(D opIndex) to perform the actual slicing. This design was + chosen in order to support mixed indexing and slicing in + multidimensional arrays; for example, in translating expressions like + $(D arr[1, 2..3, 4]). + ) + + $(P More precisely, an expression of the form $(D arr[)$(ARGUMENTS)$(D ]) + is translated into $(D arr.opIndex$(LPAREN))$(ARGUMENTS2)$(D $(RPAREN)). + Each argument $(I b)$(SUBSCRIPT i) can be either a single expression, + in which case it is passed directly as the corresponding argument $(I + c)$(SUBSCRIPT i) to $(D opIndex); or it can be a slice expression of + the form $(I x)$(SUBSCRIPT i)$(D ..)$(I y)$(SUBSCRIPT i), in which case + the corresponding argument $(I c)$(SUBSCRIPT i) to $(D opIndex) is $(D + arr.opSlice!i$(LPAREN))$(I x)$(SUBSCRIPT i)$(D , )$(I y)$(SUBSCRIPT i)$(D $(RPAREN)). + Namely: + ) + + $(TABLE2 + $(THEAD $(I op), $(I rewrite)) + $(TROW + $(D arr[1, 2, 3]), + $(D arr.opIndex(1, 2, 3)) + ) + $(TROW + $(D arr[1..2, 3..4, 5..6]), + $(D arr.opIndex(arr.opSlice!0(1,2), arr.opSlice!1(3,4), + arr.opSlice!2(5,6))) + ) + $(TROW + $(D arr[1, 2..3, 4]), + $(D arr.opIndex(1, arr.opSlice!1(2,3), 4)) + ) + ) + + $(P Similar translations are done for assignment operators involving + slicing, for example: + ) + + $(TABLE2 + $(THEAD $(I op), $(I rewrite)) + $(TROW + $(D arr[1, 2..3, 4] = c), + $(D arr.opIndexAssign(c, 1, arr.opSlice!1(2, 3), 4)) + ) + $(TROW + $(D arr[2, 3..4] += c), + $(D arr.opIndexOpAssign!"+"(c, 2, arr.opSlice!1(2, 3))) + ) + ) + + $(P The intention is that $(D opSlice!i) should return a user-defined + object that represents an interval of indices along the $(D i)'th + dimension of the array. This object is then passed to $(D opIndex) to + perform the actual slicing operation. If only one-dimensional slicing + is desired, $(D opSlice) may be declared without the compile-time + parameter $(D i). + ) + + $(P Note that in all cases, $(D arr) is only evaluated once. Thus, an + expression like $(D getArray()[1, 2..3, $-1]=c) has the effect of:) + +------ +auto __tmp = getArray(); +__tmp.opIndexAssign(c, 1, __tmp.opSlice!1(2,3), __tmp.opDollar!2 - 1); +------ + $(P where the initial function call to $(D getArray) is only executed + once. + ) - $(P If a slice expression can be rewritten using $(D opSliceAssign) or $(D opSliceOpAssign), - those are preferred over $(D opSlice). + $(P For backward compatibility, $(D a[]) and $(D a[)$(SLICE)$(D ]) can + also be overloaded by implementing $(D opSlice()) with no arguments and + $(D opSlice$(LPAREN))$(SLICE2)$(D $(RPAREN)) with two arguments, + respectively. This only applies for one-dimensional slicing, and dates + from when D did not have full support for multidimensional arrays. This + usage of $(D opSlice) is discouraged. ) -$(H2 $(LNAME2 Dollar, Dollar Operator Overloading)) +$(H3 $(LNAME2 Dollar, Dollar Operator Overloading)) $(P Within the arguments to array index and slicing operators, $(D $) - gets translated to $(D opDollar), which can be overloaded by a struct - or class object. + gets translated to $(D opDollar!i), where $(D i) is the position of the + expression $(D $) appears in. For example: + ) + + $(TABLE2 + $(THEAD $(I op), $(I rewrite)) + $(TROW + $(D arr[$-1, $-2, 3]), + $(D arr.opIndex(arr.opDollar!0 - 1, + arr.opDollar!1 - 2, 3)) + ) + $(TROW + $(D arr[1, 2, 3..$]), + $(D arr.opIndex(1, 2, arr.opSlice!2(3, arr.opDollar!2))) + ) + ) + + $(P The intention is that $(D opDollar!i) should return the length of + the array along its $(D i)'th dimension, or a user-defined object + representing the end of the array along that dimension, that is + understood by $(D opSlice) and $(D opIndex). ) ------ @@ -671,17 +873,23 @@ void test() { can be returned to implement multidimensional arrays. ) + $(P Note that $(D opDollar!i) is only evaluated once for each $(D i) + where $(D $) occurs in the corresponding position in the indexing + operation. Thus, an expression like $(D arr[$-sqrt($), 0, $-1]) has + the effect of: + ) +------ +auto __tmp1 = arr.opDollar!0; +auto __tmp2 = arr.opDollar!2; +arr.opIndex(__tmp1 - sqrt(__tmp1), 0, __tmp2 - 1); +------ + $(P If $(D opIndex) is declared with only one argument, the compile-time argument to $(D opDollar) may be omitted. In this case, it is illegal to use $(D $) inside an array indexing expression with more than one argument. ) - $(P Note that $(D opDollar) does not have to return an integer index; - it can return any type as long as that type is understood by $(D - opIndex). For example, a special token can be returned by $(D opDollar) - to implement slicing of infinite ranges. - ) $(H2 $(LNAME2 Dispatch, Forwarding)) @@ -731,6 +939,7 @@ Macros: WIKI=OperatorOverloading CATEGORY_SPEC=$0 ARGUMENTS=$(I b)$(SUBSCRIPT 1), $(I b)$(SUBSCRIPT 2), ... $(I b)$(SUBSCRIPT n) + ARGUMENTS2=$(I c)$(SUBSCRIPT 1), $(I c)$(SUBSCRIPT 2), ... $(I c)$(SUBSCRIPT n) SLICE=$(I i)..$(I j) SLICE2=$(I i), $(I j) FOO=