d: Merge upstream dmd 56589f0f4, druntime 651389b5, phobos 1516ecad9.

D front-end changes:

    - Import latest bug fixes to mainline.

D runtime changes:

    - Import latest bug fixes to mainline.

Phobos changes:

    - Import latest bug fixes to mainline.

gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd 56589f0f4.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime 651389b5.
	* src/MERGE: Merge upstream phobos 1516ecad9.
This commit is contained in:
Iain Buclaw 2022-07-06 19:45:28 +02:00
parent c785204735
commit 208fbc779c
26 changed files with 1132 additions and 506 deletions

View File

@ -1,4 +1,4 @@
529110f66d7d301d62d943a4e4482edaddeb46ea
56589f0f4d724c1c8022c57509a243f16a04228a
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.

View File

@ -1619,6 +1619,12 @@ final class CParser(AST) : Parser!AST
return;
}
if (token.value == TOK.__pragma)
{
uupragmaDirective(scanloc);
return;
}
if (token.value == TOK._import) // import declaration extension
{
auto a = parseImport();
@ -2322,6 +2328,14 @@ final class CParser(AST) : Parser!AST
break;
}
case TOK.__declspec:
{
/* Microsoft extension
*/
cparseDeclspec(specifier);
break;
}
case TOK.typeof_:
{
nextToken();
@ -3042,9 +3056,13 @@ final class CParser(AST) : Parser!AST
* extended-decl-modifier:
* dllimport
* dllexport
* noreturn
* Params:
* specifier = filled in with the attribute(s)
*/
private void cparseDeclspec()
private void cparseDeclspec(ref Specifier specifier)
{
//printf("cparseDeclspec()\n");
/* Check for dllexport, dllimport
* Ignore the rest
*/
@ -3073,6 +3091,11 @@ final class CParser(AST) : Parser!AST
dllexport = true;
nextToken();
}
else if (token.ident == Id.noreturn)
{
specifier.noreturn = true;
nextToken();
}
else
{
nextToken();
@ -3083,8 +3106,8 @@ final class CParser(AST) : Parser!AST
else
{
error("extended-decl-modifier expected");
break;
}
break;
}
}
@ -4789,6 +4812,8 @@ final class CParser(AST) : Parser!AST
// type function itself.
if (auto tf = t.isTypeFunction())
tf.next = tf.next.addSTC(STC.const_);
else if (auto tt = t.isTypeTag())
tt.mod |= MODFlags.const_;
else
t = t.addSTC(STC.const_);
return t;
@ -4961,10 +4986,40 @@ final class CParser(AST) : Parser!AST
return true;
}
}
error("C preprocessor directive `#%s` is not supported", n.toChars());
if (n.ident != Id.undef)
error("C preprocessor directive `#%s` is not supported", n.toChars());
return false;
}
/*********************************************
* VC __pragma
* https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170
* Scanner is on the `__pragma`
* Params:
* startloc = location to use for error messages
*/
private void uupragmaDirective(const ref Loc startloc)
{
const loc = startloc;
nextToken();
if (token.value != TOK.leftParenthesis)
{
error(loc, "left parenthesis expected to follow `__pragma`");
return;
}
nextToken();
if (token.value == TOK.identifier && token.ident == Id.pack)
pragmaPack(startloc, false);
else
error(loc, "unrecognized __pragma");
if (token.value != TOK.rightParenthesis)
{
error(loc, "right parenthesis expected to close `__pragma(...)`");
return;
}
nextToken();
}
/*********************************************
* C11 6.10.6 Pragma directive
* # pragma pp-tokens(opt) new-line
@ -4977,7 +5032,7 @@ final class CParser(AST) : Parser!AST
Token n;
scan(&n);
if (n.value == TOK.identifier && n.ident == Id.pack)
return pragmaPack(loc);
return pragmaPack(loc, true);
if (n.value != TOK.endOfLine)
skipToNextLine();
}
@ -4989,10 +5044,27 @@ final class CParser(AST) : Parser!AST
* Scanner is on the `pack`
* Params:
* startloc = location to use for error messages
* useScan = use scan() to retrieve next token, instead of nextToken()
*/
private void pragmaPack(const ref Loc startloc)
private void pragmaPack(const ref Loc startloc, bool useScan)
{
const loc = startloc;
/* Pull tokens from scan() or nextToken()
*/
void scan(Token* t)
{
if (useScan)
{
Lexer.scan(t);
}
else
{
nextToken();
*t = token;
}
}
Token n;
scan(&n);
if (n.value != TOK.leftParenthesis)
@ -5155,13 +5227,35 @@ final class CParser(AST) : Parser!AST
{
if (!defines || defines.length < 10) // minimum length of a #define line
return;
const length = defines.length;
defines.writeByte(0);
auto slice = defines.peekChars()[0 .. length];
OutBuffer* buf = defines;
defines = null; // prevent skipToNextLine() and parseSpecialTokenSequence()
// from appending to slice[]
const length = buf.length;
buf.writeByte(0);
auto slice = buf.peekChars()[0 .. length];
resetDefineLines(slice); // reset lexer
const(char)* endp = &slice[length - 7];
size_t[void*] defineTab; // hash table of #define's turned into Symbol's
// indexed by Identifier, returns index into symbols[]
// The memory for this is leaked
void addVar(AST.VarDeclaration v)
{
/* If it's already defined, replace the earlier
* definition
*/
if (size_t* pd = cast(void*)v.ident in defineTab)
{
//printf("replacing %s\n", v.toChars());
(*symbols)[*pd] = v;
return;
}
defineTab[cast(void*)v.ident] = symbols.length;
symbols.push(v);
}
Token n;
while (p < endp)
@ -5200,7 +5294,7 @@ final class CParser(AST) : Parser!AST
*/
AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t);
auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
symbols.push(v);
addVar(v);
nextDefineLine();
continue;
}
@ -5223,7 +5317,7 @@ final class CParser(AST) : Parser!AST
*/
AST.Expression e = new AST.RealExp(scanloc, floatvalue, t);
auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
symbols.push(v);
addVar(v);
nextDefineLine();
continue;
}
@ -5241,7 +5335,7 @@ final class CParser(AST) : Parser!AST
*/
AST.Expression e = new AST.StringExp(scanloc, str[0 .. len], len, 1, postfix);
auto v = new AST.VarDeclaration(scanloc, null, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
symbols.push(v);
addVar(v);
nextDefineLine();
continue;
}
@ -5263,6 +5357,8 @@ final class CParser(AST) : Parser!AST
}
nextDefineLine();
}
defines = buf;
}
//}

View File

@ -680,7 +680,7 @@ extern (C++) final class Module : Package
FileName.equalsExt(srcfile.toString(), c_ext) &&
FileName.exists(srcfile.toString()))
{
filename = global.preprocess(srcfile, loc, global.params.cppswitches, ifile, &defines); // run C preprocessor
filename = global.preprocess(srcfile, loc, ifile, &defines); // run C preprocessor
}
if (auto result = global.fileManager.lookup(filename))

View File

@ -11689,6 +11689,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
case Terror:
return setError();
case Tarray, Tsarray:
result = exp.incompatibleTypes();
exp.errorSupplemental("`in` is only allowed on associative arrays");
const(char)* slice = (t2b.ty == Tsarray) ? "[]" : "";
exp.errorSupplemental("perhaps use `std.algorithm.find(%s, %s%s)` instead",
exp.e1.toChars(), exp.e2.toChars(), slice);
return;
default:
result = exp.incompatibleTypes();
return;

View File

@ -299,7 +299,7 @@ extern (C++) struct Global
enum recursionLimit = 500; /// number of recursive template expansions before abort
extern (C++) FileName function(FileName, ref const Loc, ref Array!(const(char)*) cppswitches, out bool, OutBuffer* defines) preprocess;
extern (C++) FileName function(FileName, ref const Loc, out bool, OutBuffer*) preprocess;
nothrow:

View File

@ -272,7 +272,7 @@ struct Global
FileManager* fileManager;
FileName (*preprocess)(FileName, const Loc&, Array<const char *>& cppswitches, bool&, OutBuffer&);
FileName (*preprocess)(FileName, const Loc&, bool&, OutBuffer&);
/* Start gagging. Return the current number of gagged errors
*/

View File

