global.params.previewIn = value;
global.params.fix16997 = value;
global.params.noSharedAccess = FeatureState::enabled;
+ global.params.safer = FeatureState::enabled;
global.params.rvalueRefParam = FeatureState::enabled;
global.params.inclusiveInContracts = value;
global.params.systemVariables = FeatureState::enabled;
global.params.noSharedAccess = FeatureState::enabled;
break;
+ case OPT_fpreview_safer:
+ global.params.safer = FeatureState::enabled;
+ break;
+
case OPT_fpreview_rvaluerefparam:
global.params.rvalueRefParam = FeatureState::enabled;
break;
-2b89c2909de239bd603d6f36379658fe902667db
+82a5d2a7c4dd3d270537bcede2981e047bfd0e6a
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
| [hdrgen.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/hdrgen.d) | Convert an AST into D source code for `.di` header generation, as well as `-vcg-ast` and error messages |
| [json.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/json.d) | Describe the module in a `.json` file for the `-X` flag |
| [dtoh.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dtoh.d) | C++ header generation from D source files |
-| [disasm86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/backend/disasm86.d) | x86-64 dissassembly generation
+| [disasm86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/backend/x86/disasm86.d) | x86-64 disassembly generation
| [disasmarm.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/backend/arm/disasmarm.d) | AArch64 disassembly generation
### Utility
{
eSink.error(e.loc, "%s `%s` is not `nothrow`", f.kind(), f.toPrettyChars());
if (!f.isDtorDeclaration())
- errorSupplementalInferredAttr(f, 10, false, STC.nothrow_);
+ errorSupplementalInferredAttr(f, 10, false, STC.nothrow_, eSink);
import dmd.expressionsem : checkOverriddenDtor;
f.checkOverriddenDtor(null, e.loc, dd => dd.type.toTypeFunction().isNothrow, "not nothrow");
Type tb = t.toBasetype();
Type typeb = e.type.toBasetype();
- if (e.hexString && !e.committed)
+ if (e.hexString && !e.committed && tb.nextOf().isIntegral)
{
const szx = cast(ubyte) tb.nextOf().size();
if (szx != se.sz && (e.len % szx) == 0)
return;
}
- else if (fd.ident == Id._d_arrayappendT || fd.ident == Id._d_arrayappendTTrace)
- {
- // In expressionsem.d `ea ~= eb` was lowered to `_d_arrayappendT{,Trace}({file, line, funcname}, ea, eb);`.
- // The following code will rewrite it back to `ea ~= eb` and then interpret that expression.
- Expression lhs, rhs;
-
- if (fd.ident == Id._d_arrayappendT)
- {
- assert(e.arguments.length == 2);
- lhs = (*e.arguments)[0];
- rhs = (*e.arguments)[1];
- }
- else
- {
- assert(e.arguments.length == 5);
- lhs = (*e.arguments)[3];
- rhs = (*e.arguments)[4];
- }
-
- auto cae = new CatAssignExp(e.loc, lhs, rhs);
- cae.type = e.type;
-
- result = interpretRegion(cae, istate, CTFEGoal.LValue);
- return;
- }
- else if (fd.ident == Id._d_arrayappendcTX)
- assert(0, "CTFE cannot interpret _d_arrayappendcTX!");
}
else if (auto soe = ecall.isSymOffExp())
{
{
auto se = e1.isStringExp();
// Allow casting a hex string literal to short[], int[] or long[]
- if (se && se.hexString && se.postfix == StringExp.NoPostfix)
+ if (se && se.hexString && se.postfix == StringExp.NoPostfix && e.to.nextOf().isIntegral)
{
const sz = cast(size_t) e.to.nextOf().size;
if ((se.len % sz) != 0)
}
error(e.loc, "array cast from `%s` to `%s` is not supported at compile time", e1.type.toChars(), e.to.toChars());
if (se && se.hexString && se.postfix != StringExp.NoPostfix)
- errorSupplemental(e.loc, "perhaps remove postfix `%s` from hex string",
- (cast(char) se.postfix ~ "\0").ptr);
+ errorSupplemental(e.loc, "perhaps remove postfix `%.*s` from hex string", 1, &se.postfix);
result = CTFEExp.cantexp;
return;
Contract contract;
bool ctfe; /// inside a ctfe-only expression
bool traitsCompiles; /// inside __traits(compile)
-
/// ignore symbol visibility
/// https://issues.dlang.org/show_bug.cgi?id=15907
bool ignoresymbolvisibility;
-
- bool _padding0; // To keep the layout the same as when the old `SCOPE` enum bitflags were used
-
bool inCfile; /// C semantics apply
-
- bool _padding1;
- bool _padding2;
- bool _padding3;
-
bool canFree; /// is on free list
-
bool fullinst; /// fully instantiate templates
bool ctfeBlock; /// inside a `if (__ctfe)` block
bool dip1000; /// dip1000 errors enabled for this scope
if (ed.members.length == 0)
{
- .error(ed.loc, "%s `%s enum `%s` must have at least one member", ed.kind, ed.toPrettyChars, ed.toChars());
+ .error(ed.loc, "%s `%s` enum `%s` must have at least one member", ed.kind, ed.toPrettyChars, ed.toChars());
ed.errors = true;
ed.semanticRun = PASS.semanticdone;
return;
extern (C++):
override:
- void error(const ref Loc loc, const(char)* format, ...)
+ void verror(const ref Loc loc, const(char)* format, va_list ap)
{
- va_list ap;
- va_start(ap, format);
verrorReport(loc, format, ap, ErrorKind.error);
- va_end(ap);
}
- void errorSupplemental(const ref Loc loc, const(char)* format, ...)
+ void verrorSupplemental(const ref Loc loc, const(char)* format, va_list ap)
{
- va_list ap;
- va_start(ap, format);
verrorReportSupplemental(loc, format, ap, ErrorKind.error);
- va_end(ap);
}
- void warning(const ref Loc loc, const(char)* format, ...)
+ void vwarning(const ref Loc loc, const(char)* format, va_list ap)
{
- va_list ap;
- va_start(ap, format);
verrorReport(loc, format, ap, ErrorKind.warning);
- va_end(ap);
}
- void warningSupplemental(const ref Loc loc, const(char)* format, ...)
+ void vwarningSupplemental(const ref Loc loc, const(char)* format, va_list ap)
{
- va_list ap;
- va_start(ap, format);
verrorReportSupplemental(loc, format, ap, ErrorKind.warning);
- va_end(ap);
}
- void deprecation(const ref Loc loc, const(char)* format, ...)
+ void vdeprecation(const ref Loc loc, const(char)* format, va_list ap)
{
- va_list ap;
- va_start(ap, format);
verrorReport(loc, format, ap, ErrorKind.deprecation);
- va_end(ap);
}
- void deprecationSupplemental(const ref Loc loc, const(char)* format, ...)
+ void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap)
{
- va_list ap;
- va_start(ap, format);
verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation);
- va_end(ap);
}
- void message(const ref Loc loc, const(char)* format, ...)
+ void vmessage(const ref Loc loc, const(char)* format, va_list ap)
{
- va_list ap;
- va_start(ap, format);
verrorReport(loc, format, ap, ErrorKind.message);
- va_end(ap);
}
}
module dmd.errorsink;
+import core.stdc.stdarg;
+
import dmd.location;
/***************************************
nothrow:
extern (C++):
- void error(const ref Loc loc, const(char)* format, ...);
+ void verror(const ref Loc loc, const(char)* format, va_list ap);
+ void verrorSupplemental(const ref Loc loc, const(char)* format, va_list ap);
+ void vwarning(const ref Loc loc, const(char)* format, va_list ap);
+ void vwarningSupplemental(const ref Loc loc, const(char)* format, va_list ap);
+ void vmessage(const ref Loc loc, const(char)* format, va_list ap);
+ void vdeprecation(const ref Loc loc, const(char)* format, va_list ap);
+ void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap);
- void errorSupplemental(const ref Loc loc, const(char)* format, ...);
+ void error(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ verror(loc, format, ap);
+ va_end(ap);
+ }
- void warning(const ref Loc loc, const(char)* format, ...);
+ void errorSupplemental(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ verrorSupplemental(loc, format, ap);
+ va_end(ap);
+ }
- void warningSupplemental(const ref Loc loc, const(char)* format, ...);
+ void warning(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vwarning(loc, format, ap);
+ va_end(ap);
+ }
+
+ void warningSupplemental(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vwarningSupplemental(loc, format, ap);
+ va_end(ap);
+ }
+
+ void message(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vmessage(loc, format, ap);
+ va_end(ap);
+ }
- void message(const ref Loc loc, const(char)* format, ...);
+ void deprecation(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vdeprecation(loc, format, ap);
+ va_end(ap);
+ }
- void deprecation(const ref Loc loc, const(char)* format, ...);
+ void deprecationSupplemental(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vdeprecationSupplemental(loc, format, ap);
+ va_end(ap);
+ }
- void deprecationSupplemental(const ref Loc loc, const(char)* format, ...);
+ /**
+ * This will be called to indicate compilation has either
+ * finished or terminated, no more errors are possible - it's
+ * now the time to print any stored errors.
+ *
+ * The default implementation does nothing since most error sinks have no state
+ */
+ void plugSink() {}
}
/*****************************************
extern (C++):
override:
- void error(const ref Loc loc, const(char)* format, ...) { }
+ void verror(const ref Loc loc, const(char)* format, va_list ap) { }
- void errorSupplemental(const ref Loc loc, const(char)* format, ...) { }
+ void verrorSupplemental(const ref Loc loc, const(char)* format, va_list ap) { }
- void warning(const ref Loc loc, const(char)* format, ...) { }
+ void vwarning(const ref Loc loc, const(char)* format, va_list ap) { }
- void warningSupplemental(const ref Loc loc, const(char)* format, ...) { }
+ void vwarningSupplemental(const ref Loc loc, const(char)* format, va_list ap) { }
- void message(const ref Loc loc, const(char)* format, ...) { }
+ void vmessage(const ref Loc loc, const(char)* format, va_list ap) { }
- void deprecation(const ref Loc loc, const(char)* format, ...) { }
+ void vdeprecation(const ref Loc loc, const(char)* format, va_list ap) { }
- void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { }
+ void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap) { }
}
/*****************************************
bool sawErrors;
- void error(const ref Loc loc, const(char)* format, ...) { sawErrors = true; }
+ void verror(const ref Loc loc, const(char)* format, va_list ap) { sawErrors = true; }
}
/*****************************************
extern (C++):
override:
- void error(const ref Loc loc, const(char)* format, ...)
+ void verror(const ref Loc loc, const(char)* format, va_list ap)
{
fputs("Error: ", stderr);
const p = loc.toChars();
//mem.xfree(cast(void*)p); // loc should provide the free()
}
- va_list ap;
- va_start(ap, format);
vfprintf(stderr, format, ap);
fputc('\n', stderr);
- va_end(ap);
}
- void errorSupplemental(const ref Loc loc, const(char)* format, ...) { }
+ void verrorSupplemental(const ref Loc loc, const(char)* format, va_list ap) { }
- void warning(const ref Loc loc, const(char)* format, ...)
+ void vwarning(const ref Loc loc, const(char)* format, va_list ap)
{
fputs("Warning: ", stderr);
const p = loc.toChars();
//mem.xfree(cast(void*)p); // loc should provide the free()
}
- va_list ap;
- va_start(ap, format);
vfprintf(stderr, format, ap);
fputc('\n', stderr);
- va_end(ap);
}
- void warningSupplemental(const ref Loc loc, const(char)* format, ...) { }
+ void vwarningSupplemental(const ref Loc loc, const(char)* format, va_list ap) { }
- void deprecation(const ref Loc loc, const(char)* format, ...)
+ void vdeprecation(const ref Loc loc, const(char)* format, va_list ap)
{
fputs("Deprecation: ", stderr);
const p = loc.toChars();
//mem.xfree(cast(void*)p); // loc should provide the free()
}
- va_list ap;
- va_start(ap, format);
vfprintf(stderr, format, ap);
fputc('\n', stderr);
- va_end(ap);
}
- void message(const ref Loc loc, const(char)* format, ...)
+ void vmessage(const ref Loc loc, const(char)* format, va_list ap)
{
const p = loc.toChars();
if (*p)
//mem.xfree(cast(void*)p); // loc should provide the free()
}
- va_list ap;
- va_start(ap, format);
vfprintf(stderr, format, ap);
fputc('\n', stderr);
- va_end(ap);
}
- void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { }
+ void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap) { }
}
if (!(eb.isMutable || eb2.isMutable))
return;
- if (!tf.isLive && !(sc.useDIP1000 == FeatureState.enabled && sc.func && sc.func.setUnsafe()))
+ if (!tf.isLive && !(sc.useDIP1000 == FeatureState.enabled && sc.func && setFunctionToUnsafe(sc.func)))
return;
if (!gag)
override bool isLvalue()
{
- if (e1.op == EXP.assocArrayLiteral)
- return false;
- if (e1.type.ty == Tsarray ||
- (e1.op == EXP.index && e1.type.ty != Tarray))
+ auto t1b = e1.type.toBasetype();
+ if (t1b.isTypeAArray() || t1b.isTypeSArray() ||
+ (e1.isIndexExp() && t1b != t1b.isTypeDArray()))
{
return e1.isLvalue();
}
f.toPrettyChars());
if (!f.isDtorDeclaration())
- errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_);
+ errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_, global.errorSink);
f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure");
return true;
}
}
-/// Print the reason why `fd` was inferred `@system` as a supplemental error
-/// Params:
-/// fd = function to check
-/// maxDepth = up to how many functions deep to report errors
-/// deprecation = print deprecations instead of errors
-/// stc = storage class of attribute to check
-public void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc)
+/********************************************
+ * Print the reason why `fd` was inferred `@system` as a supplemental error
+ * Params:
+ * fd = function to check
+ * maxDepth = up to how many functions deep to report errors
+ * deprecation = print deprecations instead of errors
+ * stc = storage class of attribute to check
+ * eSink = where the error messages go
+ */
+public void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc, ErrorSink eSink)
{
- auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental;
+ auto errorFunc = deprecation ? &eSink.deprecationSupplemental : &eSink.errorSupplemental;
AttributeViolation* s;
const(char)* attr;
if (!s)
return;
- if (s.fmtStr)
+ if (s.format)
{
errorFunc(s.loc, deprecation ?
"which wouldn't be `%s` because of:" :
if (stc == STC.nogc || stc == STC.pure_)
{
auto f = (cast(Dsymbol) s.arg0).isFuncDeclaration();
- errorFunc(s.loc, s.fmtStr, f.kind(), f.toPrettyChars(), s.arg1 ? s.arg1.toChars() : "");
+ errorFunc(s.loc, s.format, f.kind(), f.toPrettyChars(), s.arg1 ? s.arg1.toChars() : "");
}
else
{
- errorFunc(s.loc, s.fmtStr,
+ errorFunc(s.loc, s.format,
s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : "");
}
}
- else if (auto sa = s.arg0.isDsymbol())
+ else if (s.fd)
{
- if (FuncDeclaration fd2 = sa.isFuncDeclaration())
+ if (maxDepth > 0)
{
- if (maxDepth > 0)
- {
- errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars());
- errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc);
- }
+ errorFunc(s.loc, "which calls `%s`", s.fd.toPrettyChars());
+ errorSupplementalInferredAttr(s.fd, maxDepth - 1, deprecation, stc, eSink);
}
}
+ else
+ assert(0);
}
/*******************************************
sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
prettyChars);
if (!f.isDtorDeclaration)
- errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe);
+ errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe, global.errorSink);
.errorSupplemental(f.loc, "`%s` is declared here", prettyChars);
f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system");
if (sc.func.isSafeBypassingInference())
{
.deprecation(loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars());
- errorSupplementalInferredAttr(f, 10, true, STC.safe);
+ errorSupplementalInferredAttr(f, 10, true, STC.safe, global.errorSink);
}
else if (!sc.func.safetyViolation)
{
import dmd.func : AttributeViolation;
- sc.func.safetyViolation = new AttributeViolation(loc, null, f, null, null);
+ sc.func.safetyViolation = new AttributeViolation(loc, f);
}
}
return false;
sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
if (!f.isDtorDeclaration)
- f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc);
+ f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc, global.errorSink);
}
f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc");
* Params:
* sc = scope
* argumentList = arguments to function
- * reportErrors = whether or not to report errors here. Some callers are not
+ * eSink = if not null, used to report errors. Some callers are not
* checking actual function params, so they'll do their own error reporting
* Returns:
* `true` when a semantic error occurred
*/
-private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, const bool reportErrors = true)
+private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, ErrorSink eSink)
{
Expressions* exps = argumentList.arguments;
if (!exps)
if (arg.op == EXP.type)
{
- if (reportErrors)
+ if (eSink)
{
- error(arg.loc, "cannot pass type `%s` as a function argument", arg.toChars());
+ eSink.error(arg.loc, "cannot pass type `%s` as a function argument", arg.toChars());
arg = ErrorExp.get();
}
err = true;
}
else if (arg.type.toBasetype().ty == Tfunction)
{
- if (reportErrors)
+ if (eSink)
{
- error(arg.loc, "cannot pass function `%s` as a function argument", arg.toChars());
+ eSink.error(arg.loc, "cannot pass function `%s` as a function argument", arg.toChars());
arg = ErrorExp.get();
}
err = true;
if (argumentList.names)
{
- const(char)* msg = null;
- auto resolvedArgs = tf.resolveNamedArgs(argumentList, &msg);
+ OutBuffer buf;
+ auto resolvedArgs = tf.resolveNamedArgs(argumentList, &buf);
if (!resolvedArgs)
{
// while errors are usually already caught by `tf.callMatch`,
// this can happen when calling `typeof(freefunc)`
- if (msg)
- error(loc, "%s", msg);
+ if (buf.length)
+ error(loc, "%s", buf.peekChars());
return true;
}
// note: the argument list should be mutated with named arguments / default arguments,
{
return setError();
}
- if (preFunctionParameters(sc, exp.argumentList))
+ if (preFunctionParameters(sc, exp.argumentList, global.errorSink))
{
return setError();
}
auto tiargs = new Objects();
tiargs.push(exp.newtype);
- id = new DotTemplateInstanceExp(exp.loc, id, Id._d_newThrowable, tiargs);
- id = new CallExp(exp.loc, id).expressionSemantic(sc);
- Expression idVal;
- Expression tmp = extractSideEffect(sc, "__tmpThrowable", idVal, id, true);
- // auto castTmp = new CastExp(exp.loc, tmp, exp.type);
+ id = new DotTemplateInstanceExp(exp.loc, id, Id._d_newThrowable, tiargs);
- auto ctor = new DotIdExp(exp.loc, tmp, Id.ctor).expressionSemantic(sc);
- auto ctorCall = new CallExp(exp.loc, ctor, exp.arguments);
+ id = new CallExp(exp.loc, id).expressionSemantic(sc);
- id = Expression.combine(idVal, exp.argprefix).expressionSemantic(sc);
- id = Expression.combine(id, ctorCall).expressionSemantic(sc);
- // id = Expression.combine(id, castTmp).expressionSemantic(sc);
+ exp.lowering = id.expressionSemantic(sc);
- result = id.expressionSemantic(sc);
+ result = exp;
return;
}
else if (sc.needsCodegen() && // interpreter doesn't need this lowered
if (exp.fd.ident != Id.empty)
return;
- const(char)[] s;
+ string s;
if (exp.fd.fes)
s = "__foreachbody";
else if (exp.fd.tok == TOK.reserved)
symtab = sds.symtab;
}
assert(symtab);
- Identifier id = Identifier.generateId(s, symtab.length() + 1);
+ Identifier id = Identifier.generateIdWithLoc(s, exp.loc);
exp.fd.ident = id;
if (exp.td)
exp.td.ident = id;
if (FuncExp fe = exp.e1.isFuncExp())
{
if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) ||
- preFunctionParameters(sc, exp.argumentList))
+ preFunctionParameters(sc, exp.argumentList, global.errorSink))
return setError();
// Run e1 semantic even if arguments have any errors
return;
}
if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) ||
- preFunctionParameters(sc, exp.argumentList))
+ preFunctionParameters(sc, exp.argumentList, global.errorSink))
return setError();
// Check for call operator overload
"cast from `%s` to `%s` not allowed in safe code", exp.e1.type, exp.to))
{
if (msg.length)
- errorSupplemental(exp.loc, "%s", (msg ~ '\0').ptr);
+ errorSupplemental(exp.loc, "%.*s", msg.fTuple.expand);
return setError();
}
}
"cast from `%s` to `%s` not allowed in safe code", exp.e1.type, exp.to);
// if message was printed
if (sc.func && sc.func.isSafeBypassingInference() && !sc.isDeprecated())
- deprecationSupplemental(exp.loc, "%s", (msg ~ '\0').ptr);
+ deprecationSupplemental(exp.loc, "%.*s", msg.fTuple.expand);
if (err)
return setError();
}
return;
}
- override void visit(ShlExp exp)
+ private void visitShift(BinExp exp)
{
- //printf("ShlExp::semantic(), type = %p\n", type);
if (exp.type)
{
result = exp;
result = exp.incompatibleTypes();
return;
}
+
exp.e1 = integralPromotions(exp.e1, sc);
if (exp.e2.type.toBasetype().ty != Tvector)
- exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt);
+ {
+ Type tb1 = exp.e1.type.toBasetype();
+ exp.e2 = exp.e2.castTo(sc, tb1.ty == Tvector ? tb1 : Type.tshiftcnt);
+ }
exp.type = exp.e1.type;
result = exp;
}
+ override void visit(ShlExp exp)
+ {
+ visitShift(exp);
+ }
override void visit(ShrExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
-
- if (Expression ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression e = exp.op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
- return setError();
-
- if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype()))
- {
- result = exp.incompatibleTypes();
- return;
- }
- exp.e1 = integralPromotions(exp.e1, sc);
- if (exp.e2.type.toBasetype().ty != Tvector)
- exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt);
-
- exp.type = exp.e1.type;
- result = exp;
+ visitShift(exp);
}
-
override void visit(UshrExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
-
- if (Expression ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression e = exp.op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
- return setError();
-
- if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype()))
- {
- result = exp.incompatibleTypes();
- return;
- }
- exp.e1 = integralPromotions(exp.e1, sc);
- if (exp.e2.type.toBasetype().ty != Tvector)
- exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt);
-
- exp.type = exp.e1.type;
- result = exp;
+ visitShift(exp);
}
- override void visit(AndExp exp)
+ private void visitBinaryBitOp(BinExp exp)
{
if (exp.type)
{
result = exp;
}
+ override void visit(AndExp exp)
+ {
+ visitBinaryBitOp(exp);
+ }
override void visit(OrExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
-
- if (Expression ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression e = exp.op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool)
- {
- exp.type = exp.e1.type;
- result = exp;
- return;
- }
-
- if (Expression ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- Type tb = exp.type.toBasetype();
- if (tb.ty == Tarray || tb.ty == Tsarray)
- {
- if (!isArrayOpValid(exp))
- {
- result = arrayOpInvalidError(exp);
- return;
- }
- result = exp;
- return;
- }
- if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
- {
- result = exp.incompatibleTypes();
- return;
- }
- if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
- return setError();
-
- result = exp;
+ visitBinaryBitOp(exp);
}
-
override void visit(XorExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
-
- if (Expression ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression e = exp.op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool)
- {
- exp.type = exp.e1.type;
- result = exp;
- return;
- }
-
- if (Expression ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- Type tb = exp.type.toBasetype();
- if (tb.ty == Tarray || tb.ty == Tsarray)
- {
- if (!isArrayOpValid(exp))
- {
- result = arrayOpInvalidError(exp);
- return;
- }
- result = exp;
- return;
- }
- if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
- {
- result = exp.incompatibleTypes();
- return;
- }
- if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
- return setError();
-
- result = exp;
+ visitBinaryBitOp(exp);
}
override void visit(LogicalExp exp)
{
return false;
}
+ else if (sc._module.ident == Id.atomic && sc._module.parent !is null)
+ {
+ // Allow core.internal.atomic, it is an compiler implementation for a given platform module.
+ // It is then exposed by other modules such as core.atomic and core.stdc.atomic.
+ // This is available as long as druntime is on the import path and the platform supports that operation.
+
+ // https://issues.dlang.org/show_bug.cgi?id=24846
+
+ Package parent = sc._module.parent.isPackage();
+ if (parent !is null)
+ {
+ // This can be easily converted over to apply to core.atomic and core.internal.atomic
+ if (parent.ident == Id.internal)
+ {
+ parent = parent.parent.isPackage();
+
+ if (parent !is null && parent.ident == Id.core && parent.parent is null)
+ return false;
+ }
+ }
+ }
//printf("checkSharedAccess() `%s` returnRef: %d\n", e.toChars(), returnRef);
Scope scx;
scx.eSink = global.errorSink;
scx._module = sd.getModule();
- getTypeInfoType(sd.loc, t, &scx);
- sd.requestTypeInfo = true;
+ if (global.params.useTypeInfo)
+ {
+ getTypeInfoType(sd.loc, t, &scx);
+ sd.requestTypeInfo = true;
+ }
}
else if (!sc.minst)
{
*
* Params:
* loc = location of action
- * fmt = format string for error message
+ * format = format string for error message
* arg0 = (optional) argument to format string
*/
- extern (D) final void setThrow(Loc loc, const(char)* fmt, RootObject arg0 = null)
+ extern (D) final void setThrow(Loc loc, const(char)* format, RootObject arg0 = null)
{
if (nothrowInprocess && !nothrowViolation)
{
- nothrowViolation = new AttributeViolation(loc, fmt, arg0); // action that requires GC
+ assert(format);
+ nothrowViolation = new AttributeViolation(loc, format, arg0); // action that requires GC
}
}
* The function calls non-`nothrow` function f, register that in case nothrow is being inferred
* Params:
* loc = location of call
- * f = function being called
+ * fd = function being called
*/
- extern (D) final void setThrowCall(Loc loc, FuncDeclaration f)
+ extern (D) final void setThrowCall(Loc loc, FuncDeclaration fd)
{
- return setThrow(loc, null, f);
+ if (nothrowInprocess && !nothrowViolation)
+ {
+ nothrowViolation = new AttributeViolation(loc, fd); // action that requires GC
+ }
}
/****************************************
/// The `FunctionDeclaration` is then stored in `arg0` and `fmtStr` must be `null`.
struct AttributeViolation
{
- /// location of error
- Loc loc = Loc.init;
- /// printf-style format string
- const(char)* fmtStr = null;
- /// Arguments for up to two `%s` format specifiers in format string
- RootObject arg0 = null;
- /// ditto
- RootObject arg1 = null;
- /// ditto
- RootObject arg2 = null;
+ Loc loc; /// location of error
+
+ FuncDeclaration fd; /// function is the focus of the violation
+
+ // -- OR --
+
+ const(char)* format; /// printf-style format string
+ RootObject arg0; /// Arguments for up to two `%s` format specifiers in format string
+ RootObject arg1; /// ditto
+ RootObject arg2; /// ditto
+
+ this(ref Loc loc, FuncDeclaration fd) { this.loc = loc; this.fd = fd; }
+
+ this(ref Loc loc, const(char)* format, RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
+ {
+ assert(format);
+ this.loc = loc;
+ this.format = format;
+ this.arg0 = arg0;
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ }
}
if (fmt)
fd.pureViolation = new AttributeViolation(loc, fmt, fd, arg0); // impure action
else if (arg0)
- fd.pureViolation = new AttributeViolation(loc, fmt, arg0); // call to impure function
+ {
+ if (auto sa = arg0.isDsymbol())
+ {
+ if (FuncDeclaration fd2 = sa.isFuncDeclaration())
+ {
+ fd.pureViolation = new AttributeViolation(loc, fd2); // call to impure function
+ }
+ }
+ }
if (fd.fes)
fd.fes.func.setImpure(loc, fmt, arg0);
// https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a
// https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html
// Implementation: https://github.com/dlang/dmd/pull/9817
+ FeatureState safer; // safer by default (more @safe checks in unattributed code)
+ // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md
FeatureState noSharedAccess; // read/write access to shared memory objects
bool previewIn; // `in` means `[ref] scope const`, accepts rvalues
bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract
return major * 1000 + minor;
}
+ /**
+ * Indicate to stateful error sinks that no more errors can be produced.
+ * This is to support error sinks that collect information to produce a
+ * single (say) report.
+ */
+ extern(C++) void plugErrorSinks()
+ {
+ global.errorSink.plugSink();
+ global.errorSinkNull.plugSink();
+ }
+
/**
Returns: the version as the number that would be returned for __VERSION__
*/
// https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a
// https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html
// Implementation: https://github.com/dlang/dmd/pull/9817
+ FeatureState safer; // safer by default (more @safe checks in unattributed code)
+ // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md
+
FeatureState noSharedAccess; // read/write access to shared memory objects
d_bool previewIn; // `in` means `[ref] scope const`, accepts rvalues
d_bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract
void _init();
+ /**
+ * Indicate to stateful error sinks that no more errors can be produced.
+ * This is to support error sinks that collect information to produce a
+ * single (say) report.
+ */
+ void plugErrorSinks();
+
/**
Returns: the version as the number that would be returned for __VERSION__
*/
// Builtin functions
{ "std" },
{ "core" },
+ { "internal" },
{ "config" },
{ "c_complex_float" },
{ "c_complex_double" },
{ "isRef" },
{ "isOut" },
{ "isLazy" },
+ { "isCOMClass" },
{ "hasMember" },
{ "identifier" },
{ "fullyQualifiedName" },
+ { "getBitfieldOffset" },
+ { "getBitfieldWidth" },
{ "getProtection" },
{ "getVisibility" },
{ "parent" },
import core.stdc.stdio;
-import dmd.astenums;
-import dmd.mtype;
-import dmd.expression;
-import dmd.globals;
+import dmd.astenums : Tdchar;
+import dmd.mtype : Type;
+import dmd.globals : uinteger_t;
private uinteger_t copySign(uinteger_t x, bool sign) @safe
{
static IntRange fromType(Type type, bool isUnsigned)
{
- if (!type.isIntegral() || type.toBasetype().ty == Tvector)
+ if (!type.isIntegral() || type.toBasetype().isTypeVector())
return widest();
uinteger_t mask = type.sizemask();
IntRange _cast(Type type)
{
- if (!type.isIntegral() || type.toBasetype().ty == Tvector)
+ if (!type.isIntegral() || type.toBasetype().isTypeVector())
return this;
else if (!type.isUnsigned())
return castSigned(type.sizemask());
IntRange castUnsigned(Type type)
{
- if (!type.isIntegral() || type.toBasetype().ty == Tvector)
+ if (!type.isIntegral() || type.toBasetype().isTypeVector())
return castUnsigned(ulong.max);
else if (type.toBasetype().ty == Tdchar)
return castDchar();
union_ = true;
}
- ref const(IntRange) dump(const(char)* funcName, Expression e) const return
+ ref const(IntRange) dump(Exp)(const(char)* funcName, Exp e) const return
{
printf("[(%c)%#018llx, (%c)%#018llx] @ %s ::: %s\n",
imin.negative?'-':'+', cast(ulong)imin.value,
return widest();
// Don't treat the whole range as divide by 0 if only one end of a range is 0.
- // Issue 15289
+ // https://issues.dlang.org/show_bug.cgi?id=15289
if (rhs.imax.value == 0)
{
rhs.imax.value--;
{
return IntRange(imin / rhs.imax, imax / rhs.imin);
}
+ else if (rhs.imin.negative && !rhs.imax.negative) // divisor spans [-1, 0, 1]
+ {
+ SignExtendedNumber[4] bdy = [-imin, imin, -imax, imax];
+ return IntRange.fromNumbers4(bdy.ptr);
+ }
else
{
// [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)]
if (ndigits != 2 && !utf_isValidDchar(v))
{
error(loc, "invalid UTF character \\U%08x", v);
+ if (v >= 0xD800 && v <= 0xDFFF)
+ errorSupplemental("The code unit is a UTF-16 surrogate, is the escape UTF-16 not a Unicode code point?");
v = '?'; // recover with valid UTF character
}
}
eSink.error(loc, format, args);
}
+ void errorSupplemental(T...)(const(char)* format, T args)
+ {
+ eSink.errorSupplemental(token.loc, format, args);
+ }
+
void deprecation(T...)(const ref Loc loc, const(char)* format, T args)
{
eSink.deprecation(loc, format, args);
if (*q != ct)
break;
}
- /* Remove leading spaces until start of the comment
+ /* Remove leading line feed or space
*/
int linestart = 0;
if (ct == '/')
{
- while (q < qend && (*q == ' ' || *q == '\t'))
+ if (q < qend && *q == ' ') {
++q;
+ }
}
else if (q < qend)
{
import core.stdc.stdarg;
string expected;
+ string expectedSupplemental;
bool gotError;
- void error(const ref Loc loc, const(char)* format, ...)
+ void verror(const ref Loc loc, const(char)* format, va_list ap)
{
gotError = true;
char[100] buffer = void;
+ auto actual = buffer[0 .. vsnprintf(buffer.ptr, buffer.length, format, ap)];
+ assert(expected == actual);
+ }
+
+ void errorSupplemental(const ref Loc loc, const(char)* format, ...)
+ {
+ gotError = true;
+ char[128] buffer = void;
va_list ap;
va_start(ap, format);
auto actual = buffer[0 .. vsnprintf(buffer.ptr, buffer.length, format, ap)];
va_end(ap);
- assert(expected == actual);
+ assert(expectedSupplemental == actual);
}
}
ErrorSinkTest errorSink = new ErrorSinkTest;
- void test(string sequence, string expectedError, dchar expectedReturnValue, uint expectedScanLength, bool Ccompile = false)
+ void test2(string sequence, string[2] expectedError, dchar expectedReturnValue, uint expectedScanLength, bool Ccompile = false)
{
- errorSink.expected = expectedError;
+ errorSink.expected = expectedError[0];
+ errorSink.expectedSupplemental = expectedError[1];
errorSink.gotError = false;
auto p = cast(const(char)*)sequence.ptr;
Lexer lexer = new Lexer(errorSink);
assert(expectedScanLength == actualScanLength);
}
+ void test(string sequence, string expectedError, dchar expectedReturnValue, uint expectedScanLength, bool Ccompile = false)
+ {
+ test2(sequence, [expectedError, null], expectedReturnValue, expectedScanLength, Ccompile);
+ }
+
test("c", `undefined escape sequence \c`, 'c', 1);
test("!", `undefined escape sequence \!`, '!', 1);
test(""", `undefined escape sequence \&`, '&', 1, true);
test("U0001f6" , `escape hex sequence has 6 hex digits instead of 8`, 0x0001f6, 7);
test("U0001f60", `escape hex sequence has 7 hex digits instead of 8`, 0x0001f60, 8);
- test("ud800" , `invalid UTF character \U0000d800`, '?', 5);
- test("udfff" , `invalid UTF character \U0000dfff`, '?', 5);
test("U00110000", `invalid UTF character \U00110000`, '?', 9);
test("xg0" , `undefined escape hex sequence \xg`, 'g', 2);
test(""", `unterminated named entity "`, '?', 5);
test("400", `escape octal sequence \400 is larger than \377`, 0x100, 3);
+
+ test2("uD800", [`invalid UTF character \U0000d800`, `The code unit is a UTF-16 surrogate, is the escape UTF-16 not a Unicode code point?`], '?', 5);
+ test2("uDFFF", [`invalid UTF character \U0000dfff`, `The code unit is a UTF-16 surrogate, is the escape UTF-16 not a Unicode code point?`], '?', 5);
}
unittest
return linkage == LINK.d && parameterList.varargs == VarArg.variadic;
}
- extern(D) static const(char)* getMatchError(A...)(const(char)* format, A args)
+ /*********************************
+ * Append error message to buf.
+ * Input:
+ * buf = message sink
+ * format = printf format
+ */
+ extern(C) static void getMatchError(ref OutBuffer buf, const(char)* format, ...)
{
if (global.gag && !global.params.v.showGaggedErrors)
- return null;
- OutBuffer buf;
- buf.printf(format, args);
- return buf.extractChars();
+ return;
+ va_list ap;
+ va_start(ap, format);
+ buf.vprintf(format, ap);
+ va_end(ap);
}
/********************************
*
* Params:
* argumentList = array of function arguments
- * pMessage = address to store error message, or `null`
+ * buf = if not null, append error message to it
* Returns: re-ordered argument list, or `null` on error
*/
- extern(D) Expressions* resolveNamedArgs(ArgumentList argumentList, const(char)** pMessage)
+ extern(D) Expressions* resolveNamedArgs(ArgumentList argumentList, OutBuffer* buf)
{
Expression[] args = argumentList.arguments ? (*argumentList.arguments)[] : null;
Identifier[] names = argumentList.names ? (*argumentList.names)[] : null;
const pi = findParameterIndex(name);
if (pi == -1)
{
- if (pMessage)
- *pMessage = getMatchError("no parameter named `%s`", name.toChars());
+ if (buf)
+ getMatchError(*buf, "no parameter named `%s`", name.toChars());
return null;
}
ci = pi;
if (!isVariadic)
{
// Without named args, let the caller diagnose argument overflow
- if (hasNamedArgs && pMessage)
- *pMessage = getMatchError("argument `%s` goes past end of parameter list", arg.toChars());
+ if (hasNamedArgs && buf)
+ getMatchError(*buf, "argument `%s` goes past end of parameter list", arg.toChars());
return null;
}
while (ci >= newArgs.length)
if ((*newArgs)[ci])
{
- if (pMessage)
- *pMessage = getMatchError("parameter `%s` assigned twice", parameterList[ci].toChars());
+ if (buf)
+ getMatchError(*buf, "parameter `%s` assigned twice", parameterList[ci].toChars());
return null;
}
(*newArgs)[ci++] = arg;
if (this.incomplete)
continue;
- if (pMessage)
- *pMessage = getMatchError("missing argument for parameter #%d: `%s`",
+ if (buf)
+ getMatchError(*buf, "missing argument for parameter #%d: `%s`",
i + 1, parameterToChars(parameterList[i], this, false));
return null;
}
if (fmt)
fd.nogcViolation = new AttributeViolation(loc, fmt, fd, arg0); // action that requires GC
else if (arg0)
- fd.nogcViolation = new AttributeViolation(loc, fmt, arg0); // call to non-@nogc function
+ {
+ if (auto sa = arg0.isDsymbol())
+ {
+ if (FuncDeclaration fd2 = sa.isFuncDeclaration())
+ {
+ fd.nogcViolation = new AttributeViolation(loc, fd2); // call to non-@nogc function
+ }
+ }
+ }
fd.type.toTypeFunction().isNogc = false;
if (fd.fes)
{
case TOK.identifier:
{
-
if (commaExpected)
error("comma expected separating field initializers");
const t = peek(&token);
default:
if (commaExpected)
error("comma expected separating field initializers");
+ const t = peek(&token);
+ if (t.value == TOK.colon)
+ {
+ error("incorrect syntax for associative array, expected `[]`, found `{}`");
+ while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
+ {
+ nextToken();
+ }
+ if (token.value == TOK.rightCurly)
+ {
+ nextToken();
+ }
+ break;
+ }
auto value = parseInitializer();
_is.addInit(null, value);
commaExpected = true;
error("expression expected, not `%s`", token.toChars());
Lerr:
// Anything for e, as long as it's not NULL
- e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
+ e = AST.ErrorExp.get();
nextToken();
break;
}
~this() pure nothrow
{
debug (stomp) memset(data.ptr, 0xFF, data.length);
- if (data.ptr != &smallarray[0])
+ if (data.ptr && data.ptr != &smallarray[0])
mem.xfree(data.ptr);
}
///returns elements comma separated in []
import dmd.dclass;
import dmd.declaration;
import dmd.dscope;
+import dmd.dsymbol;
import dmd.dsymbolsem : determineSize;
import dmd.errors;
import dmd.expression;
import dmd.func;
import dmd.funcsem : isRootTraitsCompilesScope;
-import dmd.globals : FeatureState;
+import dmd.globals : FeatureState, global;
import dmd.id;
import dmd.identifier;
import dmd.location;
return false;
}
+/**************************************
+ * Safer D adds safety checks to functions with the default
+ * trust setting.
+ */
+bool isSaferD(FuncDeclaration fd)
+{
+ return fd.type.toTypeFunction().trust == TRUST.default_ &&
+ global.params.safer == FeatureState.enabled;
+}
+
bool isSafe(FuncDeclaration fd)
{
if (fd.safetyInprocess)
- fd.setUnsafe();
+ setFunctionToUnsafe(fd);
return fd.type.toTypeFunction().trust == TRUST.safe;
}
bool isTrusted(FuncDeclaration fd)
{
if (fd.safetyInprocess)
- fd.setUnsafe();
+ setFunctionToUnsafe(fd);
return fd.type.toTypeFunction().trust == TRUST.trusted;
}
-/**************************************
- * The function is doing something unsafe, so mark it as unsafe.
- *
+/*****************************************************
+ * Report safety violation for function `fd`, or squirrel away
+ * error message in fd.safetyViolation if needed later.
+ * Call when `fd` was just inferred to be @system OR
+ * `fd` was @safe and an tried something unsafe.
* Params:
- * fd = func declaration to set unsafe
- * gag = surpress error message (used in escape.d)
- * loc = location of error
- * fmt = printf-style format string
+ * fd = function we're gonna rat on
+ * gag = suppress error message (used in escape.d)
+ * loc = location of error
+ * format = printf-style format string
* arg0 = (optional) argument for first %s format specifier
* arg1 = (optional) argument for second %s format specifier
* arg2 = (optional) argument for third %s format specifier
- * Returns: whether there's a safe error
*/
-extern (D) bool setUnsafe(
- FuncDeclaration fd,
- bool gag = false, Loc loc = Loc.init, const(char)* fmt = null,
- RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
+extern (D) void reportSafeError(FuncDeclaration fd, bool gag, Loc loc,
+ const(char)* format = null, RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
+{
+ if (fd.type.toTypeFunction().trust == TRUST.system) // function was just inferred to be @system
+ {
+ if (format)
+ fd.safetyViolation = new AttributeViolation(loc, format, arg0, arg1, arg2);
+ else if (arg0)
+ {
+ if (FuncDeclaration fd2 = (cast(Dsymbol) arg0).isFuncDeclaration())
+ {
+ fd.safetyViolation = new AttributeViolation(loc, fd2); // call to non-@nogc function
+ }
+ }
+ }
+ else if (fd.isSafe() || fd.isSaferD())
+ {
+ if (!gag && format)
+ .error(loc, format, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
+ }
+}
+
+
+/**********************************************
+ * Function is doing something unsafe. If inference
+ * is in process, commit the function to be @system.
+ * Params:
+ * fd = the naughty function
+ * Returns:
+ * true if this is a safe function and so an error OR is inferred to be @system,
+ * false otherwise.
+ */
+extern (D) bool setFunctionToUnsafe(FuncDeclaration fd)
{
if (fd.safetyInprocess)
{
fd.safetyInprocess = false;
fd.type.toTypeFunction().trust = TRUST.system;
- if (fmt || arg0)
- fd.safetyViolation = new AttributeViolation(loc, fmt, arg0, arg1, arg2);
if (fd.fes)
- fd.fes.func.setUnsafe();
- }
- else if (fd.isSafe())
- {
- if (!gag && fmt)
- .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
-
+ setFunctionToUnsafe(fd.fes.func);
return true;
}
+ else if (fd.isSafe() || fd.isSaferD())
+ return true;
return false;
}
+
/**************************************
* The function is calling `@system` function `f`, so mark it as unsafe.
*
*/
extern (D) bool setUnsafeCall(FuncDeclaration fd, FuncDeclaration f)
{
- return fd.setUnsafe(false, f.loc, null, f, null);
+ if (setFunctionToUnsafe(fd))
+ {
+ reportSafeError(fd, false, f.loc, null, f, null);
+ return fd.isSafe();
+ }
+ return false;
}
/**************************************
* sc = scope that the unsafe statement / expression is in
* gag = surpress error message (used in escape.d)
* loc = location of error
- * fmt = printf-style format string
+ * format = printf-style format string
* arg0 = (optional) argument for first %s format specifier
* arg1 = (optional) argument for second %s format specifier
* arg2 = (optional) argument for third %s format specifier
- * Returns: whether there's a safe error
+ * Returns: whether there is a safe error
*/
bool setUnsafe(Scope* sc,
- bool gag = false, Loc loc = Loc.init, const(char)* fmt = null,
+ bool gag = false, Loc loc = Loc.init, const(char)* format = null,
RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
{
if (sc.intypeof)
{
if (sc.varDecl.storage_class & STC.safe)
{
- .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
+ .error(loc, format, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
return true;
}
else if (!(sc.varDecl.storage_class & STC.trusted))
{
// Message wil be gagged, but still call error() to update global.errors and for
// -verrors=spec
- .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
+ .error(loc, format, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
return true;
}
return false;
}
- return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2);
+ if (setFunctionToUnsafe(sc.func))
+ {
+ if (format || arg0)
+ {
+ reportSafeError(sc.func, gag, loc, format, arg0, arg1, arg2);
+ }
+ return sc.func.isSafe(); // it is only an error if in an @safe function
+ }
+ return false;
}
/***************************************
* fs = feature state from the preview flag
* gag = surpress error message
* loc = location of error
- * msg = printf-style format string
+ * format = printf-style format string
* arg0 = (optional) argument for first %s format specifier
* arg1 = (optional) argument for second %s format specifier
* arg2 = (optional) argument for third %s format specifier
* Returns: whether an actual safe error (not deprecation) occured
*/
-bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg,
+bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* format,
RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
{
- //printf("setUnsafePreview() fs:%d %s\n", fs, msg);
+ //printf("setUnsafePreview() fs:%d %s\n", fs, fmt);
+ assert(format);
with (FeatureState) final switch (fs)
{
case disabled:
return false;
case enabled:
- return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2);
+ return sc.setUnsafe(gag, loc, format, arg0, arg1, arg2);
case default_:
if (!sc.func)
{
if (!gag && !sc.isDeprecated())
{
- deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
+ deprecation(loc, format, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
}
}
else if (!sc.func.safetyViolation)
{
import dmd.func : AttributeViolation;
- sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2);
+ sc.func.safetyViolation = new AttributeViolation(loc, format, arg0, arg1, arg2);
}
return false;
}
import dmd.parse;
import dmd.common.outbuffer;
import dmd.root.string;
-import dmd.safe : isSafe, setUnsafe;
+import dmd.safe : isSafe, isSaferD, setUnsafe;
import dmd.semantic2;
import dmd.sideeffect;
import dmd.statement;
Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
- /* Check for inference errors
- */
+ /* Check for inference errors and apply mutability checks inline */
if (!inferApplyArgTypes(fs, sc, sapply))
{
- /**
- Try and extract the parameter count of the opApply callback function, e.g.:
- int opApply(int delegate(int, float)) => 2 args
- */
bool foundMismatch = false;
size_t foreachParamCount = 0;
if (sapplyOld)
auto tf = fparam.type.nextOf().isTypeFunction();
foreachParamCount = tf.parameterList.length;
foundMismatch = true;
+
+ // Mutability check
+ if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst())
+ {
+ // First error: The call site
+ error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object",
+ fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars());
+
+ // Second error: Suggest how to fix
+ errorSupplemental(fd.loc, "Consider adding `const` or `inout` here");
+
+ return setError();
+ }
}
}
}
return setError();
}
+ // If inference succeeds, proceed with post-checks
+ if (sapply && sapply.isFuncDeclaration())
+ {
+ FuncDeclaration fd = sapply.isFuncDeclaration();
+
+ if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst())
+ {
+ // First error: The call site
+ error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object",
+ fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars());
+
+ // Second error: Suggest how to fix
+ errorSupplemental(fd.loc, "Consider adding `const` or `inout` here");
+
+ return setError();
+ }
+ }
+
Type tab = fs.aggr.type.toBasetype();
if (tab.ty == Ttuple) // don't generate new scope for tuple loops
}
ss.hasDefault = sc.sw.sdefault ||
- !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe);
+ !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe || sc.func.isSaferD);
if (!ss.hasDefault)
{
if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !sc.inCfile)
//printf("farg = %s %s\n", farg.type.toChars(), farg.toChars());
RootObject oarg = farg;
- if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || farg.isLvalue()))
+
+ if (farg.isFuncExp())
+ {
+ // When assigning an untyped (void) lambda `x => y` to a `(F)(ref F)` parameter,
+ // we don't want to deduce type void creating a void parameter
+ }
+ else if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || farg.isLvalue()))
{
/* Allow expressions that have CT-known boundaries and type [] to match with [dim]
*/
return isDeclX(d => (d.storage_class & STC.lazy_) != 0);
}
+ if (e.ident == Id.isCOMClass)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto s = getDsymbol(o);
+ AggregateDeclaration agg;
+
+ if (!s || ((agg = s.isAggregateDeclaration()) is null))
+ {
+ error(e.loc, "argument to `__traits(isCOMClass, %s)` is not a declaration", o.toChars());
+ return ErrorExp.get();
+ }
+
+ if (ClassDeclaration cd = agg.isClassDeclaration())
+ return cd.com ? True() : False();
+ else
+ return False();
+ }
if (e.ident == Id.identifier)
{
// Get identifier for symbol as a string literal
return se.expressionSemantic(sc);
}
+ if (e.ident == Id.getBitfieldOffset || e.ident == Id.getBitfieldWidth)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto s = getDsymbolWithoutExpCtx(o);
+ if (!s)
+ {
+ error(e.loc, "bitfield symbol expected not `%s`", o.toChars());
+ return ErrorExp.get();
+ }
+
+ auto vd = s.toAlias.isVarDeclaration();
+ if (!vd || !(vd.storage_class & STC.field))
+ {
+ error(e.loc, "bitfield symbol expected not %s `%s`", s.kind, s.toPrettyChars);
+ return ErrorExp.get();
+ }
+
+ uint fieldWidth;
+ uint bitOffset;
+ if (auto bf = vd.isBitFieldDeclaration())
+ {
+ fieldWidth = bf.fieldWidth;
+ bitOffset = bf.bitOffset;
+ }
+ else // just a regular field
+ {
+ const sz = size(vd.type);
+ assert(sz < uint.max / 8); // overflow check
+ fieldWidth = cast(uint)sz * 8;
+ bitOffset = 0;
+ }
+ uint value = e.ident == Id.getBitfieldOffset ? bitOffset : fieldWidth;
+ return new IntegerExp(e.loc, value, Type.tuns32);
+ }
if (e.ident == Id.getProtection || e.ident == Id.getVisibility)
{
if (dim != 1)
}
const(char)* failMessage;
const(char)** pMessage = errorHelper ? &failMessage : null;
- auto resolvedArgs = tf.resolveNamedArgs(argumentList, pMessage);
+ OutBuffer buf;
+ auto resolvedArgs = tf.resolveNamedArgs(argumentList, errorHelper ? &buf : null);
Expression[] args;
if (!resolvedArgs)
{
- if (failMessage)
+ if (buf.length)
{
- errorHelper(failMessage);
+ errorHelper(buf.peekChars());
return MATCH.nomatch;
}
if (!arg)
continue; // default argument
m = argumentMatchParameter(tf, p, arg, wildmatch, flag, sc, pMessage);
+ if (failMessage)
+ {
+ buf.reset();
+ buf.writestring(failMessage);
+ }
}
else if (p.defaultArg)
continue;
errorHelper(failMessage);
return MATCH.nomatch;
}
- if (pMessage && u >= args.length)
- *pMessage = tf.getMatchError("missing argument for parameter #%d: `%s`",
- u + 1, parameterToChars(p, tf, false));
- // If an error happened previously, `pMessage` was already filled
- else if (pMessage && !*pMessage)
- *pMessage = tf.getParamError(args[u], p);
-
if (errorHelper)
- errorHelper(*pMessage);
+ {
+ if (u >= args.length)
+ TypeFunction.getMatchError(buf, "missing argument for parameter #%d: `%s`",
+ u + 1, parameterToChars(p, tf, false));
+ // If an error happened previously, `pMessage` was already filled
+ else if (buf.length == 0)
+ buf.writestring(tf.getParamError(args[u], p));
+
+ errorHelper(buf.peekChars());
+ }
return MATCH.nomatch;
}
if (m < match)
if (errorHelper && !parameterList.varargs && args.length > nparams)
{
// all parameters had a match, but there are surplus args
- errorHelper(tf.getMatchError("expected %d argument(s), not %d", nparams, args.length));
+ OutBuffer buf2;
+ TypeFunction.getMatchError(buf2, "expected %d argument(s), not %d", nparams, args.length);
+ errorHelper(buf2.extractChars());
return MATCH.nomatch;
}
//printf("match = %d\n", match);
if (sz != trailingArgs.length)
{
if (pMessage)
- *pMessage = tf.getMatchError("expected %llu variadic argument(s), not %zu",
+ {
+ OutBuffer buf;
+ TypeFunction.getMatchError(buf, "expected %llu variadic argument(s), not %zu",
sz, trailingArgs.length);
+ *pMessage = buf.extractChars();
+ }
return MATCH.nomatch;
}
goto case Tarray;
new_call = build_nop (type, build_address (var));
setup_exp = modify_expr (var, aggregate_initializer_decl (cd));
}
- else if (global.params.ehnogc && e->thrownew)
- {
- /* Allocating a `@nogc' Exception with `_d_newThrowable' has already
- been handled by the front-end. */
- gcc_unreachable ();
- }
else
{
- /* Generate: _d_newclass() */
+ /* Generate: _d_newclass()
+ or: _d_newThrowable() */
new_call = build_expr (e->lowering);
}
D RejectNegative
Disable access to shared memory objects.
+fpreview=safer
+D RejectNegative
+Enable safety checks on all functions by default.
+
fpreview=rvaluerefparam
D RejectNegative
Enable rvalue arguments to ref parameters.
{
}
+shared Foo toLoad;
+
void oops()
{
auto f0 = new shared Foo;
auto f1 = new shared Foo;
atomicStore(f0, f1);
+
+ // https://issues.dlang.org/show_bug.cgi?id=24846
+ shared(Foo) f2 = atomicLoad(toLoad);
}
--- /dev/null
+/* REQUIRED_ARGS: -betterC -inline
+ PERMUTE_ARGS:
+*/
+
+struct InvBoneBindInfo
+{
+}
+
+
+struct Test(Value)
+{
+ void test()
+ {
+ auto t = Value.init;
+ }
+}
+
+extern(C) void main()
+{
+ Test!(InvBoneBindInfo[32]) test;
+ test.test();
+}
struct X14166 { this(int) { } X14166 opAssign(int) { return this; } }
X14166[int] aa14166;
-X14166[int] makeAA14166() { return aa14166; }
+ref X14166[int] makeAA14166() { return aa14166; }
struct Tup14166(T...) { T field; alias field this; }
Tup14166!(int, int) tup14166;
--- /dev/null
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
+import std.stdio;
+
+/// Example
+/// ---
+/// void main() {
+/// foreach (i; 0..10) {
+/// writeln("Hello, world!");
+/// }
+/// }
+/// ---
+void main() {
+
+ writeln("Hello, World!");
+
+}
void popFront(ref string) { }
static assert(!is(typeof((R r) => r.popFront)));
+
+// https://issues.dlang.org/show_bug.cgi?id=24883
+int toString(Writer)(ref Writer sink) => 3;
+int toString(void delegate(scope const(char)[]) sink) => 4;
+void put() {}
+static assert(toString(dst => put()) == 4);
// https://issues.dlang.org/show_bug.cgi?id=3147
// https://issues.dlang.org/show_bug.cgi?id=6000
// https://issues.dlang.org/show_bug.cgi?id=5225
+// https://issues.dlang.org/show_bug.cgi?id=24855
void add()
{
short w;
byte y;
static assert(!__traits(compiles, y = w / -1));
+ static assert(!__traits(compiles, y = y / w));
+
+ short z;
+ static assert(!__traits(compiles, z = w / z + 1));
}
void plus1Fail()
----
fail_compilation/b19523.d(13): Error: undefined identifier `SomeStruct`
fail_compilation/b19523.d(14): Error: function `foo` is not callable using argument types `(_error_)`
-fail_compilation/b19523.d(14): cannot pass argument `__lambda2` of type `_error_` to parameter `int delegate() arg`
+fail_compilation/b19523.d(14): cannot pass argument `__lambda_L14_C6` of type `_error_` to parameter `int delegate() arg`
fail_compilation/b19523.d(19): `b19523.foo(int delegate() arg)` declared here
----
*/
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/binexperr.d(12): Error: expression expected, not `)`
+fail_compilation/binexperr.d(12): Error: missing closing `)` after `if (A1 * (__error)`
+---
+*/
+
+void main()
+{
+ struct A1 {}
+ if (A1*) {}
+ return;
+}
--- /dev/null
+/* REQUIRED_ARGS: -preview=bitfields
+ * TEST_OUTPUT:
+---
+fail_compilation/biterrors5.d(23): Error: bitfield symbol expected not struct `biterrors5.S`
+fail_compilation/biterrors5.d(24): Error: bitfield symbol expected not variable `biterrors5.test0.i`
+---
+*/
+
+struct S
+{
+ int a,b;
+ int :2, c:3;
+}
+
+static assert(__traits(getBitfieldOffset, S.b) == 0);
+static assert(__traits(getBitfieldWidth, S.b) == 32);
+static assert(__traits(getBitfieldOffset, S.c) == 2);
+static assert(__traits(getBitfieldWidth, S.c) == 3);
+
+void test0()
+{
+ int i;
+ i = __traits(getBitfieldOffset, S);
+ i = __traits(getBitfieldOffset, i);
+}
fail_compilation/bug9631.d(80): Error: function `f` is not callable using argument types `(int, S)`
fail_compilation/bug9631.d(80): cannot pass argument `y` of type `bug9631.tem!().S` to parameter `bug9631.S s`
fail_compilation/bug9631.d(79): `bug9631.arg.f(int i, S s)` declared here
-fail_compilation/bug9631.d(81): Error: function literal `__lambda4(S s)` is not callable using argument types `(S)`
+fail_compilation/bug9631.d(81): Error: function literal `__lambda_L81_C5(S s)` is not callable using argument types `(S)`
fail_compilation/bug9631.d(81): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S s`
fail_compilation/bug9631.d(87): Error: constructor `bug9631.arg.A.this(S __param_0)` is not callable using argument types `(S)`
fail_compilation/bug9631.d(87): cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S __param_0`
fail_compilation/constraints_defs.d(49): Error: template instance `constraints_defs.main.def!(int, 0, (a) => a)` does not match template declaration `def(T, int i = 5, alias R)()`
with `T = int,
i = 0,
- R = __lambda1`
+ R = __lambda_L49_C18`
must satisfy the following constraint:
` N!T`
fail_compilation/constraints_defs.d(50): Error: template instance `imports.constraints.defa!int` does not match template declaration `defa(T, U = int)()`
` N!T
N!U`
fail_compilation/constraints_tmpl.d(43): Error: template instance `constraints_tmpl.main.lambda!((a) => a)` does not match template declaration `lambda(alias pred)()`
- with `pred = __lambda1`
+ with `pred = __lambda_L43_C13`
must satisfy the following constraint:
` N!int`
---
fail_compilation/cppvar.d(21): perhaps declare it as `__gshared` instead
fail_compilation/cppvar.d(22): Error: variable `cppvar.sharedVar` cannot have `extern(C++)` linkage because it is `shared`
fail_compilation/cppvar.d(22): perhaps declare it as `__gshared` instead
-fail_compilation/cppvar.d(30): Error: delegate `cppvar.__lambda7` cannot return type `bool[3]` because its linkage is `extern(C++)`
+fail_compilation/cppvar.d(30): Error: delegate `cppvar.__lambda_L30_C46` cannot return type `bool[3]` because its linkage is `extern(C++)`
---
*/
#line 10
TEST_OUTPUT:
---
fail_compilation/diag12829.d(15): Error: function `diag12829.test1` is `@nogc` yet allocates closure for `test1()` with the GC
-fail_compilation/diag12829.d(18): delegate `diag12829.test1.__lambda2` closes over variable `x`
+fail_compilation/diag12829.d(18): delegate `diag12829.test1.__lambda_L18_C33` closes over variable `x`
fail_compilation/diag12829.d(17): `x` declared here
fail_compilation/diag12829.d(22): function `diag12829.test1.bar` closes over variable `x`
fail_compilation/diag12829.d(17): `x` declared here
/*
TEST_OUTPUT:
---
-fail_compilation/diag15411.d(17): Error: function `diag15411.test15411.__funcliteral2` cannot access variable `i` in frame of function `diag15411.test15411`
+fail_compilation/diag15411.d(17): Error: function `diag15411.test15411.__funcliteral_L17_C15` cannot access variable `i` in frame of function `diag15411.test15411`
fail_compilation/diag15411.d(16): `i` declared here
-fail_compilation/diag15411.d(18): Error: function `diag15411.test15411.__funcliteral4` cannot access variable `i` in frame of function `diag15411.test15411`
+fail_compilation/diag15411.d(18): Error: function `diag15411.test15411.__funcliteral_L18_C15` cannot access variable `i` in frame of function `diag15411.test15411`
fail_compilation/diag15411.d(16): `i` declared here
fail_compilation/diag15411.d(26): Error: `static` function `diag15411.testNestedFunction.myFunc2` cannot access function `myFunc1` in frame of function `diag15411.testNestedFunction`
fail_compilation/diag15411.d(25): `myFunc1` declared here
/*
TEST_OUTPUT:
---
-fail_compilation/diag20268.d(12): Error: template `__lambda4` is not callable using argument types `!()(int)`
-fail_compilation/diag20268.d(11): Candidate is: `__lambda4(__T1, __T2)(x, y)`
+fail_compilation/diag20268.d(12): Error: template `__lambda_L11_C1` is not callable using argument types `!()(int)`
+fail_compilation/diag20268.d(11): Candidate is: `__lambda_L11_C1(__T1, __T2)(x, y)`
---
*/
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag24812.d(7): Error: enum `diag24812.Foo` enum `Foo` must have at least one member
+---
+*/
+enum Foo {}
/*
TEST_OUTPUT:
---
-fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda3(__T1)(x)` cannot access variable `c` in frame of function `D main`
+fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda_L13_C12(__T1)(x)` cannot access variable `c` in frame of function `D main`
fail_compilation/diag9831.d(11): `c` declared here
---
*/
/**
TEST_OUTPUT:
---
-fail_compilation/diag_funclit.d(103): Error: function literal `__lambda1(x, y, z)` is not callable using argument types `()`
+fail_compilation/diag_funclit.d(103): Error: function literal `__lambda_L103_C5(x, y, z)` is not callable using argument types `()`
fail_compilation/diag_funclit.d(103): too few arguments, expected 3, got 0
-fail_compilation/diag_funclit.d(106): Error: function literal `__lambda2(x, y, z)` is not callable using argument types `(int, string, int, int)`
+fail_compilation/diag_funclit.d(106): Error: function literal `__lambda_L106_C5(x, y, z)` is not callable using argument types `(int, string, int, int)`
fail_compilation/diag_funclit.d(106): too many arguments, expected 3, got 4
-fail_compilation/diag_funclit.d(108): Error: function literal `__lambda3(x, y, string z = "Hello")` is not callable using argument types `(int, int, string, string)`
+fail_compilation/diag_funclit.d(108): Error: function literal `__lambda_L108_C5(x, y, string z = "Hello")` is not callable using argument types `(int, int, string, string)`
fail_compilation/diag_funclit.d(108): too many arguments, expected 3, got 4
-fail_compilation/diag_funclit.d(110): Error: function literal `__lambda4(x, y, string z = "Hello")` is not callable using argument types `(int)`
+fail_compilation/diag_funclit.d(110): Error: function literal `__lambda_L110_C5(x, y, string z = "Hello")` is not callable using argument types `(int)`
fail_compilation/diag_funclit.d(110): too few arguments, expected 3, got 1
-fail_compilation/diag_funclit.d(112): Error: function literal `__lambda5(x, y, z)` is not callable using argument types `(int)`
+fail_compilation/diag_funclit.d(112): Error: function literal `__lambda_L112_C5(x, y, z)` is not callable using argument types `(int)`
fail_compilation/diag_funclit.d(112): too few arguments, expected 3, got 1
-fail_compilation/diag_funclit.d(115): Error: function literal `__lambda6(x, y, ...)` is not callable using argument types `(int)`
+fail_compilation/diag_funclit.d(115): Error: function literal `__lambda_L115_C5(x, y, ...)` is not callable using argument types `(int)`
fail_compilation/diag_funclit.d(115): too few arguments, expected 2, got 1
-fail_compilation/diag_funclit.d(117): Error: function literal `__lambda7(x, y, string z = "Hey", ...)` is not callable using argument types `(int)`
+fail_compilation/diag_funclit.d(117): Error: function literal `__lambda_L117_C5(x, y, string z = "Hey", ...)` is not callable using argument types `(int)`
fail_compilation/diag_funclit.d(117): too few arguments, expected 3, got 1
---
*/
fail_compilation/e15876_3.d(29): Error: no identifier for declarator `d(_error_ = ()
{
for (__error__
- 0; 0)
+ __error; __error)
{
__error__
}
fail_compilation/e15876_4.d(27): Error: no identifier for declarator `typeof(()
{
for (__error__
- 0; 0)
+ __error; __error)
{
__error__
}
TEST_OUTPUT:
---
fail_compilation/fail11125.d(26): Error: template instance `fail11125.filter!(function (int a) pure nothrow @nogc @safe => a + 1)` does not match template declaration `filter(alias predfun)`
- with `predfun = __lambda1`
+ with `predfun = __lambda_L26_C13`
must satisfy the following constraint:
` is(ReturnType!predfun == bool)`
fail_compilation/fail11125.d(27): Error: template instance `fail11125.filter!(function (int a) pure nothrow @nogc @safe => a + 1)` does not match template declaration `filter(alias predfun)`
- with `predfun = __lambda2`
+ with `predfun = __lambda_L27_C17`
must satisfy the following constraint:
` is(ReturnType!predfun == bool)`
---
fail_compilation/fail12236.d(21): Error: forward reference to inferred return type of function `f2`
fail_compilation/fail12236.d(21): while evaluating `pragma(msg, f2(T)(T).mangleof)`
fail_compilation/fail12236.d(27): Error: template instance `fail12236.f2!int` error instantiating
-fail_compilation/fail12236.d(31): Error: forward reference to inferred return type of function `__lambda1`
-fail_compilation/fail12236.d(31): while evaluating `pragma(msg, __lambda1(__T1)(a).mangleof)`
+fail_compilation/fail12236.d(31): Error: forward reference to inferred return type of function `__lambda_L29_C5`
+fail_compilation/fail12236.d(31): while evaluating `pragma(msg, __lambda_L29_C5(__T1)(a).mangleof)`
---
*/
fail_compilation/fail12378.d(18): Error: undefined identifier `GOES`
fail_compilation/fail12378.d(91): instantiated from here: `MapResultS!((x0) => ANYTHING - GOES, Result)`
fail_compilation/fail12378.d(17): instantiated from here: `mapS!(Result)`
-fail_compilation/fail12378.d(100): instantiated from here: `__lambda1!int`
+fail_compilation/fail12378.d(100): instantiated from here: `__lambda_L16_C19!int`
fail_compilation/fail12378.d(91): instantiated from here: `MapResultS!((y0) => iota(2).mapS!((x0) => ANYTHING - GOES), Result)`
fail_compilation/fail12378.d(16): instantiated from here: `mapS!(Result)`
---
fail_compilation/fail12378.d(40): Error: undefined identifier `GOES`
fail_compilation/fail12378.d(112): instantiated from here: `MapResultC!((x0) => ANYTHING - GOES, Result)`
fail_compilation/fail12378.d(39): instantiated from here: `mapC!(Result)`
-fail_compilation/fail12378.d(123): instantiated from here: `__lambda1!int`
+fail_compilation/fail12378.d(123): instantiated from here: `__lambda_L38_C19!int`
fail_compilation/fail12378.d(112): instantiated from here: `MapResultC!((y0) => iota(2).mapC!((x0) => ANYTHING - GOES), Result)`
fail_compilation/fail12378.d(38): instantiated from here: `mapC!(Result)`
---
fail_compilation/fail12378.d(64): Error: undefined identifier `GOES`
fail_compilation/fail12378.d(135): instantiated from here: `MapResultI!((x0) => ANYTHING - GOES, Result)`
fail_compilation/fail12378.d(63): instantiated from here: `mapI!(Result)`
-fail_compilation/fail12378.d(143): instantiated from here: `__lambda1!int`
+fail_compilation/fail12378.d(143): instantiated from here: `__lambda_L62_C19!int`
fail_compilation/fail12378.d(135): instantiated from here: `MapResultI!((y0) => iota(2).mapI!((x0) => ANYTHING - GOES), Result)`
fail_compilation/fail12378.d(62): instantiated from here: `mapI!(Result)`
---
/*
TEST_OUTPUT:
---
-fail_compilation/fail12908.d(14): Error: `pure` delegate `fail12908.main.__foreachbody1` cannot call impure function `fail12908.g`
+fail_compilation/fail12908.d(14): Error: `pure` delegate `fail12908.main.__foreachbody_L12_C5` cannot call impure function `fail12908.g`
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/fail13120.d(13): Error: `pure` delegate `fail13120.g1.__foreachbody2` cannot call impure function `fail13120.f1`
-fail_compilation/fail13120.d(13): Error: `@nogc` delegate `fail13120.g1.__foreachbody2` cannot call non-@nogc function `fail13120.f1`
+fail_compilation/fail13120.d(13): Error: `pure` delegate `fail13120.g1.__foreachbody_L12_C5` cannot call impure function `fail13120.f1`
+fail_compilation/fail13120.d(13): Error: `@nogc` delegate `fail13120.g1.__foreachbody_L12_C5` cannot call non-@nogc function `fail13120.f1`
---
*/
void f1() {}
/*
TEST_OUTPUT:
---
-fail_compilation/fail13424.d(12): Error: delegate `fail13424.S.__lambda2` cannot be struct members
-fail_compilation/fail13424.d(17): Error: delegate `fail13424.U.__lambda2` cannot be union members
-fail_compilation/fail13424.d(22): Error: delegate `fail13424.C.__lambda2` cannot be class members
+fail_compilation/fail13424.d(12): Error: delegate `fail13424.S.__lambda_L12_C35` cannot be struct members
+fail_compilation/fail13424.d(17): Error: delegate `fail13424.U.__lambda_L17_C35` cannot be union members
+fail_compilation/fail13424.d(22): Error: delegate `fail13424.C.__lambda_L22_C35` cannot be class members
---
*/
/* TEST_OUTPUT:
---
-fail_compilation/fail17969.d(10): Error: no property `sum` for type `fail17969.__lambda6!(int[]).__lambda6.MapResult2!((b) => b)`
+fail_compilation/fail17969.d(10): Error: no property `sum` for type `fail17969.__lambda_L10_C1!(int[]).__lambda_L10_C1.MapResult2!((b) => b)`
fail_compilation/fail17969.d(16): struct `MapResult2` defined here
---
* https://issues.dlang.org/show_bug.cgi?id=17969
/*
TEST_OUTPUT:
---
-fail_compilation/fail39.d(12): Error: function `fail39.main.__funcliteral2` cannot access function `foo` in frame of function `D main`
+fail_compilation/fail39.d(12): Error: function `fail39.main.__funcliteral_L12_C27` cannot access function `foo` in frame of function `D main`
fail_compilation/fail39.d(11): `foo` declared here
---
*/
fail_compilation/fail6795.d(23): Error: cannot modify expression `[0:0][0]` because it is not an lvalue
fail_compilation/fail6795.d(25): Error: cannot take address of expression `[0][0]` because it is not an lvalue
fail_compilation/fail6795.d(26): Error: cannot take address of expression `[0:0][0]` because it is not an lvalue
+fail_compilation/fail6795.d(30): Error: cannot modify expression `Some["zz"]` because it is not an lvalue
---
*/
-
void test_wrong_line_num()
{
enum int[1] sa = [0];
auto ps = &sa[0];
auto pa = &aa[0];
+
+ // https://issues.dlang.org/show_bug.cgi?id=24845
+ enum Maps : int[string] { Some = ["aa" : 12], Other = ["bb" : 24] }
+ Maps.Some["zz"] = 44;
}
fail_compilation/hexstring.d(40): Error: hex string with `dstring` type needs to be multiple of 4 bytes, not 5
fail_compilation/hexstring.d(41): Error: cannot implicitly convert expression `x"11223344"d` of type `dstring` to `immutable(float[])`
fail_compilation/hexstring.d(42): Error: cannot implicitly convert expression `x"1122"w` of type `wstring` to `immutable(ubyte[])`
+fail_compilation/hexstring.d(50): Error: array cast from `string` to `S[]` is not supported at compile time
fail_compilation/hexstring.d(28): Error: cannot implicitly convert expression `x"123F"` of type `string` to `ubyte[]`
---
*/
static assert(s0[0] == 0x12);
static assert(s0[1] == 0x3F);
immutable byte[] s1 = x"123F";
-
enum E(X) = cast(X[]) x"AABBCCDD";
static assert(E!int[0] == 0xAABBCCDD);
immutable uint[] f12 = x"1122334455"d;
immutable float[] f13 = x"11223344"d;
immutable ubyte[] f14 = x"1122"w;
+
+// https://issues.dlang.org/show_bug.cgi?id=24832
+struct S
+{
+ ushort l0, l1, l2, l3, l4, l5;
+}
+
+immutable S[] returnValues = cast(S[]) x"FFFFFFFFFFFFFFFFFFFFFFFF";
/*
TEST_OUTPUT:
---
-fail_compilation/ice10922.d(11): Error: function `__lambda4` is not callable using argument types `()`
+fail_compilation/ice10922.d(11): Error: function `__lambda_L10_C12` is not callable using argument types `()`
fail_compilation/ice10922.d(11): too few arguments, expected 1, got 0
-fail_compilation/ice10922.d(10): `ice10922.__lambda4(in uint n)` declared here
+fail_compilation/ice10922.d(10): `ice10922.__lambda_L10_C12(in uint n)` declared here
---
*/
TEST_OUTPUT:
---
fail_compilation/ice11822.d(33): Deprecation: function `ice11822.d` is deprecated
-fail_compilation/ice11822.d(16): instantiated from here: `__lambda2!int`
-fail_compilation/ice11822.d(22): instantiated from here: `S!(__lambda2)`
+fail_compilation/ice11822.d(16): instantiated from here: `__lambda_L33_C15!int`
+fail_compilation/ice11822.d(22): instantiated from here: `S!(__lambda_L33_C15)`
fail_compilation/ice11822.d(33): instantiated from here: `g!((n) => d(i))`
---
*/
TEST_OUTPUT:
---
fail_compilation/ice11850.d(15): Error: incompatible types for `(a) < ([0])`: `uint[]` and `int[]`
-fail_compilation/imports/a11850.d(9): instantiated from here: `FilterResult!(__lambda1, uint[][])`
+fail_compilation/imports/a11850.d(9): instantiated from here: `FilterResult!(__lambda_L15_C13, uint[][])`
fail_compilation/ice11850.d(15): instantiated from here: `filter!(uint[][])`
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/ice12235.d(14): Error: forward reference to inferred return type of function `__lambda1`
-fail_compilation/ice12235.d(15): Error: forward reference to inferred return type of function `__lambda1`
-fail_compilation/ice12235.d(15): while evaluating `pragma(msg, __lambda1.mangleof)`
+fail_compilation/ice12235.d(14): Error: forward reference to inferred return type of function `__lambda_L12_C5`
+fail_compilation/ice12235.d(15): Error: forward reference to inferred return type of function `__lambda_L12_C5`
+fail_compilation/ice12235.d(15): while evaluating `pragma(msg, __lambda_L12_C5.mangleof)`
---
*/
fail_compilation/ice15855.d(28): Error: no identifier for declarator `a[()
{
for (__error__
- 0; 0)
+ __error; __error)
{
__error__
}
/*
TEST_OUTPUT:
---
-fail_compilation/ice8309.d(10): Error: incompatible types for `(__lambda1) : (__lambda2)`: `double function() pure nothrow @nogc @safe` and `int function() pure nothrow @nogc @safe`
+fail_compilation/ice8309.d(10): Error: incompatible types for `(__lambda_L10_C15) : (__lambda_L10_C24)`: `double function() pure nothrow @nogc @safe` and `int function() pure nothrow @nogc @safe`
---
*/
---
fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` when expecting `(`
fail_compilation/ice8795.d-mixin-14(14): Error: expression expected, not `End of File`
-fail_compilation/ice8795.d-mixin-14(14): Error: missing closing `)` after `switch (0`
+fail_compilation/ice8795.d-mixin-14(14): Error: missing closing `)` after `switch (__error`
fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` instead of statement
fail_compilation/ice8795.d-mixin-15(15): Error: { } expected following `interface` declaration
fail_compilation/ice8795.d-mixin-15(15): Error: anonymous interfaces not allowed
fail_compilation/misc1.d(110): Error: `1 + 2` has no effect
fail_compilation/misc1.d(111): Error: `x` has no effect
fail_compilation/misc1.d(117): Deprecation: `1 * 1` has no effect
-fail_compilation/misc1.d(118): Deprecation: `__lambda3` has no effect
+fail_compilation/misc1.d(118): Deprecation: `__lambda_L118_C34` has no effect
fail_compilation/misc1.d(124): Deprecation: `false` has no effect
fail_compilation/misc1.d(127): Deprecation: `*sp++` has no effect
fail_compilation/misc1.d(128): Deprecation: `j` has no effect
fail_compilation/misc_parser_err_cov1.d(41): Error: identifier or new keyword expected following `(...)`.
fail_compilation/misc_parser_err_cov1.d(41): Error: expression expected, not `;`
fail_compilation/misc_parser_err_cov1.d(42): Error: found `}` when expecting `;` following expression
-fail_compilation/misc_parser_err_cov1.d(41): expression: `(__error) + 0`
+fail_compilation/misc_parser_err_cov1.d(41): expression: `(__error) + (__error)`
fail_compilation/misc_parser_err_cov1.d(43): Error: matching `}` expected following compound statement, not `End of File`
fail_compilation/misc_parser_err_cov1.d(33): unmatched `{`
---
/* TEST_OUTPUT:
---
fail_compilation/opapplyscope.d(113): Error: function `opapplyscope.S.opApply(scope int delegate(scope int* ptr) @safe dg)` is not callable using argument types `(int delegate(int* x) nothrow @nogc @safe)`
-fail_compilation/opapplyscope.d(113): cannot pass argument `__foreachbody3` of type `int delegate(int* x) nothrow @nogc @safe` to parameter `scope int delegate(scope int* ptr) @safe dg`
+fail_compilation/opapplyscope.d(113): cannot pass argument `__foreachbody_L113_C5` of type `int delegate(int* x) nothrow @nogc @safe` to parameter `scope int delegate(scope int* ptr) @safe dg`
---
*/
TEST_OUTPUT:
----
fail_compilation/previewin.d(4): Error: function `takeFunction` is not callable using argument types `(void function(real x) pure nothrow @nogc @safe)`
-fail_compilation/previewin.d(4): cannot pass argument `__lambda1` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
+fail_compilation/previewin.d(4): cannot pass argument `__lambda_L4_C18` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
fail_compilation/previewin.d(11): `previewin.takeFunction(void function(in real) f)` declared here
fail_compilation/previewin.d(5): Error: function `takeFunction` is not callable using argument types `(void function(scope const(real) x) pure nothrow @nogc @safe)`
-fail_compilation/previewin.d(5): cannot pass argument `__lambda2` of type `void function(scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
+fail_compilation/previewin.d(5): cannot pass argument `__lambda_L5_C18` of type `void function(scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
fail_compilation/previewin.d(11): `previewin.takeFunction(void function(in real) f)` declared here
fail_compilation/previewin.d(6): Error: function `takeFunction` is not callable using argument types `(void function(ref scope const(real) x) pure nothrow @nogc @safe)`
-fail_compilation/previewin.d(6): cannot pass argument `__lambda3` of type `void function(ref scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
+fail_compilation/previewin.d(6): cannot pass argument `__lambda_L6_C18` of type `void function(ref scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
fail_compilation/previewin.d(11): `previewin.takeFunction(void function(in real) f)` declared here
fail_compilation/previewin.d(15): Error: scope variable `arg` assigned to global variable `myGlobal`
fail_compilation/previewin.d(16): Error: scope variable `arg` assigned to global variable `myGlobal`
TEST_OUTPUT:
---
fail_compilation/retscope.d(75): Error: function `retscope.HTTP.Impl.onReceive` is `@nogc` yet allocates closure for `onReceive()` with the GC
-fail_compilation/retscope.d(77): delegate `retscope.HTTP.Impl.onReceive.__lambda1` closes over variable `this`
+fail_compilation/retscope.d(77): delegate `retscope.HTTP.Impl.onReceive.__lambda_L77_C23` closes over variable `this`
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda2` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
-fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda2` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
-fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda4` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
-fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda4` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
+fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda_L248_C21` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
+fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda_L248_C21` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
+fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda_L249_C21` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
+fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda_L249_C21` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
---
*/
--- /dev/null
+/* REQUIRED_ARGS: -preview=safer
+TEST_OUTPUT:
+---
+fail_compilation/safer.d(10): Error: `void` initializers for pointers not allowed in safe functions
+---
+*/
+
+void test1()
+{
+ int* p = void;
+}
+
+void foo3() { }
+
+void test2()
+{
+ foo3(); // should not be an error
+}
/*
TEST_OUTPUT:
---
-fail_compilation/test15306.d(15): Error: `immutable` delegate `test15306.main.__dgliteral2` cannot access mutable data `i`
-fail_compilation/test15306.d(19): Error: `shared` delegate `test15306.main.__dgliteral5` cannot access non-shared data `p`
+fail_compilation/test15306.d(15): Error: `immutable` delegate `test15306.main.__dgliteral_L15_C16` cannot access mutable data `i`
+fail_compilation/test15306.d(19): Error: `shared` delegate `test15306.main.__dgliteral_L19_C16` cannot access non-shared data `p`
---
*/
TEST_OUTPUT:
---
fail_compilation/test16193.d(39): Error: function `test16193.abc` is `@nogc` yet allocates closure for `abc()` with the GC
-fail_compilation/test16193.d(41): delegate `test16193.abc.__foreachbody2` closes over variable `x`
+fail_compilation/test16193.d(41): delegate `test16193.abc.__foreachbody_L41_C5` closes over variable `x`
fail_compilation/test16193.d(40): `x` declared here
---
*/
---
fail_compilation/test17451.d(22): Error: undefined identifier `allocator`
fail_compilation/test17451.d(23): Error: `false` has no effect
-fail_compilation/test17451.d(30): Error: variable `test17451.HashMap!(ThreadSlot).HashMap.__lambda2.v` - size of type `ThreadSlot` is invalid
+fail_compilation/test17451.d(30): Error: variable `test17451.HashMap!(ThreadSlot).HashMap.__lambda_L30_C20.v` - size of type `ThreadSlot` is invalid
fail_compilation/test17451.d(44): Error: template instance `test17451.HashMap!(ThreadSlot)` error instantiating
---
*/
---
fail_compilation/test19107.d(24): Error: template `all` is not callable using argument types `!((c) => c)(string[])`
fail_compilation/test19107.d(18): Candidate is: `all(alias pred, T)(T t)`
- with `pred = __lambda2,
+ with `pred = __lambda_L24_C15,
T = string[]`
must satisfy the following constraint:
` is(typeof(I!pred(t)))`
fail_compilation/test19971.d(16): Error: function `f` is not callable using argument types `(string)`
fail_compilation/test19971.d(16): cannot pass argument `"%s"` of type `string` to parameter `int x`
fail_compilation/test19971.d(13): `test19971.f(int x)` declared here
-fail_compilation/test19971.d(17): Error: function literal `__lambda1(int x)` is not callable using argument types `(string)`
+fail_compilation/test19971.d(17): Error: function literal `__lambda_L17_C5(int x)` is not callable using argument types `(string)`
fail_compilation/test19971.d(17): cannot pass argument `"%s"` of type `string` to parameter `int x`
---
*/
/* TEST_OUTPUT:
---
fail_compilation/test20719.d(13): Error: struct `test20719.SumType` no size because of forward reference
-fail_compilation/test20719.d(32): Error: variable `test20719.isCopyable!(SumType).__lambda2.foo` - size of type `SumType` is invalid
+fail_compilation/test20719.d(32): Error: variable `test20719.isCopyable!(SumType).__lambda_L32_C22.foo` - size of type `SumType` is invalid
fail_compilation/test20719.d(18): Error: template instance `test20719.isCopyable!(SumType)` error instantiating
---
*/
TEST_OUTPUT:
---
fail_compilation/test21912.d(28): Error: function `test21912.escapeParam` is `@nogc` yet allocates closure for `escapeParam()` with the GC
-fail_compilation/test21912.d(30): delegate `test21912.escapeParam.__lambda2` closes over variable `i`
+fail_compilation/test21912.d(30): delegate `test21912.escapeParam.__lambda_L30_C21` closes over variable `i`
fail_compilation/test21912.d(28): `i` declared here
fail_compilation/test21912.d(33): Error: function `test21912.escapeAssign` is `@nogc` yet allocates closure for `escapeAssign()` with the GC
-fail_compilation/test21912.d(35): delegate `test21912.escapeAssign.__lambda3` closes over variable `i`
+fail_compilation/test21912.d(35): delegate `test21912.escapeAssign.__lambda_L35_C10` closes over variable `i`
fail_compilation/test21912.d(33): `i` declared here
fail_compilation/test21912.d(44): Error: function `test21912.escapeAssignRef` is `@nogc` yet allocates closure for `escapeAssignRef()` with the GC
-fail_compilation/test21912.d(46): delegate `test21912.escapeAssignRef.__lambda3` closes over variable `i`
+fail_compilation/test21912.d(46): delegate `test21912.escapeAssignRef.__lambda_L46_C10` closes over variable `i`
fail_compilation/test21912.d(44): `i` declared here
fail_compilation/test21912.d(55): Error: function `test21912.escapeParamInferred` is `@nogc` yet allocates closure for `escapeParamInferred()` with the GC
-fail_compilation/test21912.d(57): delegate `test21912.escapeParamInferred.__lambda2` closes over variable `i`
+fail_compilation/test21912.d(57): delegate `test21912.escapeParamInferred.__lambda_L57_C29` closes over variable `i`
fail_compilation/test21912.d(55): `i` declared here
---
*/
/*\r
TEST_OUTPUT:\r
---\r
-fail_compilation/test23170.d(10): Error: array literal in `@nogc` delegate `test23170.__lambda5` may cause a GC allocation\r
+fail_compilation/test23170.d(10): Error: array literal in `@nogc` delegate `test23170.__lambda_L10_C15` may cause a GC allocation\r
---\r
*/\r
// https://issues.dlang.org/show_bug.cgi?id=23170\r
--- /dev/null
+// https://issues.dlang.org/show_bug.cgi?id=24353
+
+/**
+TEST_OUTPUT:
+---
+fail_compilation/test24353.d(23): Error: mutable method `test24353.S.opApply` is not callable using a `const` object
+fail_compilation/test24353.d(14): Consider adding `const` or `inout` here
+---
+*/
+
+
+struct S
+{
+ int opApply(int delegate(int) dg)
+ {
+ return 0;
+ }
+}
+
+void example()
+{
+ const S s;
+ foreach (e; s) {} // Error expected here
+}
--- /dev/null
+// https://issues.dlang.org/show_bug.cgi?id=24745
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test24745.d(12): Error: incorrect syntax for associative array, expected `[]`, found `{}`
+---
+*/
+
+void main()
+{
+ int[int] f = {1: 1, 2: 2};
+}
TEST_OUTPUT:
---
fail_compilation/testInference.d(154): Error: `pure` function `testInference.bar14049` cannot call impure function `testInference.foo14049!int.foo14049`
-fail_compilation/testInference.d(149): which calls `testInference.foo14049!int.foo14049.__lambda2`
+fail_compilation/testInference.d(149): which calls `testInference.foo14049!int.foo14049.__lambda_L147_C14`
fail_compilation/testInference.d(148): which calls `testInference.impure14049`
fail_compilation/testInference.d(143): which wasn't inferred `pure` because of:
fail_compilation/testInference.d(143): `pure` function `testInference.impure14049` cannot access mutable static data `i`
---
fail_compilation/traits_alone.d(11): Error: found `End of File` when expecting `(`
fail_compilation/traits_alone.d(11): Error: `__traits(identifier, args...)` expected
-fail_compilation/traits_alone.d(11): Error: no identifier for declarator `_error_`
+fail_compilation/traits_alone.d(11): Error: no identifier for declarator `$r:_?_error_?$`
---
*/
//used to segfault
/*
TEST_OUTPUT:
---
-fail_compilation/var_func_attr.d(19): Error: cannot implicitly convert expression `__lambda8` of type `void function() nothrow @nogc @safe` to `void function() pure`
+fail_compilation/var_func_attr.d(19): Error: cannot implicitly convert expression `__lambda_L19_C27` of type `void function() nothrow @nogc @safe` to `void function() pure`
---
*/
if (1)
{
auto s = S11776!(a => 1)();
- static assert(typeof(s).mangleof ==
- "S"~"6mangle"~tl!("56")~
+ enum expected = "S"~"6mangle"~tl!("56")~
("__T"~"6S11776"~"S"~tl!("42")~
- (id!("6mangle","Qs")~"9test11776"~"FZ"~"9__lambda1MFZ"~id!("9__lambda1","Qn"))~"Z"
- )~id!("6S11776", "QBm"));
+ (id!("6mangle","Qs")~"9test11776"~"FZ"~"17__lambda_L444_C14MFZ17__lambda_L450_C30")~"Z"
+ )~id!("6S11776", "QCm");
+ static assert(typeof(s).mangleof == expected);
}
};
}
if (is(typeof({
class C {}
static assert(C.mangleof ==
- "C6mangle"~tl!("16")~"__U10func12231aZ"~id!("10func12231a","Qn")~"FZ9__lambda1MFZ1C");
+ "C6mangle"~tl!("16")~"__U10func12231aZ"~id!("10func12231a","Qn")~"FZ17__lambda_L509_C15MFZ1C");
// ### L #
})))
{}
void func12231b()()
if (is(typeof({
class C {} static assert(C.mangleof ==
- "C6mangle"~tl!("16")~"__U10func12231bZ"~id!("10func12231b","Qn")~"FZ9__lambda1MFZ1C");
+ "C6mangle"~tl!("16")~"__U10func12231bZ"~id!("10func12231b","Qn")~"FZ17__lambda_L518_C15MFZ1C");
// L__L L LL
})) &&
is(typeof({
class C {}
static assert(C.mangleof ==
- "C6mangle"~tl!("16")~"__U10func12231bZ"~id!("10func12231b","Qn")~"FZ9__lambda2MFZ1C");
+ "C6mangle"~tl!("16")~"__U10func12231bZ"~id!("10func12231b","Qn")~"FZ17__lambda_L523_C15MFZ1C");
// L__L L LL
})))
{}
if (is(typeof({
class C {}
static assert(C.mangleof ==
- "C6mangle"~tl!("16")~"__U10func12231cZ"~id!("10func12231c","Qn")~"FZ9__lambda1MFZ1C");
+ "C6mangle"~tl!("16")~"__U10func12231cZ"~id!("10func12231c","Qn")~"FZ17__lambda_L532_C15MFZ1C");
// L__L L LL
})))
{
(){
class C {}
static assert(C.mangleof ==
- "C6mangle"~tl!("16")~"__T10func12231cZ"~id!("10func12231c","Qn")~"FZ9__lambda1MFZ1C");
+ "C6mangle"~tl!("16")~"__T10func12231cZ"~id!("10func12231c","Qn")~"FZ16__lambda_L539_C5MFZ1C");
// L__L L LL
}();
}
if (is(typeof({
class C {}
static assert(C.mangleof ==
- "C6mangle"~tl!("20")~"__U10func12231cTAyaZ"~id!("10func12231c","Qr")~"FZ9__lambda1MFZ1C");
+ "C6mangle"~tl!("20")~"__U10func12231cTAyaZ"~id!("10func12231c","Qr")~"FZ17__lambda_L548_C15MFZ1C");
// L__L L___L LL
})))
{
(){
class C {}
static assert(C.mangleof ==
- "C6mangle"~tl!("20")~"__T10func12231cTAyaZ"~id!("10func12231c","Qr")~"FZ9__lambda1MFZ1C");
+ "C6mangle"~tl!("20")~"__T10func12231cTAyaZ"~id!("10func12231c","Qr")~"FZ16__lambda_L555_C5MFZ1C");
// L__L L___L LL
}();
}
struct S21753 { void function() f1; }
void fun21753(S21753 v)() {}
alias fl21753 = (){};
-static assert((fun21753!(S21753(fl21753))).mangleof == "_D6mangle__T8fun21753VSQv6S21753S1f_DQBj10" ~ fl21753.stringof ~ "MFNaNbNiNfZvZQCbQp");
+static assert((fun21753!(S21753(fl21753))).mangleof == "_D6mangle__T8fun21753VSQv6S21753S1f_DQBj16" ~ fl21753.stringof ~ "MFNaNbNiNfZvZQChQp");
/***************************************************/
void main()
--- /dev/null
+/*
+REQUIRED_ARGS: -inline
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=24884
+
+pragma(inline, false)
+bool norm(int a) => 0;
+
+pragma(inline, false)
+void inlinebug(ref double[4] point1, ref double[4] point2, ref double[4] point3, ref double[4] abcd)
+{
+ double[4] v1 = 0.0;
+ double[4] v2 = 0.0;
+
+ v1[0] = point1[0] - point2[0];
+ v1[1] = point1[1] - point2[1];
+ v1[2] = point1[2] - point2[2];
+ v1[3] = point1[3];
+ v2[0] = point2[0] - point3[0];
+ v2[1] = point2[1] - point3[1];
+ v2[2] = point2[2] - point3[2];
+
+ int p = cast(int) &abcd;
+ int q = cast(int) &point1;
+ abcd[0] = norm(7) + p;
+ abcd[1] = q + p;
+}
+
+extern(C) void main()
+{
+ double[4] a = 0.0;
+ inlinebug(a, a, a, a);
+}
auto funcLiteral = (int x, int y)
{
- enum thisFunc = "testkeyword.main.__lambda5";
- enum thisFunc2 = "testkeyword.main.__lambda5(int x, int y)";
+ enum thisFunc = "testkeyword.main.__lambda_L98_C24";
+ enum thisFunc2 = "testkeyword.main.__lambda_L98_C24(int x, int y)";
static assert(getFuncArgFile() == thisFile);
static assert(getFuncArgLine() == 104);
TRANSFORM_OUTPUT: remove_lines("Creating library")
TEST_OUTPUT:
---
-__lambda1
+__lambda_L1073_C5
---
*/
-2b89c2909de239bd603d6f36379658fe902667db
+82a5d2a7c4dd3d270537bcede2981e047bfd0e6a
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
core/internal/container/treap.d core/internal/convert.d \
core/internal/dassert.d core/internal/destruction.d \
core/internal/entrypoint.d core/internal/gc/bits.d \
+ core/internal/gc/blkcache.d core/internal/gc/blockmeta.d \
core/internal/gc/impl/conservative/gc.d \
core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \
core/internal/gc/os.d core/internal/gc/pooltable.d \
core/internal/container/treap.lo core/internal/convert.lo \
core/internal/dassert.lo core/internal/destruction.lo \
core/internal/entrypoint.lo core/internal/gc/bits.lo \
+ core/internal/gc/blkcache.lo core/internal/gc/blockmeta.lo \
core/internal/gc/impl/conservative/gc.lo \
core/internal/gc/impl/manual/gc.lo \
core/internal/gc/impl/proto/gc.lo core/internal/gc/os.lo \
core/internal/container/treap.d core/internal/convert.d \
core/internal/dassert.d core/internal/destruction.d \
core/internal/entrypoint.d core/internal/gc/bits.d \
+ core/internal/gc/blkcache.d core/internal/gc/blockmeta.d \
core/internal/gc/impl/conservative/gc.d \
core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \
core/internal/gc/os.d core/internal/gc/pooltable.d \
@$(MKDIR_P) core/internal/gc
@: > core/internal/gc/$(am__dirstamp)
core/internal/gc/bits.lo: core/internal/gc/$(am__dirstamp)
+core/internal/gc/blkcache.lo: core/internal/gc/$(am__dirstamp)
+core/internal/gc/blockmeta.lo: core/internal/gc/$(am__dirstamp)
core/internal/gc/impl/conservative/$(am__dirstamp):
@$(MKDIR_P) core/internal/gc/impl/conservative
@: > core/internal/gc/impl/conservative/$(am__dirstamp)
static if (__traits(isCopyable, T))
copyEmplace(value, dst);
else
- memcpy(cast(void*) &value, cast(void*) &dst, elemSize);
+ memcpy(cast(void*) &dst, cast(void*) &value, elemSize);
auto elem = cast(Unqual!T*) &tmp;
destroy(*elem);
}
assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
}
+// disabled copy constructor
+@safe unittest
+{
+ static struct S
+ {
+ int val;
+ @disable this(ref S);
+ }
+ S[1] arr;
+ S s = S(1234);
+ _d_arraysetassign(arr[], s);
+ assert(arr[0].val == 1234);
+}
+
// throwing and `nothrow`
@safe nothrow unittest
{
*/
bool __setArrayAllocLength(T)(ref BlkInfo info, size_t newLength, bool isShared, size_t oldLength = ~0)
{
- import core.atomic;
import core.lifetime : TypeInfoSize;
-
- size_t typeInfoSize = TypeInfoSize!T;
-
- if (info.size <= 256)
- {
- import core.checkedint;
-
- bool overflow;
- auto newLengthPadded = addu(newLength,
- addu(SMALLPAD, typeInfoSize, overflow),
- overflow);
-
- if (newLengthPadded > info.size || overflow)
- // new size does not fit inside block
- return false;
-
- auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD);
- if (oldLength != ~0)
- {
- if (isShared)
- {
- return cas(cast(shared)length, cast(ubyte)oldLength, cast(ubyte)newLength);
- }
- else
- {
- if (*length == cast(ubyte)oldLength)
- *length = cast(ubyte)newLength;
- else
- return false;
- }
- }
- else
- {
- // setting the initial length, no cas needed
- *length = cast(ubyte)newLength;
- }
- if (typeInfoSize)
- {
- auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
- *typeInfo = cast()typeid(T);
- }
- }
- else if (info.size < PAGESIZE)
- {
- if (newLength + MEDPAD + typeInfoSize > info.size)
- // new size does not fit inside block
- return false;
- auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD);
- if (oldLength != ~0)
- {
- if (isShared)
- {
- return cas(cast(shared)length, cast(ushort)oldLength, cast(ushort)newLength);
- }
- else
- {
- if (*length == oldLength)
- *length = cast(ushort)newLength;
- else
- return false;
- }
- }
- else
- {
- // setting the initial length, no cas needed
- *length = cast(ushort)newLength;
- }
- if (typeInfoSize)
- {
- auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
- *typeInfo = cast()typeid(T);
- }
- }
- else
- {
- if (newLength + LARGEPAD > info.size)
- // new size does not fit inside block
- return false;
- auto length = cast(size_t *)(info.base);
- if (oldLength != ~0)
- {
- if (isShared)
- {
- return cas(cast(shared)length, cast(size_t)oldLength, cast(size_t)newLength);
- }
- else
- {
- if (*length == oldLength)
- *length = newLength;
- else
- return false;
- }
- }
- else
- {
- // setting the initial length, no cas needed
- *length = newLength;
- }
- if (typeInfoSize)
- {
- auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof);
- *typeInfo = cast()typeid(T);
- }
- }
- return true; // resize succeeded
+ import core.internal.gc.blockmeta : __setArrayAllocLengthImpl;
+ return __setArrayAllocLengthImpl(info, newLength, isShared, typeid(T), oldLength, TypeInfoSize!T);
}
--- /dev/null
+/**
+BlkInfo thread-local cache. Used for array appending in the conservative GC to avoid the lock when possible.
+
+Note: this used to be in rt.lifetime, but was moved here to allow GCs to take over array operations.
+*/
+module core.internal.gc.blkcache;
+
+import core.memory;
+import core.attribute;
+
+alias BlkInfo = GC.BlkInfo;
+alias BlkAttr = GC.BlkAttr;
+
+/**
+ cache for the lookup of the block info
+ */
+private enum N_CACHE_BLOCKS = 8;
+
+// note this is TLS, so no need to sync.
+BlkInfo *__blkcache_storage;
+
+static if (N_CACHE_BLOCKS == 1)
+{
+ version=single_cache;
+}
+else
+{
+ //version=simple_cache; // uncomment to test simple cache strategy
+ //version=random_cache; // uncomment to test random cache strategy
+
+ // ensure N_CACHE_BLOCKS is power of 2.
+ static assert(!((N_CACHE_BLOCKS - 1) & N_CACHE_BLOCKS));
+
+ version (random_cache)
+ {
+ int __nextRndNum = 0;
+ }
+ int __nextBlkIdx;
+}
+
+@property BlkInfo *__blkcache() nothrow
+{
+ if (!__blkcache_storage)
+ {
+ import core.stdc.stdlib;
+ import core.stdc.string;
+ import core.thread.threadbase;
+ auto tBase = ThreadBase.getThis();
+ if (tBase is null)
+ // if we don't have a thread object, this is a detached thread, and
+ // this won't be properly maintained by the GC.
+ return null;
+
+ // allocate the block cache for the first time
+ immutable size = BlkInfo.sizeof * N_CACHE_BLOCKS;
+ // use C alloc, because this may become a detached thread, and the GC
+ // would then clean up the cache without zeroing this pointer.
+ __blkcache_storage = cast(BlkInfo*) calloc(size, 1);
+ tBase.tlsGCData = __blkcache_storage;
+ }
+ return __blkcache_storage;
+}
+
+// free the allocation on thread exit.
+@standalone static ~this()
+{
+ if (__blkcache_storage)
+ {
+ import core.stdc.stdlib;
+ import core.thread.threadbase;
+ auto tBase = ThreadBase.getThis();
+ if (tBase !is null)
+ tBase.tlsGCData = null;
+ free(__blkcache_storage);
+ __blkcache_storage = null;
+ }
+}
+
+/**
+ * Indicates whether an address has been marked by the GC.
+ */
+enum IsMarked : int
+{
+ no, /// Address is not marked.
+ yes, /// Address is marked.
+ unknown, /// Address is not managed by the GC.
+}
+
+alias IsMarkedDg = IsMarked delegate(void* addr) nothrow; /// The isMarked callback function.
+
+// we expect this to be called with the lock in place
+void processGCMarks(void* data, scope IsMarkedDg isMarked) nothrow
+{
+ if (!data)
+ return;
+
+ auto cache = cast(BlkInfo*) data;
+ // called after the mark routine to eliminate block cache data when it
+ // might be ready to sweep
+
+ debug(PRINTF) printf("processing GC Marks, %x\n", cache);
+ debug(PRINTF) foreach (i; 0 .. N_CACHE_BLOCKS)
+ {
+ printf("cache entry %d has base ptr %x\tsize %d\tflags %x\n", i, cache[i].base, cache[i].size, cache[i].attr);
+ }
+ auto cache_end = cache + N_CACHE_BLOCKS;
+ for (;cache < cache_end; ++cache)
+ {
+ if (cache.base != null && isMarked(cache.base) == IsMarked.no)
+ {
+ debug(PRINTF) printf("clearing cache entry at %x\n", cache.base);
+ cache.base = null; // clear that data.
+ }
+ }
+}
+
+unittest
+{
+ // Bugzilla 10701 - segfault in GC
+ ubyte[] result; result.length = 4096;
+ GC.free(result.ptr);
+ GC.collect();
+}
+
+/**
+ Get the cached block info of an interior pointer. Returns null if the
+ interior pointer's block is not cached.
+
+ NOTE: The following note was not valid, but is retained for historical
+ purposes. The data cannot be cleared because the stack contains a
+ reference to the affected block (e.g. through `interior`). Therefore,
+ the element will not be collected, and the data will remain valid.
+
+ ORIGINAL: The base ptr in this struct can be cleared asynchronously by the GC,
+ so any use of the returned BlkInfo should copy it and then check the
+ base ptr of the copy before actually using it.
+ */
+BlkInfo *__getBlkInfo(void *interior) nothrow
+{
+ BlkInfo *ptr = __blkcache;
+ if (ptr is null)
+ // if for some reason we don't have a cache, return null.
+ return null;
+ version (single_cache)
+ {
+ if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size)
+ return ptr;
+ return null; // not in cache.
+ }
+ else version (simple_cache)
+ {
+ foreach (i; 0..N_CACHE_BLOCKS)
+ {
+ if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size)
+ return ptr;
+ ptr++;
+ }
+ }
+ else
+ {
+ // try to do a smart lookup, using __nextBlkIdx as the "head"
+ auto curi = ptr + __nextBlkIdx;
+ for (auto i = curi; i >= ptr; --i)
+ {
+ if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size)
+ return i;
+ }
+
+ for (auto i = ptr + N_CACHE_BLOCKS - 1; i > curi; --i)
+ {
+ if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size)
+ return i;
+ }
+ }
+ return null; // not in cache.
+}
+
+void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow
+{
+ auto cache = __blkcache;
+ if (cache is null)
+ // no cache to use.
+ return;
+
+ version (single_cache)
+ {
+ *cache = bi;
+ return;
+ }
+ else
+ {
+ version (simple_cache)
+ {
+ if (curpos)
+ *curpos = bi;
+ else
+ {
+ // note, this is a super-simple algorithm that does not care about
+ // most recently used. It simply uses a round-robin technique to
+ // cache block info. This means that the ordering of the cache
+ // doesn't mean anything. Certain patterns of allocation may
+ // render the cache near-useless.
+ cache[__nextBlkIdx] = bi;
+ __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1);
+ }
+ }
+ else version (random_cache)
+ {
+ // strategy: if the block currently is in the cache, move the
+ // current block index to the a random element and evict that
+ // element.
+ if (!curpos)
+ {
+ __nextBlkIdx = (__nextRndNum = 1664525 * __nextRndNum + 1013904223) & (N_CACHE_BLOCKS - 1);
+ curpos = cache + __nextBlkIdx;
+ }
+ else
+ {
+ __nextBlkIdx = curpos - cache;
+ }
+ *curpos = bi;
+ }
+ else
+ {
+ //
+ // strategy: If the block currently is in the cache, swap it with
+ // the head element. Otherwise, move the head element up by one,
+ // and insert it there.
+ //
+ if (!curpos)
+ {
+ __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1);
+ curpos = cache + __nextBlkIdx;
+ }
+ else if (curpos !is cache + __nextBlkIdx)
+ {
+ *curpos = cache[__nextBlkIdx];
+ curpos = cache + __nextBlkIdx;
+ }
+ *curpos = bi;
+ }
+ }
+}
--- /dev/null
+/**
+ Functions to manipulate metadata in-block.
+
+ functionality was moved from rt.lifetime
+ */
+module core.internal.gc.blockmeta;
+
+import core.memory;
+
+alias BlkInfo = GC.BlkInfo;
+alias BlkAttr = GC.BlkAttr;
+
+enum : size_t
+{
+ PAGESIZE = 4096,
+ BIGLENGTHMASK = ~(PAGESIZE - 1),
+ SMALLPAD = 1,
+ MEDPAD = ushort.sizeof,
+ LARGEPREFIX = 16, // 16 bytes padding at the front of the array
+ LARGEPAD = LARGEPREFIX + 1,
+ MAXSMALLSIZE = 256-SMALLPAD,
+ MAXMEDSIZE = (PAGESIZE / 2) - MEDPAD
+}
+
+// size used to store the TypeInfo at the end of an allocation for structs that have a destructor
+size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
+{
+ if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
+ {
+ auto sti = cast(TypeInfo_Struct)cast(void*)ti;
+ if (sti.xdtor)
+ return size_t.sizeof;
+ }
+ return 0;
+}
+
+/**
+ Set the allocated length of the array block. This is called
+ any time an array is appended to or its length is set.
+
+ The allocated block looks like this for blocks < PAGESIZE:
+
+ |elem0|elem1|elem2|...|elemN-1|emptyspace|N*elemsize|
+
+
+ The size of the allocated length at the end depends on the block size:
+
+ a block of 16 to 256 bytes has an 8-bit length.
+
+ a block with 512 to pagesize/2 bytes has a 16-bit length.
+
+ For blocks >= pagesize, the length is a size_t and is at the beginning of the
+ block. The reason we have to do this is because the block can extend into
+ more pages, so we cannot trust the block length if it sits at the end of the
+ block, because it might have just been extended. If we can prove in the
+ future that the block is unshared, we may be able to change this, but I'm not
+ sure it's important.
+
+ In order to do put the length at the front, we have to provide 16 bytes
+ buffer space in case the block has to be aligned properly. In x86, certain
+ SSE instructions will only work if the data is 16-byte aligned. In addition,
+ we need the sentinel byte to prevent accidental pointers to the next block.
+ Because of the extra overhead, we only do this for page size and above, where
+ the overhead is minimal compared to the block size.
+
+ So for those blocks, it looks like:
+
+ |N*elemsize|padding|elem0|elem1|...|elemN-1|emptyspace|sentinelbyte|
+
+ where elem0 starts 16 bytes after the first byte.
+ */
+bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = ~0) pure nothrow
+{
+ size_t typeInfoSize = structTypeInfoSize(tinext);
+ return __setArrayAllocLengthImpl(info, newlength, isshared, tinext, oldlength, typeInfoSize);
+}
+
+// the impl function, used both above and in core.internal.array.utils
+bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength, size_t typeInfoSize) pure nothrow
+{
+ import core.atomic;
+
+ if (info.size <= 256)
+ {
+ import core.checkedint;
+
+ bool overflow;
+ auto newlength_padded = addu(newlength,
+ addu(SMALLPAD, typeInfoSize, overflow),
+ overflow);
+
+ if (newlength_padded > info.size || overflow)
+ // new size does not fit inside block
+ return false;
+
+ auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD);
+ if (oldlength != ~0)
+ {
+ if (isshared)
+ {
+ return cas(cast(shared)length, cast(ubyte)oldlength, cast(ubyte)newlength);
+ }
+ else
+ {
+ if (*length == cast(ubyte)oldlength)
+ *length = cast(ubyte)newlength;
+ else
+ return false;
+ }
+ }
+ else
+ {
+ // setting the initial length, no cas needed
+ *length = cast(ubyte)newlength;
+ }
+ if (typeInfoSize)
+ {
+ auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
+ *typeInfo = cast() tinext;
+ }
+ }
+ else if (info.size < PAGESIZE)
+ {
+ if (newlength + MEDPAD + typeInfoSize > info.size)
+ // new size does not fit inside block
+ return false;
+ auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD);
+ if (oldlength != ~0)
+ {
+ if (isshared)
+ {
+ return cas(cast(shared)length, cast(ushort)oldlength, cast(ushort)newlength);
+ }
+ else
+ {
+ if (*length == oldlength)
+ *length = cast(ushort)newlength;
+ else
+ return false;
+ }
+ }
+ else
+ {
+ // setting the initial length, no cas needed
+ *length = cast(ushort)newlength;
+ }
+ if (typeInfoSize)
+ {
+ auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
+ *typeInfo = cast() tinext;
+ }
+ }
+ else
+ {
+ if (newlength + LARGEPAD > info.size)
+ // new size does not fit inside block
+ return false;
+ auto length = cast(size_t *)(info.base);
+ if (oldlength != ~0)
+ {
+ if (isshared)
+ {
+ return cas(cast(shared)length, cast(size_t)oldlength, cast(size_t)newlength);
+ }
+ else
+ {
+ if (*length == oldlength)
+ *length = newlength;
+ else
+ return false;
+ }
+ }
+ else
+ {
+ // setting the initial length, no cas needed
+ *length = newlength;
+ }
+ if (typeInfoSize)
+ {
+ auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof);
+ *typeInfo = cast()tinext;
+ }
+ }
+ return true; // resize succeeded
+}
+
+/**
+ 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
+{
+ if (info.size <= 256)
+ return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD);
+
+ if (info.size < PAGESIZE)
+ return *cast(ushort *)(info.base + info.size - structTypeInfoSize(tinext) - MEDPAD);
+
+ return *cast(size_t *)(info.base);
+}
+
+/**
+ get the padding required to allocate size bytes. Note that the padding is
+ 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
+{
+ return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext));
+}
import core.internal.container.treap;
import core.internal.spinlock;
import core.internal.gc.pooltable;
+import core.internal.gc.blkcache;
import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
import core.stdc.string : memcpy, memset, memmove;
foreach (i, size; binsize)
{
- short end = (PAGESIZE / size) * size;
+ short end = cast(short) ((PAGESIZE / size) * size);
short bsz = size / 16;
foreach (off; 0..PAGESIZE/16)
{
markProcPid = 0;
// process GC marks then sweep
thread_suspendAll();
- thread_processGCMarks(&isMarked);
+ thread_processTLSGCData(&clearBlkCacheData);
thread_resumeAll();
break;
case ChildStatus.running:
markAll!(markConservative!false)();
}
- thread_processGCMarks(&isMarked);
+ thread_processTLSGCData(&clearBlkCacheData);
thread_resumeAll();
isFinal = false;
}
return freedPages;
}
+ /**
+ * Clear the block cache data if it exists, given the data which is the
+ * block info cache.
+ *
+ * Warning! This should only be called while the world is stopped inside
+ * the fullcollect function after all live objects have been marked, but
+ * before sweeping.
+ */
+ void *clearBlkCacheData(void* data) scope nothrow
+ {
+ processGCMarks(data, &isMarked);
+ return data;
+ }
+
/**
* Returns true if the addr lies within a marked block.
*
* Warning! This should only be called while the world is stopped inside
* the fullcollect function after all live objects have been marked, but before sweeping.
*/
- int isMarked(void *addr) scope nothrow
+ IsMarked isMarked(void *addr) scope nothrow
{
// first, we find the Pool this block is in, then check to see if the
// mark bit is clear.
}
else static if (is(S == struct))
{
- enum hasElaborateDestructor = __traits(hasMember, S, "__dtor")
- || anySatisfy!(.hasElaborateDestructor, Fields!S);
+ // Once https://issues.dlang.org/show_bug.cgi?id=24865 is fixed, then
+ // this should be the implementation, but until that's fixed, we need the
+ // uncommented code.
+ // enum hasElaborateDestructor = __traits(hasMember, S, "__xdtor");
+
+ enum hasElaborateDestructor = hasDtor([__traits(allMembers, S)]);
}
else
{
}
}
+private bool hasDtor(string[] members)
+{
+ foreach (name; members)
+ {
+ if (name == "__xdtor")
+ return true;
+ }
+
+ return false;
+}
+
+@safe unittest
+{
+ static struct NoDestructor {}
+ static assert(!hasElaborateDestructor!NoDestructor);
+ static assert(!hasElaborateDestructor!(NoDestructor[42]));
+ static assert(!hasElaborateDestructor!(NoDestructor[0]));
+ static assert(!hasElaborateDestructor!(NoDestructor[]));
+
+ static struct HasDestructor { ~this() {} }
+ static assert( hasElaborateDestructor!HasDestructor);
+ static assert( hasElaborateDestructor!(HasDestructor[42]));
+ static assert(!hasElaborateDestructor!(HasDestructor[0]));
+ static assert(!hasElaborateDestructor!(HasDestructor[]));
+
+ static struct HasDestructor2 { HasDestructor s; }
+ static assert( hasElaborateDestructor!HasDestructor2);
+ static assert( hasElaborateDestructor!(HasDestructor2[42]));
+ static assert(!hasElaborateDestructor!(HasDestructor2[0]));
+ static assert(!hasElaborateDestructor!(HasDestructor2[]));
+
+ static class HasFinalizer { ~this() {} }
+ static assert(!hasElaborateDestructor!HasFinalizer);
+
+ static struct HasUnion { union { HasDestructor s; } }
+ static assert(!hasElaborateDestructor!HasUnion);
+ static assert(!hasElaborateDestructor!(HasUnion[42]));
+ static assert(!hasElaborateDestructor!(HasUnion[0]));
+ static assert(!hasElaborateDestructor!(HasUnion[]));
+
+ static assert(!hasElaborateDestructor!int);
+ static assert(!hasElaborateDestructor!(int[0]));
+ static assert(!hasElaborateDestructor!(int[42]));
+ static assert(!hasElaborateDestructor!(int[]));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=24865
+@safe unittest
+{
+ static struct S2 { ~this() {} }
+ static struct S3 { S2 field; }
+ static struct S6 { S3[0] field; }
+
+ static assert( hasElaborateDestructor!S2);
+ static assert( hasElaborateDestructor!S3);
+ static assert(!hasElaborateDestructor!S6);
+}
+
// std.traits.hasElaborateCopyDestructor
template hasElaborateCopyConstructor(S)
{
this(int x, int y) {}
}
- static assert(hasElaborateCopyConstructor!S);
+ static assert( hasElaborateCopyConstructor!S);
static assert(!hasElaborateCopyConstructor!(S[0][1]));
static struct S2
this(int x, int y) {}
}
- static assert(hasElaborateCopyConstructor!S3);
+ static assert( hasElaborateCopyConstructor!S3);
+
+ static struct S4 { union { S s; } }
+
+ static assert(!hasElaborateCopyConstructor!S4);
}
template hasElaborateAssign(S)
else static if (is(S == struct))
{
enum hasElaborateAssign = is(typeof(S.init.opAssign(rvalueOf!S))) ||
- is(typeof(S.init.opAssign(lvalueOf!S))) ||
- anySatisfy!(.hasElaborateAssign, Fields!S);
+ is(typeof(S.init.opAssign(lvalueOf!S)));
}
else
{
}
}
+unittest
+{
+ {
+ static struct S {}
+ static assert(!hasElaborateAssign!S);
+ static assert(!hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct S { int i; }
+ static assert(!hasElaborateAssign!S);
+ static assert(!hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct S { void opAssign(S) {} }
+ static assert( hasElaborateAssign!S);
+ static assert( hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct S { void opAssign(ref S) {} }
+ static assert( hasElaborateAssign!S);
+ static assert( hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct S { void opAssign(int) {} }
+ static assert(!hasElaborateAssign!S);
+ static assert(!hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct S { this(this) {} }
+ static assert( hasElaborateAssign!S);
+ static assert( hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=24834
+ /+
+ {
+ static struct S { this(ref S) {} }
+ static assert( hasElaborateAssign!S);
+ static assert( hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ +/
+ {
+ static struct S { ~this() {} }
+ static assert( hasElaborateAssign!S);
+ static assert( hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct S { @disable void opAssign(S); }
+ static assert(!hasElaborateAssign!S);
+ static assert(!hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct Member {}
+ static struct S { Member member; }
+ static assert(!hasElaborateAssign!S);
+ static assert(!hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct Member { void opAssign(Member) {} }
+ static struct S { Member member; }
+ static assert( hasElaborateAssign!S);
+ static assert( hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct Member {}
+ static struct S { Member member; void opAssign(S) {} }
+ static assert( hasElaborateAssign!S);
+ static assert( hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct Member { @disable void opAssign(Member); }
+ static struct S { Member member; }
+ static assert(!hasElaborateAssign!S);
+ static assert(!hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct Member { @disable void opAssign(Member); }
+ static struct S { Member member; void opAssign(S) {} }
+ static assert( hasElaborateAssign!S);
+ static assert( hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct Member { void opAssign(Member) {} }
+ static struct S { Member member; @disable void opAssign(S); }
+ static assert(!hasElaborateAssign!S);
+ static assert(!hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+ {
+ static struct Member { void opAssign(Member) {} }
+ static struct S { union { Member member; } }
+ static assert(!hasElaborateAssign!S);
+ static assert(!hasElaborateAssign!(S[10]));
+ static assert(!hasElaborateAssign!(S[0]));
+ static assert(!hasElaborateAssign!(S[]));
+ }
+
+ static assert(!hasElaborateAssign!int);
+ static assert(!hasElaborateAssign!(string[]));
+ static assert(!hasElaborateAssign!Object);
+}
+
template hasIndirections(T)
{
static if (is(T == struct) || is(T == union))
enum hasIndirections = anySatisfy!(.hasIndirections, typeof(T.tupleof));
+ else static if (__traits(isAssociativeArray, T) || is(T == class) || is(T == interface))
+ enum hasIndirections = true;
else static if (is(T == E[N], E, size_t N))
enum hasIndirections = T.sizeof && is(E == void) ? true : hasIndirections!(BaseElemOf!E);
else static if (isFunctionPointer!T)
enum hasIndirections = false;
else
- enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T ||
- __traits(isAssociativeArray, T) || is (T == class) || is(T == interface);
+ enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T;
}
template hasUnsharedIndirections(T)
auto init = __traits(initSymbol, T);
void* p;
- static if (__traits(getLinkage, T) == "Windows")
+ static if (__traits(isCOMClass, T))
{
+ // If this is a COM class we allocate it using malloc.
+ // This allows the reference counting to outlive the reference known about by the GC.
+
p = pureMalloc(init.length);
if (!p)
onOutOfMemoryError();
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Authors: Sean Kelly, Alex Rønne Petersen
* Source: $(DRUNTIMESRC core/_memory.d)
+ * Macros:
+ * WARN_UNINITIALIZED=$(RED Warning):
+ * $1 will be uninitialized, and may happen to hold pointers to GC memory.
+ * Consider zeroing out any uninitialized bytes which won't be immediately written to.
*/
module core.memory;
* a = A bit field containing any bits to set for this memory block.
*
* Returns:
- * The result of a call to getAttr after the specified bits have been
+ * The result of a call to $(LREF getAttr) after the specified bits have been
* set.
*/
static uint setAttr( const scope void* p, uint a ) nothrow
* a = A bit field containing any bits to clear for this memory block.
*
* Returns:
- * The result of a call to getAttr after the specified bits have been
+ * The result of a call to $(LREF getAttr) after the specified bits have been
* cleared.
*/
static uint clrAttr( const scope void* p, uint a ) nothrow
* A reference to the allocated memory or null if insufficient memory
* is available.
*
+ * $(WARN_UNINITIALIZED Allocated memory)
+ *
* Throws:
* OutOfMemoryError on allocation failure.
*/
/**
* Requests an aligned block of managed memory from the garbage collector.
- * This memory may be deleted at will with a call to free, or it may be
+ * This memory may be deleted at will with a call to $(LREF free), or it may be
* discarded and cleaned up automatically during a collection run. If
* allocation fails, this function will call onOutOfMemory which is
* expected to throw an OutOfMemoryError.
* Information regarding the allocated memory block or BlkInfo.init on
* error.
*
+ * $(WARN_UNINITIALIZED Allocated memory)
+ *
* Throws:
* OutOfMemoryError on allocation failure.
*/
* zero or the pointer does not point to the base of an GC allocated
* memory block.
*
+ * $(WARN_UNINITIALIZED Any extra bytes past the initial size)
+ *
* Throws:
* `OutOfMemoryError` on allocation failure.
*/
* The size in bytes of the extended memory block referenced by p or zero
* if no extension occurred.
*
+ * $(WARN_UNINITIALIZED Any extension bytes)
+ *
* Note:
* Extend may also be used to extend slices (or memory blocks with
* $(LREF APPENDABLE) info). However, use the return value only
* If p references memory not originally allocated by this garbage
* collector, if p points to the interior of a memory block, or if this
* method is called from a finalizer, no action will be taken. The block
- * will not be finalized regardless of whether the FINALIZE attribute is
+ * will not be finalized regardless of whether the $(LREF FINALIZE) attribute is
* set. If finalization is desired, call $(REF1 destroy, object) prior to `GC.free`.
*
* Params:
/**
* Returns the true size of the memory block referenced by p. This value
- * represents the maximum number of bytes for which a call to realloc may
+ * represents the maximum number of bytes for which a call to $(LREF realloc) may
* resize the existing block in place. If p references memory not
* originally allocated by this garbage collector, points to the interior
* of a memory block, or if p is null, zero will be returned.
atomicStore!(MemoryOrder.raw)(thisThread.toThread.m_isRunning, true);
}
thisThread.m_isDaemon = true;
- thisThread.tlsGCdataInit();
+ thisThread.tlsRTdataInit();
Thread.setThis( thisThread );
version (Darwin)
if ( addr == GetCurrentThreadId() )
{
thisThread.m_hndl = GetCurrentThreadHandle();
- thisThread.tlsGCdataInit();
+ thisThread.tlsRTdataInit();
Thread.setThis( thisThread );
}
else
thisThread.m_hndl = OpenThreadHandle( addr );
impersonate_thread(addr,
{
- thisThread.tlsGCdataInit();
+ thisThread.tlsRTdataInit();
Thread.setThis( thisThread );
});
}
alias ScanDg = void delegate(void* pstart, void* pend) nothrow;
alias rt_tlsgc_scan =
externDFunc!("rt.tlsgc.scan", void function(void*, scope ScanDg) nothrow);
-
- alias rt_tlsgc_processGCMarks =
- externDFunc!("rt.tlsgc.processGCMarks", void function(void*, scope IsMarkedDg) nothrow);
}
return (no_context || not_registered);
}
- package void tlsGCdataInit() nothrow @nogc
+ ref void* tlsGCData() nothrow @nogc
{
- m_tlsgcdata = rt_tlsgc_init();
+ return m_tlsgcdata;
+ }
+
+ package void tlsRTdataInit() nothrow @nogc
+ {
+ m_tlsrtdata = rt_tlsgc_init();
}
package void initDataStorage() nothrow
m_main.bstack = getStackBottom();
m_main.tstack = m_main.bstack;
- tlsGCdataInit();
+ tlsRTdataInit();
}
package void destroyDataStorage() nothrow @nogc
{
- rt_tlsgc_destroy(m_tlsgcdata);
- m_tlsgcdata = null;
+ rt_tlsgc_destroy(m_tlsrtdata);
+ m_tlsrtdata = null;
}
package void destroyDataStorageIfAvail() nothrow @nogc
{
- if (m_tlsgcdata)
+ if (m_tlsrtdata)
destroyDataStorage();
}
StackContext* m_curr;
bool m_lock;
private void* m_tlsgcdata;
+ private void* m_tlsrtdata;
///////////////////////////////////////////////////////////////////////////
// Thread Context and GC Scanning Support
scanWindowsOnly(scan, t);
}
- if (t.m_tlsgcdata !is null)
- rt_tlsgc_scan(t.m_tlsgcdata, (p1, p2) => scan(ScanType.tls, p1, p2));
+ if (t.m_tlsrtdata !is null)
+ rt_tlsgc_scan(t.m_tlsrtdata, (p1, p2) => scan(ScanType.tls, p1, p2));
}
}
}
-/**
- * Indicates whether an address has been marked by the GC.
- */
-enum IsMarked : int
-{
- no, /// Address is not marked.
- yes, /// Address is marked.
- unknown, /// Address is not managed by the GC.
-}
-
-alias IsMarkedDg = int delegate(void* addr) nothrow; /// The isMarked callback function.
+// GC-specific processing of TLSGC data.
+alias ProcessTLSGCDataDg = void* delegate(void* data) nothrow;
-/**
- * This routine allows the runtime to process any special per-thread handling
- * for the GC. This is needed for taking into account any memory that is
- * referenced by non-scanned pointers but is about to be freed. That currently
- * means the array append cache.
- *
- * Params:
- * isMarked = The function used to check if $(D addr) is marked.
- *
- * In:
- * This routine must be called just prior to resuming all threads.
- */
-extern(C) void thread_processGCMarks(scope IsMarkedDg isMarked) nothrow
+void thread_processTLSGCData(ProcessTLSGCDataDg dg) nothrow
{
for (ThreadBase t = ThreadBase.sm_tbeg; t; t = t.next)
- {
- /* Can be null if collection was triggered between adding a
- * thread and calling rt_tlsgc_init.
- */
- if (t.m_tlsgcdata !is null)
- rt_tlsgc_processGCMarks(t.m_tlsgcdata, isMarked);
- }
+ t.m_tlsgcdata = dg(t.m_tlsgcdata);
}
-
/**
* Returns the stack top of the currently active stack within the calling
* thread.
import core.attribute : weak;
import core.internal.array.utils : __arrayStart, __arrayClearPad;
import core.memory;
+import core.internal.gc.blkcache;
+import core.internal.gc.blockmeta;
debug(PRINTF) import core.stdc.stdio;
static import rt.tlsgc;
extern (C) void _d_monitordelete(Object h, bool det);
- enum : size_t
- {
- PAGESIZE = 4096,
- BIGLENGTHMASK = ~(PAGESIZE - 1),
- SMALLPAD = 1,
- MEDPAD = ushort.sizeof,
- LARGEPREFIX = 16, // 16 bytes padding at the front of the array
- LARGEPAD = LARGEPREFIX + 1,
- MAXSMALLSIZE = 256-SMALLPAD,
- MAXMEDSIZE = (PAGESIZE / 2) - MEDPAD
- }
}
// Now-removed symbol, kept around for ABI
return ti;
}
-// size used to store the TypeInfo at the end of an allocation for structs that have a destructor
-size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
-{
- if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
- {
- auto sti = cast(TypeInfo_Struct)cast(void*)ti;
- if (sti.xdtor)
- return size_t.sizeof;
- }
- return 0;
-}
-
-/** dummy class used to lock for shared array appending */
-private class ArrayAllocLengthLock
-{}
-
-/**
- Set the allocated length of the array block. This is called
- any time an array is appended to or its length is set.
-
- The allocated block looks like this for blocks < PAGESIZE:
-
- |elem0|elem1|elem2|...|elemN-1|emptyspace|N*elemsize|
-
-
- The size of the allocated length at the end depends on the block size:
-
- a block of 16 to 256 bytes has an 8-bit length.
-
- a block with 512 to pagesize/2 bytes has a 16-bit length.
-
- For blocks >= pagesize, the length is a size_t and is at the beginning of the
- block. The reason we have to do this is because the block can extend into
- more pages, so we cannot trust the block length if it sits at the end of the
- block, because it might have just been extended. If we can prove in the
- future that the block is unshared, we may be able to change this, but I'm not
- sure it's important.
-
- In order to do put the length at the front, we have to provide 16 bytes
- buffer space in case the block has to be aligned properly. In x86, certain
- SSE instructions will only work if the data is 16-byte aligned. In addition,
- we need the sentinel byte to prevent accidental pointers to the next block.
- Because of the extra overhead, we only do this for page size and above, where
- the overhead is minimal compared to the block size.
-
- So for those blocks, it looks like:
-
- |N*elemsize|padding|elem0|elem1|...|elemN-1|emptyspace|sentinelbyte|
-
- where elem0 starts 16 bytes after the first byte.
- */
-bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = ~0) pure nothrow
-{
- import core.atomic;
-
- size_t typeInfoSize = structTypeInfoSize(tinext);
-
- if (info.size <= 256)
- {
- import core.checkedint;
-
- bool overflow;
- auto newlength_padded = addu(newlength,
- addu(SMALLPAD, typeInfoSize, overflow),
- overflow);
-
- if (newlength_padded > info.size || overflow)
- // new size does not fit inside block
- return false;
-
- auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD);
- if (oldlength != ~0)
- {
- if (isshared)
- {
- return cas(cast(shared)length, cast(ubyte)oldlength, cast(ubyte)newlength);
- }
- else
- {
- if (*length == cast(ubyte)oldlength)
- *length = cast(ubyte)newlength;
- else
- return false;
- }
- }
- else
- {
- // setting the initial length, no cas needed
- *length = cast(ubyte)newlength;
- }
- if (typeInfoSize)
- {
- auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
- *typeInfo = cast() tinext;
- }
- }
- else if (info.size < PAGESIZE)
- {
- if (newlength + MEDPAD + typeInfoSize > info.size)
- // new size does not fit inside block
- return false;
- auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD);
- if (oldlength != ~0)
- {
- if (isshared)
- {
- return cas(cast(shared)length, cast(ushort)oldlength, cast(ushort)newlength);
- }
- else
- {
- if (*length == oldlength)
- *length = cast(ushort)newlength;
- else
- return false;
- }
- }
- else
- {
- // setting the initial length, no cas needed
- *length = cast(ushort)newlength;
- }
- if (typeInfoSize)
- {
- auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
- *typeInfo = cast() tinext;
- }
- }
- else
- {
- if (newlength + LARGEPAD > info.size)
- // new size does not fit inside block
- return false;
- auto length = cast(size_t *)(info.base);
- if (oldlength != ~0)
- {
- if (isshared)
- {
- return cas(cast(shared)length, cast(size_t)oldlength, cast(size_t)newlength);
- }
- else
- {
- if (*length == oldlength)
- *length = newlength;
- else
- return false;
- }
- }
- else
- {
- // setting the initial length, no cas needed
- *length = newlength;
- }
- if (typeInfoSize)
- {
- auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof);
- *typeInfo = cast()tinext;
- }
- }
- return true; // resize succeeded
-}
-
-/**
- get the allocation size of the array for the given block (without padding or type info)
- */
-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);
-
- if (info.size < PAGESIZE)
- return *cast(ushort *)(info.base + info.size - structTypeInfoSize(tinext) - MEDPAD);
-
- return *cast(size_t *)(info.base);
-}
-
-/**
- get the padding required to allocate size bytes. Note that the padding is
- NOT included in the passed in size. Therefore, do NOT call this function
- with the size of an allocated block.
- */
-private size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted
-{
- return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext));
-}
-
/**
allocate an array memory block by applying the proper padding and
assigning block attributes if not inherited from the existing block
return bi;
}
-/**
- cache for the lookup of the block info
- */
-private enum N_CACHE_BLOCKS=8;
-
-// note this is TLS, so no need to sync.
-BlkInfo *__blkcache_storage;
-
-static if (N_CACHE_BLOCKS==1)
-{
- version=single_cache;
-}
-else
-{
- //version=simple_cache; // uncomment to test simple cache strategy
- //version=random_cache; // uncomment to test random cache strategy
-
- // ensure N_CACHE_BLOCKS is power of 2.
- static assert(!((N_CACHE_BLOCKS - 1) & N_CACHE_BLOCKS));
-
- version (random_cache)
- {
- int __nextRndNum = 0;
- }
- int __nextBlkIdx;
-}
-
-@property BlkInfo *__blkcache() nothrow
-{
- if (!__blkcache_storage)
- {
- import core.stdc.stdlib;
- import core.stdc.string;
- // allocate the block cache for the first time
- immutable size = BlkInfo.sizeof * N_CACHE_BLOCKS;
- __blkcache_storage = cast(BlkInfo *)malloc(size);
- memset(__blkcache_storage, 0, size);
- }
- return __blkcache_storage;
-}
-
-// called when thread is exiting.
-static ~this()
-{
- // free the blkcache
- if (__blkcache_storage)
- {
- import core.stdc.stdlib;
- free(__blkcache_storage);
- __blkcache_storage = null;
- }
-}
-
-
-// we expect this to be called with the lock in place
-void processGCMarks(BlkInfo* cache, scope rt.tlsgc.IsMarkedDg isMarked) nothrow
-{
- // called after the mark routine to eliminate block cache data when it
- // might be ready to sweep
-
- debug(PRINTF) printf("processing GC Marks, %x\n", cache);
- if (cache)
- {
- debug(PRINTF) foreach (i; 0 .. N_CACHE_BLOCKS)
- {
- printf("cache entry %d has base ptr %x\tsize %d\tflags %x\n", i, cache[i].base, cache[i].size, cache[i].attr);
- }
- auto cache_end = cache + N_CACHE_BLOCKS;
- for (;cache < cache_end; ++cache)
- {
- if (cache.base != null && !isMarked(cache.base))
- {
- debug(PRINTF) printf("clearing cache entry at %x\n", cache.base);
- cache.base = null; // clear that data.
- }
- }
- }
-}
-
-unittest
-{
- // Bugzilla 10701 - segfault in GC
- ubyte[] result; result.length = 4096;
- GC.free(result.ptr);
- GC.collect();
-}
-
-/**
- Get the cached block info of an interior pointer. Returns null if the
- interior pointer's block is not cached.
-
- NOTE: The base ptr in this struct can be cleared asynchronously by the GC,
- so any use of the returned BlkInfo should copy it and then check the
- base ptr of the copy before actually using it.
-
- TODO: Change this function so the caller doesn't have to be aware of this
- issue. Either return by value and expect the caller to always check
- the base ptr as an indication of whether the struct is valid, or set
- the BlkInfo as a side-effect and return a bool to indicate success.
- */
-BlkInfo *__getBlkInfo(void *interior) nothrow
-{
- BlkInfo *ptr = __blkcache;
- version (single_cache)
- {
- if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size)
- return ptr;
- return null; // not in cache.
- }
- else version (simple_cache)
- {
- foreach (i; 0..N_CACHE_BLOCKS)
- {
- if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size)
- return ptr;
- ptr++;
- }
- }
- else
- {
- // try to do a smart lookup, using __nextBlkIdx as the "head"
- auto curi = ptr + __nextBlkIdx;
- for (auto i = curi; i >= ptr; --i)
- {
- if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size)
- return i;
- }
-
- for (auto i = ptr + N_CACHE_BLOCKS - 1; i > curi; --i)
- {
- if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size)
- return i;
- }
- }
- return null; // not in cache.
-}
-
-void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow
-{
- version (single_cache)
- {
- *__blkcache = bi;
- }
- else
- {
- version (simple_cache)
- {
- if (curpos)
- *curpos = bi;
- else
- {
- // note, this is a super-simple algorithm that does not care about
- // most recently used. It simply uses a round-robin technique to
- // cache block info. This means that the ordering of the cache
- // doesn't mean anything. Certain patterns of allocation may
- // render the cache near-useless.
- __blkcache[__nextBlkIdx] = bi;
- __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1);
- }
- }
- else version (random_cache)
- {
- // strategy: if the block currently is in the cache, move the
- // current block index to the a random element and evict that
- // element.
- auto cache = __blkcache;
- if (!curpos)
- {
- __nextBlkIdx = (__nextRndNum = 1664525 * __nextRndNum + 1013904223) & (N_CACHE_BLOCKS - 1);
- curpos = cache + __nextBlkIdx;
- }
- else
- {
- __nextBlkIdx = curpos - cache;
- }
- *curpos = bi;
- }
- else
- {
- //
- // strategy: If the block currently is in the cache, swap it with
- // the head element. Otherwise, move the head element up by one,
- // and insert it there.
- //
- auto cache = __blkcache;
- if (!curpos)
- {
- __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1);
- curpos = cache + __nextBlkIdx;
- }
- else if (curpos !is cache + __nextBlkIdx)
- {
- *curpos = cache[__nextBlkIdx];
- curpos = cache + __nextBlkIdx;
- }
- *curpos = bi;
- }
- }
-}
-
/**
Shrink the "allocated" length of an array to be the exact size of the array.
struct Data
{
typeof(rt.sections.initTLSRanges()) tlsRanges;
- rt.lifetime.BlkInfo** blockInfoCache;
}
/**
// do module specific initialization
data.tlsRanges = rt.sections.initTLSRanges();
- data.blockInfoCache = &rt.lifetime.__blkcache_storage;
-
return data;
}
// do module specific marking
rt.sections.scanTLSRanges((cast(Data*)data).tlsRanges, dg);
}
-
-alias int delegate(void* addr) nothrow IsMarkedDg;
-
-/**
- * GC sweep hook, called FOR each thread. Can be used to free
- * additional thread local memory or associated data structures. Note
- * that only memory allocated from the GC can have marks.
- */
-void processGCMarks(void* data, scope IsMarkedDg dg) nothrow
-{
- // do module specific sweeping
- rt.lifetime.processGCMarks(*(cast(Data*)data).blockInfoCache, dg);
-}
-2a730adc07b0a708b31dd8e592f56df4adbaf4be
+dbc09d8230f0e273af8a78546e5431a7783478b5
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
assert(arr.minElement!"a.val".val == 0);
}
+// https://issues.dlang.org/show_bug.cgi?id=24827
+@safe unittest
+{
+ static struct S
+ {
+ int i;
+ bool destroyed;
+
+ this(int i) @safe
+ {
+ this.i = i;
+ }
+
+ ~this() @safe
+ {
+ destroyed = true;
+ }
+
+ bool opEquals()(auto ref S rhs)
+ {
+ return this.i == rhs.i;
+ }
+
+ int opCmp()(auto ref S rhs)
+ {
+ if (this.i < rhs.i)
+ return -1;
+
+ return this.i == rhs.i ? 0 : 1;
+ }
+
+ @safe invariant
+ {
+ assert(!destroyed);
+ }
+ }
+
+ auto arr = [S(19), S(2), S(145), S(7)];
+ assert(minElement(arr) == S(2));
+}
+
/**
Iterates the passed range and returns the maximal element.
A custom mapping function can be passed to `map`.
assert(arr[0].getI == 2);
}
+// https://issues.dlang.org/show_bug.cgi?id=24827
+@safe unittest
+{
+ static struct S
+ {
+ int i;
+ bool destroyed;
+
+ this(int i) @safe
+ {
+ this.i = i;
+ }
+
+ ~this() @safe
+ {
+ destroyed = true;
+ }
+
+ bool opEquals()(auto ref S rhs)
+ {
+ return this.i == rhs.i;
+ }
+
+ int opCmp()(auto ref S rhs)
+ {
+ if (this.i < rhs.i)
+ return -1;
+
+ return this.i == rhs.i ? 0 : 1;
+ }
+
+ @safe invariant
+ {
+ assert(!destroyed);
+ }
+ }
+
+ auto arr = [S(19), S(2), S(145), S(7)];
+ assert(maxElement(arr) == S(145));
+}
+
// minPos
/**
Computes a subrange of `range` starting at the first occurrence of `range`'s
}
else
{
+ import core.stdc.string : memcpy, memset;
// Time to reallocate.
// We need to almost duplicate what's in druntime, except we
// have better access to the capacity field.
if (u)
{
// extend worked, update the capacity
+ // if the type has indirections, we need to zero any new
+ // data that we requested, as the existing data may point
+ // at large unused blocks.
+ static if (hasIndirections!T)
+ {
+ immutable addedSize = u - (_data.capacity * T.sizeof);
+ () @trusted { memset(_data.arr.ptr + _data.capacity, 0, addedSize); }();
+ }
+
_data.capacity = u / T.sizeof;
return;
}
auto bi = (() @trusted => GC.qalloc(nbytes, blockAttribute!T))();
_data.capacity = bi.size / T.sizeof;
- import core.stdc.string : memcpy;
if (len)
() @trusted { memcpy(bi.base, _data.arr.ptr, len * T.sizeof); }();
+
_data.arr = (() @trusted => (cast(Unqual!T*) bi.base)[0 .. len])();
+
+ // we requested new bytes that are not in the existing
+ // data. If T has pointers, then this new data could point at stale
+ // objects from the last time this block was allocated. Zero that
+ // new data out, it may point at large unused blocks!
+ static if (hasIndirections!T)
+ () @trusted {
+ memset(bi.base + (len * T.sizeof), 0, (newlen - len) * T.sizeof);
+ }();
+
_data.tryExtendBlock = true;
// leave the old data, for safety reasons
}
app2.toString();
}
+// https://issues.dlang.org/show_bug.cgi?id=24856
+@system unittest
+{
+ import core.memory : GC;
+ import std.stdio : writeln;
+ import std.algorithm.searching : canFind;
+ GC.disable();
+ scope(exit) GC.enable();
+ void*[] freeme;
+ // generate some poison blocks to allocate from.
+ auto poison = cast(void*) 0xdeadbeef;
+ foreach (i; 0 .. 10)
+ {
+ auto blk = new void*[7];
+ blk[] = poison;
+ freeme ~= blk.ptr;
+ }
+
+ foreach (p; freeme)
+ GC.free(p);
+
+ int tests = 0;
+ foreach (i; 0 .. 10)
+ {
+ Appender!(void*[]) app;
+ app.put(null);
+ // if not a realloc of one of the deadbeef pointers, continue
+ if (!freeme.canFind(app.data.ptr))
+ continue;
+ ++tests;
+ assert(!app.data.ptr[0 .. app.capacity].canFind(poison), "Appender not zeroing data!");
+ }
+ // just notify in the log whether this test actually could be done.
+ if (tests == 0)
+ writeln("WARNING: test of Appender zeroing did not occur");
+}
+
//Calculates an efficient growth scheme based on the old capacity
//of data, and the minimum requested capacity.
//arg curLen: The current length
enum RightShiftOp = ">>>=";
}
- static if (is(T == bool))
+ static if (is(T : bool))
{
enum createAccessors =
// getter
foreach (i; 0 .. 63)
assert(bitsSet(1UL << i).equal([i]));
}
+
+// Fix https://issues.dlang.org/show_bug.cgi?id=24095
+@safe @nogc pure unittest
+{
+ enum Bar : bool
+ {
+ a,
+ b,
+ }
+
+ struct Foo
+ {
+ mixin(bitfields!(Bar, "bar", 1, ubyte, "", 7,));
+ }
+
+ Foo foo;
+ foo.bar = Bar.a;
+ assert(foo.bar == Bar.a);
+ foo.bar = Bar.b;
+ assert(foo.bar == Bar.b);
+}
struct DList(T)
{
import std.range : Take;
+ import std.traits : isMutable;
/*
A Node with a Payload. A PayNode.
{
import std.algorithm.mutation : move;
- return (new PayNode(BaseNode(prev, next), move(arg))).asBaseNode();
+ static if (isMutable!Stuff)
+ return (new PayNode(BaseNode(prev, next), move(arg))).asBaseNode();
+ else
+ return (new PayNode(BaseNode(prev, next), arg)).asBaseNode();
}
void initialize() nothrow @safe pure
list.removeFront();
assert(list[].walkLength == 0);
}
+
+// https://issues.dlang.org/show_bug.cgi?id=24637
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ struct A
+ {
+ int c;
+ }
+
+ DList!A B;
+ B.insert(A(1));
+ assert(B[].equal([A(1)]));
+
+ const a = A(3);
+ B.insert(a);
+ assert(B[].equal([A(1), A(3)]));
+}
string s1 = "123";
auto a1 = parse!(int, string, Yes.doCount)(s1);
assert(a1.data == 123 && a1.count == 3);
-
- // parse only accepts lvalues
- static assert(!__traits(compiles, parse!int("123")));
}
///
Returns:
a `string`, a `wstring` or a `dstring`, according to the type of hexData.
+
+See_Also:
+ Use $(REF fromHexString, std, digest) for run time conversions.
+ Note, these functions are not drop-in replacements and have different
+ input requirements.
+ This template inherits its data syntax from builtin
+ $(LINK2 $(ROOT_DIR)spec/lex.html#hex_string, hex strings).
+ See $(REF fromHexString, std, digest) for its own respective requirements.
*/
template hexString(string hexData)
if (hexData.isHexLiteral)
assert(!secureEqual(hex1, hex2));
}
}
+
+/**
+ * Validates a hex string.
+ *
+ * Checks whether all characters following an optional "0x" suffix
+ * are valid hexadecimal digits.
+ *
+ * Params:
+ * hex = hexdecimal encoded byte array
+ * Returns:
+ * true = if valid
+ */
+bool isHexString(String)(String hex) @safe pure nothrow @nogc
+if (isSomeString!String)
+{
+ import std.ascii : isHexDigit;
+
+ if ((hex.length >= 2) && (hex[0 .. 2] == "0x"))
+ {
+ hex = hex[2 .. $];
+ }
+
+ foreach (digit; hex)
+ {
+ if (!digit.isHexDigit)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+///
+@safe unittest
+{
+ assert(isHexString("0x0123456789ABCDEFabcdef"));
+ assert(isHexString("0123456789ABCDEFabcdef"));
+ assert(!isHexString("g"));
+ assert(!isHexString("#"));
+}
+
+/**
+ * Converts a hex text string to a range of bytes.
+ *
+ * The input to this function MUST be valid.
+ * $(REF isHexString, std, digest) can be used to check for this if needed.
+ *
+ * Params:
+ * hex = String representation of a hexdecimal-encoded byte array.
+ * Returns:
+ * A forward range of bytes.
+ */
+auto fromHexStringAsRange(String)(String hex) @safe pure nothrow @nogc
+if (isSomeString!String)
+{
+ return HexStringDecoder!String(hex);
+}
+
+///
+@safe unittest
+{
+ import std.range.primitives : ElementType, isForwardRange;
+ import std.traits : ReturnType;
+
+ // The decoder implements a forward range.
+ static assert(isForwardRange!(ReturnType!(fromHexStringAsRange!string)));
+ static assert(isForwardRange!(ReturnType!(fromHexStringAsRange!wstring)));
+ static assert(isForwardRange!(ReturnType!(fromHexStringAsRange!dstring)));
+
+ // The element type of the range is always `ubyte`.
+ static assert(
+ is(ElementType!(ReturnType!(fromHexStringAsRange!string)) == ubyte)
+ );
+ static assert(
+ is(ElementType!(ReturnType!(fromHexStringAsRange!wstring)) == ubyte)
+ );
+ static assert(
+ is(ElementType!(ReturnType!(fromHexStringAsRange!dstring)) == ubyte)
+ );
+}
+
+@safe unittest
+{
+ import std.array : staticArray;
+
+ // `staticArray` consumes the range returned by `fromHexStringAsRange`.
+ assert("0x0000ff".fromHexStringAsRange.staticArray!3 == [0, 0, 0xFF]);
+ assert("0x0000ff"w.fromHexStringAsRange.staticArray!3 == [0, 0, 0xFF]);
+ assert("0x0000ff"d.fromHexStringAsRange.staticArray!3 == [0, 0, 0xFF]);
+ assert("0xff12ff".fromHexStringAsRange.staticArray!1 == [0xFF]);
+ assert("0x12ff".fromHexStringAsRange.staticArray!2 == [0x12, 255]);
+ assert(
+ "0x3AaAA".fromHexStringAsRange.staticArray!4 == [0x3, 0xAA, 0xAA, 0x00]
+ );
+}
+
+/**
+ * Converts a hex text string to a range of bytes.
+ *
+ * Params:
+ * hex = String representation of a hexdecimal-encoded byte array.
+ * Returns:
+ * An newly allocated array of bytes.
+ * Throws:
+ * Exception on invalid input.
+ * Example:
+ * ---
+ * ubyte[] dby = "0xBA".fromHexString;
+ * ---
+ * See_Also:
+ * $(REF fromHexString, std, digest) for a range version of the function.
+ */
+ubyte[] fromHexString(String)(String hex) @safe pure
+if (isSomeString!String)
+{
+ // This function is trivial, yet necessary for consistency.
+ // It provides a similar API to its `toHexString` counterpart.
+
+ if (!hex.isHexString)
+ {
+ import std.conv : text;
+
+ throw new Exception(
+ "The provided character sequence `"
+ ~ hex.text
+ ~ "` is not a valid hex string."
+ );
+ }
+
+ if ((hex.length >= 2) && (hex[0 .. 2] == "0x"))
+ {
+ hex = hex[2 .. $];
+ }
+
+ auto decoder = HexStringDecoder!String(hex);
+ auto result = new ubyte[](decoder.length);
+
+ size_t idx = 0;
+ foreach (b; decoder)
+ {
+ result[idx++] = b;
+ }
+ return result;
+}
+
+///
+@safe unittest
+{
+ // Single byte
+ assert("0xff".fromHexString == [255]);
+ assert("0xff"w.fromHexString == [255]);
+ assert("0xff"d.fromHexString == [255]);
+ assert("0xC0".fromHexString == [192]);
+ assert("0x00".fromHexString == [0]);
+
+ // Nothing
+ assert("".fromHexString == []);
+ assert(""w.fromHexString == []);
+ assert(""d.fromHexString == []);
+
+ // Nothing but a prefix
+ assert("0x".fromHexString == []);
+ assert("0x"w.fromHexString == []);
+ assert("0x"d.fromHexString == []);
+
+ // Half a byte
+ assert("0x1".fromHexString == [0x01]);
+ assert("0x1"w.fromHexString == [0x01]);
+ assert("0x1"d.fromHexString == [0x01]);
+
+ // Mixed case is fine.
+ assert("0xAf".fromHexString == [0xAF]);
+ assert("0xaF".fromHexString == [0xAF]);
+
+ // Multiple bytes
+ assert("0xfff".fromHexString == [0x0F, 0xFF]);
+ assert("0x123AaAa".fromHexString == [0x01, 0x23, 0xAA, 0xAA]);
+ assert("EBBBBF".fromHexString == [0xEB, 0xBB, 0xBF]);
+
+ // md5 sum
+ assert("d41d8cd98f00b204e9800998ecf8427e".fromHexString == [
+ 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04,
+ 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E,
+ ]);
+}
+
+///
+@safe unittest
+{
+ // Cycle self-test
+ const ubyte[] initial = [0x00, 0x12, 0x34, 0xEB];
+ assert(initial == initial.toHexString().fromHexString());
+}
+
+private ubyte hexDigitToByte(dchar hexDigit) @safe pure nothrow @nogc
+{
+ static int hexDigitToByteImpl(dchar hexDigit)
+ {
+ if (hexDigit >= '0' && hexDigit <= '9')
+ {
+ return hexDigit - '0';
+ }
+ else if (hexDigit >= 'A' && hexDigit <= 'F')
+ {
+ return hexDigit - 'A' + 10;
+ }
+ else if (hexDigit >= 'a' && hexDigit <= 'f')
+ {
+ return hexDigit - 'a' + 10;
+ }
+
+ assert(false, "Cannot convert invalid hex digit.");
+ }
+
+ return hexDigitToByteImpl(hexDigit) & 0xFF;
+}
+
+@safe unittest
+{
+ assert(hexDigitToByte('0') == 0x0);
+ assert(hexDigitToByte('9') == 0x9);
+ assert(hexDigitToByte('a') == 0xA);
+ assert(hexDigitToByte('b') == 0xB);
+ assert(hexDigitToByte('A') == 0xA);
+ assert(hexDigitToByte('C') == 0xC);
+}
+
+private struct HexStringDecoder(String)
+if (isSomeString!String)
+{
+ String hex;
+ ubyte front;
+ bool empty;
+
+ this(String hex)
+ {
+ if ((hex.length >= 2) && (hex[0 .. 2] == "0x"))
+ {
+ hex = hex[2 .. $];
+ }
+
+ if (hex.length == 0)
+ {
+ empty = true;
+ return;
+ }
+
+ const oddInputLength = (hex.length % 2 == 1);
+
+ if (oddInputLength)
+ {
+ front = hexDigitToByte(hex[0]);
+ hex = hex[1 .. $];
+ }
+ else
+ {
+ front = cast(ubyte)(hexDigitToByte(hex[0]) << 4 | hexDigitToByte(hex[1]));
+ hex = hex[2 .. $];
+ }
+
+ this.hex = hex;
+ }
+
+ void popFront()
+ {
+ if (hex.length == 0)
+ {
+ empty = true;
+ return;
+ }
+
+ front = cast(ubyte)(hexDigitToByte(hex[0]) << 4 | hexDigitToByte(hex[1]));
+ hex = hex[2 .. $];
+ }
+
+ typeof(this) save()
+ {
+ return this;
+ }
+
+ size_t length() const
+ {
+ if (this.empty)
+ {
+ return 0;
+ }
+
+ // current front + remainder
+ return 1 + (hex.length >> 1);
+ }
+}
+
+@safe unittest
+{
+ auto decoder = HexStringDecoder!string("");
+ assert(decoder.empty);
+ assert(decoder.length == 0);
+
+ decoder = HexStringDecoder!string("0x");
+ assert(decoder.empty);
+ assert(decoder.length == 0);
+}
+
+@safe unittest
+{
+ auto decoder = HexStringDecoder!string("0x0077FF");
+ assert(!decoder.empty);
+ assert(decoder.length == 3);
+ assert(decoder.front == 0x00);
+
+ decoder.popFront();
+ assert(!decoder.empty);
+ assert(decoder.length == 2);
+ assert(decoder.front == 0x77);
+
+ decoder.popFront();
+ assert(!decoder.empty);
+ assert(decoder.length == 1);
+ assert(decoder.front == 0xFF);
+
+ decoder.popFront();
+ assert(decoder.length == 0);
+ assert(decoder.empty);
+}
+
+@safe unittest
+{
+ auto decoder = HexStringDecoder!string("0x7FF");
+ assert(!decoder.empty);
+ assert(decoder.length == 2);
+ assert(decoder.front == 0x07);
+
+ decoder.popFront();
+ assert(!decoder.empty);
+ assert(decoder.length == 1);
+ assert(decoder.front == 0xFF);
+
+ decoder.popFront();
+ assert(decoder.length == 0);
+ assert(decoder.empty);
+}
else static if (is(typeof(
(T val) {
const FormatSpec!Char f;
- static struct S {void put(scope Char s){}}
+ static struct S
+ {
+ @disable this(this);
+ void put(scope Char s){}
+ }
S s;
val.toString(s, f);
- static assert(!__traits(compiles, val.toString(s, FormatSpec!Char())),
- "force toString to take parameters by ref");
- static assert(!__traits(compiles, val.toString(S(), f)),
- "force toString to take parameters by ref");
})))
{
enum hasToString = HasToStringResult.customPutWriterFormatSpec;
}
else static if (is(typeof(
(T val) {
- static struct S {void put(scope Char s){}}
+ static struct S
+ {
+ @disable this(this);
+ void put(scope Char s){}
+ }
S s;
val.toString(s);
- static assert(!__traits(compiles, val.toString(S())),
- "force toString to take parameters by ref");
})))
{
enum hasToString = HasToStringResult.customPutWriter;
static assert(hasToString!(G, char) == customPutWriter);
static assert(hasToString!(H, char) == customPutWriterFormatSpec);
static assert(hasToString!(I, char) == customPutWriterFormatSpec);
- static assert(hasToString!(J, char) == hasSomeToString);
+ static assert(hasToString!(J, char) == hasSomeToString
+ || hasToString!(J, char) == constCharSinkFormatSpec); // depends on -preview=rvaluerefparam
static assert(hasToString!(K, char) == constCharSinkFormatSpec);
- static assert(hasToString!(L, char) == none);
+ static assert(hasToString!(L, char) == customPutWriterFormatSpec);
static if (hasPreviewIn)
{
static assert(hasToString!(M, char) == inCharSinkFormatSpec);
static assert(hasToString!(G, char) == customPutWriter);
static assert(hasToString!(H, char) == customPutWriterFormatSpec);
static assert(hasToString!(I, char) == customPutWriterFormatSpec);
- static assert(hasToString!(J, char) == hasSomeToString);
+ static assert(hasToString!(J, char) == hasSomeToString
+ || hasToString!(J, char) == constCharSinkFormatSpec); // depends on -preview=rvaluerefparam
static assert(hasToString!(K, char) == constCharSinkFormatSpec);
- static assert(hasToString!(L, char) == none);
+ static assert(hasToString!(L, char) == HasToStringResult.customPutWriterFormatSpec);
static if (hasPreviewIn)
{
static assert(hasToString!(M, char) == inCharSinkFormatSpec);
static assert(hasToString!(inout(G), char) == customPutWriter);
static assert(hasToString!(inout(H), char) == customPutWriterFormatSpec);
static assert(hasToString!(inout(I), char) == customPutWriterFormatSpec);
- static assert(hasToString!(inout(J), char) == hasSomeToString);
+ static assert(hasToString!(inout(J), char) == hasSomeToString
+ || hasToString!(inout(J), char) == constCharSinkFormatSpec); // depends on -preview=rvaluerefparam
static assert(hasToString!(inout(K), char) == constCharSinkFormatSpec);
- static assert(hasToString!(inout(L), char) == none);
+ static assert(hasToString!(inout(L), char) == customPutWriterFormatSpec);
static if (hasPreviewIn)
{
static assert(hasToString!(inout(M), char) == inCharSinkFormatSpec);
import std.format.spec : FormatSpec;
import std.format.internal.read;
-import std.traits : isSomeString;
+import std.meta : allSatisfy;
+import std.traits : isSomeString, isType;
/**
Reads an input range according to a format string and stores the read
/// ditto
uint formattedRead(alias fmt, Range, Args...)(auto ref Range r, auto ref Args args)
-if (isSomeString!(typeof(fmt)))
+if (!isType!fmt && isSomeString!(typeof(fmt)))
{
import std.format : checkFormatException;
import std.meta : staticMap;
assert(aa2 == ["hello":1, "world":2]);
}
+/**
+Reads an input range according to a format string and returns a tuple of Args
+with the read values.
+
+Format specifiers with format character $(B 'd'), $(B 'u') and $(B
+'c') can take a $(B '*') parameter for skipping values.
+
+The second version of `formattedRead` takes the format string as
+template argument. In this case, it is checked for consistency at
+compile-time.
+
+Params:
+ Args = a variadic list of types of the arguments
+ */
+template formattedRead(Args...)
+if (Args.length && allSatisfy!(isType, Args))
+{
+ import std.typecons : Tuple;
+
+ /**
+ Params:
+ r = an $(REF_ALTTEXT input range, isInputRange, std, range, primitives),
+ where the formatted input is read from
+ fmt = a $(MREF_ALTTEXT format string, std,format)
+ Range = the type of the input range `r`
+ Char = the character type used for `fmt`
+
+ Returns:
+ A Tuple!Args with the elements filled.
+
+ Throws:
+ A $(REF_ALTTEXT FormatException, FormatException, std, format)
+ if reading did not succeed.
+ */
+ Tuple!Args formattedRead(Range, Char)(auto ref Range r, const(Char)[] fmt)
+ {
+ import core.lifetime : forward;
+ import std.format : enforceFmt;
+
+ Tuple!Args args;
+ const numArgsFilled = .formattedRead(forward!r, fmt, args.expand);
+ enforceFmt(numArgsFilled == Args.length, "Failed reading into all format arguments");
+ return args;
+ }
+}
+
+///
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+ import std.format : FormatException;
+ import std.typecons : tuple;
+
+ auto complete = "hello!34.5:124".formattedRead!(string, double, int)("%s!%s:%s");
+ assert(complete == tuple("hello", 34.5, 124));
+
+ // reading ends early
+ assertThrown!FormatException("hello!34.5:".formattedRead!(string, double, int)("%s!%s:%s"));
+}
+
+/// Skipping values
+@safe pure unittest
+{
+ import std.format : FormatException;
+ import std.typecons : tuple;
+
+ auto result = "orange: (12%) 15.25".formattedRead!(string, double)("%s: (%*d%%) %f");
+ assert(result == tuple("orange", 15.25));
+}
+
+/// ditto
+template formattedRead(alias fmt, Args...)
+if (!isType!fmt && isSomeString!(typeof(fmt)) && Args.length && allSatisfy!(isType, Args))
+{
+ import std.typecons : Flag, Tuple, Yes;
+ Tuple!Args formattedRead(Range)(auto ref Range r)
+ {
+ import core.lifetime : forward;
+ import std.format : enforceFmt;
+
+ Tuple!Args args;
+ const numArgsFilled = .formattedRead!fmt(forward!r, args.expand);
+ enforceFmt(numArgsFilled == Args.length, "Failed reading into all format arguments");
+ return args;
+ }
+}
+
+/// The format string can be checked at compile-time
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+ import std.format : FormatException;
+ import std.typecons : tuple;
+
+ auto expected = tuple("hello", 124, 34.5);
+ auto result = "hello!124:34.5".formattedRead!("%s!%s:%s", string, int, double);
+ assert(result == expected);
+
+ assertThrown!FormatException("hello!34.5:".formattedRead!("%s!%s:%s", string, double, int));
+}
+
+/// Compile-time consistency check
+@safe pure unittest
+{
+ import std.format : FormatException;
+ import std.typecons : tuple;
+
+ static assert(!__traits(compiles, "orange: (12%) 15.25".formattedRead!("%s: (%*d%%) %f", string, double)));
+}
+
/**
Reads a value from the given _input range and converts it according to a
format specifier.
Example:
-------------
-sharedLog = new FileLogger(yourFile);
+sharedLog = new shared FileLogger(yourFile);
-------------
The example sets a new `FileLogger` as new `sharedLog`.
The default `Logger` is thread-safe.
-------------
if (sharedLog !is myLogger)
- sharedLog = new myLogger;
+ sharedLog = new shared myLogger;
-------------
*/
@property shared(Logger) sharedLog() @safe
auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes);
-------------
*/
- this(const string fn, const LogLevel lv = LogLevel.all) @safe
+ this(this This)(const string fn, const LogLevel lv = LogLevel.all)
{
this(fn, lv, CreateFolder.yes);
}
auto l2 = new FileLogger(file, LogLevel.fatal);
-------------
*/
- this(const string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe
+ this(this This)(const string fn, const LogLevel lv, CreateFolder createFileNameFolder)
{
import std.file : exists, mkdirRecurse;
import std.path : dirName;
" created in '", d,"' could not be created."));
}
- this.file_.open(this.filename, "a");
+ // Cast away `shared` when the constructor is inferred shared.
+ () @trusted { (cast() this.file_).open(this.filename, "a"); }();
}
/** A constructor for the `FileLogger` Logger that takes a reference to
assert(tl !is null);
stdThreadLocalLog.logLevel = LogLevel.all;
}
+
+@safe unittest
+{
+ // we don't need to actually run the code, only make sure
+ // it compiles
+ static _() {
+ auto l = new shared FileLogger("");
+ }
+}
current default `Logger`. This reference can be used to assign a new
default `Logger`.
-------------
-sharedLog = new FileLogger("New_Default_Log_File.log");
+sharedLog = new shared FileLogger("New_Default_Log_File.log");
-------------
Additional `Logger` can be created by creating a new instance of the
}
// Convert the current value to signed exponent, normalized form
- void toNormalized(T,U)(ref T sig, ref U exp)
+ void toNormalized(T,U)(ref T sig, ref U exp) const
{
sig = significand;
auto shift = (T.sizeof*8) - precision;
}
/// Returns: real part
- @property CustomFloat re() { return this; }
+ @property CustomFloat re() const { return this; }
/// Returns: imaginary part
static @property CustomFloat im() { return CustomFloat(0.0f); }
}
/// Fetches the stored value either as a `float`, `double` or `real`.
- @property F get(F)()
+ @property F get(F)() const
if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0)
{
import std.conv : text;
// Define an opBinary `CustomFloat op CustomFloat` so that those below
// do not match equally, which is disallowed by the spec:
// https://dlang.org/spec/operatoroverloading.html#binary
- real opBinary(string op,T)(T b)
+ real opBinary(string op,T)(T b) const
if (__traits(compiles, mixin(`get!real`~op~`b.get!real`)))
{
return mixin(`get!real`~op~`b.get!real`);
}
/// ditto
- real opBinary(string op,T)(T b)
+ real opBinary(string op,T)(T b) const
if ( __traits(compiles, mixin(`get!real`~op~`b`)) &&
!__traits(compiles, mixin(`get!real`~op~`b.get!real`)))
{
}
/// ditto
- real opBinaryRight(string op,T)(T a)
+ real opBinaryRight(string op,T)(T a) const
if ( __traits(compiles, mixin(`a`~op~`get!real`)) &&
!__traits(compiles, mixin(`get!real`~op~`b`)) &&
!__traits(compiles, mixin(`get!real`~op~`b.get!real`)))
}
/// ditto
- int opCmp(T)(auto ref T b)
+ int opCmp(T)(auto ref T b) const
if (__traits(compiles, cast(real) b))
{
auto x = get!real;
assertThrown!AssertError(a = float.infinity);
}
+@safe unittest
+{
+ const CustomFloat!16 x = CustomFloat!16(3);
+ assert(x.get!float == 3);
+ assert(x.re.get!float == 3);
+ assert(x + x == 6);
+ assert(x + 1 == 4);
+ assert(2 + x == 5);
+ assert(x < 4);
+}
+
private bool isCorrectCustomFloat(uint precision, uint exponentWidth, CustomFloatFlags flags) @safe pure nothrow @nogc
{
// Restrictions from bitfield
if (childpid == 0)
{
// Trusted because args and all entries are always zero-terminated
- (() @trusted =>
- core.sys.posix.unistd.execvp(args[0], &args[0]) ||
- perror(args[0]) // failed to execute
- )();
- return;
+ (() @trusted {
+ core.sys.posix.unistd.execvp(args[0], &args[0]);
+ perror(args[0]);
+ core.sys.posix.unistd._exit(1);
+ })();
+ assert(0, "Child failed to exec");
}
if (browser)
// Trusted because it's allocated via strdup above
enum socket_t : SOCKET { INVALID_SOCKET }
private const int _SOCKET_ERROR = SOCKET_ERROR;
+ /**
+ * On Windows, there is no `SO_REUSEPORT`.
+ * However, `SO_REUSEADDR` is equivalent to `SO_REUSEPORT` there.
+ * $(LINK https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse)
+ */
+ private enum SO_REUSEPORT = SO_REUSEADDR;
private int _lasterr() nothrow @nogc
{
DEBUG = SO_DEBUG, /// Record debugging information
BROADCAST = SO_BROADCAST, /// Allow transmission of broadcast messages
REUSEADDR = SO_REUSEADDR, /// Allow local reuse of address
+ /**
+ * Allow local reuse of port
+ *
+ * On Windows, this is equivalent to `SocketOption.REUSEADDR`.
+ * There is in fact no option named `REUSEPORT`.
+ * However, `SocketOption.REUSEADDR` matches the behavior of
+ * `SocketOption.REUSEPORT` on other platforms. Further details on this
+ * topic can be found here:
+ * $(LINK https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse)
+ *
+ * On Linux, this ensures fair distribution of incoming connections accross threads.
+ *
+ * See_Also:
+ * https://lwn.net/Articles/542629/
+ */
+ REUSEPORT = SO_REUSEPORT,
LINGER = SO_LINGER, /// Linger on close if unsent data is present
OOBINLINE = SO_OOBINLINE, /// Receive out-of-band data in band
SNDBUF = SO_SNDBUF, /// Send buffer size
assert(Iota!3 == AliasSeq!(0, 1, 2));
}
-/* The number that the dim-th argument's tag is multiplied by when
- * converting TagTuples to and from case indices ("caseIds").
- *
- * Named by analogy to the stride that the dim-th index into a
- * multidimensional static array is multiplied by to calculate the
- * offset of a specific element.
- */
-private size_t stride(size_t dim, lengths...)()
-{
- import core.checkedint : mulu;
-
- size_t result = 1;
- bool overflow = false;
-
- static foreach (i; 0 .. dim)
- {
- result = mulu(result, lengths[i], overflow);
- }
-
- /* The largest number matchImpl uses, numCases, is calculated with
- * stride!(SumTypes.length), so as long as this overflow check
- * passes, we don't need to check for overflow anywhere else.
- */
- assert(!overflow, "Integer overflow");
- return result;
-}
-
private template matchImpl(Flag!"exhaustive" exhaustive, handlers...)
{
auto ref matchImpl(SumTypes...)(auto ref SumTypes args)
if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
{
- alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes));
- alias TagTuple = .TagTuple!(SumTypes);
-
- /*
- * A list of arguments to be passed to a handler needed for the case
- * labeled with `caseId`.
- */
- template handlerArgs(size_t caseId)
+ // Single dispatch (fast path)
+ static if (args.length == 1)
{
- enum tags = TagTuple.fromCaseId(caseId);
- enum argsFrom(size_t i : tags.length) = "";
- enum argsFrom(size_t i) = "args[" ~ toCtString!i ~ "].get!(SumTypes[" ~ toCtString!i ~ "]" ~
- ".Types[" ~ toCtString!(tags[i]) ~ "])(), " ~ argsFrom!(i + 1);
- enum handlerArgs = argsFrom!0;
- }
+ /* When there's only one argument, the caseId is just that
+ * argument's tag, so there's no need for TagTuple.
+ */
+ enum handlerArgs(size_t caseId) =
+ "args[0].get!(SumTypes[0].Types[" ~ toCtString!caseId ~ "])()";
- /* An AliasSeq of the types of the member values in the argument list
- * returned by `handlerArgs!caseId`.
- *
- * Note that these are the actual (that is, qualified) types of the
- * member values, which may not be the same as the types listed in
- * the arguments' `.Types` properties.
- */
- template valueTypes(size_t caseId)
+ alias valueTypes(size_t caseId) =
+ typeof(args[0].get!(SumTypes[0].Types[caseId])());
+
+ enum numCases = SumTypes[0].Types.length;
+ }
+ // Multiple dispatch (slow path)
+ else
{
- enum tags = TagTuple.fromCaseId(caseId);
+ alias typeCounts = Map!(typeCount, SumTypes);
+ alias stride(size_t i) = .stride!(i, typeCounts);
+ alias TagTuple = .TagTuple!typeCounts;
+
+ alias handlerArgs(size_t caseId) = .handlerArgs!(caseId, typeCounts);
- template getType(size_t i)
+ /* An AliasSeq of the types of the member values in the argument list
+ * returned by `handlerArgs!caseId`.
+ *
+ * Note that these are the actual (that is, qualified) types of the
+ * member values, which may not be the same as the types listed in
+ * the arguments' `.Types` properties.
+ */
+ template valueTypes(size_t caseId)
{
- enum tid = tags[i];
- alias T = SumTypes[i].Types[tid];
- alias getType = typeof(args[i].get!T());
+ enum tags = TagTuple.fromCaseId(caseId);
+
+ template getType(size_t i)
+ {
+ enum tid = tags[i];
+ alias T = SumTypes[i].Types[tid];
+ alias getType = typeof(args[i].get!T());
+ }
+
+ alias valueTypes = Map!(getType, Iota!(tags.length));
}
- alias valueTypes = Map!(getType, Iota!(tags.length));
+ /* The total number of cases is
+ *
+ * Π SumTypes[i].Types.length for 0 ≤ i < SumTypes.length
+ *
+ * Conveniently, this is equal to stride!(SumTypes.length), so we can
+ * use that function to compute it.
+ */
+ enum numCases = stride!(SumTypes.length);
}
- /* The total number of cases is
- *
- * Π SumTypes[i].Types.length for 0 ≤ i < SumTypes.length
- *
- * Or, equivalently,
- *
- * ubyte[SumTypes[0].Types.length]...[SumTypes[$-1].Types.length].sizeof
- *
- * Conveniently, this is equal to stride!(SumTypes.length), so we can
- * use that function to compute it.
- */
- enum numCases = stride!(SumTypes.length);
-
/* Guaranteed to never be a valid handler index, since
* handlers.length <= size_t.max.
*/
mixin("alias ", handlerName!hid, " = handler;");
}
- immutable argsId = TagTuple(args).toCaseId;
+ // Single dispatch (fast path)
+ static if (args.length == 1)
+ immutable argsId = args[0].tag;
+ // Multiple dispatch (slow path)
+ else
+ immutable argsId = TagTuple(args).toCaseId;
final switch (argsId)
{
}
}
+// Predicate for staticMap
private enum typeCount(SumType) = SumType.Types.length;
-/* A TagTuple represents a single possible set of tags that `args`
- * could have at runtime.
+/* A TagTuple represents a single possible set of tags that the arguments to
+ * `matchImpl` could have at runtime.
*
* Because D does not allow a struct to be the controlling expression
* of a switch statement, we cannot dispatch on the TagTuple directly.
* When there is only one argument, the caseId is equal to that
* argument's tag.
*/
-private struct TagTuple(SumTypes...)
+private struct TagTuple(typeCounts...)
{
- size_t[SumTypes.length] tags;
+ size_t[typeCounts.length] tags;
alias tags this;
- alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes));
+ alias stride(size_t i) = .stride!(i, typeCounts);
invariant
{
static foreach (i; 0 .. tags.length)
{
- assert(tags[i] < SumTypes[i].Types.length, "Invalid tag");
+ assert(tags[i] < typeCounts[i], "Invalid tag");
}
}
- this(ref const(SumTypes) args)
+ this(SumTypes...)(ref const SumTypes args)
+ if (allSatisfy!(isSumType, SumTypes) && args.length == typeCounts.length)
{
static foreach (i; 0 .. tags.length)
{
}
}
+/* The number that the dim-th argument's tag is multiplied by when
+ * converting TagTuples to and from case indices ("caseIds").
+ *
+ * Named by analogy to the stride that the dim-th index into a
+ * multidimensional static array is multiplied by to calculate the
+ * offset of a specific element.
+ */
+private size_t stride(size_t dim, lengths...)()
+{
+ import core.checkedint : mulu;
+
+ size_t result = 1;
+ bool overflow = false;
+
+ static foreach (i; 0 .. dim)
+ {
+ result = mulu(result, lengths[i], overflow);
+ }
+
+ /* The largest number matchImpl uses, numCases, is calculated with
+ * stride!(SumTypes.length), so as long as this overflow check
+ * passes, we don't need to check for overflow anywhere else.
+ */
+ assert(!overflow, "Integer overflow");
+ return result;
+}
+
+/* A list of arguments to be passed to a handler needed for the case
+ * labeled with `caseId`.
+ */
+private template handlerArgs(size_t caseId, typeCounts...)
+{
+ enum tags = TagTuple!typeCounts.fromCaseId(caseId);
+
+ alias handlerArgs = AliasSeq!();
+
+ static foreach (i; 0 .. tags.length)
+ {
+ handlerArgs = AliasSeq!(
+ handlerArgs,
+ "args[" ~ toCtString!i ~ "].get!(SumTypes[" ~ toCtString!i ~ "]" ~
+ ".Types[" ~ toCtString!(tags[i]) ~ "])(), "
+ );
+ }
+}
+
// Matching
@safe unittest
{
/**
* Detect whether type `T` is an aggregate type.
*/
-enum bool isAggregateType(T) = is(T == struct) || is(T == union) ||
- is(T == class) || is(T == interface);
+template isAggregateType(T)
+{
+ static if (is(T == enum))
+ enum isAggregateType = isAggregateType!(OriginalType!T);
+ else
+ enum isAggregateType = is(T == struct) || is(T == class) || is(T == interface) || is(T == union);
+}
///
@safe unittest
{
- class C;
- union U;
- struct S;
- interface I;
+ class C {}
+ union U {}
+ struct S {}
+ interface I {}
static assert( isAggregateType!C);
static assert( isAggregateType!U);
static assert(!isAggregateType!(int[]));
static assert(!isAggregateType!(C[string]));
static assert(!isAggregateType!(void delegate(int)));
+
+ enum ES : S { a = S.init }
+ enum EC : C { a = C.init }
+ enum EI : I { a = I.init }
+ enum EU : U { a = U.init }
+
+ static assert( isAggregateType!ES);
+ static assert( isAggregateType!EC);
+ static assert( isAggregateType!EI);
+ static assert( isAggregateType!EU);
}
/**
* is the same as `T`. For pointer and slice types, it is `T` with the
* outer-most layer of qualifiers dropped.
*/
-package(std) template DeducedParameterType(T)
+package(std) alias DeducedParameterType(T) = DeducedParameterTypeImpl!T;
+/// ditto
+package(std) alias DeducedParameterType(alias T) = DeducedParameterTypeImpl!T;
+
+private template DeducedParameterTypeImpl(T)
{
static if (is(T == U*, U) || is(T == U[], U))
- alias DeducedParameterType = Unqual!T;
+ alias DeducedParameterTypeImpl = Unqual!T;
else
- alias DeducedParameterType = T;
+ alias DeducedParameterTypeImpl = T;
}
@safe unittest
}
static assert(is(DeducedParameterType!NoCopy == NoCopy));
+ static assert(is(DeducedParameterType!(inout(NoCopy)) == inout(NoCopy)));
}
@safe unittest
{
static if (useQualifierCast)
{
- this.data = cast() value;
+ static if (hasElaborateAssign!T)
+ {
+ import core.lifetime : copyEmplace;
+ copyEmplace(cast() value, this.data);
+ }
+ else
+ this.data = cast() value;
}
else
{
- // As we're escaping a copy of `value`, deliberately leak a copy:
- static union DontCallDestructor
- {
- T value;
- }
- DontCallDestructor copy = DontCallDestructor(value);
- this.data = *cast(Payload*) ©
+ import core.lifetime : copyEmplace;
+ copyEmplace(cast() value, cast() *cast(T*) &this.data);
}
}
return Rebindable2!T(value);
}
+// Verify that the destructor is called properly if there is one.
+@system unittest
+{
+ {
+ bool destroyed;
+
+ struct S
+ {
+ int i;
+
+ this(int i) @safe
+ {
+ this.i = i;
+ }
+
+ ~this() @safe
+ {
+ destroyed = true;
+ }
+ }
+
+ {
+ auto foo = rebindable2(S(42));
+
+ // Whether destruction has occurred here depends on whether the
+ // temporary gets moved or not, so we won't assume that it has or
+ // hasn't happened. What we care about here is that foo gets destroyed
+ // properly when it leaves the scope.
+ destroyed = false;
+ }
+ assert(destroyed);
+
+ {
+ auto foo = rebindable2(const S(42));
+ destroyed = false;
+ }
+ assert(destroyed);
+ }
+
+ // Test for double destruction with qualifer cast being used
+ {
+ static struct S
+ {
+ int i;
+ bool destroyed;
+
+ this(int i) @safe
+ {
+ this.i = i;
+ }
+
+ ~this() @safe
+ {
+ destroyed = true;
+ }
+
+ @safe invariant
+ {
+ assert(!destroyed);
+ }
+ }
+
+ {
+ auto foo = rebindable2(S(42));
+ assert(typeof(foo).useQualifierCast);
+ assert(foo.data.i == 42);
+ assert(!foo.data.destroyed);
+ }
+ {
+ auto foo = rebindable2(S(42));
+ destroy(foo);
+ }
+ {
+ auto foo = rebindable2(const S(42));
+ assert(typeof(foo).useQualifierCast);
+ assert(foo.data.i == 42);
+ assert(!foo.data.destroyed);
+ }
+ {
+ auto foo = rebindable2(const S(42));
+ destroy(foo);
+ }
+ }
+
+ // Test for double destruction without qualifer cast being used
+ {
+ static struct S
+ {
+ int i;
+ bool destroyed;
+
+ this(int i) @safe
+ {
+ this.i = i;
+ }
+
+ ~this() @safe
+ {
+ destroyed = true;
+ }
+
+ @disable ref S opAssign()(auto ref S rhs);
+
+ @safe invariant
+ {
+ assert(!destroyed);
+ }
+ }
+
+ {
+ auto foo = rebindable2(S(42));
+ assert(!typeof(foo).useQualifierCast);
+ assert((cast(S*)&(foo.data)).i == 42);
+ assert(!(cast(S*)&(foo.data)).destroyed);
+ }
+ {
+ auto foo = rebindable2(S(42));
+ destroy(foo);
+ }
+ }
+}
+
+// Verify that if there is an overloaded assignment operator, it's not assigned
+// to garbage.
+@safe unittest
+{
+ static struct S
+ {
+ int i;
+ bool destroyed;
+
+ this(int i) @safe
+ {
+ this.i = i;
+ }
+
+ ~this() @safe
+ {
+ destroyed = true;
+ }
+
+ ref opAssign()(auto ref S rhs)
+ {
+ assert(!this.destroyed);
+ this.i = rhs.i;
+ return this;
+ }
+ }
+
+ {
+ auto foo = rebindable2(S(42));
+ foo = S(99);
+ assert(foo.data.i == 99);
+ }
+ {
+ auto foo = rebindable2(S(42));
+ foo = const S(99);
+ assert(foo.data.i == 99);
+ }
+}
+
+// Verify that postblit or copy constructor is called properly if there is one.
+@system unittest
+{
+ // postblit with type qualifier cast
+ {
+ static struct S
+ {
+ int i;
+ static bool copied;
+
+ this(this) @safe
+ {
+ copied = true;
+ }
+ }
+
+ {
+ auto foo = rebindable2(S(42));
+
+ // Whether a copy has occurred here depends on whether the
+ // temporary gets moved or not, so we won't assume that it has or
+ // hasn't happened. What we care about here is that foo gets copied
+ // properly when we copy it below.
+ S.copied = false;
+
+ auto bar = foo;
+ assert(S.copied);
+ }
+ {
+ auto foo = rebindable2(const S(42));
+ assert(typeof(foo).useQualifierCast);
+ S.copied = false;
+
+ auto bar = foo;
+ assert(S.copied);
+ }
+ }
+
+ // copy constructor with type qualifier cast
+ {
+ static struct S
+ {
+ int i;
+ static bool copied;
+
+ this(ref inout S rhs) @safe inout
+ {
+ this.i = i;
+ copied = true;
+ }
+ }
+
+ {
+ auto foo = rebindable2(S(42));
+ assert(typeof(foo).useQualifierCast);
+ S.copied = false;
+
+ auto bar = foo;
+ assert(S.copied);
+ }
+ {
+ auto foo = rebindable2(const S(42));
+ S.copied = false;
+
+ auto bar = foo;
+ assert(S.copied);
+ }
+ }
+
+ // FIXME https://issues.dlang.org/show_bug.cgi?id=24829
+
+ // Making this work requires either reworking how the !useQualiferCast
+ // version works so that the compiler can correctly generate postblit
+ // constructors and copy constructors as appropriate, or an explicit
+ // postblit or copy constructor needs to be added for such cases, which
+ // gets pretty complicated if we want to correctly add the same attributes
+ // that T's postblit or copy constructor has.
+
+ /+
+ // postblit without type qualifier cast
+ {
+ static struct S
+ {
+ int* ptr;
+ static bool copied;
+
+ this(int i)
+ {
+ ptr = new int(i);
+ }
+
+ this(this) @safe
+ {
+ if (ptr !is null)
+ ptr = new int(*ptr);
+ copied = true;
+ }
+
+ @disable ref S opAssign()(auto ref S rhs);
+ }
+
+ {
+ auto foo = rebindable2(S(42));
+ assert(!typeof(foo).useQualifierCast);
+ S.copied = false;
+
+ auto bar = foo;
+ assert(S.copied);
+ assert(*(cast(S*)&(foo.data)).ptr == *(cast(S*)&(bar.data)).ptr);
+ assert((cast(S*)&(foo.data)).ptr !is (cast(S*)&(bar.data)).ptr);
+ }
+ {
+ auto foo = rebindable2(const S(42));
+ S.copied = false;
+
+ auto bar = foo;
+ assert(S.copied);
+ assert(*(cast(S*)&(foo.data)).ptr == *(cast(S*)&(bar.data)).ptr);
+ assert((cast(S*)&(foo.data)).ptr !is (cast(S*)&(bar.data)).ptr);
+ }
+ }
+
+ // copy constructor without type qualifier cast
+ {
+ static struct S
+ {
+ int* ptr;
+ static bool copied;
+
+ this(int i)
+ {
+ ptr = new int(i);
+ }
+
+ this(ref inout S rhs) @safe inout
+ {
+ if (rhs.ptr !is null)
+ ptr = new inout int(*rhs.ptr);
+ copied = true;
+ }
+
+ @disable ref S opAssign()(auto ref S rhs);
+ }
+
+ {
+ auto foo = rebindable2(S(42));
+ assert(!typeof(foo).useQualifierCast);
+ S.copied = false;
+
+ auto bar = foo;
+ assert(S.copied);
+ assert(*(cast(S*)&(foo.data)).ptr == *(cast(S*)&(bar.data)).ptr);
+ assert((cast(S*)&(foo.data)).ptr !is (cast(S*)&(bar.data)).ptr);
+ }
+ {
+ auto foo = rebindable2(const S(42));
+ S.copied = false;
+
+ auto bar = foo;
+ assert(S.copied);
+ assert(*(cast(S*)&(foo.data)).ptr == *(cast(S*)&(bar.data)).ptr);
+ assert((cast(S*)&(foo.data)).ptr !is (cast(S*)&(bar.data)).ptr);
+ }
+ }
+ +/
+}
+
/**
Similar to `Rebindable!(T)` but strips all qualifiers from the reference as
opposed to just constness / immutability. Primary intended use case is with
import std.array : appender, Appender;
import std.conv : to, toTextRange, text;
import std.exception;
-import std.windows.charset;
string sysErrorString(
DWORD errCode,