mirror of
https://github.com/openRuyi-Project/gcc.git
synced 2026-09-07 22:42:06 +00:00
D front-end changes:
- Import dmd v2.106.1-rc.1.
- Unrecognized pragmas are no longer an error by default.
D runtime changes:
- Import druntime v2.106.1-rc.1.
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream dmd f1a045928e.
* dmd/VERSION: Bump version to v2.106.1-rc.1.
* gdc.texi (fignore-unknown-pragmas): Update documentation.
* d-builtins.cc (covariant_with_builtin_type_p): Update for new
front-end interface.
* d-lang.cc (d_parse_file): Likewise.
* typeinfo.cc (make_frontend_typeinfo): Likewise.
libphobos/ChangeLog:
* libdruntime/MERGE: Merge upstream druntime f1a045928e.
* libdruntime/Makefile.am (DRUNTIME_DSOURCES): Add
core/stdc/stdatomic.d.
* libdruntime/Makefile.in: Regenerate.
688 lines
19 KiB
D
688 lines
19 KiB
D
/**
|
|
This module contains support array (vector) operations
|
|
Copyright: Copyright Digital Mars 2000 - 2019.
|
|
License: Distributed under the
|
|
$(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
|
|
(See accompanying file LICENSE)
|
|
Source: $(DRUNTIMESRC core/_internal/_array/_operations.d)
|
|
*/
|
|
module core.internal.array.operations;
|
|
import core.internal.traits : Filter, staticMap, Unqual;
|
|
|
|
version (GNU) version = GNU_OR_LDC;
|
|
version (LDC) version = GNU_OR_LDC;
|
|
|
|
/**
|
|
* Perform array (vector) operations and store the result in `res`. Operand
|
|
* types and operations are passed as template arguments in Reverse Polish
|
|
* Notation (RPN).
|
|
|
|
* Operands can be slices or scalar types. The element types of all
|
|
* slices and all scalar types must be implicitly convertible to `T`.
|
|
*
|
|
* Operations are encoded as strings, e.g. `"+"`, `"%"`, `"*="`. Unary
|
|
* operations are prefixed with "u", e.g. `"u-"`, `"u~"`. Only the last
|
|
* operation can and must be an assignment (`"="`) or op-assignment (`"op="`).
|
|
*
|
|
* All slice operands must have the same length as the result slice.
|
|
*
|
|
* Params: T[] = type of result slice
|
|
* Args = operand types and operations in RPN
|
|
* res = the slice in which to store the results
|
|
* args = operand values
|
|
*
|
|
* Returns: the slice containing the result
|
|
*/
|
|
T[] arrayOp(T : T[], Args...)(T[] res, Filter!(isType, Args) args) @trusted
|
|
{
|
|
alias scalarizedExp = staticMap!(toElementType, Args);
|
|
alias check = typeCheck!(true, T, scalarizedExp); // must support all scalar ops
|
|
|
|
foreach (argsIdx, arg; typeof(args))
|
|
{
|
|
static if (is(arg == U[], U))
|
|
{
|
|
assert(res.length == args[argsIdx].length, "Mismatched array lengths for vector operation");
|
|
}
|
|
}
|
|
|
|
size_t pos;
|
|
static if (vectorizeable!(T[], Args))
|
|
{
|
|
alias vec = .vec!T;
|
|
alias load = .load!(T, vec.length);
|
|
alias store = .store!(T, vec.length);
|
|
|
|
// Given that there are at most as many scalars broadcast as there are
|
|
// operations in any `ary[] = ary[] op const op const`, it should always be
|
|
// worthwhile to choose vector operations.
|
|
if (!__ctfe && res.length >= vec.length)
|
|
{
|
|
mixin(initScalarVecs!Args);
|
|
|
|
auto n = res.length / vec.length;
|
|
do
|
|
{
|
|
mixin(vectorExp!Args ~ ";");
|
|
pos += vec.length;
|
|
}
|
|
while (--n);
|
|
}
|
|
}
|
|
for (; pos < res.length; ++pos)
|
|
mixin(scalarExp!Args ~ ";");
|
|
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
|
|
// SIMD helpers
|
|
|
|
version (DigitalMars)
|
|
{
|
|
import core.simd;
|
|
|
|
template vec(T)
|
|
{
|
|
enum regsz = 16; // SSE2
|
|
enum N = regsz / T.sizeof;
|
|
alias vec = __vector(T[N]);
|
|
}
|
|
|
|
void store(T, size_t N)(T* p, const scope __vector(T[N]) val)
|
|
{
|
|
pragma(inline, true);
|
|
alias vec = __vector(T[N]);
|
|
|
|
static if (is(T == float))
|
|
cast(void) __simd_sto(XMM.STOUPS, *cast(vec*) p, val);
|
|
else static if (is(T == double))
|
|
cast(void) __simd_sto(XMM.STOUPD, *cast(vec*) p, val);
|
|
else
|
|
cast(void) __simd_sto(XMM.STODQU, *cast(vec*) p, val);
|
|
}
|
|
|
|
const(__vector(T[N])) load(T, size_t N)(const scope T* p)
|
|
{
|
|
import core.simd;
|
|
|
|
pragma(inline, true);
|
|
alias vec = __vector(T[N]);
|
|
|
|
static if (is(T == float))
|
|
return cast(typeof(return)) __simd(XMM.LODUPS, *cast(const vec*) p);
|
|
else static if (is(T == double))
|
|
return cast(typeof(return)) __simd(XMM.LODUPD, *cast(const vec*) p);
|
|
else
|
|
return cast(typeof(return)) __simd(XMM.LODDQU, *cast(const vec*) p);
|
|
}
|
|
|
|
__vector(T[N]) binop(string op, T, size_t N)(const scope __vector(T[N]) a, const scope __vector(T[N]) b)
|
|
{
|
|
pragma(inline, true);
|
|
return mixin("a " ~ op ~ " b");
|
|
}
|
|
|
|
__vector(T[N]) unaop(string op, T, size_t N)(const scope __vector(T[N]) a)
|
|
if (op[0] == 'u')
|
|
{
|
|
pragma(inline, true);
|
|
return mixin(op[1 .. $] ~ "a");
|
|
}
|
|
}
|
|
|
|
// mixin gen
|
|
|
|
/**
|
|
Check whether operations on operand types are supported. This
|
|
template recursively reduces the expression tree and determines
|
|
intermediate types.
|
|
Type checking is done here rather than in the compiler to provide more
|
|
detailed error messages.
|
|
|
|
Params:
|
|
fail = whether to fail (static assert) with a human-friendly error message
|
|
T = type of result
|
|
Args = operand types and operations in RPN
|
|
Returns:
|
|
The resulting type of the expression
|
|
See_Also:
|
|
$(LREF arrayOp)
|
|
*/
|
|
template typeCheck(bool fail, T, Args...)
|
|
{
|
|
enum idx = staticIndexOf!(not!isType, Args);
|
|
static if (isUnaryOp(Args[idx]))
|
|
{
|
|
alias UT = Args[idx - 1];
|
|
enum op = Args[idx][1 .. $];
|
|
static if (is(typeof((UT a) => mixin(op ~ "cast(int) a")) RT == return))
|
|
alias typeCheck = typeCheck!(fail, T, Args[0 .. idx - 1], RT, Args[idx + 1 .. $]);
|
|
else static if (fail)
|
|
static assert(0, "Unary `" ~ op ~ "` not supported for type `" ~ UT.stringof ~ "`.");
|
|
}
|
|
else static if (isBinaryOp(Args[idx]))
|
|
{
|
|
alias LHT = Args[idx - 2];
|
|
alias RHT = Args[idx - 1];
|
|
enum op = Args[idx];
|
|
static if (is(typeof((LHT a, RHT b) => mixin("a " ~ op ~ " b")) RT == return))
|
|
alias typeCheck = typeCheck!(fail, T, Args[0 .. idx - 2], RT, Args[idx + 1 .. $]);
|
|
else static if (fail)
|
|
static assert(0,
|
|
"Binary `" ~ op ~ "` not supported for types `"
|
|
~ LHT.stringof ~ "` and `" ~ RHT.stringof ~ "`.");
|
|
}
|
|
else static if (Args[idx] == "=" || isBinaryAssignOp(Args[idx]))
|
|
{
|
|
alias RHT = Args[idx - 1];
|
|
enum op = Args[idx];
|
|
static if (is(T == __vector(ET[N]), ET, size_t N))
|
|
{
|
|
// no `cast(T)` before assignment for vectors
|
|
static if (is(typeof((T res, RHT b) => mixin("res " ~ op ~ " b")) RT == return)
|
|
&& // workaround https://issues.dlang.org/show_bug.cgi?id=17758
|
|
(op != "=" || is(Unqual!T == Unqual!RHT)))
|
|
alias typeCheck = typeCheck!(fail, T, Args[0 .. idx - 1], RT, Args[idx + 1 .. $]);
|
|
else static if (fail)
|
|
static assert(0,
|
|
"Binary op `" ~ op ~ "` not supported for types `"
|
|
~ T.stringof ~ "` and `" ~ RHT.stringof ~ "`.");
|
|
}
|
|
else
|
|
{
|
|
static if (is(typeof((RHT b) => mixin("cast(T) b"))))
|
|
{
|
|
static if (is(typeof((T res, T b) => mixin("res " ~ op ~ " b")) RT == return))
|
|
alias typeCheck = typeCheck!(fail, T, Args[0 .. idx - 1], RT, Args[idx + 1 .. $]);
|
|
else static if (fail)
|
|
static assert(0,
|
|
"Binary op `" ~ op ~ "` not supported for types `"
|
|
~ T.stringof ~ "` and `" ~ T.stringof ~ "`.");
|
|
}
|
|
else static if (fail)
|
|
static assert(0,
|
|
"`cast(" ~ T.stringof ~ ")` not supported for type `" ~ RHT.stringof ~ "`.");
|
|
}
|
|
}
|
|
else
|
|
static assert(0);
|
|
}
|
|
/// ditto
|
|
template typeCheck(bool fail, T, ResultType)
|
|
{
|
|
alias typeCheck = ResultType;
|
|
}
|
|
|
|
version (GNU_OR_LDC)
|
|
{
|
|
// leave it to the auto-vectorizer
|
|
enum vectorizeable(E : E[], Args...) = false;
|
|
}
|
|
else
|
|
{
|
|
// check whether arrayOp is vectorizable
|
|
template vectorizeable(E : E[], Args...)
|
|
{
|
|
static if (is(vec!E))
|
|
{
|
|
// type check with vector types
|
|
enum vectorizeable = is(typeCheck!(false, vec!E, staticMap!(toVecType, Args)));
|
|
}
|
|
else
|
|
enum vectorizeable = false;
|
|
}
|
|
|
|
version (X86_64) unittest
|
|
{
|
|
static assert(vectorizeable!(double[], const(double)[], double[], "+", "="));
|
|
static assert(!vectorizeable!(double[], const(ulong)[], double[], "+", "="));
|
|
// Vector type are (atm.) not implicitly convertible and would require
|
|
// lots of SIMD intrinsics. Therefor leave mixed type array ops to
|
|
// GDC/LDC's auto-vectorizers.
|
|
static assert(!vectorizeable!(double[], const(uint)[], uint, "+", "="));
|
|
}
|
|
}
|
|
|
|
bool isUnaryOp(scope string op) pure nothrow @safe @nogc
|
|
{
|
|
return op[0] == 'u';
|
|
}
|
|
|
|
bool isBinaryOp(scope string op) pure nothrow @safe @nogc
|
|
{
|
|
if (op == "^^")
|
|
return true;
|
|
if (op.length != 1)
|
|
return false;
|
|
switch (op[0])
|
|
{
|
|
case '+', '-', '*', '/', '%', '|', '&', '^':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool isBinaryAssignOp(string op)
|
|
{
|
|
return op.length >= 2 && op[$ - 1] == '=' && isBinaryOp(op[0 .. $ - 1]);
|
|
}
|
|
|
|
// Generate mixin expression to perform scalar arrayOp loop expression, assumes
|
|
// `pos` to be the current slice index, `args` to contain operand values, and
|
|
// `res` the target slice.
|
|
enum scalarExp(Args...) =
|
|
(){
|
|
string[] stack;
|
|
size_t argsIdx;
|
|
|
|
static if (is(Args[0] == U[], U))
|
|
alias Type = U;
|
|
else
|
|
alias Type = Args[0];
|
|
|
|
foreach (i, arg; Args)
|
|
{
|
|
static if (is(arg == T[], T))
|
|
stack ~= "args[" ~ argsIdx++.toString ~ "][pos]";
|
|
else static if (is(arg))
|
|
stack ~= "args[" ~ argsIdx++.toString ~ "]";
|
|
else static if (isUnaryOp(arg))
|
|
{
|
|
auto op = arg[0] == 'u' ? arg[1 .. $] : arg;
|
|
// Explicitly use the old integral promotion rules
|
|
// See also: https://dlang.org/changelog/2.078.0.html#fix16997
|
|
static if (is(Type : int))
|
|
stack[$ - 1] = "cast(typeof(" ~ stack[$ -1] ~ "))" ~ op ~ "cast(int)("~ stack[$ - 1] ~ ")";
|
|
else
|
|
stack[$ - 1] = op ~ stack[$ - 1];
|
|
}
|
|
else static if (arg == "=")
|
|
{
|
|
stack[$ - 1] = "res[pos] = cast(T)(" ~ stack[$ - 1] ~ ")";
|
|
}
|
|
else static if (isBinaryAssignOp(arg))
|
|
{
|
|
stack[$ - 1] = "res[pos] " ~ arg ~ " cast(T)(" ~ stack[$ - 1] ~ ")";
|
|
}
|
|
else static if (isBinaryOp(arg))
|
|
{
|
|
stack[$ - 2] = "(" ~ stack[$ - 2] ~ " " ~ arg ~ " " ~ stack[$ - 1] ~ ")";
|
|
stack.length -= 1;
|
|
}
|
|
else
|
|
assert(0, "Unexpected op " ~ arg);
|
|
}
|
|
assert(stack.length == 1);
|
|
return stack[0];
|
|
}();
|
|
|
|
// Generate mixin statement to perform vector loop initialization, assumes
|
|
// `args` to contain operand values.
|
|
enum initScalarVecs(Args...) =
|
|
() {
|
|
size_t scalarsIdx, argsIdx;
|
|
string res;
|
|
foreach (arg; Args)
|
|
{
|
|
static if (is(arg == T[], T))
|
|
{
|
|
++argsIdx;
|
|
}
|
|
else static if (is(arg))
|
|
res ~= "immutable vec scalar" ~ scalarsIdx++.toString ~ " = args["
|
|
~ argsIdx++.toString ~ "];\n";
|
|
}
|
|
return res;
|
|
}();
|
|
|
|
// Generate mixin expression to perform vector arrayOp loop expression, assumes
|
|
// `pos` to be the current slice index, `args` to contain operand values, and
|
|
// `res` the target slice.
|
|
enum vectorExp(Args...) =
|
|
() {
|
|
size_t scalarsIdx, argsIdx;
|
|
string[] stack;
|
|
foreach (arg; Args)
|
|
{
|
|
static if (is(arg == T[], T))
|
|
stack ~= "load(&args[" ~ argsIdx++.toString ~ "][pos])";
|
|
else static if (is(arg))
|
|
{
|
|
++argsIdx;
|
|
stack ~= "scalar" ~ scalarsIdx++.toString;
|
|
}
|
|
else static if (isUnaryOp(arg))
|
|
{
|
|
auto op = arg[0] == 'u' ? arg[1 .. $] : arg;
|
|
stack[$ - 1] = "unaop!\"" ~ arg ~ "\"(" ~ stack[$ - 1] ~ ")";
|
|
}
|
|
else static if (arg == "=")
|
|
{
|
|
stack[$ - 1] = "store(&res[pos], " ~ stack[$ - 1] ~ ")";
|
|
}
|
|
else static if (isBinaryAssignOp(arg))
|
|
{
|
|
stack[$ - 1] = "store(&res[pos], binop!\"" ~ arg[0 .. $ - 1]
|
|
~ "\"(load(&res[pos]), " ~ stack[$ - 1] ~ "))";
|
|
}
|
|
else static if (isBinaryOp(arg))
|
|
{
|
|
stack[$ - 2] = "binop!\"" ~ arg ~ "\"(" ~ stack[$ - 2] ~ ", " ~ stack[$ - 1] ~ ")";
|
|
stack.length -= 1;
|
|
}
|
|
else
|
|
assert(0, "Unexpected op " ~ arg);
|
|
}
|
|
assert(stack.length == 1);
|
|
return stack[0];
|
|
}();
|
|
|
|
// other helpers
|
|
|
|
enum isType(T) = true;
|
|
enum isType(alias a) = false;
|
|
template not(alias tmlp)
|
|
{
|
|
enum not(Args...) = !tmlp!Args;
|
|
}
|
|
/**
|
|
Find element in `haystack` for which `pred` is true.
|
|
|
|
Params:
|
|
pred = the template predicate
|
|
haystack = elements to search
|
|
Returns:
|
|
The first index for which `pred!haystack[index]` is true or -1.
|
|
*/
|
|
template staticIndexOf(alias pred, haystack...)
|
|
{
|
|
static if (pred!(haystack[0]))
|
|
enum staticIndexOf = 0;
|
|
else
|
|
{
|
|
enum next = staticIndexOf!(pred, haystack[1 .. $]);
|
|
enum staticIndexOf = next == -1 ? -1 : next + 1;
|
|
}
|
|
}
|
|
/// converts slice types to their element type, preserves anything else
|
|
alias toElementType(E : E[]) = E;
|
|
alias toElementType(S) = S;
|
|
alias toElementType(alias op) = op;
|
|
/// converts slice types to their element type, preserves anything else
|
|
alias toVecType(E : E[]) = vec!E;
|
|
alias toVecType(S) = vec!S;
|
|
alias toVecType(alias op) = op;
|
|
|
|
string toString(size_t num)
|
|
{
|
|
import core.internal.string : unsignedToTempString;
|
|
version (D_BetterC)
|
|
{
|
|
// Workaround for https://issues.dlang.org/show_bug.cgi?id=19268
|
|
if (__ctfe)
|
|
{
|
|
char[20] fixedbuf = void;
|
|
char[] buf = unsignedToTempString(num, fixedbuf);
|
|
char[] result = new char[buf.length];
|
|
result[] = buf[];
|
|
return (() @trusted => cast(string) result)();
|
|
}
|
|
else
|
|
{
|
|
// Failing at execution rather than during compilation is
|
|
// not good, but this is in `core.internal` so it should
|
|
// not be used by the unwary.
|
|
assert(0, __FUNCTION__ ~ " not available in -betterC except during CTFE.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char[20] buf = void;
|
|
return unsignedToTempString(num, buf).idup;
|
|
}
|
|
}
|
|
|
|
bool contains(T)(const scope T[] ary, const scope T[] vals...)
|
|
{
|
|
foreach (v1; ary)
|
|
foreach (v2; vals)
|
|
if (v1 == v2)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// tests
|
|
|
|
version (CoreUnittest) template TT(T...)
|
|
{
|
|
alias TT = T;
|
|
}
|
|
|
|
version (CoreUnittest) template _arrayOp(Args...)
|
|
{
|
|
alias _arrayOp = arrayOp!Args;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
static void check(string op, TA, TB, T, size_t N)(TA a, TB b, const scope ref T[N] exp)
|
|
{
|
|
T[N] res;
|
|
_arrayOp!(T[], TA, TB, op, "=")(res[], a, b);
|
|
foreach (i; 0 .. N)
|
|
assert(res[i] == exp[i]);
|
|
}
|
|
|
|
static void check2(string unaOp, string binOp, TA, TB, T, size_t N)(TA a, TB b, const scope ref T[N] exp)
|
|
{
|
|
T[N] res;
|
|
_arrayOp!(T[], TA, TB, unaOp, binOp, "=")(res[], a, b);
|
|
foreach (i; 0 .. N)
|
|
assert(res[i] == exp[i]);
|
|
}
|
|
|
|
static void test(T, string op, size_t N = 16)(T a, T b, T exp)
|
|
{
|
|
T[N] va = a, vb = b, vexp = exp;
|
|
|
|
check!op(va[], vb[], vexp);
|
|
check!op(va[], b, vexp);
|
|
check!op(a, vb[], vexp);
|
|
}
|
|
|
|
static void test2(T, string unaOp, string binOp, size_t N = 16)(T a, T b, T exp)
|
|
{
|
|
T[N] va = a, vb = b, vexp = exp;
|
|
|
|
check2!(unaOp, binOp)(va[], vb[], vexp);
|
|
check2!(unaOp, binOp)(va[], b, vexp);
|
|
check2!(unaOp, binOp)(a, vb[], vexp);
|
|
}
|
|
|
|
alias UINTS = TT!(ubyte, ushort, uint, ulong);
|
|
alias INTS = TT!(byte, short, int, long);
|
|
alias FLOATS = TT!(float, double);
|
|
|
|
foreach (T; TT!(UINTS, INTS, FLOATS))
|
|
{
|
|
test!(T, "+")(1, 2, 3);
|
|
test!(T, "-")(3, 2, 1);
|
|
static if (__traits(compiles, { import std.math; }))
|
|
test!(T, "^^")(2, 3, 8);
|
|
|
|
test2!(T, "u-", "+")(3, 2, 1);
|
|
}
|
|
|
|
foreach (T; TT!(UINTS, INTS))
|
|
{
|
|
test!(T, "|")(1, 2, 3);
|
|
test!(T, "&")(3, 1, 1);
|
|
test!(T, "^")(3, 1, 2);
|
|
|
|
test2!(T, "u~", "+")(3, cast(T)~2, 5);
|
|
}
|
|
|
|
foreach (T; TT!(INTS, FLOATS))
|
|
{
|
|
test!(T, "-")(1, 2, -1);
|
|
test2!(T, "u-", "+")(-3, -2, -1);
|
|
test2!(T, "u-", "*")(-3, -2, -6);
|
|
}
|
|
|
|
foreach (T; TT!(UINTS, INTS, FLOATS))
|
|
{
|
|
test!(T, "*")(2, 3, 6);
|
|
test!(T, "/")(8, 4, 2);
|
|
test!(T, "%")(8, 6, 2);
|
|
}
|
|
}
|
|
|
|
// test handling of v op= exp
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
uint[32] c;
|
|
arrayOp!(uint[], uint, "+=")(c[], 2);
|
|
foreach (v; c)
|
|
assert(v == 2);
|
|
static if (__traits(compiles, { import std.math; }))
|
|
{
|
|
arrayOp!(uint[], uint, "^^=")(c[], 3);
|
|
foreach (v; c)
|
|
assert(v == 8);
|
|
}
|
|
}
|
|
|
|
// proper error message for UDT lacking certain ops
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static assert(!is(typeof(&arrayOp!(int[4][], int[4], "+="))));
|
|
static assert(!is(typeof(&arrayOp!(int[4][], int[4], "u-", "="))));
|
|
|
|
static struct S
|
|
{
|
|
}
|
|
|
|
static assert(!is(typeof(&arrayOp!(S[], S, "+="))));
|
|
static assert(!is(typeof(&arrayOp!(S[], S[], "*", S, "+="))));
|
|
static struct S2
|
|
{
|
|
S2 opBinary(string op)(in S2) @nogc pure nothrow
|
|
{
|
|
return this;
|
|
}
|
|
|
|
ref S2 opOpAssign(string op)(in S2) @nogc pure nothrow
|
|
{
|
|
return this;
|
|
}
|
|
}
|
|
|
|
static assert(is(typeof(&arrayOp!(S2[], S2[], S2[], S2, "*", "+", "="))));
|
|
static assert(is(typeof(&arrayOp!(S2[], S2[], S2, "*", "+="))));
|
|
}
|
|
|
|
// test mixed type array op
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
uint[32] a = 0xF;
|
|
float[32] res = 2.0f;
|
|
arrayOp!(float[], const(uint)[], uint, "&", "*=")(res[], a[], 12);
|
|
foreach (v; res[])
|
|
assert(v == 24.0f);
|
|
}
|
|
|
|
// test mixed type array op
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static struct S
|
|
{
|
|
float opBinary(string op)(in S) @nogc const pure nothrow
|
|
{
|
|
return 2.0f;
|
|
}
|
|
}
|
|
|
|
float[32] res = 24.0f;
|
|
S[32] s;
|
|
arrayOp!(float[], const(S)[], const(S)[], "+", "/=")(res[], s[], s[]);
|
|
foreach (v; res[])
|
|
assert(v == 12.0f);
|
|
}
|
|
|
|
// test scalar after operation argument
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
float[32] res, a = 2, b = 3;
|
|
float c = 4;
|
|
arrayOp!(float[], const(float)[], const(float)[], "*", float, "+", "=")(res[], a[], b[], c);
|
|
foreach (v; res[])
|
|
assert(v == 2 * 3 + 4);
|
|
}
|
|
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
// https://issues.dlang.org/show_bug.cgi?id=17964
|
|
uint bug(){
|
|
uint[] a = [1, 2, 3, 5, 6, 7];
|
|
uint[] b = [1, 2, 3, 5, 6, 7];
|
|
a[] |= ~b[];
|
|
return a[1];
|
|
}
|
|
enum x = bug();
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=19796
|
|
nothrow pure @safe unittest
|
|
{
|
|
double[] data = [0.5];
|
|
double[] result;
|
|
result.length = data.length;
|
|
result[] = -data[];
|
|
assert(result[0] == -0.5);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=21110
|
|
pure unittest
|
|
{
|
|
import core.exception;
|
|
|
|
static void assertThrown(T : Throwable, E)(lazy E expression, string msg)
|
|
{
|
|
try
|
|
expression;
|
|
catch (T)
|
|
return;
|
|
assert(0, "msg");
|
|
}
|
|
|
|
int[] dst;
|
|
int[] a;
|
|
int[] b;
|
|
a.length = 3;
|
|
b.length = 3;
|
|
dst.length = 4;
|
|
|
|
void func() { dst[] = a[] + b[]; }
|
|
assertThrown!AssertError(func(), "Array operations with mismatched lengths must throw an error");
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=24272
|
|
unittest
|
|
{
|
|
static struct B
|
|
{
|
|
B opOpAssign(string op)(B other)
|
|
{
|
|
static int g;
|
|
g++;
|
|
throw new Exception("");
|
|
}
|
|
}
|
|
|
|
B[] bArr;
|
|
bArr[] += B();
|
|
}
|