@ -1449,7 +1449,20 @@ public:
buf.writestring(" = ");
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
d.aliassym.accept(this);
/*
https://issues.dlang.org/show_bug.cgi?id=23223
https://issues.dlang.org/show_bug.cgi?id=23222
This special case (initially just for modules) avoids some segfaults
and nicer -vcg-ast output.
*/
if (d.aliassym.isModule())
{
buf.writestring(d.aliassym.ident.toString());
}
else
{
d.aliassym.accept(this);
}
}
else if (d.type.ty == Tfunction)
{
@ -3916,6 +3929,8 @@ private void typeToBufferx(Type t, OutBuffer* buf, HdrGenState* hgs)
void visitTag(TypeTag t)
{
if (t.mod & MODFlags.const_)
buf.writestring("const ");
buf.writestring(Token.toChars(t.tok));
buf.writeByte(' ');
if (t.id)

View File

@ -4437,15 +4437,7 @@ extern (C++) final class TypeFunction : TypeNext
// Check escaping through `this`
if (tthis && tthis.isMutable())
{
auto tb = tthis.toBasetype();
AggregateDeclaration ad;
if (auto tc = tb.isTypeClass())
ad = tc.sym;
else if (auto ts = tb.isTypeStruct())
ad = ts.sym;
else
assert(0);
foreach (VarDeclaration v; ad.fields)
foreach (VarDeclaration v; isAggregate(tthis).fields)
{
if (v.hasPointers())
return stc;
@ -6655,16 +6647,18 @@ extern (C++) final class TypeTag : Type
Type resolved; /// type after semantic() in case there are more others
/// pointing to this instance, which can happen with
/// struct S { int a; } s1, *s2;
MOD mod; /// modifiers to apply after type is resolved (only MODFlags.const_ at the moment)
extern (D) this(const ref Loc loc, TOK tok, Identifier id, Type base, Dsymbols* members)
{
//printf("TypeTag %p\n", this);
//printf("TypeTag ctor %s %p\n", id ? id.toChars() : "null".ptr, this);
super(Ttag);
this.loc = loc;
this.tok = tok;
this.id = id;
this.base = base;
this.members = members;
this.mod = 0;
}
override const(char)* kind() const
@ -6674,6 +6668,7 @@ extern (C++) final class TypeTag : Type
override TypeTag syntaxCopy()
{
//printf("TypeTag syntaxCopy()\n");
// No semantic analysis done, no need to copy
return this;
}

View File

@ -273,6 +273,7 @@ enum TOK : ubyte
__cdecl,
__declspec,
__stdcall,
__pragma,
__attribute__,
}
@ -582,6 +583,7 @@ private immutable TOK[] keywords =
TOK.__cdecl,
TOK.__declspec,
TOK.__stdcall,
TOK.__pragma,
TOK.__attribute__,
];
@ -610,7 +612,7 @@ static immutable TOK[TOK.max + 1] Ckeywords =
restrict, return_, int16, signed, sizeof_, static_, struct_, switch_, typedef_,
union_, unsigned, void_, volatile, while_, asm_, typeof_,
_Alignas, _Alignof, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn,
_Static_assert, _Thread_local, _import, __cdecl, __declspec, __stdcall, __attribute__ ];
_Static_assert, _Thread_local, _import, __cdecl, __declspec, __stdcall, __pragma, __attribute__ ];
foreach (kw; Ckwds)
tab[kw] = cast(TOK) kw;
@ -880,6 +882,7 @@ extern (C++) struct Token
TOK.__cdecl : "__cdecl",
TOK.__declspec : "__declspec",
TOK.__stdcall : "__stdcall",
TOK.__pragma : "__pragma",
TOK.__attribute__ : "__attribute__",
];

View File

@ -282,6 +282,7 @@ enum class TOK : unsigned char
cdecl_,
declspec,
stdcall,
pragma,
attribute__,
MAX,

View File

@ -1778,8 +1778,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
{
/* struct S s, *p;
*/
//printf("already resolved\n");
return mtype.resolved;
return mtype.resolved.addSTC(mtype.mod);
}
/* Find the current scope by skipping tag scopes.
@ -1850,7 +1849,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
{
mtype.id = Identifier.generateId("__tag"[]);
declareTag();
return mtype.resolved;
return mtype.resolved.addSTC(mtype.mod);
}
/* look for pre-existing declaration
@ -1863,7 +1862,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
if (mtype.tok == TOK.enum_ && !mtype.members)
.error(mtype.loc, "`enum %s` is incomplete without members", mtype.id.toChars()); // C11 6.7.2.3-3
declareTag();
return mtype.resolved;
return mtype.resolved.addSTC(mtype.mod);
}
/* A redeclaration only happens if both declarations are in
@ -1963,7 +1962,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
declareTag();
}
}
return mtype.resolved;
return mtype.resolved.addSTC(mtype.mod);
}
switch (type.ty)

View File

@ -1,15 +1,13 @@
// https://issues.dlang.org/show_bug.cgi?id=3004
/*
REQUIRED_ARGS: -ignore -v
TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|import|semantic|entry|library|function object|\s*$)")
TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|import|semantic|entry|library|function object|function core|\s*$)")
TEST_OUTPUT:
---
pragma GNU_attribute (__error)
pragma GNU_attribute (__error)
code test3004
function test3004.test
function core.internal.array.appending._d_arrayappendcTXImpl!(char[], char)._d_arrayappendcTX
function core.internal.array.utils._d_HookTraceImpl!(char[], _d_arrayappendcTX, "Cannot append to array if compiling without support for runtime type information!")._d_HookTraceImpl
---
*/

View File

@ -7,6 +7,9 @@ TEST_OUTPUT_FILE: extra-files/vcg-ast.d.cg
module vcg;
alias xyz = __traits(parent, {});
alias named = vcg;
template Seq(A...)
{
alias Seq = A;

View File

@ -0,0 +1,20 @@
/**
TEST_OUTPUT:
---
fail_compilation/diag_in_array.d(17): Error: incompatible types for `(3) in (a)`: `int` and `int[4]`
fail_compilation/diag_in_array.d(17): `in` is only allowed on associative arrays
fail_compilation/diag_in_array.d(17): perhaps use `std.algorithm.find(3, a[])` instead
fail_compilation/diag_in_array.d(19): Error: incompatible types for `("s") in (b)`: `string` and `string[]`
fail_compilation/diag_in_array.d(19): `in` is only allowed on associative arrays
fail_compilation/diag_in_array.d(19): perhaps use `std.algorithm.find("s", b)` instead
---
*/
void main()
{
int[4] a;
string[] b;
if (3 in a)
return;
auto c = "s" in b;
}

View File

@ -1,4 +1,4 @@
148608b7935c3f9a4ea3a26f74cb90cd07efc91c
651389b52243dcadb338dd0c14dd27e7850cda8d
The first line of this file holds the git revision number of the last
merge done from the dlang/druntime repository.

View File

@ -168,6 +168,7 @@ inout(char)[] find(alias pred)(inout(char)[] str)
}
bool parse(T : size_t)(const(char)[] optname, ref inout(char)[] str, ref T res, const(char)[] errName, bool mayHaveSuffix = false)
if (is(T == size_t))
in { assert(str.length); }
do
{
@ -242,6 +243,22 @@ do
if (v > res.max)
return parseError("a number " ~ T.max.stringof ~ " or below", optname, str[0 .. i], errName);
str = str[i .. $];
res = v;
return true;
}
bool parse(T : size_t)(const(char)[] optname, ref inout(char)[] str, ref T res, const(char)[] errName, bool mayHaveSuffix = false)
if (!is(T == size_t))
in { assert(str.length); }
do
{
const oldStr = str;
size_t v;
if (!parse!size_t(optname, str, v, errName, mayHaveSuffix))
return false;
if (v > res.max)
return parseError("a number " ~ T.max.stringof ~ " or below", optname, oldStr[0 .. $-str.length], errName);
res = cast(T) v;
return true;
}

View File

