Type::thash_t = Type::tsize_t;
/* Set-up target C ABI. */
- this->c.longsize = int_size_in_bytes (long_integer_type_node);
- this->c.long_doublesize = int_size_in_bytes (long_double_type_node);
+ this->c.boolsize = (BOOL_TYPE_SIZE / BITS_PER_UNIT);
+ this->c.shortsize = (SHORT_TYPE_SIZE / BITS_PER_UNIT);
+ this->c.intsize = (INT_TYPE_SIZE / BITS_PER_UNIT);
+ this->c.longsize = (LONG_TYPE_SIZE / BITS_PER_UNIT);
+ this->c.long_longsize = (LONG_LONG_TYPE_SIZE / BITS_PER_UNIT);
+ this->c.long_doublesize = (LONG_DOUBLE_TYPE_SIZE / BITS_PER_UNIT);
this->c.wchar_tsize = (WCHAR_TYPE_SIZE / BITS_PER_UNIT);
+
this->c.bitFieldStyle = targetm.ms_bitfield_layout_p (unknown_type_node)
? TargetC::BitFieldStyle::MS : TargetC::BitFieldStyle::Gcc_Clang;
-cb49e99f80e8111c71035b88fe47fe7d855c300f
+cf63dd8e5a77ecb68cf5e7c43bf7b6c4c1154bbe
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
-v2.099.0-beta.1
+v2.099.0-rc.1
/**
Frees resources associated.
*/
- extern (C++) void dtor() nothrow @trusted
+ extern (C++) void dtor() pure nothrow @trusted
{
if (fileMapping)
{
if (fileMapping.active)
fileMapping.close();
- fileMapping = null;
}
else
{
debug (stomp) memset(data.ptr, 0xFF, data.length);
- free(data.ptr);
+ pureFree(data.ptr);
}
}
*/
extern (C++) ~this() pure nothrow @trusted
{
- if (fileMapping)
- {
- if (fileMapping.active)
- fileMapping.close();
- fileMapping = null;
- }
- else
- {
- debug (stomp) memset(data.ptr, 0xFF, data.length);
- pureFree(data.ptr);
- }
+ dtor();
}
/// For porting with ease from dmd.backend.outbuf.Outbuffer
*/
extern (C++) void destroy() pure nothrow @trusted
{
- if (fileMapping && fileMapping.active)
- {
- fileMapping.close();
- data = null;
- offset = 0;
- }
- else
- {
- debug (stomp) memset(data.ptr, 0xFF, data.length);
- pureFree(extractData());
- }
+ dtor();
+ fileMapping = null;
+ data = null;
+ offset = 0;
}
/**
linkage = LINK.c;
Ccompile = true;
- // Configure sizes for C `long`, `long double`, `wchar_t`
+ // Configure sizes for C `long`, `long double`, `wchar_t`, ...
+ this.boolsize = target.boolsize;
+ this.shortsize = target.shortsize;
+ this.intsize = target.intsize;
this.longsize = target.longsize;
+ this.long_longsize = target.long_longsize;
this.long_doublesize = target.long_doublesize;
this.wchar_tsize = target.wchar_tsize;
case TKW.xshort:
case TKW.xsigned | TKW.xshort:
case TKW.xsigned | TKW.xshort | TKW.xint:
- case TKW.xshort | TKW.xint: t = AST.Type.tint16; break;
+ case TKW.xshort | TKW.xint: t = integerTypeForSize(shortsize); break;
case TKW.xunsigned | TKW.xshort | TKW.xint:
- case TKW.xunsigned | TKW.xshort: t = AST.Type.tuns16; break;
+ case TKW.xunsigned | TKW.xshort: t = unsignedTypeForSize(shortsize); break;
case TKW.xint:
case TKW.xsigned:
- case TKW.xsigned | TKW.xint: t = AST.Type.tint32; break;
+ case TKW.xsigned | TKW.xint: t = integerTypeForSize(intsize); break;
case TKW.xunsigned:
- case TKW.xunsigned | TKW.xint: t = AST.Type.tuns32; break;
+ case TKW.xunsigned | TKW.xint: t = unsignedTypeForSize(intsize); break;
case TKW.xlong:
case TKW.xsigned | TKW.xlong:
case TKW.xsigned | TKW.xlong | TKW.xint:
- case TKW.xlong | TKW.xint: t = longsize == 4 ? AST.Type.tint32 : AST.Type.tint64; break;
+ case TKW.xlong | TKW.xint: t = integerTypeForSize(longsize); break;
case TKW.xunsigned | TKW.xlong | TKW.xint:
- case TKW.xunsigned | TKW.xlong: t = longsize == 4 ? AST.Type.tuns32 : AST.Type.tuns64; break;
+ case TKW.xunsigned | TKW.xlong: t = unsignedTypeForSize(longsize); break;
case TKW.xllong:
case TKW.xsigned | TKW.xllong:
case TKW.xsigned | TKW.xllong | TKW.xint:
- case TKW.xllong | TKW.xint: t = AST.Type.tint64; break;
+ case TKW.xllong | TKW.xint: t = integerTypeForSize(long_longsize); break;
case TKW.xunsigned | TKW.xllong | TKW.xint:
- case TKW.xunsigned | TKW.xllong: t = AST.Type.tuns64; break;
+ case TKW.xunsigned | TKW.xllong: t = unsignedTypeForSize(long_longsize); break;
case TKW.xvoid: t = AST.Type.tvoid; break;
- case TKW.xbool: t = AST.Type.tbool; break;
+ case TKW.xbool: t = boolsize == 1 ? AST.Type.tbool : integerTypeForSize(boolsize); break;
case TKW.xfloat: t = AST.Type.tfloat32; break;
case TKW.xdouble: t = AST.Type.tfloat64; break;
}
/***********************
+ * Return suitable signed integer type for the given size
+ * Params:
+ * size = size of type
+ * Returns:
+ * corresponding signed D integer type
+ */
+ private AST.Type integerTypeForSize(ubyte size)
+ {
+ if (size <= 1)
+ return AST.Type.tint8;
+ if (size <= 2)
+ return AST.Type.tint16;
+ if (size <= 4)
+ return AST.Type.tint32;
+ if (size <= 8)
+ return AST.Type.tint64;
+ error("unsupported integer type");
+ return AST.Type.terror;
+ }
+
+ /***********************
+ * Return suitable unsigned integer type for the given size
+ * Params:
+ * size = size of type
+ * Returns:
+ * corresponding unsigned D integer type
+ */
+ private AST.Type unsignedTypeForSize(ubyte size)
+ {
+ if (size <= 1)
+ return AST.Type.tuns8;
+ if (size <= 2)
+ return AST.Type.tuns16;
+ if (size <= 4)
+ return AST.Type.tuns32;
+ if (size <= 8)
+ return AST.Type.tuns64;
+ error("unsupported integer type");
+ return AST.Type.terror;
+ }
+
+ /***********************
* Return suitable D float type for C `long double`
* Params:
* flags = kind of float to return (real, imaginary, complex).
* Ds char16_t
* u <source-name> # vendor extended type
*/
+ if (t.isimaginary() || t.iscomplex())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=22806
+ // Complex and imaginary types are represented in the same way as
+ // arrays or vectors in C++. First substitute the outer type, then
+ // write out the mangle string of the underlying type.
+ if (substitute(t))
+ return;
+ append(t);
+ CV_qualifiers(t);
+
+ if (t.isimaginary())
+ buf.writeByte('G'); // 'G' means imaginary
+ else
+ buf.writeByte('C'); // 'C' means complex
+
+ switch (t.ty)
+ {
+ case Timaginary32:
+ case Tcomplex32:
+ return Type.tfloat32.accept(this);
+ case Timaginary64:
+ case Tcomplex64:
+ return Type.tfloat64.accept(this);
+ case Timaginary80:
+ case Tcomplex80:
+ return Type.tfloat80.accept(this);
+ default:
+ assert(0);
+ }
+ }
+
char c;
char p = 0;
switch (t.ty)
case Tchar: c = 'c'; break;
case Twchar: p = 'D'; c = 's'; break; // since C++11
case Tdchar: p = 'D'; c = 'i'; break; // since C++11
- case Timaginary32: p = 'G'; c = 'f'; break; // 'G' means imaginary
- case Timaginary64: p = 'G'; c = 'd'; break;
- case Timaginary80: p = 'G'; c = 'e'; break;
- case Tcomplex32: p = 'C'; c = 'f'; break; // 'C' means complex
- case Tcomplex64: p = 'C'; c = 'd'; break;
- case Tcomplex80: p = 'C'; c = 'e'; break;
default:
return error(t);
else if (id == Id.__c_ulonglong)
return writeBasicType(t, 0, 'y');
else if (id == Id.__c_complex_float)
- return writeBasicType(t, 'C', 'f');
+ return Type.tcomplex32.accept(this);
else if (id == Id.__c_complex_double)
- return writeBasicType(t, 'C', 'd');
+ return Type.tcomplex64.accept(this);
else if (id == Id.__c_complex_real)
- return writeBasicType(t, 'C', 'e');
+ return Type.tcomplex80.accept(this);
doSymbol(t);
}
}
}
-/***********************
- * Mangle basic type ty to buf.
- */
-
-private void tyToDecoBuffer(OutBuffer* buf, int ty)
-{
- const c = mangleChar[ty];
- buf.writeByte(c);
- if (c == 'z')
- buf.writeByte(ty == Tint128 ? 'i' : 'k');
-}
-
-/*********************************
- * Mangling for mod.
- */
-private void MODtoDecoBuffer(OutBuffer* buf, MOD mod)
-{
- switch (mod)
- {
- case 0:
- break;
- case MODFlags.const_:
- buf.writeByte('x');
- break;
- case MODFlags.immutable_:
- buf.writeByte('y');
- break;
- case MODFlags.shared_:
- buf.writeByte('O');
- break;
- case MODFlags.shared_ | MODFlags.const_:
- buf.writestring("Ox");
- break;
- case MODFlags.wild:
- buf.writestring("Ng");
- break;
- case MODFlags.wildconst:
- buf.writestring("Ngx");
- break;
- case MODFlags.shared_ | MODFlags.wild:
- buf.writestring("ONg");
- break;
- case MODFlags.shared_ | MODFlags.wildconst:
- buf.writestring("ONgx");
- break;
- default:
- assert(0);
- }
-}
-
private extern (C++) final class Mangler : Visitor
{
alias visit = Visitor.visit;
public:
static assert(Key.sizeof == size_t.sizeof);
- AssocArray!(Type, size_t) types; // Type => (offset+1) in buf
- AssocArray!(Identifier, size_t) idents; // Identifier => (offset+1) in buf
+
OutBuffer* buf;
- Type rootType;
+ Backref backref;
extern (D) this(OutBuffer* buf, Type rootType = null)
{
this.buf = buf;
- this.rootType = rootType;
- }
-
- /**
- * writes a back reference with the relative position encoded with base 26
- * using upper case letters for all digits but the last digit which uses
- * a lower case letter.
- * The decoder has to look up the referenced position to determine
- * whether the back reference is an identifier (starts with a digit)
- * or a type (starts with a letter).
- *
- * Params:
- * pos = relative position to encode
- */
- void writeBackRef(size_t pos)
- {
- buf.writeByte('Q');
- enum base = 26;
- size_t mul = 1;
- while (pos >= mul * base)
- mul *= base;
- while (mul >= base)
- {
- auto dig = cast(ubyte)(pos / mul);
- buf.writeByte('A' + dig);
- pos -= dig * mul;
- mul /= base;
- }
- buf.writeByte('a' + cast(ubyte)pos);
- }
-
- /**
- * Back references a non-basic type
- *
- * The encoded mangling is
- * 'Q' <relative position of first occurrence of type>
- *
- * Params:
- * t = the type to encode via back referencing
- *
- * Returns:
- * true if the type was found. A back reference has been encoded.
- * false if the type was not found. The current position is saved for later back references.
- */
- bool backrefType(Type t)
- {
- if (t.isTypeBasic())
- return false;
-
- /**
- * https://issues.dlang.org/show_bug.cgi?id=21591
- *
- * Special case for unmerged TypeFunctions: use the generic merged
- * function type as backref cache key to avoid missed backrefs.
- *
- * Merging is based on mangling, so we need to avoid an infinite
- * recursion by excluding the case where `t` is the root type passed to
- * `mangleToBuffer()`.
- */
- if (t != rootType)
- {
- if (t.isFunction_Delegate_PtrToFunction())
- {
- t = t.merge2();
- }
- }
-
- return backrefImpl(types, t);
- }
-
- /**
- * Back references a single identifier
- *
- * The encoded mangling is
- * 'Q' <relative position of first occurrence of type>
- *
- * Params:
- * id = the identifier to encode via back referencing
- *
- * Returns:
- * true if the identifier was found. A back reference has been encoded.
- * false if the identifier was not found. The current position is saved for later back references.
- */
- bool backrefIdentifier(Identifier id)
- {
- return backrefImpl(idents, id);
- }
-
- private extern(D) bool backrefImpl(T)(ref AssocArray!(T, size_t) aa, T key)
- {
- auto p = aa.getLvalue(key);
- if (*p)
- {
- const offset = *p - 1;
- writeBackRef(buf.length - offset);
- return true;
- }
- *p = buf.length + 1;
- return false;
+ this.backref = Backref(rootType);
}
void mangleSymbol(Dsymbol s)
void mangleType(Type t)
{
- if (!backrefType(t))
+ if (!backref.addRefToType(buf, t))
t.accept(this);
}
void mangleIdentifier(Identifier id, Dsymbol s)
{
- if (!backrefIdentifier(id))
- toBuffer(id.toString(), s);
+ if (!backref.addRefToIdentifier(buf, id))
+ toBuffer(buf, id.toString(), s);
}
////////////////////////////////////////////////////////////////////////////
// Write argument types
foreach (idx, param; t.parameterList)
- param.accept(this);
+ mangleParameter(param);
//if (buf.data[buf.length - 1] == '@') assert(0);
buf.writeByte('Z' - t.parameterList.varargs); // mark end of arg list
if (tret !is null)
//printf("TypeTuple.toDecoBuffer() t = %p, %s\n", t, t.toChars());
visit(cast(Type)t);
Parameter._foreach(t.arguments, (idx, param) {
- param.accept(this);
+ mangleParameter(param);
return 0;
});
buf.writeByte('Z');
else
buf.writeByte('0');
- /* There can be multiple different declarations in the same
- * function that have the same mangled name.
- * This results in localNum having a non-zero number, which
- * is used to add a fake parent of the form `__Sddd` to make
- * the mangled names unique.
- * https://issues.dlang.org/show_bug.cgi?id=20565
- */
if (localNum)
- {
- uint ndigits = 1;
- auto n = localNum;
- while (n >= 10)
- {
- n /= 10;
- ++ndigits;
- }
- buf.printf("%u__S%u", ndigits + 3, localNum);
- }
+ writeLocalParent(buf, localNum);
}
}
}
}
- /************************************************************
- * Write length prefixed string to buf.
- */
- extern (D) void toBuffer(const(char)[] id, Dsymbol s)
- {
- const len = id.length;
- if (buf.length + len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone
- s.error("excessive length %llu for symbol, possible recursive expansion?", cast(ulong)(buf.length + len));
- else
- {
- buf.print(len);
- buf.writestring(id);
- }
- }
-
- /************************************************************
- * Try to obtain an externally mangled identifier from a declaration.
- * If the declaration is at global scope or mixed in at global scope,
- * the user might want to call it externally, so an externally mangled
- * name is returned. Member functions or nested functions can't be called
- * externally in C, so in that case null is returned. C++ does support
- * namespaces, so extern(C++) always gives a C++ mangled name.
- *
- * See also: https://issues.dlang.org/show_bug.cgi?id=20012
- *
- * Params:
- * d = declaration to mangle
- *
- * Returns:
- * an externally mangled name or null if the declaration cannot be called externally
- */
- extern (D) static const(char)[] externallyMangledIdentifier(Declaration d)
- {
- const par = d.toParent(); //toParent() skips over mixin templates
- if (!par || par.isModule() || d.linkage == LINK.cpp ||
- (d.linkage == LINK.c && d.isCsymbol() && d.isFuncDeclaration()))
- {
- if (d.linkage != LINK.d && d.localNum)
- d.error("the same declaration cannot be in multiple scopes with non-D linkage");
- final switch (d.linkage)
- {
- case LINK.d:
- break;
- case LINK.c:
- case LINK.windows:
- case LINK.objc:
- return d.ident.toString();
- case LINK.cpp:
- {
- const p = target.cpp.toMangle(d);
- return p.toDString();
- }
- case LINK.default_:
- case LINK.system:
- d.error("forward declaration");
- return d.ident.toString();
- }
- }
- return null;
- }
-
override void visit(Declaration d)
{
//printf("Declaration.mangle(this = %p, '%s', parent = '%s', linkage = %d)\n",
if (d.mangleOverride)
{
buf.writeByte('X');
- toBuffer(d.mangleOverride, d);
+ toBuffer(buf, d.mangleOverride, d);
continue;
}
if (const id = externallyMangledIdentifier(d))
{
buf.writeByte('X');
- toBuffer(id, d);
+ toBuffer(buf, id, d);
continue;
}
if (!d.type || !d.type.deco)
if (s.ident)
mangleIdentifier(s.ident, s);
else
- toBuffer(s.toString(), s);
+ toBuffer(buf, s.toString(), s);
//printf("Dsymbol.mangle() %s = %s\n", s.toChars(), id);
}
override void visit(RealExp e)
{
buf.writeByte('e');
- realToMangleBuffer(e.value);
- }
-
- void realToMangleBuffer(real_t value)
- {
- /* Rely on %A to get portable mangling.
- * Must munge result to get only identifier characters.
- *
- * Possible values from %A => mangled result
- * NAN => NAN
- * -INF => NINF
- * INF => INF
- * -0X1.1BC18BA997B95P+79 => N11BC18BA997B95P79
- * 0X1.9P+2 => 19P2
- */
- if (CTFloat.isNaN(value))
- {
- buf.writestring("NAN"); // no -NAN bugs
- return;
- }
-
- if (value < CTFloat.zero)
- {
- buf.writeByte('N');
- value = -value;
- }
-
- if (CTFloat.isInfinity(value))
- {
- buf.writestring("INF");
- return;
- }
-
- char[36] buffer = void;
- // 'A' format yields [-]0xh.hhhhp+-d
- const n = CTFloat.sprint(buffer.ptr, 'A', value);
- assert(n < buffer.length);
- foreach (const c; buffer[2 .. n])
- {
- switch (c)
- {
- case '-':
- buf.writeByte('N');
- break;
-
- case '+':
- case '.':
- break;
-
- default:
- buf.writeByte(c);
- break;
- }
- }
+ realToMangleBuffer(buf, e.value);
}
override void visit(ComplexExp e)
{
buf.writeByte('c');
- realToMangleBuffer(e.toReal());
+ realToMangleBuffer(buf, e.toReal());
buf.writeByte('c'); // separate the two
- realToMangleBuffer(e.toImaginary());
+ realToMangleBuffer(buf, e.toImaginary());
}
override void visit(NullExp e)
////////////////////////////////////////////////////////////////////////////
- override void visit(Parameter p)
+ void mangleParameter(Parameter p)
{
// https://dlang.org/spec/abi.html#Parameter
visitWithMask(p.type, (stc & STC.in_) ? MODFlags.const_ : 0);
}
}
+
+/***************************************
+ * Manage back reference mangling
+ */
+private struct Backref
+{
+ /**
+ * Back references a non-basic type
+ *
+ * The encoded mangling is
+ * 'Q' <relative position of first occurrence of type>
+ *
+ * Params:
+ * t = the type to encode via back referencing
+ *
+ * Returns:
+ * true if the type was found. A back reference has been encoded.
+ * false if the type was not found. The current position is saved for later back references.
+ */
+ bool addRefToType(OutBuffer* buf, Type t)
+ {
+ if (t.isTypeBasic())
+ return false;
+
+ /**
+ * https://issues.dlang.org/show_bug.cgi?id=21591
+ *
+ * Special case for unmerged TypeFunctions: use the generic merged
+ * function type as backref cache key to avoid missed backrefs.
+ *
+ * Merging is based on mangling, so we need to avoid an infinite
+ * recursion by excluding the case where `t` is the root type passed to
+ * `mangleToBuffer()`.
+ */
+ if (t != rootType)
+ {
+ if (t.isFunction_Delegate_PtrToFunction())
+ {
+ t = t.merge2();
+ }
+ }
+
+ return backrefImpl(buf, types, t);
+ }
+
+ /**
+ * Back references a single identifier
+ *
+ * The encoded mangling is
+ * 'Q' <relative position of first occurrence of type>
+ *
+ * Params:
+ * id = the identifier to encode via back referencing
+ *
+ * Returns:
+ * true if the identifier was found. A back reference has been encoded.
+ * false if the identifier was not found. The current position is saved for later back references.
+ */
+ bool addRefToIdentifier(OutBuffer* buf, Identifier id)
+ {
+ return backrefImpl(buf, idents, id);
+ }
+
+ private:
+
+ extern(D) bool backrefImpl(T)(OutBuffer* buf, ref AssocArray!(T, size_t) aa, T key)
+ {
+ auto p = aa.getLvalue(key);
+ if (*p)
+ {
+ const offset = *p - 1;
+ writeBackRef(buf, buf.length - offset);
+ return true;
+ }
+ *p = buf.length + 1;
+ return false;
+ }
+
+ Type rootType; /// avoid infinite recursion
+ AssocArray!(Type, size_t) types; /// Type => (offset+1) in buf
+ AssocArray!(Identifier, size_t) idents; /// Identifier => (offset+1) in buf
+}
+
+
+/***********************
+ * Mangle basic type ty to buf.
+ */
+
+private void tyToDecoBuffer(OutBuffer* buf, int ty)
+{
+ const c = mangleChar[ty];
+ buf.writeByte(c);
+ if (c == 'z')
+ buf.writeByte(ty == Tint128 ? 'i' : 'k');
+}
+
+/*********************************
+ * Mangling for mod.
+ */
+private void MODtoDecoBuffer(OutBuffer* buf, MOD mod)
+{
+ switch (mod)
+ {
+ case 0:
+ break;
+ case MODFlags.const_:
+ buf.writeByte('x');
+ break;
+ case MODFlags.immutable_:
+ buf.writeByte('y');
+ break;
+ case MODFlags.shared_:
+ buf.writeByte('O');
+ break;
+ case MODFlags.shared_ | MODFlags.const_:
+ buf.writestring("Ox");
+ break;
+ case MODFlags.wild:
+ buf.writestring("Ng");
+ break;
+ case MODFlags.wildconst:
+ buf.writestring("Ngx");
+ break;
+ case MODFlags.shared_ | MODFlags.wild:
+ buf.writestring("ONg");
+ break;
+ case MODFlags.shared_ | MODFlags.wildconst:
+ buf.writestring("ONgx");
+ break;
+ default:
+ assert(0);
+ }
+}
+
+
+/**
+ * writes a back reference with the relative position encoded with base 26
+ * using upper case letters for all digits but the last digit which uses
+ * a lower case letter.
+ * The decoder has to look up the referenced position to determine
+ * whether the back reference is an identifier (starts with a digit)
+ * or a type (starts with a letter).
+ *
+ * Params:
+ * buf = buffer to write to
+ * pos = relative position to encode
+ */
+private
+void writeBackRef(OutBuffer* buf, size_t pos)
+{
+ buf.writeByte('Q');
+ enum base = 26;
+ size_t mul = 1;
+ while (pos >= mul * base)
+ mul *= base;
+ while (mul >= base)
+ {
+ auto dig = cast(ubyte)(pos / mul);
+ buf.writeByte('A' + dig);
+ pos -= dig * mul;
+ mul /= base;
+ }
+ buf.writeByte('a' + cast(ubyte)pos);
+}
+
+
+/************************************************************
+ * Write length prefixed string to buf.
+ */
+private
+extern (D) void toBuffer(OutBuffer* buf, const(char)[] id, Dsymbol s)
+{
+ const len = id.length;
+ if (buf.length + len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone
+ s.error("excessive length %llu for symbol, possible recursive expansion?", cast(ulong)(buf.length + len));
+ else
+ {
+ buf.print(len);
+ buf.writestring(id);
+ }
+}
+
+
+/*****
+ * There can be multiple different declarations in the same
+ * function that have the same mangled name.
+ * This results in localNum having a non-zero number, which
+ * is used to add a fake parent of the form `__Sddd` to make
+ * the mangled names unique.
+ * https://issues.dlang.org/show_bug.cgi?id=20565
+ * Params:
+ * buf = buffer to write to
+ * localNum = local symbol number
+ */
+private
+void writeLocalParent(OutBuffer* buf, uint localNum)
+{
+ uint ndigits = 1;
+ auto n = localNum;
+ while (n >= 10)
+ {
+ n /= 10;
+ ++ndigits;
+ }
+ buf.printf("%u__S%u", ndigits + 3, localNum);
+}
+
+/*************************
+ * Write real to buffer.
+ * Params:
+ * buf = buffer to write to
+ * value = real to write
+ */
+private
+void realToMangleBuffer(OutBuffer* buf, real_t value)
+{
+ /* Rely on %A to get portable mangling.
+ * Must munge result to get only identifier characters.
+ *
+ * Possible values from %A => mangled result
+ * NAN => NAN
+ * -INF => NINF
+ * INF => INF
+ * -0X1.1BC18BA997B95P+79 => N11BC18BA997B95P79
+ * 0X1.9P+2 => 19P2
+ */
+ if (CTFloat.isNaN(value))
+ {
+ buf.writestring("NAN"); // no -NAN bugs
+ return;
+ }
+
+ if (value < CTFloat.zero)
+ {
+ buf.writeByte('N');
+ value = -value;
+ }
+
+ if (CTFloat.isInfinity(value))
+ {
+ buf.writestring("INF");
+ return;
+ }
+
+ char[36] buffer = void;
+ // 'A' format yields [-]0xh.hhhhp+-d
+ const n = CTFloat.sprint(buffer.ptr, 'A', value);
+ assert(n < buffer.length);
+ foreach (const c; buffer[2 .. n])
+ {
+ switch (c)
+ {
+ case '-':
+ buf.writeByte('N');
+ break;
+
+ case '+':
+ case '.':
+ break;
+
+ default:
+ buf.writeByte(c);
+ break;
+ }
+ }
+}
+
+/************************************************************
+ * Try to obtain an externally mangled identifier from a declaration.
+ * If the declaration is at global scope or mixed in at global scope,
+ * the user might want to call it externally, so an externally mangled
+ * name is returned. Member functions or nested functions can't be called
+ * externally in C, so in that case null is returned. C++ does support
+ * namespaces, so extern(C++) always gives a C++ mangled name.
+ *
+ * See also: https://issues.dlang.org/show_bug.cgi?id=20012
+ *
+ * Params:
+ * d = declaration to mangle
+ *
+ * Returns:
+ * an externally mangled name or null if the declaration cannot be called externally
+ */
+private
+extern (D) const(char)[] externallyMangledIdentifier(Declaration d)
+{
+ const par = d.toParent(); //toParent() skips over mixin templates
+ if (!par || par.isModule() || d.linkage == LINK.cpp ||
+ (d.linkage == LINK.c && d.isCsymbol() && d.isFuncDeclaration()))
+ {
+ if (d.linkage != LINK.d && d.localNum)
+ d.error("the same declaration cannot be in multiple scopes with non-D linkage");
+ final switch (d.linkage)
+ {
+ case LINK.d:
+ break;
+ case LINK.c:
+ case LINK.windows:
+ case LINK.objc:
+ return d.ident.toString();
+ case LINK.cpp:
+ {
+ const p = target.cpp.toMangle(d);
+ return p.toDString();
+ }
+ case LINK.default_:
+ case LINK.system:
+ d.error("forward declaration");
+ return d.ident.toString();
+ }
+ }
+ return null;
+}
+
+
const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found";
errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr);
}
+ else if (FileName.ext(this.arg) || !loc.isValid())
+ {
+ // Modules whose original argument name has an extension, or do not
+ // have a valid location come from the command-line.
+ // Error that their file cannot be found and return early.
+ .error(loc, "cannot find input file `%s`", srcfile.toChars());
+ return false;
+ }
else
{
// if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module
//printf("sc.stc = %x\n", sc.stc);
//printf("storage_class = x%x\n", storage_class);
- if (global.params.vcomplex)
- dsym.type.checkComplexTransition(dsym.loc, sc);
+ dsym.type.checkComplexTransition(dsym.loc, sc);
// Calculate type size + safety checks
if (sc.func && !sc.intypeof)
else
assert(0);
- if (global.params.vcomplex)
- exp.type.checkComplexTransition(exp.loc, sc);
+ exp.type.checkComplexTransition(exp.loc, sc);
result = e;
}
return setError();
}
- if (global.params.vcomplex)
- ta.checkComplexTransition(exp.loc, sc);
+ ta.checkComplexTransition(exp.loc, sc);
Expression e;
auto tb = ta.toBasetype();
if (res == 1)
return readToFileBuffer(name);
- const fullName = lookForSourceFile(name, global.path ? (*global.path)[] : null);
- if (!fullName)
- return null;
-
- return readToFileBuffer(fullName);
+ return null;
}
extern(C++) FileBuffer* lookup(const(char)* filename)
if (thandle.ty == Tstruct)
{
vthis.storage_class |= STC.ref_;
- // if member function is marked 'inout', then 'this' is 'return ref'
- if (type.ty == Tfunction && (cast(TypeFunction)type).isInOutQual())
- vthis.storage_class |= STC.return_;
+
+ /* if member function is marked 'inout', then 'this' is 'return ref'
+ * The same thing is done for `ref inout` parameters in TypeFunction's semantic routine.
+ */
+ if (auto tf = type.isTypeFunction())
+ {
+ /* This feature was a mistake, but existing code relies on it.
+ * So only disable it in @safe code and DIP1000 code
+ */
+ if (!(global.params.useDIP1000 == FeatureState.enabled &&
+ tf.trust == TRUST.safe))
+ {
+ if (tf.isInOutQual())
+ vthis.storage_class |= STC.return_;
+ }
+ }
}
}
bool Ccompile; /// true if compiling ImportC
// The following are valid only if (Ccompile == true)
+ ubyte boolsize; /// size of a C _Bool, default 1
+ ubyte shortsize; /// size of a C short, default 2
+ ubyte intsize; /// size of a C int, default 4
ubyte longsize; /// size of C long, 4 or 8
+ ubyte long_longsize; /// size of a C long long, default 8
ubyte long_doublesize; /// size of C long double, 8 or D real.sizeof
ubyte wchar_tsize; /// size of C wchar_t, 2 or 4
case FLAGS.decimal | FLAGS.long_:
/* First that fits: long, long long
*/
- if (longsize == 4)
+ if (longsize == 4 || long_longsize == 4)
{
if (n & 0xFFFFFFFF_80000000L)
result = TOK.int64Literal;
/* First that fits: long, unsigned long, long long,
* unsigned long long
*/
- if (longsize == 4)
+ if (longsize == 4 || long_longsize == 4)
{
if (n & 0x8000000000000000L)
result = TOK.uns64Literal;
case FLAGS.decimal | FLAGS.unsigned | FLAGS.long_:
/* First that fits: unsigned long, unsigned long long
*/
- if (longsize == 4)
+ if (longsize == 4 || long_longsize == 4)
{
if (n & 0xFFFFFFFF00000000L)
result = TOK.uns64Literal;
case '2':
case '3':
case '4':
+ if (!linemarker)
+ goto Lerr;
flags = true; // linemarker flags seen
++p;
if ('0' <= *p && *p <= '9')
goto Lcovariant;
}
else if (t1n.ty == t2n.ty && t1n.implicitConvTo(t2n))
+ {
+ if (t1.isref && t2.isref)
+ {
+ // Treat like pointers to t1n and t2n
+ if (t1n.constConv(t2n) < MATCH.constant)
+ goto Lnotcovariant;
+ }
goto Lcovariant;
+ }
else if (t1n.ty == Tnull)
{
// NULL is covariant with any pointer type, but not with any
int fd = name.toCStringThen!(slice => open(slice.ptr, O_RDONLY));
if (fd == -1)
{
- //printf("\topen error, errno = %d\n",errno);
+ //perror("\topen error");
return result;
}
//printf("\tfile opened\n");
if (fstat(fd, &buf))
{
- perror("\tfstat error");
+ //perror("\tfstat error");
close(fd);
return result;
}
numread = .read(fd, buffer, size);
if (numread != size)
{
- perror("\tread error");
+ //perror("\tread error");
goto err2;
}
if (close(fd) == -1)
{
- perror("\tclose error");
+ //perror("\tclose error");
goto err;
}
// Always store a wchar ^Z past end of buffer so scanner has a
}
}
+private
+{
+ version (linux) version (PPC)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=22823
+ // Define our own version of stat_t, as older versions of the compiler
+ // had the st_size field at the wrong offset on PPC.
+ alias stat_t_imported = core.sys.posix.sys.stat.stat_t;
+ static if (stat_t_imported.st_size.offsetof != 48)
+ {
+ extern (C) nothrow @nogc:
+ struct stat_t
+ {
+ ulong[6] __pad1;
+ ulong st_size;
+ ulong[6] __pad2;
+ }
+ version (CRuntime_Glibc)
+ {
+ int fstat64(int, stat_t*) @trusted;
+ alias fstat = fstat64;
+ int stat64(const scope char*, stat_t*) @system;
+ alias stat = stat64;
+ }
+ else
+ {
+ int fstat(int, stat_t*) @trusted;
+ int stat(const scope char*, stat_t*) @system;
+ }
+ }
+ }
+}
import core.stdc.stdlib;
import core.stdc.string;
+import dmd.common.string : SmallBuffer;
enum isSearchFunction(alias fun) = is(searchFunctionType!fun);
alias searchFunctionType(alias fun) = typeof(() {int x; return fun("", x);}());
/* Need buffer to store trial strings in
*/
char[30] tmp = void;
- char[] buf;
- if (seed.length <= tmp.sizeof - 1)
- buf = tmp;
- else
- {
- buf = (cast(char*)alloca(seed.length + 1))[0 .. seed.length + 1]; // leave space for extra char
- if (!buf.ptr)
- return null; // no matches
- }
+ auto sb = SmallBuffer!char(seed.length + 1, tmp[]);
+ char[] buf = sb[];
int cost = int.max;
searchFunctionType!dg p = null;
* space for an extra char for insertions
*/
char[30] tmp = void; // stack allocations are fastest
- char[] buf;
- if (seed.length <= tmp.sizeof - 1)
- buf = tmp;
- else
- {
- buf = (cast(char*)alloca(seed.length + 1))[0 .. seed.length + 1]; // leave space for extra char
- if (!buf.ptr)
- return null; // no matches
- }
+ auto sb = SmallBuffer!char(seed.length + 1, tmp[]);
+ char[] buf = sb[];
buf[0 .. index] = seed[0 .. index];
cost = int.max; // start with worst possible match
auto toCStringThen(alias dg)(const(char)[] src) nothrow
{
import dmd.root.rmem : mem;
+ import dmd.common.string : SmallBuffer;
const len = src.length + 1;
char[512] small = void;
- scope ptr = (src.length < (small.length - 1))
- ? small[0 .. len]
- : (cast(char*)mem.xmalloc(len))[0 .. len];
- scope (exit)
- {
- if (&ptr[0] != &small[0])
- mem.xfree(&ptr[0]);
- }
+ auto sb = SmallBuffer!char(len, small[]);
+ scope ptr = sb[];
ptr[0 .. src.length] = src[];
ptr[src.length] = '\0';
return dg(ptr);
{
stc |= STC.variadic;
auto vtypeb = vtype.toBasetype();
- if (vtypeb.ty == Tarray)
+ if (vtypeb.ty == Tarray || vtypeb.ty == Tclass)
{
/* Since it'll be pointing into the stack for the array
* contents, it needs to be `scope`
funcdecl.checkDmain(); // Check main() parameters and return type
}
- if (global.params.vcomplex && f.next !is null)
+ if (f.next !is null)
f.next.checkComplexTransition(funcdecl.loc, sc);
if (funcdecl.returns && !funcdecl.fbody.isErrorStatement())
// Eliminate maybescope's
{
// Create and fill array[] with maybe candidates from the `this` and the parameters
- VarDeclaration[] array = void;
-
VarDeclaration[10] tmp = void;
size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.dim : 0);
- if (dim <= tmp.length)
- array = tmp[0 .. dim];
- else
- {
- auto ptr = cast(VarDeclaration*)mem.xmalloc(dim * VarDeclaration.sizeof);
- array = ptr[0 .. dim];
- }
+
+ import dmd.common.string : SmallBuffer;
+ auto sb = SmallBuffer!VarDeclaration(dim, tmp[]);
+ VarDeclaration[] array = sb[];
+
size_t n = 0;
if (funcdecl.vthis)
array[n++] = funcdecl.vthis;
array[n++] = v;
}
}
-
eliminateMaybeScopes(array[0 .. n]);
-
- if (dim > tmp.length)
- mem.xfree(array.ptr);
}
// Infer STC.scope_
Gcc_Clang, /// gcc and clang
}
bool crtDestructorsSupported = true; /// Not all platforms support crt_destructor
+ ubyte boolsize; /// size of a C `_Bool` type
+ ubyte shortsize; /// size of a C `short` or `unsigned short` type
+ ubyte intsize; /// size of a C `int` or `unsigned int` type
ubyte longsize; /// size of a C `long` or `unsigned long` type
+ ubyte long_longsize; /// size of a C `long long` or `unsigned long long` type
ubyte long_doublesize; /// size of a C `long double`
ubyte wchar_tsize; /// size of a C `wchar_t` type
Runtime runtime; /// vendor of the C runtime to link against
};
uint8_t crtDestructorsSupported; // Not all platforms support crt_destructor
+ uint8_t boolsize; // size of a C '_Bool' type
+ uint8_t shortsize; // size of a C 'short' or 'unsigned short' type
+ uint8_t intsize; // size of a C 'int' or 'unsigned int' type
uint8_t longsize; // size of a C 'long' or 'unsigned long' type
+ uint8_t long_longsize; // size of a C 'long long' or 'unsigned long long' type
uint8_t long_doublesize; // size of a C 'long double'
uint8_t wchar_tsize; // size of a C 'wchar_t' type
Runtime runtime;
comment,
// Operators
- lessThan, // 54
+ lessThan,
greaterThan,
lessOrEqual,
greaterOrEqual,
notIdentity,
is_,
- leftShift, // 64
+ leftShift,
rightShift,
leftShiftAssign,
rightShiftAssign,
orOr,
// Numeric literals
- int32Literal, // 104,
+ int32Literal,
uns32Literal,
int64Literal,
uns64Literal,
imaginary80Literal,
// Char constants
- charLiteral, // 116,
+ charLiteral,
wcharLiteral,
dcharLiteral,
// Leaf operators
- identifier, // 119,
+ identifier,
string_,
hexadecimalString,
this_,
error,
// Basic types
- void_, // 127
+ void_,
int8,
uns8,
int16,
bool_,
// Aggregates
- struct_, // 151
+ struct_,
class_,
interface_,
union_,
immutable_,
// Statements
- if_, // 181
+ if_,
else_,
while_,
for_,
onScopeSuccess,
// Contracts
- invariant_, // 205
+ invariant_,
// Testing
unittest_,
ref_,
macro_,
- parameters, // 210
+ parameters,
traits,
pure_,
nothrow_,
}
if (e.ident == Id.isDeprecated)
{
- if (global.params.vcomplex)
- {
- if (isTypeX(t => t.iscomplex() || t.isimaginary()).toBool().hasValue(true))
- return True();
- }
+ if (isTypeX(t => t.iscomplex() || t.isimaginary()).toBool().hasValue(true))
+ return True();
return isDsymX(t => t.isDeprecated());
}
if (e.ident == Id.isFuture)
return mtype;
}
//printf("TypeFunction::semantic() this = %p\n", this);
- //printf("TypeFunction::semantic() %s, sc.stc = %llx, fargs = %p\n", toChars(), sc.stc, fargs);
+ //printf("TypeFunction::semantic() %s, sc.stc = %llx\n", mtype.toChars(), sc.stc);
bool errors = false;
fparam.storageClass &= ~STC.return_; // https://issues.dlang.org/show_bug.cgi?id=18963
}
}
+
+ if (i + 1 == dim && tf.parameterList.varargs == VarArg.typesafe &&
+ (t.isTypeDArray() || t.isTypeClass()))
+ {
+ /* This is because they can be constructed on the stack
+ * https://dlang.org/spec/function.html#typesafe_variadic_functions
+ */
+ .error(loc, "typesafe variadic function parameter `%s` of type `%s` cannot be marked `return`",
+ fparam.ident ? fparam.ident.toChars() : "", t.toChars());
+ errors = true;
+ }
}
if (fparam.storageClass & STC.out_)
--- /dev/null
+// { dg-additional-options "-mavx" { target avx_runtime } }
+// { dg-do compile { target { avx_runtime || vect_sizes_16B_8B } } }
+// { dg-skip-if "needs gcc/config.d" { ! d_runtime } }
+import core.simd;
+
+double dot (double2 a) {
+ return a.ptr[0] * a.ptr[1];
+}
-// REQUIRED_ARGS: -m64 -O -inline
-// DISABLED: win32 linux32 freebsd32 osx32 netbsd32 dragonflybsd32
// https://issues.dlang.org/show_bug.cgi?id=21727
-
+// { dg-additional-options "-mavx" { target avx_runtime } }
+// { dg-do run { target { avx_runtime || vect_sizes_16B_8B } } }
+// { dg-skip-if "needs gcc/config.d" { ! d_runtime } }
import core.simd;
@nogc nothrow pure @safe:
pragma(inline, true) Float4 twoTimes(const ref Float4 a)
{
- version (D_SIMD)
- return Float4(cast(float4) __simd(XMM.ADDPS, a.mVector, a.mVector));
- else // Allow non-DMD compilers to compile this test.
- return Float4(a.mVector + a.mVector);
+ return Float4(a.mVector + a.mVector);
}
pragma(inline, false) Float4 fourTimes(const Float4 a)
// { dg-additional-options "-mavx" { target avx_runtime } }
// { dg-do run { target { avx_runtime || vect_sizes_16B_8B } } }
// { dg-skip-if "needs gcc/config.d" { ! d_runtime } }
-// { dg-skip-if "needs gcc/config.d" { ! d_runtime } }
import core.simd;
void main()
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88957
-// { dg-do compile }
+// { dg-additional-options "-mavx" { target avx_runtime } }
+// { dg-do compile { target { avx_runtime || vect_sizes_16B_8B } } }
// { dg-additional-options "-fsanitize=undefined" }
alias int4 = __vector(int[4]);
+++ /dev/null
-// REQUIRED_ARGS: -O -m64
-import core.simd;
-
-double dot (double2 a) {
- return a.ptr[0] * a.ptr[1];
-}
-
-void main () { }
--- /dev/null
+struct S { @disable this(); }
+// Does not compile: "default construction is disabled for type `S`"
+extern __gshared S gVariable1;
* TEST_OUTPUT:
---
fail_compilation/fail17927.d(13): Error: scope variable `this` may not be returned
+fail_compilation/fail17927.d(15): Error: scope variable `this` may not be returned
fail_compilation/fail17927.d(21): Error: scope variable `ptr` may not be returned
fail_compilation/fail17927.d(23): Error: scope variable `ptr` may not be returned
---
*/
-
// https://issues.dlang.org/show_bug.cgi?id=17927
struct String {
+++ /dev/null
-/* REQUIRED_ARGS: -m64
- * TEST_OUTPUT:
----
-fail_compilation/fix17751.d(15): Error: last parameter to `__simd()` must be a constant
----
- */
-
-// https://issues.dlang.org/show_bug.cgi?id=17751
-
-import core.simd;
-
-pure @safe V1 simd(XMM opcode, V1, V2)(V1 op1, V2 op2, ubyte imm8)
- if (is(V1 == __vector) && is(V2 == __vector))
-{
- return cast(V1)__simd(opcode, op1, op2, imm8);
-}
-
-void main()
-{
- float4 a, b;
- a = simd!(XMM.CMPPD)(a, b, 0x7A);
-}
--- /dev/null
+/* TEST_OUTPUT:
+---
+fail_compilation/issue22826.d(7): Error: #line integer ["filespec"]\n expected
+fail_compilation/issue22826.d(7): Error: declaration expected, not `3`
+---
+*/
+#line 12 "issue22826.d" 3
--- /dev/null
+/* TEST_OUTPUT:
+---
+fail_compilation/test21546.d(113): Error: cannot implicitly convert expression `pc` of type `const(int)* delegate() return` to `int* delegate() return`
+fail_compilation/test21546.d(114): Error: cannot implicitly convert expression `pc` of type `const(int)* delegate() return` to `immutable(int)* delegate() return`
+fail_compilation/test21546.d(115): Error: cannot implicitly convert expression `pi` of type `immutable(int)* delegate() return` to `int* delegate() return`
+fail_compilation/test21546.d(213): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() ref return` to `int delegate() ref return`
+fail_compilation/test21546.d(214): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() ref return` to `immutable(int) delegate() ref return`
+fail_compilation/test21546.d(215): Error: cannot implicitly convert expression `di` of type `immutable(int) delegate() ref return` to `int delegate() ref return`
+fail_compilation/test21546.d(305): Error: cannot implicitly convert expression `[dgi]` of type `immutable(int) delegate() ref return[]` to `int delegate() ref return[]`
+---
+ */
+// https://issues.dlang.org/show_bug.cgi?id=21546
+
+#line 100
+
+alias Pm = int* delegate() return;
+alias Pc = const(int)* delegate() return;
+alias Pi = immutable(int)* delegate() return;
+
+void f()
+{
+ Pm pm;
+ Pc pc;
+ Pi pi;
+ pc = pm;
+ pc = pi;
+
+ pm = pc;
+ pi = pc;
+ pm = pi;
+}
+
+#line 200
+
+alias DGm = ref int delegate() return;
+alias DGc = ref const(int) delegate() return;
+alias DGi = ref immutable(int) delegate() return;
+
+void g()
+{
+ DGm dm;
+ DGc dc;
+ DGi di;
+ dc = dm;
+ dc = di;
+
+ dm = dc;
+ di = dc;
+ dm = di;
+}
+
+#line 300
+
+void h()
+{
+ immutable int i = 0;
+ DGi dgi = ref() => i;
+ DGm[] dgms = [ dgi ];
+}
--- /dev/null
+/* TEST_OUTPUT:
+---
+fail_compilation/test22023.d(102): Error: typesafe variadic function parameter `a` of type `int[]` cannot be marked `return`
+fail_compilation/test22023.d(107): Error: typesafe variadic function parameter `c` of type `test22023.C` cannot be marked `return`
+---
+*/
+
+// issues.dlang.org/show_bug.cgi?id=22023
+
+#line 100
+
+@safe:
+ref int f(return int[] a ...)
+{
+ return a[2];
+}
+
+ref int g(return C c ...)
+{
+ return c.x;
+}
+
+class C
+{
+ int x;
+}
--- /dev/null
+/* REQUIRED_ARGS: -preview=dip1000
+ * TEST_OUTPUT:
+---
+fail_compilation/test22818.d(104): Error: scope variable `c` may not be returned
+---
+*/
+
+// issues.dlang.org/show_bug.cgi?id=22818
+
+#line 100
+
+@safe:
+ref int g(C c ...)
+{
+ return c.x;
+}
+
+class C
+{
+ int x;
+}
assert(a is c);
static if (T.mant_dig == 64 && T.max_exp == 16384)
+ {
enum size = 10; // x87, exclude padding
+ enum mant_dig = T.mant_dig;
+ }
+ else static if (T.mant_dig == 106)
+ {
+ enum size = 8; // IBM, only look at first index
+ enum mant_dig = 53;
+ }
else
+ {
enum size = T.sizeof;
+ enum mant_dig = T.mant_dig;
+ }
const pa = (cast(ubyte*) &a)[0 .. size];
// the highest 2 bits of the mantissa should be set, everything else zero
- assert(bittst(pa, T.mant_dig - 1));
- assert(bittst(pa, T.mant_dig - 2));
- foreach(p; 0..T.mant_dig - 2)
+ assert(bittst(pa, mant_dig - 1));
+ assert(bittst(pa, mant_dig - 2));
+ foreach(p; 0..mant_dig - 2)
assert(!bittst(pa, p));
}
void testin1(in uint p) { static assert(!__traits(isRef, p)); }
// By ref because of size
void testin2(in ulong[64] p) { static assert(__traits(isRef, p)); }
-// By value or ref depending on size
-void testin3(in ValueT p) { static assert(!__traits(isRef, p)); }
+// By value or ref depending on size (or structs always passed by reference)
+void testin3(in ValueT p) { static assert(!__traits(isRef, p) || true); }
void testin3(in RefT p) { static assert(__traits(isRef, p)); }
-// By ref because of size
+// By ref because of size (or arrays always passed by reference)
void testin4(in ValueT[64] p) { static assert(__traits(isRef, p)); }
void testin4(in RefT[4] p) { static assert(__traits(isRef, p)); }
+++ /dev/null
-/* REQUIRED_ARGS: -O -inline -noboundscheck
- */
-// https://github.com/dlang/pull/13220
-
-version (D_SIMD)
-{
-
-mixin template VectorOps(VectorType, ArrayType: BaseType[N], BaseType, size_t N)
-{
- enum Count = N;
- alias Base = BaseType;
-
- BaseType* ptr() return pure nothrow @nogc
- {
- return array.ptr;
- }
-
- // Unary operators
- VectorType opUnary(string op)() pure nothrow @safe @nogc
- {
- VectorType res = void;
- mixin("res.array[] = " ~ op ~ "array[];");
- return res;
- }
-
- // Binary operators
- VectorType opBinary(string op)(VectorType other) pure const nothrow @safe @nogc
- {
- VectorType res = void;
- mixin("res.array[] = array[] " ~ op ~ " other.array[];");
- return res;
- }
-
- // Assigning a BaseType value
- void opAssign(BaseType e) pure nothrow @safe @nogc
- {
- array[] = e;
- }
-
- // Assigning a static array
- void opAssign(ArrayType v) pure nothrow @safe @nogc
- {
- array[] = v[];
- }
-
- void opOpAssign(string op)(VectorType other) pure nothrow @safe @nogc
- {
- mixin("array[] " ~ op ~ "= other.array[];");
- }
-
- // Assigning a dyn array
- this(ArrayType v) pure nothrow @safe @nogc
- {
- array[] = v[];
- }
-
- // Broadcast constructor
- this(BaseType x) pure nothrow @safe @nogc
- {
- array[] = x;
- }
-
- ref inout(BaseType) opIndex(size_t i) inout pure nothrow @safe @nogc
- {
- return array[i];
- }
-}
-
-// Note: can't be @safe with this signature
-Vec loadUnaligned(Vec)(const(BaseType!Vec)* pvec) @trusted
-{
- // Since this vector is emulated, it doesn't have alignement constraints
- // and as such we can just cast it.
- return *cast(Vec*)(pvec);
-}
-
-private template BaseType(V)
-{
- alias typeof( ( { V v; return v; }()).array[0]) BaseType;
-}
-
-struct int4
-{
- int[4] array;
- mixin VectorOps!(int4, int[4]);
-}
-
-alias __m128i = int4;
-}
-
-int main()
-{
- version (D_SIMD)
- {
- int4 A = [1, 2, 3, 4];
- int4 ia = A;
- ia.ptr[2] = 5;
- int4 C = ia;
- int[4] result = [1, 2, 5, 4];
- assert(C.array == result);
- }
- return 0;
-}
int[2] array = [16, 678];
union U { int i; bool b; }
U u;
- u.i = 0xDEADBEEF;
+ u.i = 0xBFBFBFBF;
assert(array[u.b] == 678);
}
const int z = 6;
C3748 c;
- inout(int)* getX() inout
+ inout(int)* getX() inout return
{
static assert(!__traits(compiles, {
x = 4;
{
int x;
inout(int) screwUpVal(ref inout(int) _) inout { return x; }
- ref inout(int) screwUpRef(ref inout(int) _) inout { return x; }
- inout(int)* screwUpPtr(ref inout(int) _) inout { return &x; }
- inout(int)[] screwUpArr(ref inout(int) _) inout { return (&x)[0 .. 1]; }
+ ref inout(int) screwUpRef(ref inout(int) _) inout return { return x; }
+ inout(int)* screwUpPtr(ref inout(int) _) inout return { return &x; }
+ inout(int)[] screwUpArr(ref inout(int) _) inout return { return (&x)[0 .. 1]; }
}
void test10758(ref inout(int) wx, inout(int)* wp, inout(int)[] wa, inout(S10758) ws)
struct S10761
{
int x;
- inout(int)* screwUp() inout { return &x; }
+ inout(int)* screwUp() inout return { return &x; }
}
-inout(int)* delegate() inout memfn10761(inout(int)* x)
+inout(int)* delegate() inout return memfn10761(inout(int)* x)
{
auto s = new inout S10761(1);
auto dg = &s.screwUp;
- static assert(is(typeof(dg) == inout(int)* delegate() inout));
+ static assert(is(typeof(dg) == inout(int)* delegate() inout return));
return dg;
}
auto dg_m = memfn10761(&mx);
auto dg_c = memfn10761(&cx);
auto dg_i = memfn10761(&ix);
- alias DG = const(int)* delegate() const;
+ alias DG = const(int)* delegate() return const;
static assert(is(typeof(dg_m) == DG));
static assert(is(typeof(dg_c) == DG));
static assert(is(typeof(dg_i) == DG));
{
int x;
- ref inout(int) foo() inout
+ ref inout(int) foo() inout return
{
return x;
}
testType!(fn) ([ 0b0 ]);
testType!(S!fn) ([ 0b100 ]);
testType!(NullType) ([ 0b0 ]);
- version(D_LP64)
+ static if (__traits(compiles, __vector(float[4])))
testType!(__vector(float[4])) ([ 0b00 ]);
testType!(Object[int]) ([ 0b1 ]);
-55528bd1e963d858eaa63901fc818b957c349fbc
+caf14b0f4ebbae4157aac89368d6278332ee2aa1
The first line of this file holds the git revision number of the last
merge done from the dlang/druntime repository.
* Retrieve statistics about garbage collection.
* Useful for debugging and tuning.
*/
- core.memory.GC.Stats stats() nothrow;
+ core.memory.GC.Stats stats() @safe nothrow @nogc;
/**
* Retrieve profile statistics about garbage collection.
* Useful for debugging and tuning.
*/
- core.memory.GC.ProfileStats profileStats() nothrow @safe;
+ core.memory.GC.ProfileStats profileStats() @safe nothrow @nogc;
/**
* add p to list of roots
onOutOfMemoryError();
}
- wordtype test(size_t i) const nothrow
+ wordtype test(size_t i) const scope @trusted pure nothrow @nogc
in
{
assert(i < nbits);
return core.bitop.bt(data, i);
}
- int set(size_t i) nothrow
+ int set(size_t i) scope @trusted pure nothrow @nogc
in
{
assert(i < nbits);
return core.bitop.bts(data, i);
}
- int clear(size_t i) nothrow
+ int clear(size_t i) scope @trusted pure nothrow @nogc
in
{
assert(i <= nbits);
}
// return non-zero if bit already set
- size_t setLocked(size_t i) nothrow
+ size_t setLocked(size_t i) scope @trusted pure nothrow @nogc
{
version (GNU)
{
}
else version (D_InlineAsm_X86)
{
- asm @nogc nothrow {
+ asm pure @nogc nothrow {
mov EAX, this;
mov ECX, data[EAX];
mov EDX, i;
}
else version (D_InlineAsm_X86_64)
{
- asm @nogc nothrow {
+ asm pure @nogc nothrow {
mov RAX, this;
mov RAX, data[RAX];
mov RDX, i;
import core.gc.gcinterface;
import core.internal.container.treap;
+import core.internal.spinlock;
+import core.internal.gc.pooltable;
import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
import core.stdc.string : memcpy, memset, memmove;
Gcx *gcx; // implementation
- import core.internal.spinlock;
static gcLock = shared(AlignedSpinLock)(SpinLock.Contention.lengthy);
static bool _inFinalizer;
__gshared bool isPrecise = false;
*
* Throws: InvalidMemoryOperationError on recursive locking during finalization.
*/
- static void lockNR() @nogc nothrow
+ static void lockNR() @safe @nogc nothrow
{
if (_inFinalizer)
onInvalidMemoryOperationError();
else if (pagenum + newsz <= pool.npages)
{
// Attempt to expand in place (TODO: merge with extend)
- if (lpool.pagetable[pagenum + psz] != B_FREE)
+ if (lpool.pagetable[pagenum + psz] != Bins.B_FREE)
return doMalloc();
auto newPages = newsz - psz;
debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize);
debug (PRINTF) printFreeInfo(pool);
- memset(&lpool.pagetable[pagenum + psz], B_PAGEPLUS, newPages);
+ memset(&lpool.pagetable[pagenum + psz], Bins.B_PAGEPLUS, newPages);
lpool.bPageOffsets[pagenum] = cast(uint) newsz;
for (auto offset = psz; offset < newsz; offset++)
lpool.bPageOffsets[pagenum + offset] = cast(uint) offset;
auto lpool = cast(LargeObjectPool*) pool;
size_t pagenum = lpool.pagenumOf(p);
- if (lpool.pagetable[pagenum] != B_PAGE)
+ if (lpool.pagetable[pagenum] != Bins.B_PAGE)
return 0;
size_t psz = lpool.bPageOffsets[pagenum];
if (pagenum + psz >= lpool.npages)
return 0;
- if (lpool.pagetable[pagenum + psz] != B_FREE)
+ if (lpool.pagetable[pagenum + psz] != Bins.B_FREE)
return 0;
size_t freesz = lpool.bPageOffsets[pagenum + psz];
return 0;
size_t sz = freesz > maxsz ? maxsz : freesz;
debug (MEMSTOMP) memset(pool.baseAddr + (pagenum + psz) * PAGESIZE, 0xF0, sz * PAGESIZE);
- memset(lpool.pagetable + pagenum + psz, B_PAGEPLUS, sz);
+ memset(lpool.pagetable + pagenum + psz, Bins.B_PAGEPLUS, sz);
lpool.bPageOffsets[pagenum] = cast(uint) (psz + sz);
for (auto offset = psz; offset < psz + sz; offset++)
lpool.bPageOffsets[pagenum + offset] = cast(uint) offset;
debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]);
debug(PRINTF) if (pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]);
- bin = cast(Bins)pool.pagetable[pagenum];
+ bin = pool.pagetable[pagenum];
// Verify that the pointer is at the beginning of a block,
// no action should be taken if p is an interior pointer
- if (bin > B_PAGE) // B_PAGEPLUS or B_FREE
+ if (bin > Bins.B_PAGE) // B_PAGEPLUS or B_FREE
return;
size_t off = (sentinel_sub(p) - pool.baseAddr);
size_t base = baseOffset(off, bin);
if (pool.isLargeObject) // if large alloc
{
biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Large;
- assert(bin == B_PAGE);
+ assert(bin == Bins.B_PAGE);
auto lpool = cast(LargeObjectPool*) pool;
// Free pages
pool = gcx.findPool(p);
assert(pool);
pagenum = pool.pagenumOf(p);
- bin = cast(Bins)pool.pagetable[pagenum];
- assert(bin <= B_PAGE);
+ bin = pool.pagetable[pagenum];
+ assert(bin <= Bins.B_PAGE);
assert(p == cast(void*)baseOffset(cast(size_t)p, bin));
debug (PTRCHECK2)
{
- if (bin < B_PAGE)
+ if (bin < Bins.B_PAGE)
{
// Check that p is not on a free list
List *list;
}
- core.memory.GC.Stats stats() nothrow
+ core.memory.GC.Stats stats() @safe nothrow @nogc
{
typeof(return) ret;
//
// Implementation of getStats
//
- private void getStatsNoSync(out core.memory.GC.Stats stats) nothrow
+ private void getStatsNoSync(out core.memory.GC.Stats stats) @trusted nothrow @nogc
{
- foreach (pool; gcx.pooltable[0 .. gcx.npools])
+ // This function is trusted for two reasons: `pool.pagetable` is a pointer,
+ // which is being sliced in the below foreach, and so is `binPageChain`,
+ // also sliced a bit later in this function.
+ // However, both usages are safe as long as the assumption that `npools`
+ // defines the limit for `pagetable`'s length holds true (see allocation).
+ // The slicing happens at __LINE__ + 4 and __LINE__ + 24.
+ // `@trusted` delegates are not used to prevent any performance issue.
+ foreach (pool; gcx.pooltable[])
{
foreach (bin; pool.pagetable[0 .. pool.npages])
{
- if (bin == B_FREE)
+ if (bin == Bins.B_FREE)
stats.freeSize += PAGESIZE;
else
stats.usedSize += PAGESIZE;
}
size_t freeListSize;
- foreach (n; 0 .. B_PAGE)
+ foreach (n; 0 .. Bins.B_PAGE)
{
immutable sz = binsize[n];
for (List *list = gcx.bucket[n]; list; list = list.next)
freeListSize += sz;
- foreach (pool; gcx.pooltable[0 .. gcx.npools])
+ foreach (pool; gcx.pooltable[])
{
if (pool.isLargeObject)
continue;
}
-enum
+enum Bins : ubyte
{
B_16,
B_32,
B_MAX,
}
-
-alias ubyte Bins;
-
-
struct List
{
List *next;
}
// non power of two sizes optimized for small remainder within page (<= 64 bytes)
-immutable short[B_NUMSMALL + 1] binsize = [ 16, 32, 48, 64, 96, 128, 176, 256, 368, 512, 816, 1024, 1360, 2048, 4096 ];
-immutable short[PAGESIZE / 16][B_NUMSMALL + 1] binbase = calcBinBase();
+immutable short[Bins.B_NUMSMALL + 1] binsize = [ 16, 32, 48, 64, 96, 128, 176, 256, 368, 512, 816, 1024, 1360, 2048, 4096 ];
+immutable short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] binbase = calcBinBase();
-short[PAGESIZE / 16][B_NUMSMALL + 1] calcBinBase()
+short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] calcBinBase()
{
- short[PAGESIZE / 16][B_NUMSMALL + 1] bin;
+ short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] bin;
foreach (i, size; binsize)
{
size_t baseOffset(size_t offset, Bins bin) @nogc nothrow
{
- assert(bin <= B_PAGE);
+ assert(bin <= Bins.B_PAGE);
return (offset & ~(PAGESIZE - 1)) + binbase[bin][(offset & (PAGESIZE - 1)) >> 4];
}
static assert(PAGESIZE % (GCBits.BITS_PER_WORD * 16) == 0);
// bitmask with bits set at base offsets of objects
-immutable PageBits[B_NUMSMALL] baseOffsetBits = (){
- PageBits[B_NUMSMALL] bits;
- foreach (bin; 0..B_NUMSMALL)
+immutable PageBits[Bins.B_NUMSMALL] baseOffsetBits = (){
+ PageBits[Bins.B_NUMSMALL] bits;
+ foreach (bin; 0 .. Bins.B_NUMSMALL)
{
size_t size = binsize[bin];
const top = PAGESIZE - size + 1; // ensure <size> bytes available even if unaligned
struct Gcx
{
- import core.internal.spinlock;
auto rootsLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
auto rangesLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
Treap!Root roots;
debug(INVARIANT) bool inCollection;
uint disabled; // turn off collections if >0
- import core.internal.gc.pooltable;
- private @property size_t npools() pure const nothrow { return pooltable.length; }
PoolTable!Pool pooltable;
- List*[B_NUMSMALL] bucket; // free list for each small size
+ List*[Bins.B_NUMSMALL] bucket; // free list for each small size
// run a collection when reaching those thresholds (number of used pages)
float smallCollectThreshold, largeCollectThreshold;
else
alias leakDetector = LeakDetector;
- SmallObjectPool*[B_NUMSMALL] recoverPool;
+ SmallObjectPool*[Bins.B_NUMSMALL] recoverPool;
version (Posix) __gshared Gcx* instance;
void initialize()
debug(INVARIANT) initialized = false;
- for (size_t i = 0; i < npools; i++)
+ foreach (Pool* pool; this.pooltable[])
{
- Pool *pool = pooltable[i];
mappedPages -= pool.npages;
pool.Dtor();
cstdlib.free(pool);
if (!inCollection)
(cast()rangesLock).unlock();
- for (size_t i = 0; i < B_NUMSMALL; i++)
+ for (size_t i = 0; i < Bins.B_NUMSMALL; i++)
{
size_t j = 0;
List* prev, pprev, ppprev; // keep a short history to inspect in the debugger
ConservativeGC._inFinalizer = true;
scope (failure) ConservativeGC._inFinalizer = false;
- foreach (pool; pooltable[0 .. npools])
+ foreach (pool; this.pooltable[])
{
if (!pool.finals.nbits) continue;
/**
* Computes the bin table using CTFE.
*/
- static byte[2049] ctfeBins() nothrow
+ static Bins[2049] ctfeBins() nothrow
{
- byte[2049] ret;
+ Bins[2049] ret;
size_t p = 0;
- for (Bins b = B_16; b <= B_2048; b++)
+ for (Bins b = Bins.B_16; b <= Bins.B_2048; b++)
for ( ; p <= binsize[b]; p++)
ret[p] = b;
return ret;
}
- static const byte[2049] binTable = ctfeBins();
+ static immutable Bins[2049] binTable = ctfeBins();
/**
* Allocate a new pool of at least size bytes.
bool tryAlloc() nothrow
{
- foreach (p; pooltable[0 .. npools])
+ foreach (p; this.pooltable[])
{
if (!p.isLargeObject || p.freepages < npages)
continue;
}
// Allocate successively larger pools up to 8 megs
- if (npools)
- { size_t n;
+ if (this.pooltable.length)
+ {
+ size_t n;
- n = config.minPoolSize + config.incPoolSize * npools;
+ n = config.minPoolSize + config.incPoolSize * this.pooltable.length;
if (n > config.maxPoolSize)
n = config.maxPoolSize; // cap pool size
n /= PAGESIZE; // convert bytes to pages
List* allocPage(Bins bin) nothrow
{
//debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin);
- for (size_t n = 0; n < npools; n++)
+ foreach (Pool* pool; this.pooltable[])
{
- Pool* pool = pooltable[n];
if (pool.isLargeObject)
continue;
if (List* p = (cast(SmallObjectPool*)pool).allocPage(bin))
// let dmd allocate a register for this.pools
auto pools = pooltable.pools;
- const highpool = pooltable.npools - 1;
+ const highpool = pooltable.length - 1;
const minAddr = pooltable.minAddr;
size_t memSize = pooltable.maxAddr - minAddr;
Pool* pool = null;
bitpos -= rng.bmplength;
rng.pbase += rng.bmplength;
}
- import core.bitop;
if (!core.bitop.bt(rng.ptrbmp, bitpos))
{
debug(MARK_PRINTF) printf("\t\tskipping non-pointer\n");
printf("\t\tfound pool %p, base=%p, pn = %lld, bin = %d\n", pool, pool.baseAddr, cast(long)pn, bin);
// Adjust bit to be at start of allocated memory block
- if (bin < B_PAGE)
+ if (bin < Bins.B_PAGE)
{
// We don't care abou setting pointsToBase correctly
// because it's ignored for small object pools anyhow.
goto LaddRange;
}
}
- else if (bin == B_PAGE)
+ else if (bin == Bins.B_PAGE)
{
biti = offset >> Pool.ShiftBy.Large;
//debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
goto LaddLargeRange;
}
}
- else if (bin == B_PAGEPLUS)
+ else if (bin == Bins.B_PAGEPLUS)
{
pn -= pool.bPageOffsets[pn];
biti = pn * (PAGESIZE >> Pool.ShiftBy.Large);
else
{
// Don't mark bits in B_FREE pages
- assert(bin == B_FREE);
+ assert(bin == Bins.B_FREE);
}
}
LnextPtr:
{
debug(COLLECT_PRINTF) printf("preparing mark.\n");
- for (size_t n = 0; n < npools; n++)
+ foreach (Pool* pool; this.pooltable[])
{
- Pool* pool = pooltable[n];
if (pool.isLargeObject)
pool.mark.zero();
else
size_t freedLargePages;
size_t freedSmallPages;
size_t freed;
- for (size_t n = 0; n < npools; n++)
+ foreach (Pool* pool; this.pooltable[])
{
size_t pn;
- Pool* pool = pooltable[n];
if (pool.isLargeObject)
{
{
npages = pool.bPageOffsets[pn];
Bins bin = cast(Bins)pool.pagetable[pn];
- if (bin == B_FREE)
+ if (bin == Bins.B_FREE)
{
numFree += npages;
continue;
}
- assert(bin == B_PAGE);
+ assert(bin == Bins.B_PAGE);
size_t biti = pn;
if (!pool.mark.test(biti))
debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
leakDetector.log_free(q, sentinel_size(q, npages * PAGESIZE - SENTINEL_EXTRA));
- pool.pagetable[pn..pn+npages] = B_FREE;
+ pool.pagetable[pn..pn+npages] = Bins.B_FREE;
if (pn < pool.searchStart) pool.searchStart = pn;
freedLargePages += npages;
pool.freepages += npages;
{
Bins bin = cast(Bins)pool.pagetable[pn];
- if (bin < B_PAGE)
+ if (bin < Bins.B_PAGE)
{
auto freebitsdata = pool.freebits.data + pn * PageBits.length;
auto markdata = pool.mark.data + pn * PageBits.length;
{
pool.freeAllPageBits(pn);
- pool.pagetable[pn] = B_FREE;
+ pool.pagetable[pn] = Bins.B_FREE;
// add to free chain
pool.binPageChain[pn] = cast(uint) pool.searchStart;
pool.searchStart = pn;
assert(freedLargePages <= usedLargePages);
usedLargePages -= freedLargePages;
- debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedLargePages, npools);
+ debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n",
+ freed, freedLargePages, this.pooltable.length);
assert(freedSmallPages <= usedSmallPages);
usedSmallPages -= freedSmallPages;
private SmallObjectPool* setNextRecoverPool(Bins bin, size_t poolIndex) nothrow
{
Pool* pool;
- while (poolIndex < npools &&
- ((pool = pooltable[poolIndex]).isLargeObject ||
+ while (poolIndex < this.pooltable.length &&
+ ((pool = this.pooltable[poolIndex]).isLargeObject ||
pool.recoverPageFirst[bin] >= pool.npages))
poolIndex++;
- return recoverPool[bin] = poolIndex < npools ? cast(SmallObjectPool*)pool : null;
+ return recoverPool[bin] = poolIndex < this.pooltable.length ? cast(SmallObjectPool*)pool : null;
}
version (COLLECT_FORK)
import core.stdc.stdlib : _Exit;
debug (PRINTF_TO_FILE)
{
- import core.stdc.stdio : fflush;
fflush(null); // avoid duplicated FILE* output
}
version (OSX)
// init bucket lists
bucket[] = null;
- foreach (Bins bin; 0..B_NUMSMALL)
+ foreach (Bins bin; Bins.B_16 .. Bins.B_NUMSMALL)
setNextRecoverPool(bin, 0);
stop = currTime;
auto pn = offset / PAGESIZE;
auto bins = cast(Bins)pool.pagetable[pn];
size_t biti = void;
- if (bins < B_PAGE)
+ if (bins < Bins.B_PAGE)
{
biti = baseOffset(offset, bins) >> pool.ShiftBy.Small;
// doesn't need to check freebits because no pointer must exist
// to a block that was free before starting the collection
}
- else if (bins == B_PAGE)
+ else if (bins == Bins.B_PAGE)
{
biti = pn * (PAGESIZE >> pool.ShiftBy.Large);
}
- else if (bins == B_PAGEPLUS)
+ else if (bins == Bins.B_PAGEPLUS)
{
pn -= pool.bPageOffsets[pn];
biti = pn * (PAGESIZE >> pool.ShiftBy.Large);
}
- else // bins == B_FREE
+ else // bins == Bins.B_FREE
{
- assert(bins == B_FREE);
+ assert(bins == Bins.B_FREE);
return IsMarked.no;
}
return pool.mark.test(biti) ? IsMarked.yes : IsMarked.no;
/* ============================ Parallel scanning =============================== */
version (COLLECT_PARALLEL):
- import core.sync.event;
+
import core.atomic;
+ import core.cpuid;
+ import core.sync.event;
+
private: // disable invariants for background threads
static struct ScanThreadData
int maxParallelThreads() nothrow
{
- import core.cpuid;
auto threads = threadsPerCPU();
if (threads == 0)
GCBits is_pointer; // precise GC only: per-word, not per-block like the rest of them (SmallObjectPool only)
size_t npages;
size_t freepages; // The number of pages not in use.
- ubyte* pagetable;
+ Bins* pagetable;
bool isLargeObject;
enum PageRecovered = uint.max;
// first of chain of pages to recover (SmallObjectPool only)
- uint[B_NUMSMALL] recoverPageFirst;
+ uint[Bins.B_NUMSMALL] recoverPageFirst;
// precise GC: TypeInfo.rtInfo for allocation (LargeObjectPool only)
immutable(size_t)** rtinfo;
noscan.alloc(nbits);
appendable.alloc(nbits);
- pagetable = cast(ubyte*)cstdlib.malloc(npages);
+ pagetable = cast(Bins*)cstdlib.malloc(npages * Bins.sizeof);
if (!pagetable)
onOutOfMemoryErrorNoGC();
}
}
- memset(pagetable, B_FREE, npages);
+ memset(pagetable, Bins.B_FREE, npages);
this.npages = npages;
this.freepages = npages;
}
public
- @property bool isFree() const pure nothrow
+ @property bool isFree() const scope @safe pure nothrow @nogc
{
return npages == freepages;
}
{
size_t offset = cast(size_t)(p - baseAddr);
size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pagetable[pn];
+ Bins bin = pagetable[pn];
// Adjust bit to be at start of allocated memory block
- if (bin < B_NUMSMALL)
+ if (bin < Bins.B_NUMSMALL)
{
auto baseOff = baseOffset(offset, bin);
const biti = baseOff >> Pool.ShiftBy.Small;
return null;
return baseAddr + baseOff;
}
- if (bin == B_PAGE)
+ if (bin == Bins.B_PAGE)
{
return baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
}
- if (bin == B_PAGEPLUS)
+ if (bin == Bins.B_PAGEPLUS)
{
size_t pageOffset = bPageOffsets[pn];
offset -= pageOffset * PAGESIZE;
return baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
}
// we are in a B_FREE page
- assert(bin == B_FREE);
+ assert(bin == Bins.B_FREE);
return null;
}
{
for (size_t i = 0; i < npages; i++)
{
- Bins bin = cast(Bins)pagetable[i];
- assert(bin < B_MAX);
+ Bins bin = pagetable[i];
+ assert(bin < Bins.B_MAX);
}
}
}
uint np = bPageOffsets[n];
assert(np > 0 && np <= npages - n);
- if (pagetable[n] == B_PAGE)
+ if (pagetable[n] == Bins.B_PAGE)
{
for (uint p = 1; p < np; p++)
{
- assert(pagetable[n + p] == B_PAGEPLUS);
+ assert(pagetable[n + p] == Bins.B_PAGEPLUS);
assert(bPageOffsets[n + p] == p);
}
}
- else if (pagetable[n] == B_FREE)
+ else if (pagetable[n] == Bins.B_FREE)
{
for (uint p = 1; p < np; p++)
{
- assert(pagetable[n + p] == B_FREE);
+ assert(pagetable[n + p] == Bins.B_FREE);
}
assert(bPageOffsets[n + np - 1] == np);
}
//debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n);
size_t largest = 0;
- if (pagetable[searchStart] == B_PAGEPLUS)
+ if (pagetable[searchStart] == Bins.B_PAGEPLUS)
{
searchStart -= bPageOffsets[searchStart]; // jump to B_PAGE
searchStart += bPageOffsets[searchStart];
}
- while (searchStart < npages && pagetable[searchStart] == B_PAGE)
+ while (searchStart < npages && pagetable[searchStart] == Bins.B_PAGE)
searchStart += bPageOffsets[searchStart];
for (size_t i = searchStart; i < npages; )
{
- assert(pagetable[i] == B_FREE);
+ assert(pagetable[i] == Bins.B_FREE);
auto p = bPageOffsets[i];
if (p > n)
if (p == n)
{
L_found:
- pagetable[i] = B_PAGE;
+ pagetable[i] = Bins.B_PAGE;
bPageOffsets[i] = cast(uint) n;
if (n > 1)
{
- memset(&pagetable[i + 1], B_PAGEPLUS, n - 1);
+ memset(&pagetable[i + 1], Bins.B_PAGEPLUS, n - 1);
for (auto offset = 1; offset < n; offset++)
bPageOffsets[i + offset] = cast(uint) offset;
}
largest = p;
i += p;
- while (i < npages && pagetable[i] == B_PAGE)
+ while (i < npages && pagetable[i] == Bins.B_PAGE)
{
// we have the size information, so we skip a whole bunch of pages.
i += bPageOffsets[i];
for (size_t i = pagenum; i < npages + pagenum; i++)
{
- assert(pagetable[i] < B_FREE);
- pagetable[i] = B_FREE;
+ assert(pagetable[i] < Bins.B_FREE);
+ pagetable[i] = Bins.B_FREE;
}
freepages += npages;
largestFree = freepages; // invalidate
*/
void setFreePageOffsets(size_t page, size_t num) nothrow @nogc
{
- assert(pagetable[page] == B_FREE);
- assert(pagetable[page + num - 1] == B_FREE);
+ assert(pagetable[page] == Bins.B_FREE);
+ assert(pagetable[page + num - 1] == Bins.B_FREE);
bPageOffsets[page] = cast(uint)num;
if (num > 1)
bPageOffsets[page + num - 1] = cast(uint)num;
{
static if (bwd)
{
- if (page > 0 && pagetable[page - 1] == B_FREE)
+ if (page > 0 && pagetable[page - 1] == Bins.B_FREE)
{
auto sz = bPageOffsets[page - 1];
page -= sz;
}
static if (fwd)
{
- if (page + num < npages && pagetable[page + num] == B_FREE)
+ if (page + num < npages && pagetable[page + num] == Bins.B_FREE)
num += bPageOffsets[page + num];
}
setFreePageOffsets(page, num);
if (cast(size_t)p & (PAGESIZE - 1)) // check for interior pointer
return 0;
size_t pagenum = pagenumOf(p);
- Bins bin = cast(Bins)pagetable[pagenum];
- if (bin != B_PAGE)
+ Bins bin = pagetable[pagenum];
+ if (bin != Bins.B_PAGE)
return 0;
return bPageOffsets[pagenum];
}
*/
size_t getSize(size_t pn) const nothrow @nogc
{
- assert(pagetable[pn] == B_PAGE);
+ assert(pagetable[pn] == Bins.B_PAGE);
return cast(size_t) bPageOffsets[pn] * PAGESIZE;
}
size_t offset = cast(size_t)(p - baseAddr);
size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pagetable[pn];
+ Bins bin = pagetable[pn];
- if (bin == B_PAGEPLUS)
+ if (bin == Bins.B_PAGEPLUS)
pn -= bPageOffsets[pn];
- else if (bin != B_PAGE)
+ else if (bin != Bins.B_PAGE)
return info; // no info for free pages
info.base = baseAddr + pn * PAGESIZE;
{
foreach (pn; 0 .. npages)
{
- Bins bin = cast(Bins)pagetable[pn];
- if (bin > B_PAGE)
+ Bins bin = pagetable[pn];
+ if (bin > Bins.B_PAGE)
continue;
size_t biti = pn;
size_t n = 1;
for (; pn + n < npages; ++n)
- if (pagetable[pn + n] != B_PAGEPLUS)
+ if (pagetable[pn + n] != Bins.B_PAGEPLUS)
break;
debug (MEMSTOMP) memset(baseAddr + pn * PAGESIZE, 0xF3, n * PAGESIZE);
freePages(pn, n);
{
//base.Invariant();
uint cntRecover = 0;
- foreach (Bins bin; 0 .. B_NUMSMALL)
+ foreach (Bins bin; Bins.B_16 .. Bins.B_NUMSMALL)
{
for (auto pn = recoverPageFirst[bin]; pn < npages; pn = binPageChain[pn])
{
uint cntFree = 0;
for (auto pn = searchStart; pn < npages; pn = binPageChain[pn])
{
- assert(pagetable[pn] == B_FREE);
+ assert(pagetable[pn] == Bins.B_FREE);
cntFree++;
}
assert(cntFree == freepages);
do
{
size_t pagenum = pagenumOf(p);
- Bins bin = cast(Bins)pagetable[pagenum];
- assert(bin < B_PAGE);
+ Bins bin = pagetable[pagenum];
+ assert(bin < Bins.B_PAGE);
if (p != cast(void*)baseOffset(cast(size_t)p, bin)) // check for interior pointer
return 0;
const biti = cast(size_t)(p - baseAddr) >> ShiftBy.Small;
BlkInfo info;
size_t offset = cast(size_t)(p - baseAddr);
size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pagetable[pn];
+ Bins bin = pagetable[pn];
- if (bin >= B_PAGE)
+ if (bin >= Bins.B_PAGE)
return info;
auto base = cast(void*)baseOffset(cast(size_t)p, bin);
{
foreach (pn; 0 .. npages)
{
- Bins bin = cast(Bins)pagetable[pn];
- if (bin >= B_PAGE)
+ Bins bin = pagetable[pn];
+ if (bin >= Bins.B_PAGE)
continue;
immutable size = binsize[bin];
if (searchStart >= npages)
return null;
- assert(pagetable[searchStart] == B_FREE);
+ assert(pagetable[searchStart] == Bins.B_FREE);
L1:
size_t pn = searchStart;
searchStart = binPageChain[searchStart];
binPageChain[pn] = Pool.PageRecovered;
- pagetable[pn] = cast(ubyte)bin;
+ pagetable[pn] = bin;
freepages--;
// Convert page to free list
{
uint nReallyFree;
foreach (i; 0..pool.npages) {
- if (pool.pagetable[i] >= B_FREE) nReallyFree++;
+ if (pool.pagetable[i] >= Bins.B_FREE) nReallyFree++;
}
printf("Pool %p: %d really free, %d supposedly free\n", pool, nReallyFree, pool.freepages);
size_t offset = cast(size_t)(p - pool.baseAddr);
size_t biti;
size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pool.pagetable[pn];
+ Bins bin = pool.pagetable[pn];
biti = (offset & (PAGESIZE - 1)) >> pool.shiftBy;
debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti);
}
assert(p + (260 << 20) == q);
assert(q + (65 << 20) == r);
GC.free(q);
- // should trigger "assert(bin == B_FREE);" in mark due to dangling pointer q:
+ // should trigger "assert(bin == Bins.B_FREE);" in mark due to dangling pointer q:
GC.collect();
// should trigger "break;" in extendNoSync:
size_t sz = GC.extend(p, 64 << 20, 66 << 20); // trigger size after p large enough (but limited)
{
import core.stdc.string : memmove;
-nothrow:
- void Dtor()
+ void Dtor() nothrow @nogc
{
cstdlib.free(pools);
pools = null;
npools = 0;
}
- bool insert(Pool* pool)
+ bool insert(Pool* pool) nothrow @nogc
{
auto newpools = cast(Pool **)cstdlib.realloc(pools, (npools + 1) * pools[0].sizeof);
if (!newpools)
return true;
}
- @property size_t length() pure const
+ @property size_t length() const scope @safe pure nothrow @nogc
{
return npools;
}
- ref inout(Pool*) opIndex(size_t idx) inout pure
+ ref inout(Pool*) opIndex(size_t idx) inout return @trusted pure nothrow @nogc
in { assert(idx < length); }
do
{
return pools[idx];
}
- inout(Pool*)[] opSlice(size_t a, size_t b) inout pure
+ inout(Pool*)[] opSlice(size_t a, size_t b) inout return @trusted pure nothrow @nogc
in { assert(a <= length && b <= length); }
do
{
return pools[a .. b];
}
+ /// Returns: A slice over all pools in this `PoolTable`
+ inout(Pool*)[] opSlice() inout return @trusted pure nothrow @nogc
+ {
+ return this.pools[0 .. this.length];
+ }
+
alias opDollar = length;
/**
* Return null if not in a Pool.
* Assume pooltable[] is sorted.
*/
- Pool *findPool(void *p) nothrow
+ Pool *findPool(void *p) nothrow @nogc
{
if (p >= minAddr && p < maxAddr)
{
}
// semi-stable partition, returns right half for which pred is false
- Pool*[] minimize() pure
+ Pool*[] minimize() pure nothrow @nogc
{
static void swap(ref Pool* a, ref Pool* b)
{
return pools[npools .. len];
}
- void Invariant() const
+ void Invariant() const nothrow @nogc
{
if (!npools) return;
assert(_maxAddr == pools[npools - 1].topAddr);
}
- @property const(void)* minAddr() pure const { return _minAddr; }
- @property const(void)* maxAddr() pure const { return _maxAddr; }
+ @property const(void)* minAddr() const @safe pure nothrow @nogc { return _minAddr; }
+ @property const(void)* maxAddr() const @safe pure nothrow @nogc { return _maxAddr; }
package:
Pool** pools;
{
byte* baseAddr, topAddr;
size_t freepages, npages, ptIndex;
- @property bool isFree() const pure nothrow { return freepages == npages; }
+ @property bool isFree() const scope pure nothrow @nogc { return freepages == npages; }
}
PoolTable!MockPool pooltable;
return instance.query( p );
}
- core.memory.GC.Stats gc_stats() nothrow
+ core.memory.GC.Stats gc_stats() @safe nothrow @nogc
{
return instance.stats();
}
- core.memory.GC.ProfileStats gc_profileStats() nothrow @safe
+ core.memory.GC.ProfileStats gc_profileStats() @safe nothrow @nogc
{
return instance.profileStats();
}
}
extern (C) BlkInfo_ gc_query(return scope void* p) pure nothrow;
- extern (C) GC.Stats gc_stats ( ) nothrow @nogc;
+ extern (C) GC.Stats gc_stats ( ) @safe nothrow @nogc;
extern (C) GC.ProfileStats gc_profileStats ( ) nothrow @nogc @safe;
}
* Returns runtime stats for currently active GC implementation
* See `core.memory.GC.Stats` for list of available metrics.
*/
- static Stats stats() nothrow
+ static Stats stats() @safe nothrow @nogc
{
return gc_stats();
}
///
inout(T)* data() inout @safe { return _Get_data()._Myptr; }
///
- inout(T)[] as_array() inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; }
+ inout(T)[] as_array() return scope inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; }
///
ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; }
///
inout(T)* data() inout @safe { return __get_pointer(); }
///
- inout(T)[] as_array() inout nothrow @trusted { return __get_pointer()[0 .. size()]; }
+ inout(T)[] as_array() return scope inout nothrow @trusted { return __get_pointer()[0 .. size()]; }
///
ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; }
extern(D) @safe @nogc:
pragma(inline, true)
{
- ref inout(Alloc) _Getal() inout pure nothrow { return _Mypair._Myval1; }
- ref inout(ValTy) _Get_data() inout pure nothrow { return _Mypair._Myval2; }
+ ref inout(Alloc) _Getal() return inout pure nothrow { return _Mypair._Myval1; }
+ ref inout(ValTy) _Get_data() return inout pure nothrow { return _Mypair._Myval2; }
}
void _Orphan_all() nothrow { _Get_data._Base._Orphan_all(); }
{
struct stat_t
{
- c_ulong st_dev;
- ino_t st_ino;
+ dev_t st_dev;
+ static if (!__USE_FILE_OFFSET64)
+ {
+ ushort __pad1;
+ ino_t st_ino;
+ }
+ else
+ ino_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
- c_ulong st_rdev;
+ dev_t st_rdev;
+ ushort __pad2;
off_t st_size;
- c_ulong st_blksize;
- c_ulong st_blocks;
- c_ulong st_atime;
- c_ulong st_atime_nsec;
- c_ulong st_mtime;
- c_ulong st_mtime_nsec;
- c_ulong st_ctime;
- c_ulong st_ctime_nsec;
+ blksize_t st_blksize;
+ blkcnt_t st_blocks;
+ static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
+ {
+ timespec st_atim;
+ timespec st_mtim;
+ timespec st_ctim;
+ extern(D) @safe @property inout pure nothrow
+ {
+ ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
+ ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
+ ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
+ }
+ }
+ else
+ {
+ time_t st_atime;
+ c_ulong st_atimensec;
+ time_t st_mtime;
+ c_ulong st_mtimensec;
+ time_t st_ctime;
+ c_ulong st_ctimensec;
+ }
c_ulong __unused4;
c_ulong __unused5;
}
+ static if (__USE_FILE_OFFSET64)
+ static assert(stat_t.sizeof == 104);
+ else
+ static assert(stat_t.sizeof == 88);
}
else version (PPC64)
{
struct stat_t
{
- c_ulong st_dev;
+ dev_t st_dev;
ino_t st_ino;
nlink_t st_nlink;
mode_t st_mode;
uid_t st_uid;
gid_t st_gid;
- c_ulong st_rdev;
+ int __pad2;
+ dev_t st_rdev;
off_t st_size;
- c_ulong st_blksize;
- c_ulong st_blocks;
- c_ulong st_atime;
- c_ulong st_atime_nsec;
- c_ulong st_mtime;
- c_ulong st_mtime_nsec;
- c_ulong st_ctime;
- c_ulong st_ctime_nsec;
+ blksize_t st_blksize;
+ blkcnt_t st_blocks;
+ static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
+ {
+ timespec st_atim;
+ timespec st_mtim;
+ timespec st_ctim;
+ extern(D) @safe @property inout pure nothrow
+ {
+ ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
+ ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
+ ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
+ }
+ }
+ else
+ {
+ time_t st_atime;
+ c_ulong st_atimensec;
+ time_t st_mtime;
+ c_ulong st_mtimensec;
+ time_t st_ctime;
+ c_ulong st_ctimensec;
+ }
c_ulong __unused4;
c_ulong __unused5;
c_ulong __unused6;
}
+ static assert(stat_t.sizeof == 144);
}
else version (RISCV_Any)
{
+/
struct Duration
{
+ /++
+ Converts this `Duration` to a `string`.
+
+ The string is meant to be human readable, not machine parseable (e.g.
+ whether there is an `'s'` on the end of the unit name usually depends on
+ whether it's plural or not, and empty units are not included unless the
+ Duration is `zero`). Any code needing a specific string format should
+ use `total` or `split` to get the units needed to create the desired
+ string format and create the string itself.
+
+ The format returned by toString may or may not change in the future.
+
+ Params:
+ sink = A sink object, expected to be a delegate or aggregate
+ implementing `opCall` that accepts a `scope const(char)[]`
+ as argument.
+ +/
+ void toString (SinkT) (scope SinkT sink) const scope
+ {
+ static immutable units = [
+ "weeks", "days", "hours", "minutes", "seconds", "msecs", "usecs"
+ ];
+
+ static void appListSep(SinkT sink, uint pos, bool last)
+ {
+ if (pos == 0)
+ return;
+ if (!last)
+ sink(", ");
+ else
+ sink(pos == 1 ? " and " : ", and ");
+ }
+
+ static void appUnitVal(string units)(SinkT sink, long val)
+ {
+ immutable plural = val != 1;
+ string unit;
+ static if (units == "seconds")
+ unit = plural ? "secs" : "sec";
+ else static if (units == "msecs")
+ unit = "ms";
+ else static if (units == "usecs")
+ unit = "μs";
+ else
+ unit = plural ? units : units[0 .. $-1];
+ sink(signedToTempString(val));
+ sink(" ");
+ sink(unit);
+ }
+
+ if (_hnsecs == 0)
+ {
+ sink("0 hnsecs");
+ return;
+ }
+
+ long hnsecs = _hnsecs;
+ uint pos;
+ static foreach (unit; units)
+ {
+ if (auto val = splitUnitsFromHNSecs!unit(hnsecs))
+ {
+ appListSep(sink, pos++, hnsecs == 0);
+ appUnitVal!unit(sink, val);
+ }
+ if (hnsecs == 0)
+ return;
+ }
+ if (hnsecs != 0)
+ {
+ appListSep(sink, pos++, true);
+ appUnitVal!"hnsecs"(sink, hnsecs);
+ }
+ }
+
@safe pure:
public:
}
}
-
- /++
- Converts this `Duration` to a `string`.
-
- The string is meant to be human readable, not machine parseable (e.g.
- whether there is an `'s'` on the end of the unit name usually depends on
- whether it's plural or not, and empty units are not included unless the
- Duration is `zero`). Any code needing a specific string format should
- use `total` or `split` to get the units needed to create the desired
- string format and create the string itself.
-
- The format returned by toString may or may not change in the future.
- +/
- string toString() const nothrow pure @safe
+ /// Ditto
+ string toString() const scope nothrow
{
- static void appListSep(ref string res, uint pos, bool last)
- {
- if (pos == 0)
- return;
- if (!last)
- res ~= ", ";
- else
- res ~= pos == 1 ? " and " : ", and ";
- }
-
- static void appUnitVal(string units)(ref string res, long val)
- {
- immutable plural = val != 1;
- string unit;
- static if (units == "seconds")
- unit = plural ? "secs" : "sec";
- else static if (units == "msecs")
- unit = "ms";
- else static if (units == "usecs")
- unit = "μs";
- else
- unit = plural ? units : units[0 .. $-1];
- res ~= signedToTempString(val);
- res ~= " ";
- res ~= unit;
- }
-
- if (_hnsecs == 0)
- return "0 hnsecs";
-
- template TT(T...) { alias T TT; }
- alias units = TT!("weeks", "days", "hours", "minutes", "seconds", "msecs", "usecs");
-
- long hnsecs = _hnsecs; string res; uint pos;
- foreach (unit; units)
- {
- if (auto val = splitUnitsFromHNSecs!unit(hnsecs))
- {
- appListSep(res, pos++, hnsecs == 0);
- appUnitVal!unit(res, val);
- }
- if (hnsecs == 0)
- break;
- }
- if (hnsecs != 0)
- {
- appListSep(res, pos++, true);
- appUnitVal!"hnsecs"(res, hnsecs);
- }
- return res;
+ string result;
+ this.toString((in char[] data) { result ~= data; });
+ return result;
}
///
assert(myTime == 123.msecs);
}
+// Ensure `toString` doesn't allocate if the sink doesn't
+version (CoreUnittest) @safe pure nothrow @nogc unittest
+{
+ char[256] buffer; size_t len;
+ scope sink = (in char[] data) {
+ assert(data.length + len <= buffer.length);
+ buffer[len .. len + data.length] = data[];
+ len += data.length;
+ };
+ auto dur = Duration(-12_096_020_900_003);
+ dur.toString(sink);
+ assert(buffer[0 .. len] == "-2 weeks, -2 secs, -90 ms, and -3 hnsecs");
+}
+
/++
Converts a $(D TickDuration) to the given units as either an integral
value or a floating point value.
/**
* Get the message describing the error.
- * Base behavior is to return the `Throwable.msg` field.
- * Override to return some other error message.
+ *
+ * This getter is an alternative way to access the Exception's message,
+ * with the added advantage of being override-able in subclasses.
+ * Subclasses are hence free to do their own memory managements without
+ * being tied to the requirement of providing a `string` in a field.
+ *
+ * The default behavior is to return the `Throwable.msg` field.
*
* Returns:
- * Error message
+ * A message representing the cause of the `Throwable`
*/
- @__future const(char)[] message() const
+ @__future const(char)[] message() const @safe nothrow
{
return this.msg;
}
-1a3e80ec25afab6123cdcfe20186f36f006b68bb
+41aaf8c2636df0e2e3ad39933b321d2b4cd231fa
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
fileSize = makeUlong(sizeLow, sizeHigh);
return result;
}
- static trustedReadFile(HANDLE hFile, void *lpBuffer, ulong nNumberOfBytesToRead)
+ static trustedReadFile(HANDLE hFile, void *lpBuffer, size_t nNumberOfBytesToRead)
{
// Read by chunks of size < 4GB (Windows API limit)
- ulong totalNumRead = 0;
+ size_t totalNumRead = 0;
while (totalNumRead != nNumberOfBytesToRead)
{
const uint chunkSize = min(nNumberOfBytesToRead - totalNumRead, 0xffff_0000);
}
///
-@system unittest
+@safe unittest
{
auto args = ["prog", "--foo", "-b"];
text = The text to printed at the beginning of the help output.
opt = The `Option` extracted from the `getopt` parameter.
*/
-void defaultGetoptPrinter(string text, Option[] opt)
+void defaultGetoptPrinter(string text, Option[] opt) @safe
{
import std.stdio : stdout;
+ // stdout global __gshared is trusted with a locked text writer
+ auto w = (() @trusted => stdout.lockingTextWriter())();
- defaultGetoptFormatter(stdout.lockingTextWriter(), text, opt);
+ defaultGetoptFormatter(w, text, opt);
}
/** This function writes the passed text and `Option` into an output range
}
/**
- Moves the front of `r` out and returns it. Leaves `r.front` in a
- destroyable state that does not allocate any resources (usually equal
- to its `.init` value).
+ Moves the front of `r` out and returns it.
+
+ If `r.front` is a struct with a destructor or copy constructor defined, it
+ is reset to its `.init` value after its value is moved. Otherwise, it is
+ left unchanged.
+
+ In either case, `r.front` is left in a destroyable state that does not
+ allocate any resources.
*/
ElementType!R moveFront(R)(R r)
{
/**
* Assigns a value to a `SumType`.
*
- * Assigning to a `SumType` is `@system` if any of the
- * `SumType`'s members contain pointers or references, since
- * those members may be reachable through external references,
- * and overwriting them could therefore lead to memory
- * corruption.
+ * If any of the `SumType`'s members other than the one being assigned
+ * to contain pointers or references, it is possible for the assignment
+ * to cause memory corruption (see the
+ * ["Memory corruption" example](#memory-corruption) below for an
+ * illustration of how). Therefore, such assignments are considered
+ * `@system`.
*
* An individual assignment can be `@trusted` if the caller can
- * guarantee that there are no outstanding references to $(I any)
- * of the `SumType`'s members when the assignment occurs.
+ * guarantee that there are no outstanding references to any `SumType`
+ * members that contain pointers or references at the time the
+ * assignment occurs.
+ *
+ * Examples:
+ *
+ * $(DIVID memory-corruption, $(H3 Memory corruption))
+ *
+ * This example shows how assignment to a `SumType` can be used to
+ * cause memory corruption in `@system` code. In `@safe` code, the
+ * assignment `s = 123` would not be allowed.
+ *
+ * ---
+ * SumType!(int*, int) s = new int;
+ * s.tryMatch!(
+ * (ref int* p) {
+ * s = 123; // overwrites `p`
+ * return *p; // undefined behavior
+ * }
+ * );
+ * ---
*/
ref SumType opAssign(T rhs);
}
/**
* Assigns a value to a `SumType`.
*
- * Assigning to a `SumType` is `@system` if any of the `SumType`'s
- * $(I other) members contain pointers or references, since those
- * members may be reachable through external references, and
- * overwriting them could therefore lead to memory corruption.
+ * If any of the `SumType`'s members other than the one being assigned
+ * to contain pointers or references, it is possible for the assignment
+ * to cause memory corruption (see the
+ * ["Memory corruption" example](#memory-corruption) below for an
+ * illustration of how). Therefore, such assignments are considered
+ * `@system`.
*
* An individual assignment can be `@trusted` if the caller can
- * guarantee that, when the assignment occurs, there are no
- * outstanding references to any such members.
+ * guarantee that there are no outstanding references to any `SumType`
+ * members that contain pointers or references at the time the
+ * assignment occurs.
+ *
+ * Examples:
+ *
+ * $(DIVID memory-corruption, $(H3 Memory corruption))
+ *
+ * This example shows how assignment to a `SumType` can be used to
+ * cause memory corruption in `@system` code. In `@safe` code, the
+ * assignment `s = 123` would not be allowed.
+ *
+ * ---
+ * SumType!(int*, int) s = new int;
+ * s.tryMatch!(
+ * (ref int* p) {
+ * s = 123; // overwrites `p`
+ * return *p; // undefined behavior
+ * }
+ * );
+ * ---
*/
ref SumType opAssign(T rhs)
{
}
/// True if `T` is a [SumType] or implicitly converts to one, otherwise false.
-enum bool isSumType(T) = is(T : SumType!Args, Args...);
+template isSumType(T)
+{
+ static if (is(T : SumType!Args, Args...))
+ {
+ enum isSumType = true;
+ }
+ else static if (is(T == struct) && __traits(getAliasThis, T).length > 0)
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21975
+ import std.traits : ReturnType;
+
+ alias AliasThisType = ReturnType!((T t) =>
+ __traits(getMember, t, __traits(getAliasThis, T)[0])
+ );
+ enum isSumType = .isSumType!AliasThisType;
+ }
+ else
+ {
+ enum isSumType = false;
+ }
+}
///
@safe unittest
assert(!isSumType!ContainsSumType);
}
+@safe unittest
+{
+ static struct AliasThisVar(T)
+ {
+ SumType!T payload;
+ alias payload this;
+ }
+
+ static struct AliasThisFunc(T)
+ {
+ SumType!T payload;
+ ref get() { return payload; }
+ alias get this;
+ }
+
+ static assert(isSumType!(AliasThisVar!int));
+ static assert(isSumType!(AliasThisFunc!int));
+}
+
/**
* Calls a type-appropriate function with the value held in a [SumType].
*