@ -2130,6 +2130,15 @@ extern (C) void thread_init() @nogc nothrow
}
else version (Posix)
{
version (OpenBSD)
{
// OpenBSD does not support SIGRTMIN or SIGRTMAX
// Use SIGUSR1 for SIGRTMIN, SIGUSR2 for SIGRTMIN + 1
// And use 32 for SIGRTMAX (32 is the max signal number on OpenBSD)
enum SIGRTMIN = SIGUSR1;
enum SIGRTMAX = 32;
}
if ( suspendSignalNumber == 0 )
{
suspendSignalNumber = SIGRTMIN;

View File

@ -1,7 +1,5 @@
/**
* This code handles decoding UTF strings for foreach loops. There are 6
* combinations of conversions between char, wchar, and dchar, and 2 of each
* of those.
* This code handles decoding UTF strings for foreach loops.
*
* Copyright: Copyright Digital Mars 2004 - 2010.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
@ -15,9 +13,64 @@ import core.internal.utf : decode, toUTF8;
/**********************************************/
/* 1 argument versions */
// dg is D, but _aApplycd() is C
extern (D) alias int delegate(void *) dg_t;
/**
Delegate type corresponding to transformed loop body
The parameter is a pointer to the current `char`, `wchar` or `dchar`
Returns: non-zero when a `break` statement is hit
*/
extern (D) alias dg_t = int delegate(void* c);
// Note: dg is extern(D), but _aApplycd() is extern(C)
/**
Loop over a string while changing the UTF encoding
There are 6 combinations of conversions between `char`, `wchar`, and `dchar`,
and 2 of each of those.
The naming convention is as follows:
_aApply{c,d,w}{c,d,w}{1,2}
The first letter corresponds to the input string encoding, and the second letter corresponds to the target character type.
- c = `char`
- w = `wchar`
- d = `dchar`
The `1` variant only produces the character, the `2` variant also produces a loop index.
Examples:
---
void main()
{
string str;
wtring wstr;
dstring dstr;
foreach (dchar c; str) {}
// _aApplycd1
foreach (wchar c; dstr) {}
// _aApplydw1
foreach (i, wchar c; str) {}
// _aApplycw2
foreach (wchar w; wstr) {}
// no conversion
}
---
Params:
aa = input string
dg = foreach body transformed into a delegate, similar to `opApply`
Returns:
non-zero when the loop was exited through a `break`
*/
extern (C) int _aApplycd1(in char[] aa, dg_t dg)
{
int result;
@ -78,8 +131,7 @@ unittest
assert(i == 4);
}
/*****************************/
/// ditto
extern (C) int _aApplywd1(in wchar[] aa, dg_t dg)
{
int result;
@ -140,8 +192,7 @@ unittest
assert(i == 4);
}
/*****************************/
/// ditto
extern (C) int _aApplycw1(in char[] aa, dg_t dg)
{
int result;
@ -215,8 +266,7 @@ unittest
assert(i == 5);
}
/*****************************/
/// ditto
extern (C) int _aApplywc1(in wchar[] aa, dg_t dg)
{
int result;
@ -296,8 +346,7 @@ unittest
assert(i == 9);
}
/*****************************/
/// ditto
extern (C) int _aApplydc1(in dchar[] aa, dg_t dg)
{
int result;
@ -373,8 +422,7 @@ unittest
assert(i == 9);
}
/*****************************/
/// ditto
extern (C) int _aApplydw1(in dchar[] aa, dg_t dg)
{
int result;
@ -446,9 +494,20 @@ unittest
/****************************************************************************/
/* 2 argument versions */
// dg is D, but _aApplycd2() is C
extern (D) alias int delegate(void *, void *) dg2_t;
/**
Delegate type corresponding to transformed loop body
Parameters are pointers to a `size_t` loop index, and the current `char`, `wchar` or `dchar`.
Returns: non-zero when a `break` statement is hit
*/
extern (D) alias dg2_t = int delegate(void* i, void* c);
// Note: dg is extern(D), but _aApplycd2() is extern(C)
/**
Variants of _aApplyXXX that include a loop index.
*/
extern (C) int _aApplycd2(in char[] aa, dg2_t dg)
{
int result;
@ -516,8 +575,7 @@ unittest
assert(i == 4);
}
/*****************************/
/// ditto
extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg)
{
int result;
@ -585,8 +643,7 @@ unittest
assert(i == 4);
}
/*****************************/
/// ditto
extern (C) int _aApplycw2(in char[] aa, dg2_t dg)
{
int result;
@ -665,8 +722,7 @@ unittest
assert(i == 5);
}
/*****************************/
/// ditto
extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg)
{
int result;
@ -751,8 +807,7 @@ unittest
assert(i == 9);
}
/*****************************/
/// ditto
extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg)
{
int result;
@ -832,8 +887,7 @@ unittest
assert(i == 9);
}
/*****************************/
/// ditto
extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg)
{ int result;

View File

@ -1,7 +1,5 @@
/**
* This code handles decoding UTF strings for foreach_reverse loops. There are
* 6 combinations of conversions between char, wchar, and dchar, and 2 of each
* of those.
* This code handles decoding UTF strings for `foreach_reverse` loops.
*
* Copyright: Copyright Digital Mars 2004 - 2010.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
@ -15,9 +13,27 @@ import core.internal.utf;
/**********************************************/
/* 1 argument versions */
// dg is D, but _aApplyRcd() is C
extern (D) alias int delegate(void *) dg_t;
// Note: dg is extern(D), but _aApplyRcd() is extern(C)
/**
Delegate type corresponding to transformed loop body
The parameter is a pointer to the current `char`, `wchar` or `dchar`
Returns: non-zero when a `break` statement is hit
*/
extern (D) alias dg_t = int delegate(void* c);
/**
Same as `_aApplyXXX` functions, but for `foreach_reverse`
Params:
aa = input string
dg = foreach body transformed into a delegate, similar to `opApply`
Returns:
non-zero when the loop was exited through a `break`
*/
extern (C) int _aApplyRcd1(in char[] aa, dg_t dg)
{ int result;
@ -90,8 +106,7 @@ unittest
assert(i == 4);
}
/*****************************/
/// ditto
extern (C) int _aApplyRwd1(in wchar[] aa, dg_t dg)
{ int result;
@ -154,8 +169,7 @@ unittest
assert(i == 4);
}
/*****************************/
/// ditto
extern (C) int _aApplyRcw1(in char[] aa, dg_t dg)
{ int result;
@ -241,8 +255,7 @@ unittest
assert(i == 5);
}
/*****************************/
/// ditto
extern (C) int _aApplyRwc1(in wchar[] aa, dg_t dg)
{ int result;
@ -326,8 +339,7 @@ unittest
assert(i == 9);
}
/*****************************/
/// ditto
extern (C) int _aApplyRdc1(in dchar[] aa, dg_t dg)
{ int result;
@ -405,8 +417,7 @@ unittest
assert(i == 9);
}
/*****************************/
/// ditto
extern (C) int _aApplyRdw1(in dchar[] aa, dg_t dg)
{ int result;
@ -477,9 +488,20 @@ unittest
/****************************************************************************/
/* 2 argument versions */
// dg is D, but _aApplyRcd2() is C
extern (D) alias int delegate(void *, void *) dg2_t;
/**
Delegate type corresponding to transformed loop body
Parameters are pointers to a `size_t` loop index, and the current `char`, `wchar` or `dchar`.
Returns: non-zero when a `break` statement is hit
*/
extern (D) alias dg2_t = int delegate(void* i, void* c);
// Note: dg is extern(D), but _aApplyRcd2() is extern(C)
/**
Variants of _aApplyRXXX that include a loop index.
*/
extern (C) int _aApplyRcd2(in char[] aa, dg2_t dg)
{ int result;
size_t i;
@ -555,8 +577,7 @@ unittest
assert(i == 4);
}
/*****************************/
/// ditto
extern (C) int _aApplyRwd2(in wchar[] aa, dg2_t dg)
{ int result;
@ -621,8 +642,7 @@ unittest
assert(i == 4);
}
/*****************************/
/// ditto
extern (C) int _aApplyRcw2(in char[] aa, dg2_t dg)
{ int result;
@ -710,8 +730,7 @@ unittest
assert(i == 5);
}
/*****************************/
/// ditto
extern (C) int _aApplyRwc2(in wchar[] aa, dg2_t dg)
{ int result;
@ -797,8 +816,7 @@ unittest
assert(i == 9);
}
/*****************************/
/// ditto
extern (C) int _aApplyRdc2(in dchar[] aa, dg2_t dg)
{ int result;
@ -877,8 +895,7 @@ unittest
assert(i == 9);
}
/*****************************/
/// ditto
extern (C) int _aApplyRdw2(in dchar[] aa, dg2_t dg)
{ int result;

View File

@ -50,7 +50,7 @@ struct AA
private struct Impl
{
private:
this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS)
this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS) nothrow
{
keysz = cast(uint) ti.key.tsize;
valsz = cast(uint) ti.value.tsize;
@ -125,7 +125,7 @@ private:
}
}
void grow(scope const TypeInfo keyti)
void grow(scope const TypeInfo keyti) pure nothrow
{
// If there are so many deleted entries, that growing would push us
// below the shrink threshold, we just purge deleted entries instead.
@ -135,7 +135,7 @@ private:
resize(GROW_FAC * dim);
}
void shrink(scope const TypeInfo keyti)
void shrink(scope const TypeInfo keyti) pure nothrow
{
if (dim > INIT_NUM_BUCKETS)
resize(dim / GROW_FAC);
@ -233,7 +233,7 @@ package void entryDtor(void* p, const TypeInfo_Struct sti)
extra[1].destroy(p + talign(extra[0].tsize, extra[1].talign));
}
private bool hasDtor(const TypeInfo ti)
private bool hasDtor(const TypeInfo ti) pure nothrow
{
import rt.lifetime : unqualify;
@ -246,7 +246,7 @@ private bool hasDtor(const TypeInfo ti)
return false;
}
private immutable(void)* getRTInfo(const TypeInfo ti)
private immutable(void)* getRTInfo(const TypeInfo ti) pure nothrow
{
// classes are references
const isNoClass = ti && typeid(ti) !is typeid(TypeInfo_Class);
@ -254,7 +254,7 @@ private immutable(void)* getRTInfo(const TypeInfo ti)
}
// build type info for Entry with additional key and value fields
TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti)
TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti) nothrow
{
import rt.lifetime : unqualify;
@ -319,7 +319,8 @@ TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo va
}
// build appropriate RTInfo at runtime
immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize)
immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo,
immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize) pure nothrow
{
enum bitsPerWord = 8 * size_t.sizeof;
@ -456,7 +457,7 @@ private size_t mix(size_t h) @safe pure nothrow @nogc
return h;
}
private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti)
private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti) nothrow
{
immutable hash = keyti.getHash(pkey);
// highest bit is set to distinguish empty/deleted from filled buckets
@ -485,6 +486,18 @@ pure nothrow @nogc unittest
// API Implementation
//------------------------------------------------------------------------------
/** Allocate associative array data.
* Called for `new SomeAA` expression.
* Params:
* ti = TypeInfo for the associative array
* Returns:
* A new associative array.
*/
extern (C) Impl* _aaNew(const TypeInfo_AssociativeArray ti)
{
return new Impl(ti);
}
/// Determine number of entries in associative array.
extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc
{
@ -736,7 +749,15 @@ extern (C) int _aaApply2(AA aa, const size_t keysz, dg2_t dg)
return 0;
}
/// Construct an associative array of type ti from keys and value
/** Construct an associative array of type ti from corresponding keys and values.
* Called for an AA literal `[k1:v1, k2:v2]`.
* Params:
* ti = TypeInfo for the associative array
* keys = array of keys
* vals = array of values
* Returns:
* A new associative array opaque pointer, or null if `keys` is empty.
*/
extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys,
void[] vals)
{

View File

@ -19,8 +19,10 @@ private
debug(PRINTF) import core.stdc.stdio;
}
/**
* Keep for backward binary compatibility. This function can be removed in the future.
/*
* Superseded array assignment hook. Does not take into account destructors:
* https://issues.dlang.org/show_bug.cgi?id=13661
* Kept for backward binary compatibility. This function can be removed in the future.
*/
extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to)
{
@ -40,15 +42,44 @@ extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to)
}
/**
* Does array assignment (not construction) from another
* lvalue array of the same element type.
* Handles overlapping copies.
* Input:
* ti TypeInfo of the element type.
* dst Points target memory. Its .length is equal to the element count, not byte length.
* src Points source memory. Its .length is equal to the element count, not byte length.
* ptmp Temporary memory for element swapping.
*/
Does array assignment (not construction) from another array of the same
element type.
Handles overlapping copies.
The `_d_arrayassign_l` variant assumes the right hand side is an lvalue,
while `_d_arrayassign_r` assumes it's an rvalue, which means it doesn't have to call copy constructors.
Used for static array assignment with non-POD element types:
---
struct S
{
~this() {} // destructor, so not Plain Old Data
}
void main()
{
S[3] arr;
S[3] lvalue;
arr = lvalue;
// Generates:
// S _tmp;
// _d_arrayassign_l(typeid(S), (cast(void*) lvalue.ptr)[0..lvalue.length], (cast(void*) arr.ptr)[0..arr.length], &_tmp);
S[3] getRvalue() {return lvalue;}
arr = getRvalue();
// Similar, but `_d_arrayassign_r`
}
---
Params:
ti = `TypeInfo` of the array element type.
dst = target memory. Its `.length` is equal to the element count, not byte length.
src = source memory. Its `.length` is equal to the element count, not byte length.
ptmp = Temporary memory for element swapping, must have capacity of `ti.tsize` bytes.
Returns: `dst`
*/
extern (C) void[] _d_arrayassign_l(TypeInfo ti, void[] src, void[] dst, void* ptmp)
{
debug(PRINTF) printf("_d_arrayassign_l(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize);
@ -131,16 +162,7 @@ unittest // Bugzilla 14024
assert(op == "YzXy", op);
}
/**
* Does array assignment (not construction) from another
* rvalue array of the same element type.
* Input:
* ti TypeInfo of the element type.
* dst Points target memory. Its .length is equal to the element count, not byte length.
* src Points source memory. Its .length is equal to the element count, not byte length.
* It is always allocated on stack and never overlapping with dst.
* ptmp Temporary memory for element swapping.
*/
/// ditto
extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* ptmp)
{
debug(PRINTF) printf("_d_arrayassign_r(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize);
@ -163,9 +185,22 @@ extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* pt
}
/**
* Do assignment to an array.
* p[0 .. count] = value;
*/
Set all elements of an array to a single value.
---
p[0 .. count] = value;
---
Takes into account postblits and destructors, for Plain Old Data elements,
`rt/memset.d` is used.
Params:
p = pointer to start of array
value = bytes of the element to set. Size is derived from `ti`.
count = amount of array elements to set
ti = type info of the array element type / `value`
Returns: `p`
*/
extern (C) void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti)
{
void* pstart = p;

View File

@ -48,16 +48,39 @@ deprecated extern (C) void lifetime_init()
}
/**
*
*/
Allocate memory using the garbage collector
DMD uses this to allocate closures:
---
void f(byte[24] x)
{
return () => x; // `x` is on stack, must be moved to heap to keep it alive
}
---
Params:
sz = number of bytes to allocate
Returns: pointer to `sz` bytes of free, uninitialized memory, managed by the GC.
*/
extern (C) void* _d_allocmemory(size_t sz) @weak
{
return GC.malloc(sz);
}
/**
*
*/
Create a new class instance.
Allocates memory and sets fields to their initial value, but does not call a constructor.
---
new Object() // _d_newclass(typeid(Object))
---
Params:
ci = `TypeInfo_Class` object, to provide instance size and initial bytes to copy
Returns: newly created object
*/
extern (C) Object _d_newclass(const ClassInfo ci) @weak
{
import core.stdc.stdlib;
@ -352,7 +375,7 @@ bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, co
/**
get the allocation size of the array for the given block (without padding or type info)
*/
size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow
private size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow
{
if (info.size <= 256)
return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD);
@ -366,7 +389,7 @@ size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow
/**
get the start of the array for the given block
*/
void *__arrayStart(return scope BlkInfo info) nothrow pure
private void *__arrayStart(return scope BlkInfo info) nothrow pure
{
return info.base + ((info.size & BIGLENGTHMASK) ? LARGEPREFIX : 0);
}
@ -376,7 +399,7 @@ void *__arrayStart(return scope BlkInfo info) nothrow pure
NOT included in the passed in size. Therefore, do NOT call this function
with the size of an allocated block.
*/
size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted
private size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted
{
return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext));
}
@ -401,7 +424,7 @@ private void __arrayClearPad(ref BlkInfo info, size_t arrsize, size_t padsize) n
allocate an array memory block by applying the proper padding and
assigning block attributes if not inherited from the existing block
*/
BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure
private BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure
{
import core.checkedint;
@ -423,7 +446,7 @@ BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tin
return bi;
}
BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext)
private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext)
{
import core.checkedint;
@ -446,7 +469,7 @@ BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti,
/**
cache for the lookup of the block info
*/
enum N_CACHE_BLOCKS=8;
private enum N_CACHE_BLOCKS=8;
// note this is TLS, so no need to sync.
BlkInfo *__blkcache_storage;
@ -644,10 +667,15 @@ void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow
}
/**
* Shrink the "allocated" length of an array to be the exact size of the array.
* It doesn't matter what the current allocated length of the array is, the
* user is telling the runtime that he knows what he is doing.
*/
Shrink the "allocated" length of an array to be the exact size of the array.
It doesn't matter what the current allocated length of the array is, the
user is telling the runtime that he knows what he is doing.
Params:
ti = `TypeInfo` of array type
arr = array to shrink. Its `.length` is element length, not byte length, despite `void` type
*/
extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/
{
// note, we do not care about shared. We are setting the length no matter
@ -690,7 +718,7 @@ extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/
}
}
package bool hasPostblit(in TypeInfo ti)
package bool hasPostblit(in TypeInfo ti) nothrow pure
{
return (&ti.postblit).funcptr !is &TypeInfo.postblit;
}
@ -726,12 +754,21 @@ void __doPostblit(void *ptr, size_t len, const TypeInfo ti)
/**
* set the array capacity. If the array capacity isn't currently large enough
* to hold the requested capacity (in number of elements), then the array is
* resized/reallocated to the appropriate size. Pass in a requested capacity
* of 0 to get the current capacity. Returns the number of elements that can
* actually be stored once the resizing is done.
*/
Set the array capacity.
If the array capacity isn't currently large enough
to hold the requested capacity (in number of elements), then the array is
resized/reallocated to the appropriate size.
Pass in a requested capacity of 0 to get the current capacity.
Params:
ti = type info of element type
newcapacity = requested new capacity
p = pointer to array to set. Its `length` is left unchanged.
Returns: the number of elements that can actually be stored once the resizing is done
*/
extern(C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* p) @weak
in
{
@ -902,9 +939,18 @@ Lcontinue:
}
/**
* Allocate a new uninitialized array of length elements.
* ti is the type of the resulting array, or pointer to element.
*/
Allocate an array with the garbage collector.
Has three variants:
- `_d_newarrayU` leave elements uninitialized
- `_d_newarrayT` initializes to 0 (e.g `new int[]`)
- `_d_newarrayiT` initializes based on initializer retrieved from TypeInfo (e.g `new float[]`)
Params:
ti = the type of the resulting array, (may also be the corresponding `array.ptr` type)
length = `.length` of resulting array
Returns: newly allocated array
*/
extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow @weak
{
import core.exception : onOutOfMemoryError;
@ -961,11 +1007,7 @@ Lcontinue:
return arrstart[0..length];
}
/**
* Allocate a new array of length elements.
* ti is the type of the resulting array, or pointer to element.
* (For when the array is initialized to 0)
*/
/// ditto
extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @weak
{
import core.stdc.string;
@ -978,9 +1020,7 @@ extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @w
return result;
}
/**
* For when the array has a non-zero initializer.
*/
/// ditto
extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @weak
{
import core.internal.traits : AliasSeq;
@ -1016,10 +1056,10 @@ extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @
}
/**
*
/*
* Helper for creating multi-dimensional arrays
*/
void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims)
private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims)
{
debug(PRINTF) printf("_d_newarrayOpT(ndims = %d)\n", dims.length);
if (dims.length == 0)
@ -1058,8 +1098,30 @@ void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims)
/**
*
*/
Create a new multi-dimensional array
Has two variants:
- `_d_newarraymTX` which initializes to 0
- `_d_newarraymiTX` which initializes elements based on `TypeInfo`
---
void main()
{
new int[][](10, 20);
// _d_newarraymTX(typeid(float), [10, 20]);
new float[][][](10, 20, 30);
// _d_newarraymiTX(typeid(float), [10, 20, 30]);
}
---
Params:
ti = `TypeInfo` of the array type
dims = array length values for each dimension
Returns:
newly allocated array
*/
extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak
{
debug(PRINTF) printf("_d_newarraymT(dims.length = %d)\n", dims.length);
@ -1072,10 +1134,7 @@ extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak
}
}
/**
*
*/
/// ditto
extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak
{
debug(PRINTF) printf("_d_newarraymiT(dims.length = %d)\n", dims.length);
@ -1089,9 +1148,31 @@ extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak
}
/**
* Allocate an uninitialized non-array item.
* This is an optimization to avoid things needed for arrays like the __arrayPad(size).
*/
Allocate an uninitialized non-array item.
This is an optimization to avoid things needed for arrays like the __arrayPad(size).
- `_d_newitemU` leaves the item uninitialized
- `_d_newitemT` zero initializes the item
- `_d_newitemiT` uses a non-zero initializer from `TypeInfo`
Used to allocate struct instances on the heap.
---
struct Sz {int x = 0;}
struct Si {int x = 3;}
void main()
{
new Sz(); // _d_newitemT(typeid(Sz))
new Si(); // _d_newitemiT(typeid(Si))
}
---
Params:
_ti = `TypeInfo` of item to allocate
Returns:
newly allocated item
*/
extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak
{
auto ti = unqualify(_ti);
@ -1115,7 +1196,7 @@ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak
return p;
}
/// Same as above, zero initializes the item.
/// ditto
extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak
{
import core.stdc.string;
@ -1135,15 +1216,6 @@ extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak
return p;
}
/**
*
*/
struct Array
{
size_t length;
byte* data;
}
debug(PRINTF)
{
extern(C) void printArrayCache()
@ -1426,6 +1498,7 @@ extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true)
}
}
/// Backwards compatibility
extern (C) void rt_finalize(void* p, bool det = true) nothrow
{
rt_finalize2(p, det, true);
@ -1444,8 +1517,29 @@ extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow
/**
* Resize dynamic arrays with 0 initializers.
*/
Resize a dynamic array by setting the `.length` property
Newly created elements are initialized to their default value.
Has two variants:
- `_d_arraysetlengthT` for arrays with elements that initialize to 0
- `_d_arraysetlengthiT` for non-zero initializers retrieved from `TypeInfo`
---
void main()
{
int[] a = [1, 2];
a.length = 3; // gets lowered to `_d_arraysetlengthT(typeid(int[]), 3, &a)`
}
---
Params:
ti = `TypeInfo` of array
newlength = new value for the array's `.length`
p = pointer to array to update the `.length` of.
While it's cast to `void[]`, its `.length` is still treated as element length.
Returns: `*p` after being updated
*/
extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p) @weak
in
{
@ -1639,15 +1733,7 @@ do
return *p;
}
/**
* Resize arrays for non-zero initializers.
* p pointer to array lvalue to be updated
* newlength new .length property of array
* sizeelem size of each element of array
* initsize size of initializer
* ... initializer
*/
/// ditto
extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) @weak
in
{
@ -1859,8 +1945,31 @@ do
/**
*
*/
Given an array of length `size` that needs to be expanded to `newlength`,
compute a new capacity.
Better version by Dave Fladebo:
This uses an inverse logorithmic algorithm to pre-allocate a bit more
space for larger arrays.
- Arrays smaller than PAGESIZE bytes are left as-is, so for the most
common cases, memory allocation is 1 to 1. The small overhead added
doesn't affect small array perf. (it's virtually the same as
current).
- Larger arrays have some space pre-allocated.
- As the arrays grow, the relative pre-allocated space shrinks.
- The logorithmic algorithm allocates relatively more space for
mid-size arrays, making it very fast for medium arrays (for
mid-to-large arrays, this turns out to be quite a bit faster than the
equivalent realloc() code in C, on Linux at least. Small arrays are
just as fast as GCC).
- Perhaps most importantly, overall memory usage and stress on the GC
is decreased significantly for demanding environments.
Params:
newlength = new `.length`
size = old `.length`
Returns: new capacity for array
*/
size_t newCapacity(size_t newlength, size_t size)
{
version (none)
@ -1869,24 +1978,6 @@ size_t newCapacity(size_t newlength, size_t size)
}
else
{
/*
* Better version by Dave Fladebo:
* This uses an inverse logorithmic algorithm to pre-allocate a bit more
* space for larger arrays.
* - Arrays smaller than PAGESIZE bytes are left as-is, so for the most
* common cases, memory allocation is 1 to 1. The small overhead added
* doesn't affect small array perf. (it's virtually the same as
* current).
* - Larger arrays have some space pre-allocated.
* - As the arrays grow, the relative pre-allocated space shrinks.
* - The logorithmic algorithm allocates relatively more space for
* mid-size arrays, making it very fast for medium arrays (for
* mid-to-large arrays, this turns out to be quite a bit faster than the
* equivalent realloc() code in C, on Linux at least. Small arrays are
* just as fast as GCC).
* - Perhaps most importantly, overall memory usage and stress on the GC
* is decreased significantly for demanding environments.
*/
size_t newcap = newlength * size;
size_t newext = 0;
@ -1940,10 +2031,17 @@ size_t newCapacity(size_t newlength, size_t size)
}
/**************************************
* Extend an array by n elements.
* Caller must initialize those elements.
*/
/**
Extend an array by n elements.
Caller must initialize those elements.
Params:
ti = type info of array type (not element type)
px = array to append to, cast to `byte[]` while keeping the same `.length`. Will be updated.
n = number of elements to append
Returns: `px` after being appended to
*/
extern (C)
byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak
{
@ -2047,8 +2145,21 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n
/**
* Append dchar to char[]
*/
Append `dchar` to `char[]`, converting UTF-32 to UTF-8
---
void main()
{
char[] s;
s ~= 'α';
}
---
Params:
x = array to append to cast to `byte[]`. Will be modified.
c = `dchar` to append
Returns: updated `x` cast to `void[]`
*/
extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) @weak
{
// c could encode into from 1 to 4 characters
@ -2129,8 +2240,23 @@ unittest
/**
* Append dchar to wchar[]
*/
Append `dchar` to `wchar[]`, converting UTF-32 to UTF-16
---
void main()
{
dchar x;
wchar[] s;
s ~= 'α';
}
---
Params:
x = array to append to cast to `byte[]`. Will be modified.
c = `dchar` to append
Returns: updated `x` cast to `void[]`
*/
extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak
{
// c could encode into from 1 to 2 w characters
@ -2162,8 +2288,24 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak
/**
*
*/
Concatenate two arrays into a new array
---
void main()
{
int[] x = [10, 20, 30];
int[] y = [40, 50];
int[] c = x ~ y; // _d_arraycatT(typeid(int[]), (cast(byte*) x)[0..x.length], (cast(byte*) y)[0..y.length]);
}
---
Params:
ti = type that the two arrays share
x = left hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length
y = right hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length
Returns:
resulting concatenated array, with `.length` equal to new element length despite `byte` type
*/
extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak
out (result)
{
@ -2228,8 +2370,27 @@ do
/**
*
*/
Concatenate multiple arrays at once
This is more efficient than repeatedly concatenating pairs of arrays because the total size is known in advance.
```
void main()
{
int[] a, b, c;
int[] res = a ~ b ~ c;
// _d_arraycatnTX(typeid(int[]),
// [(cast(byte*)a.ptr)[0..a.length], (cast(byte*)b.ptr)[0..b.length], (cast(byte*)c.ptr)[0..c.length]]);
}
```
Params:
ti = type of arrays to concatenate and resulting array
arrs = array of arrays to concatenate, cast to `byte[]` while keeping `.length` the same
Returns:
newly created concatenated array, `.length` equal to the total element length despite `void` type
*/
extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak
{
import core.stdc.string;
@ -2268,8 +2429,27 @@ extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak
/**
* Allocate the array, rely on the caller to do the initialization of the array.
*/
Allocate an array literal
Rely on the caller to do the initialization of the array.
---
int[] getArr()
{
return [10, 20];
// auto res = cast(int*) _d_arrayliteralTX(typeid(int[]), 2);
// res[0] = 10;
// res[1] = 20;
// return res[0..2];
}
---
Params:
ti = `TypeInfo` of resulting array type
length = `.length` of array literal
Returns: pointer to allocated array
*/
extern (C)
void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak
{

View File

@ -1,4 +1,4 @@
a4a18d21c4ea7930f80309f85e38c571c5f6d4b8
1516ecad932d88a1618163384e6f69009d125391
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.

View File

@ -1695,9 +1695,9 @@ Complex!T log(T)(Complex!T x) @safe pure nothrow @nogc
*/
Complex!T log10(T)(Complex!T x) @safe pure nothrow @nogc
{
static import std.math;
import std.math.constants : LN10;
return log(x) / Complex!T(std.math.log(10.0));
return log(x) / Complex!T(LN10);
}
///

View File

@ -176,9 +176,9 @@ class FileException : Exception
private this(scope const(char)[] name, scope const(char)[] msg, string file, size_t line, uint errno) @safe pure
{
if (msg.empty)
super(name.idup, file, line);
super(name is null ? "(null)" : name.idup, file, line);
else
super(text(name, ": ", msg), file, line);
super(text(name is null ? "(null)" : name, ": ", msg), file, line);
this.errno = errno;
}
@ -1067,11 +1067,38 @@ private void removeImpl(scope const(char)[] name, scope const(FSChar)* namez) @t
if (!name)
{
import core.stdc.string : strlen;
auto len = strlen(namez);
auto len = namez ? strlen(namez) : 0;
name = namez[0 .. len];
}
cenforce(core.stdc.stdio.remove(namez) == 0,
"Failed to remove file " ~ name);
"Failed to remove file " ~ (name is null ? "(null)" : name));
}
}
@safe unittest
{
import std.exception : collectExceptionMsg, assertThrown;
string filename = null; // e.g. as returned by File.tmpfile.name
version (linux)
{
// exact exception message is OS-dependent
auto msg = filename.remove.collectExceptionMsg!FileException;
assert("Failed to remove file (null): Bad address" == msg, msg);
}
else version (Windows)
{
import std.algorithm.searching : startsWith;
// don't test exact message on windows, it's language dependent
auto msg = filename.remove.collectExceptionMsg!FileException;
assert(msg.startsWith("(null):"), msg);
}
else
{
assertThrown!FileException(filename.remove);
}
}

View File

@ -2862,14 +2862,16 @@ float ldexp(float n, int exp) @safe pure nothrow @nogc { return core.math.ldex
private
{
import std.math : floatTraits, RealFormat;
version (INLINE_YL2X) {} else
// Coefficients shared across log(), log2(), log10().
template LogCoeffs(T)
{
static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
import std.math : floatTraits, RealFormat;
static if (floatTraits!T.realFormat == RealFormat.ieeeQuadruple)
{
// Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x)
static immutable real[13] logCoeffsP = [
// Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x)
// Theoretical peak relative error = 5.3e-37
static immutable real[13] logP = [
1.313572404063446165910279910527789794488E4L,
7.771154681358524243729929227226708890930E4L,
2.014652742082537582487669938141683759923E5L,
@ -2884,7 +2886,7 @@ private
4.998469661968096229986658302195402690910E-1L,
1.538612243596254322971797716843006400388E-6L
];
static immutable real[13] logCoeffsQ = [
static immutable real[13] logQ = [
3.940717212190338497730839731583397586124E4L,
2.626900195321832660448791748036714883242E5L,
7.777690340007566932935753241556479363645E5L,
@ -2900,9 +2902,18 @@ private
1.0
];
// Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2)
// log2 uses the same coefficients as log.
alias log2P = logP;
alias log2Q = logQ;
// log10 uses the same coefficients as log.
alias log10P = logP;
alias log10Q = logQ;
// Coefficients for log(x) = z + z^^3 P(z^^2)/Q(z^^2)
// where z = 2(x-1)/(x+1)
static immutable real[6] logCoeffsR = [
// Theoretical peak relative error = 1.1e-35
static immutable real[6] logR = [
1.418134209872192732479751274970992665513E5L,
-8.977257995689735303686582344659576526998E4L,
2.048819892795278657810231591630928516206E4L,
@ -2910,7 +2921,7 @@ private
8.057002716646055371965756206836056074715E1L,
-8.828896441624934385266096344596648080902E-1L
];
static immutable real[7] logCoeffsS = [
static immutable real[7] logS = [
1.701761051846631278975701529965589676574E6L,
-1.332535117259762928288745111081235577029E6L,
4.001557694070773974936904547424676279307E5L,
@ -2922,8 +2933,9 @@ private
}
else
{
// Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x)
static immutable real[7] logCoeffsP = [
// Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x)
// Theoretical peak relative error = 2.32e-20
static immutable real[7] logP = [
2.0039553499201281259648E1L,
5.7112963590585538103336E1L,
6.0949667980987787057556E1L,
@ -2932,7 +2944,7 @@ private
4.9854102823193375972212E-1L,
4.5270000862445199635215E-5L,
];
static immutable real[7] logCoeffsQ = [
static immutable real[7] logQ = [
6.0118660497603843919306E1L,
2.1642788614495947685003E2L,
3.0909872225312059774938E2L,
@ -2942,15 +2954,42 @@ private
1.0000000000000000000000E0L,
];
// Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2)
// Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x)
// Theoretical peak relative error = 6.2e-22
static immutable real[7] log2P = [
1.0747524399916215149070E2L,
3.4258224542413922935104E2L,
4.2401812743503691187826E2L,
2.5620629828144409632571E2L,
7.7671073698359539859595E1L,
1.0767376367209449010438E1L,
4.9962495940332550844739E-1L,
];
static immutable real[8] log2Q = [
3.2242573199748645407652E2L,
1.2695660352705325274404E3L,
2.0307734695595183428202E3L,
1.6911722418503949084863E3L,
7.7952888181207260646090E2L,
1.9444210022760132894510E2L,
2.3479774160285863271658E1L,
1.0000000000000000000000E0,
];
// log10 uses the same coefficients as log2.
alias log10P = log2P;
alias log10Q = log2Q;
// Coefficients for log(x) = z + z^^3 P(z^^2)/Q(z^^2)
// where z = 2(x-1)/(x+1)
static immutable real[4] logCoeffsR = [
// Theoretical peak relative error = 6.16e-22
static immutable real[4] logR = [
-3.5717684488096787370998E1L,
1.0777257190312272158094E1L,
-7.1990767473014147232598E-1L,
1.9757429581415468984296E-3L,
];
static immutable real[4] logCoeffsS = [
static immutable real[4] logS = [
-4.2861221385716144629696E2L,
1.9361891836232102174846E2L,
-2.6201045551331104417768E1L,
@ -2972,83 +3011,13 @@ private
*/
real log(real x) @safe pure nothrow @nogc
{
import std.math.constants : LN2, LOG2, SQRT1_2;
import std.math.traits : isInfinity, isNaN, signbit;
import std.math.algebraic : poly;
version (INLINE_YL2X)
return core.math.yl2x(x, LN2);
else
{
// C1 + C2 = LN2.
enum real C1 = 6.93145751953125E-1L;
enum real C2 = 1.428606820309417232121458176568075500134E-6L;
// Special cases.
if (isNaN(x))
return x;
if (isInfinity(x) && !signbit(x))
return x;
if (x == 0.0)
return -real.infinity;
if (x < 0.0)
return real.nan;
// Separate mantissa from exponent.
// Note, frexp is used so that denormal numbers will be handled properly.
real y, z;
int exp;
x = frexp(x, exp);
// Logarithm using log(x) = z + z^^3 R(z) / S(z),
// where z = 2(x - 1)/(x + 1)
if ((exp > 2) || (exp < -2))
{
if (x < SQRT1_2)
{ // 2(2x - 1)/(2x + 1)
exp -= 1;
z = x - 0.5;
y = 0.5 * z + 0.5;
}
else
{ // 2(x - 1)/(x + 1)
z = x - 0.5;
z -= 0.5;
y = 0.5 * x + 0.5;
}
x = z / y;
z = x * x;
z = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
z += exp * C2;
z += x;
z += exp * C1;
return z;
}
// Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
if (x < SQRT1_2)
{
exp -= 1;
x = 2.0 * x - 1.0;
}
else
{
x = x - 1.0;
}
z = x * x;
y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
y += exp * C2;
z = y - 0.5 * z;
// Note, the sum of above terms does not exceed x/4,
// so it contributes at most about 1/4 lsb to the error.
z += x;
z += exp * C1;
return z;
import std.math.constants : LN2;
return core.math.yl2x(x, LN2);
}
else
return logImpl(x);
}
///
@ -3060,6 +3029,84 @@ real log(real x) @safe pure nothrow @nogc
assert(feqrel(log(E), 1) >= real.mant_dig - 1);
}
private T logImpl(T)(T x) @safe pure nothrow @nogc
{
import std.math.constants : SQRT1_2;
import std.math.algebraic : poly;
import std.math.traits : isInfinity, isNaN, signbit;
alias coeffs = LogCoeffs!T;
// C1 + C2 = LN2.
enum T C1 = 6.93145751953125E-1L;
enum T C2 = 1.428606820309417232121458176568075500134E-6L;
// Special cases.
if (isNaN(x))
return x;
if (isInfinity(x) && !signbit(x))
return x;
if (x == 0.0)
return -T.infinity;
if (x < 0.0)
return T.nan;
// Separate mantissa from exponent.
// Note, frexp is used so that denormal numbers will be handled properly.
T y, z;
int exp;
x = frexp(x, exp);
// Logarithm using log(x) = z + z^^3 R(z) / S(z),
// where z = 2(x - 1)/(x + 1)
if ((exp > 2) || (exp < -2))
{
if (x < SQRT1_2)
{ // 2(2x - 1)/(2x + 1)
exp -= 1;
z = x - 0.5;
y = 0.5 * z + 0.5;
}
else
{ // 2(x - 1)/(x + 1)
z = x - 0.5;
z -= 0.5;
y = 0.5 * x + 0.5;
}
x = z / y;
z = x * x;
z = x * (z * poly(z, coeffs.logR) / poly(z, coeffs.logS));
z += exp * C2;
z += x;
z += exp * C1;
return z;
}
// Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
if (x < SQRT1_2)
{
exp -= 1;
x = 2.0 * x - 1.0;
}
else
{
x = x - 1.0;
}
z = x * x;
y = x * (z * poly(x, coeffs.logP) / poly(x, coeffs.logQ));
y += exp * C2;
z = y - 0.5 * z;
// Note, the sum of above terms does not exceed x/4,
// so it contributes at most about 1/4 lsb to the error.
z += x;
z += exp * C1;
return z;
}
/**************************************
* Calculate the base-10 logarithm of x.
*
@ -3072,87 +3119,13 @@ real log(real x) @safe pure nothrow @nogc
*/
real log10(real x) @safe pure nothrow @nogc
{
import std.math.constants : LOG2, LN2, SQRT1_2;
import std.math.algebraic : poly;
import std.math.traits : isNaN, isInfinity, signbit;
version (INLINE_YL2X)
return core.math.yl2x(x, LOG2);
else
{
// log10(2) split into two parts.
enum real L102A = 0.3125L;
enum real L102B = -1.14700043360188047862611052755069732318101185E-2L;
// log10(e) split into two parts.
enum real L10EA = 0.5L;
enum real L10EB = -6.570551809674817234887108108339491770560299E-2L;
// Special cases are the same as for log.
if (isNaN(x))
return x;
if (isInfinity(x) && !signbit(x))
return x;
if (x == 0.0)
return -real.infinity;
if (x < 0.0)
return real.nan;
// Separate mantissa from exponent.
// Note, frexp is used so that denormal numbers will be handled properly.
real y, z;
int exp;
x = frexp(x, exp);
// Logarithm using log(x) = z + z^^3 R(z) / S(z),
// where z = 2(x - 1)/(x + 1)
if ((exp > 2) || (exp < -2))
{
if (x < SQRT1_2)
{ // 2(2x - 1)/(2x + 1)
exp -= 1;
z = x - 0.5;
y = 0.5 * z + 0.5;
}
else
{ // 2(x - 1)/(x + 1)
z = x - 0.5;
z -= 0.5;
y = 0.5 * x + 0.5;
}
x = z / y;
z = x * x;
y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
goto Ldone;
}
// Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
if (x < SQRT1_2)
{
exp -= 1;
x = 2.0 * x - 1.0;
}
else
x = x - 1.0;
z = x * x;
y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
y = y - 0.5 * z;
// Multiply log of fraction by log10(e) and base 2 exponent by log10(2).
// This sequence of operations is critical and it may be horribly
// defeated by some compiler optimizers.
Ldone:
z = y * L10EB;
z += x * L10EB;
z += exp * L102B;
z += y * L10EA;
z += x * L10EA;
z += exp * L102A;
return z;
import std.math.constants : LOG2;
return core.math.yl2x(x, LOG2);
}
else
return log10Impl(x);
}
///
@ -3163,6 +3136,88 @@ real log10(real x) @safe pure nothrow @nogc
assert(fabs(log10(1000) - 3) < .000001);
}
private T log10Impl(T)(T x) @safe pure nothrow @nogc
{
import std.math.constants : SQRT1_2;
import std.math.algebraic : poly;
import std.math.traits : isNaN, isInfinity, signbit;
alias coeffs = LogCoeffs!T;
// log10(2) split into two parts.
enum T L102A = 0.3125L;
enum T L102B = -1.14700043360188047862611052755069732318101185E-2L;
// log10(e) split into two parts.
enum T L10EA = 0.5L;
enum T L10EB = -6.570551809674817234887108108339491770560299E-2L;
// Special cases are the same as for log.
if (isNaN(x))
return x;
if (isInfinity(x) && !signbit(x))
return x;
if (x == 0.0)
return -T.infinity;
if (x < 0.0)
return T.nan;
// Separate mantissa from exponent.
// Note, frexp is used so that denormal numbers will be handled properly.
T y, z;
int exp;
x = frexp(x, exp);
// Logarithm using log(x) = z + z^^3 R(z) / S(z),
// where z = 2(x - 1)/(x + 1)
if ((exp > 2) || (exp < -2))
{
if (x < SQRT1_2)
{ // 2(2x - 1)/(2x + 1)
exp -= 1;
z = x - 0.5;
y = 0.5 * z + 0.5;
}
else
{ // 2(x - 1)/(x + 1)
z = x - 0.5;
z -= 0.5;
y = 0.5 * x + 0.5;
}
x = z / y;
z = x * x;
y = x * (z * poly(z, coeffs.logR) / poly(z, coeffs.logS));
goto Ldone;
}
// Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
if (x < SQRT1_2)
{
exp -= 1;
x = 2.0 * x - 1.0;
}
else
x = x - 1.0;
z = x * x;
y = x * (z * poly(x, coeffs.log10P) / poly(x, coeffs.log10Q));
y = y - 0.5 * z;
// Multiply log of fraction by log10(e) and base 2 exponent by log10(2).
// This sequence of operations is critical and it may be horribly
// defeated by some compiler optimizers.
Ldone:
z = y * L10EB;
z += x * L10EB;
z += exp * L102B;
z += y * L10EA;
z += x * L10EA;
z += exp * L102A;
return z;
}
/**
* Calculates the natural logarithm of 1 + x.
*
@ -3179,29 +3234,15 @@ real log10(real x) @safe pure nothrow @nogc
*/
real log1p(real x) @safe pure nothrow @nogc
{
import std.math.traits : isNaN, isInfinity, signbit;
import std.math.constants : LN2;
version (INLINE_YL2X)
{
// On x87, yl2xp1 is valid if and only if -0.5 <= lg(x) <= 0.5,
// ie if -0.29 <= x <= 0.414
import std.math.constants : LN2;
return (core.math.fabs(x) <= 0.25) ? core.math.yl2xp1(x, LN2) : core.math.yl2x(x+1, LN2);
}
else
{
// Special cases.
if (isNaN(x) || x == 0.0)
return x;
if (isInfinity(x) && !signbit(x))
return x;
if (x == -1.0)
return -real.infinity;
if (x < -1.0)
return real.nan;
return log(x + 1.0);
}
return log1pImpl(x);
}
///
@ -3220,6 +3261,23 @@ real log1p(real x) @safe pure nothrow @nogc
assert(log1p(real.infinity) == real.infinity);
}
private T log1pImpl(T)(T x) @safe pure nothrow @nogc
{
import std.math.traits : isNaN, isInfinity, signbit;
// Special cases.
if (isNaN(x) || x == 0.0)
return x;
if (isInfinity(x) && !signbit(x))
return x;
if (x == -1.0)
return -T.infinity;
if (x < -1.0)
return T.nan;
return logImpl(x + 1.0);
}
/***************************************
* Calculates the base-2 logarithm of x:
* $(SUB log, 2)x
@ -3233,78 +3291,10 @@ real log1p(real x) @safe pure nothrow @nogc
*/
real log2(real x) @safe pure nothrow @nogc
{
import std.math.traits : isNaN, isInfinity, signbit;
import std.math.constants : SQRT1_2, LOG2E;
import std.math.algebraic : poly;
version (INLINE_YL2X)
return core.math.yl2x(x, 1.0L);
else
{
// Special cases are the same as for log.
if (isNaN(x))
return x;
if (isInfinity(x) && !signbit(x))
return x;
if (x == 0.0)
return -real.infinity;
if (x < 0.0)
return real.nan;
// Separate mantissa from exponent.
// Note, frexp is used so that denormal numbers will be handled properly.
real y, z;
int exp;
x = frexp(x, exp);
// Logarithm using log(x) = z + z^^3 R(z) / S(z),
// where z = 2(x - 1)/(x + 1)
if ((exp > 2) || (exp < -2))
{
if (x < SQRT1_2)
{ // 2(2x - 1)/(2x + 1)
exp -= 1;
z = x - 0.5;
y = 0.5 * z + 0.5;
}
else
{ // 2(x - 1)/(x + 1)
z = x - 0.5;
z -= 0.5;
y = 0.5 * x + 0.5;
}
x = z / y;
z = x * x;
y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
goto Ldone;
}
// Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
if (x < SQRT1_2)
{
exp -= 1;
x = 2.0 * x - 1.0;
}
else
x = x - 1.0;
z = x * x;
y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
y = y - 0.5 * z;
// Multiply log of fraction by log10(e) and base 2 exponent by log10(2).
// This sequence of operations is critical and it may be horribly
// defeated by some compiler optimizers.
Ldone:
z = y * (LOG2E - 1.0);
z += x * (LOG2E - 1.0);
z += y;
z += x;
z += exp;
return z;
}
return log2Impl(x);
}
///
@ -3323,6 +3313,79 @@ real log2(real x) @safe pure nothrow @nogc
assert(isClose(log2(1024.0L), 10, 1e-18));
}
private T log2Impl(T)(T x) @safe pure nothrow @nogc
{
import std.math.traits : isNaN, isInfinity, signbit;
import std.math.constants : SQRT1_2, LOG2E;
import std.math.algebraic : poly;
alias coeffs = LogCoeffs!T;
// Special cases are the same as for log.
if (isNaN(x))
return x;
if (isInfinity(x) && !signbit(x))
return x;
if (x == 0.0)
return -T.infinity;
if (x < 0.0)
return T.nan;
// Separate mantissa from exponent.
// Note, frexp is used so that denormal numbers will be handled properly.
T y, z;
int exp;
x = frexp(x, exp);
// Logarithm using log(x) = z + z^^3 R(z) / S(z),
// where z = 2(x - 1)/(x + 1)
if ((exp > 2) || (exp < -2))
{
if (x < SQRT1_2)
{ // 2(2x - 1)/(2x + 1)
exp -= 1;
z = x - 0.5;
y = 0.5 * z + 0.5;
}
else
{ // 2(x - 1)/(x + 1)
z = x - 0.5;
z -= 0.5;
y = 0.5 * x + 0.5;
}
x = z / y;
z = x * x;
y = x * (z * poly(z, coeffs.logR) / poly(z, coeffs.logS));
goto Ldone;
}
// Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
if (x < SQRT1_2)
{
exp -= 1;
x = 2.0 * x - 1.0;
}
else
x = x - 1.0;
z = x * x;
y = x * (z * poly(x, coeffs.log2P) / poly(x, coeffs.log2Q));
y = y - 0.5 * z;
// Multiply log of fraction by log10(e) and base 2 exponent by log10(2).
// This sequence of operations is critical and it may be horribly
// defeated by some compiler optimizers.
Ldone:
z = y * (LOG2E - 1.0);
z += x * (LOG2E - 1.0);
z += y;
z += x;
z += exp;
return z;
}
/*****************************************
* Extracts the exponent of x as a signed integral value.
*
@ -3337,35 +3400,23 @@ real log2(real x) @safe pure nothrow @nogc
* $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) )
* )
*/
real logb(real x) @trusted nothrow @nogc
pragma(inline, true)
real logb(real x) @trusted pure nothrow @nogc
{
version (InlineAsm_X87_MSVC)
{
version (X86_64)
{
asm pure nothrow @nogc
{
naked ;
fld real ptr [RCX] ;
fxtract ;
fstp ST(0) ;
ret ;
}
}
else
{
asm pure nothrow @nogc
{
fld x ;
fxtract ;
fstp ST(0) ;
}
}
}
return logbAsm(x);
else
return core.stdc.math.logbl(x);
return logbImpl(x);
}
/// ditto
pragma(inline, true)
double logb(double x) @trusted pure nothrow @nogc { return logbImpl(x); }
/// ditto
pragma(inline, true)
float logb(float x) @trusted pure nothrow @nogc { return logbImpl(x); }
///
@safe @nogc nothrow unittest
{
@ -3377,6 +3428,83 @@ real logb(real x) @trusted nothrow @nogc
assert(logb(-real.infinity) == real.infinity);
}
@safe @nogc nothrow unittest
{
import std.meta : AliasSeq;
import std.typecons : Tuple;
import std.math.traits : isNaN;
static foreach (F; AliasSeq!(float, double, real))
{{
alias T = Tuple!(F, F);
T[17] vals = // x, logb(x)
[
T(1.0 , 0 ),
T(100.0 , 6 ),
T(0.0 , -F.infinity),
T(-0.0 , -F.infinity),
T(1024 , 10 ),
T(-2000 , 10 ),
T(0x0.1p-127 , -131 ),
T(0x0.01p-127 , -135 ),
T(0x0.011p-127 , -135 ),
T(F.nan , F.nan ),
T(-F.nan , F.nan ),
T(F.infinity , F.infinity ),
T(-F.infinity , F.infinity ),
T(F.min_normal , F.min_exp-1),
T(-F.min_normal, F.min_exp-1),
T(F.max , F.max_exp-1),
T(-F.max , F.max_exp-1),
];
foreach (elem; vals)
{
if (isNaN(elem[1]))
assert(isNaN(logb(elem[1])));
else
assert(logb(elem[0]) == elem[1]);
}
}}
}
version (InlineAsm_X87_MSVC)
private T logbAsm(T)(T x) @trusted pure nothrow @nogc
{
version (X86_64)
{
asm pure nothrow @nogc
{
naked ;
fld real ptr [RCX] ;
fxtract ;
fstp ST(0) ;
ret ;
}
}
else
{
asm pure nothrow @nogc
{
fld x ;
fxtract ;
fstp ST(0) ;
}
}
}
private T logbImpl(T)(T x) @trusted pure nothrow @nogc
{
import std.math.traits : isFinite;
// Handle special cases.
if (!isFinite(x))
return x * x;
if (x == 0)
return -1 / (x * x);
return ilogb(x);
}
/*************************************
* Efficiently calculates x * 2$(SUPERSCRIPT n).
*