d/ast_node.o \
d/astcodegen.o \
d/astenums.o \
- d/basicmangle.o \
d/blockexit.o \
d/builtin.o \
d/canthrow.o \
d/cond.o \
d/constfold.o \
d/cparse.o \
- d/cppmangle.o \
d/ctfeexpr.o \
d/ctorflow.o \
d/cxxfrontend.o \
d/dimport.o \
d/dinterpret.o \
d/dmacro.o \
- d/dmangle.o \
d/dmodule.o \
d/doc.o \
d/dscope.o \
d/lambdacomp.o \
d/lexer.o \
d/location.o \
+ d/mangle-basic.o \
+ d/mangle-cpp.o \
+ d/mangle-package.o \
d/mtype.o \
d/mustuse.o \
d/nogc.o \
$(DCOMPILE) $(D_INCLUDES) $<
$(DPOSTCOMPILE)
+d/mangle-%.o: d/dmd/mangle/%.d
+ $(DCOMPILE) $(D_INCLUDES) $<
+ $(DPOSTCOMPILE)
+
d/root-%.o: d/dmd/root/%.d
$(DCOMPILE) $(D_INCLUDES) $<
$(DPOSTCOMPILE)
if (ad != NULL)
{
/* Recursively search through attribute decls. */
- Dsymbols *decls = ad->include (NULL);
+ Dsymbols *decls = dmd::include (ad, NULL);
if (decls && decls->length)
{
for (size_t i = 0; i < decls->length; i++)
}
else if (kind == ErrorKind::warning)
{
- if (global.gag || global.params.warnings == DIAGNOSTICoff)
+ if (global.gag || global.params.useWarnings == DIAGNOSTICoff)
{
if (global.gag)
global.gaggedWarnings++;
}
/* Warnings don't count if not treated as errors. */
- if (global.params.warnings == DIAGNOSTICerror)
+ if (global.params.useWarnings == DIAGNOSTICerror)
global.warnings++;
diag_kind = DK_WARNING;
}
else if (kind == ErrorKind::warning)
{
- if (global.params.warnings == DIAGNOSTICoff || global.gag)
+ if (global.params.useWarnings == DIAGNOSTICoff || global.gag)
return;
}
else if (kind == ErrorKind::deprecation)
/* Build and return typeinfo type for TYPE. */
Type *
-getTypeInfoType (const Loc &loc, Type *type, Scope *sc, bool genObjCode)
+getTypeInfoType (const Loc &loc, Type *type, Scope *sc)
{
gcc_assert (type->ty != TY::Terror);
check_typeinfo_type (loc, sc);
- create_typeinfo (type, sc ? sc->_module->importedFrom : NULL, genObjCode);
+ create_typeinfo (type, sc ? sc->_module->importedFrom : NULL);
return type->vtinfo->type;
}
/* Warnings and deprecations are disabled by default. */
global.params.useDeprecated = DIAGNOSTICinform;
- global.params.warnings = DIAGNOSTICoff;
+ global.params.useWarnings = DIAGNOSTICoff;
global.params.v.errorLimit = flag_max_errors;
global.params.v.messageStyle = MessageStyle::gnu;
case OPT_Wall:
if (value)
- global.params.warnings = DIAGNOSTICinform;
+ global.params.useWarnings = DIAGNOSTICinform;
break;
case OPT_Wdeprecated:
case OPT_Werror:
if (value)
- global.params.warnings = DIAGNOSTICerror;
+ global.params.useWarnings = DIAGNOSTICerror;
break;
case OPT_Wspeculative:
/* Error about use of deprecated features. */
if (global.params.useDeprecated == DIAGNOSTICinform
- && global.params.warnings == DIAGNOSTICerror)
+ && global.params.useWarnings == DIAGNOSTICerror)
global.params.useDeprecated = DIAGNOSTICerror;
if (flag_excess_precision == EXCESS_PRECISION_DEFAULT)
/* The front-end parser only has access to `compileEnv', synchronize its
fields with params. */
global.compileEnv.previewIn = global.params.previewIn;
+ global.compileEnv.transitionIn = global.params.v.vin;
global.compileEnv.ddocOutput = global.params.ddoc.doOutput;
global.compileEnv.cCharLookupTable =
IdentifierCharLookup::forTable (IdentifierTable::C11);
return (tb->ty == TY::Tstruct || tb->ty == TY::Tsarray);
}
+
+/* Returns true if the specified bit-field FIELD contributes to the alignment
+ of the containing aggregate. */
+
+bool
+TargetC::contributesToAggregateAlignment(BitFieldDeclaration *field)
+{
+ if (this->bitFieldStyle == TargetC::BitFieldStyle::MS)
+ return true;
+
+ if (PCC_BITFIELD_TYPE_MATTERS)
+ {
+ /* Named bit-fields contribute to alignment. Some targets also apply the
+ same rules to unnamed bit-fields too. */
+ if (!field->isAnonymous () || targetm.align_anon_bitfield ())
+ return true;
+ }
+
+ return false;
+}
extern void check_typeinfo_type (const Loc &, Scope *, Expression * = NULL);
extern tree build_typeinfo (const Loc &, Type *, Expression * = NULL);
extern tree build_typeinfo (Expression *, Type *);
-extern void create_typeinfo (Type *, Module *, bool = true);
+extern void create_typeinfo (Type *, Module *);
extern void create_tinfo_types (Module *);
extern void layout_cpp_typeinfo (ClassDeclaration *);
extern tree get_cpp_typeinfo_decl (ClassDeclaration *);
void visit (AttribDeclaration *d) final override
{
- Dsymbols *ds = d->include (NULL);
+ Dsymbols *ds = dmd::include (d, NULL);
if (!ds)
return;
{
StructLiteralExp *sle = StructLiteralExp::create (sd->loc, sd, NULL);
- if (!sd->fill (sd->loc, *sle->elements, true))
+ if (!dmd::fill (sd, sd->loc, *sle->elements, true))
gcc_unreachable ();
sle->type = sd->type;
-4ccb01fde535c7ad6ad4bdae2516c99420751814
+2b89c2909de239bd603d6f36379658fe902667db
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
| [frontend.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/frontend.d) | An interface for using DMD as a library |
| [errors.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errors.d) | Error reporting implementation |
| [errorsink.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errorsink.d) | Error reporting interface |
+| [sarif.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sarif.d) | Generates SARIF reports for errors and warnings. |
| [target.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) |
| [compiler.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/compiler.d) | Describe a back-end compiler and implements compiler-specific actions |
| [deps.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/deps.d) | Implement the `-deps` and `-makedeps` switches |
| File | Purpose |
|-------------------------------------------------------------------------------|------------------------------------------------------|
-| [lib.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib.d) | Abstract library class |
-| [libelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libelf.d) | Library in ELF format (Unix) |
-| [libmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libmach.d) | Library in Mach-O format (macOS) |
-| [libmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libmscoff.d) | Library in COFF format (32/64-bit Windows) |
-| [scanelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanelf.d) | Extract symbol names from a library in ELF format |
-| [scanmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanmach.d) | Extract symbol names from a library in Mach-O format |
-| [scanmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanmscoff.d) | Extract symbol names from a library in COFF format |
+| [lib/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/package.d) | Abstract library class |
+| [lib/elf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/elf.d) | Library in ELF format (Unix) |
+| [lib/mach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/mach.d) | Library in Mach-O format (macOS) |
+| [lib/mscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/mscoff.d) | Library in COFF format (32/64-bit Windows) |
+| [lib/scanelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanelf.d) | Extract symbol names from a library in ELF format |
+| [lib/scanmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmach.d) | Extract symbol names from a library in Mach-O format |
+| [lib/scanmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmscoff.d) | Extract symbol names from a library in COFF format |
### Code generation / back-end interfacing
| File | Purpose |
|-----------------------------------------------------------------------------------|------------------------------------------------------------------|
-| [cppmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmangle.d) | C++ name mangling |
-| [cppmanglewin.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmanglewin.d) | C++ name mangling for Windows |
-| [basicmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/basicmangle.d) | D name mangling for basic types |
-| [dmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmangle.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) |
+| [mangle/cpp.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/cpp.d) | C++ name mangling |
+| [mangle/cppwin.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/cppwin.d) | C++ name mangling for Windows |
+| [mangle/basic.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/basic.d) | D name mangling for basic types |
+| [mangle/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/package.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) |
### Linking
import dmd.dsymbol;
import dmd.errors;
import dmd.expression;
+import dmd.funcsem : overloadApply;
import dmd.location;
import dmd.tokens;
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
-import dmd.dsymbolsem;
+import dmd.dsymbolsem : dsymbolSemantic, determineFields, search, determineSize, include;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
StorageClass storage_class; ///
uint structsize; /// size of struct
uint alignsize; /// size of struct for alignment purposes
- VarDeclarations fields; /// VarDeclaration fields
+ VarDeclarations fields; /// VarDeclaration fields including flattened AnonDeclaration members
Dsymbol deferred; /// any deferred semantic2() or semantic3() symbol
/// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface
return fields.length - isNested() - (vthis2 !is null);
}
- /***************************************
- * Collect all instance fields, then determine instance size.
- * Returns:
- * false if failed to determine the size.
- */
- extern (D) final bool determineSize(const ref Loc loc)
- {
- //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
-
- // The previous instance size finalizing had:
- if (type.ty == Terror || errors)
- return false; // failed already
- if (sizeok == Sizeok.done)
- return true; // succeeded
-
- if (!members)
- {
- .error(loc, "%s `%s` unknown size", kind, toPrettyChars);
- return false;
- }
-
- if (_scope)
- dsymbolSemantic(this, null);
-
- // Determine the instance size of base class first.
- if (auto cd = isClassDeclaration())
- {
- cd = cd.baseClass;
- if (cd && !cd.determineSize(loc))
- goto Lfail;
- }
-
- // Determine instance fields when sizeok == Sizeok.none
- if (!this.determineFields())
- goto Lfail;
- if (sizeok != Sizeok.done)
- finalizeSize();
-
- // this aggregate type has:
- if (type.ty == Terror)
- return false; // marked as invalid during the finalizing.
- if (sizeok == Sizeok.done)
- return true; // succeeded to calculate instance size.
-
- Lfail:
- // There's unresolvable forward reference.
- if (type != Type.terror)
- error(loc, "%s `%s` no size because of forward reference", kind, toPrettyChars);
- // Don't cache errors from speculative semantic, might be resolvable later.
- // https://issues.dlang.org/show_bug.cgi?id=16574
- if (!global.gag)
- {
- type = Type.terror;
- errors = true;
- }
- return false;
- }
-
abstract void finalizeSize();
override final uinteger_t size(const ref Loc loc)
{
//printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
- bool ok = determineSize(loc);
+ bool ok = determineSize(this, loc);
//printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
return ok ? structsize : SIZE_INVALID;
}
return errors;
}
- /***************************************
- * Fill out remainder of elements[] with default initializers for fields[].
- * Params:
- * loc = location
- * elements = explicit arguments which given to construct object.
- * ctorinit = true if the elements will be used for default initialization.
- * Returns:
- * false if any errors occur.
- * Otherwise, returns true and the missing arguments will be pushed in elements[].
- */
- final bool fill(const ref Loc loc, ref Expressions elements, bool ctorinit)
- {
- //printf("AggregateDeclaration::fill() %s\n", toChars());
- assert(sizeok == Sizeok.done);
- const nfields = nonHiddenFields();
- bool errors = false;
-
- size_t dim = elements.length;
- elements.setDim(nfields);
- foreach (size_t i; dim .. nfields)
- elements[i] = null;
-
- // Fill in missing any elements with default initializers
- foreach (i; 0 .. nfields)
- {
- if (elements[i])
- continue;
-
- auto vd = fields[i];
- auto vx = vd;
- if (vd._init && vd._init.isVoidInitializer())
- vx = null;
-
- // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
- size_t fieldi = i;
- foreach (j; 0 .. nfields)
- {
- if (i == j)
- continue;
- auto v2 = fields[j];
- if (!vd.isOverlappedWith(v2))
- continue;
-
- if (elements[j])
- {
- vx = null;
- break;
- }
- if (v2._init && v2._init.isVoidInitializer())
- continue;
-
- version (all)
- {
- /* Prefer first found non-void-initialized field
- * union U { int a; int b = 2; }
- * U u; // Error: overlapping initialization for field a and b
- */
- if (!vx)
- {
- vx = v2;
- fieldi = j;
- }
- else if (v2._init)
- {
- .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
- errors = true;
- }
- }
- else
- {
- // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always
-
- /* Prefer explicitly initialized field
- * union U { int a; int b = 2; }
- * U u; // OK (u.b == 2)
- */
- if (!vx || !vx._init && v2._init)
- {
- vx = v2;
- fieldi = j;
- }
- else if (vx != vd && !vx.isOverlappedWith(v2))
- {
- // Both vx and v2 fills vd, but vx and v2 does not overlap
- }
- else if (vx._init && v2._init)
- {
- .error(loc, "overlapping default initialization for field `%s` and `%s`",
- v2.toChars(), vd.toChars());
- errors = true;
- }
- else
- assert(vx._init || !vx._init && !v2._init);
- }
- }
- if (vx)
- {
- Expression e;
- if (vx.type.size() == 0)
- {
- e = null;
- }
- else if (vx._init)
- {
- assert(!vx._init.isVoidInitializer());
- if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057
- {
- .error(loc, "%s `%s` recursive initialization of field", vx.kind(), vx.toPrettyChars());
- errors = true;
- }
- else
- e = vx.getConstInitializer(false);
- }
- else
- {
- if ((vx.storage_class & STC.nodefaultctor) && !ctorinit)
- {
- .error(loc, "field `%s.%s` must be initialized because it has no default constructor",
- type.toChars(), vx.toChars());
- errors = true;
- }
- /* https://issues.dlang.org/show_bug.cgi?id=12509
- * Get the element of static array type.
- */
- Type telem = vx.type;
- if (telem.ty == Tsarray)
- {
- /* We cannot use Type::baseElemOf() here.
- * If the bottom of the Tsarray is an enum type, baseElemOf()
- * will return the base of the enum, and its default initializer
- * would be different from the enum's.
- */
- TypeSArray tsa;
- while ((tsa = telem.toBasetype().isTypeSArray()) !is null)
- telem = tsa.next;
- if (telem.ty == Tvoid)
- telem = Type.tuns8.addMod(telem.mod);
- }
- if (telem.needsNested() && ctorinit)
- e = telem.defaultInit(loc);
- else
- e = telem.defaultInitLiteral(loc);
- }
- elements[fieldi] = e;
- }
- }
- foreach (e; elements)
- {
- if (e && e.op == EXP.error)
- return false;
- }
-
- return !errors;
- }
-
override final Type getType()
{
/* Apply storage classes to forward references. (Issue 22254)
/****************************************
* Place a field (mem) into an aggregate (agg), which can be a struct, union or class
* Params:
+ * loc = source location for error messages
* nextoffset = location just past the end of the previous field in the aggregate.
* Updated to be just past the end of this field to be placed, i.e. the future nextoffset
* memsize = size of field
* aligned offset to place field at
*
*/
-public uint placeField(ref uint nextoffset, uint memsize, uint memalignsize,
- structalign_t alignment, ref uint aggsize, ref uint aggalignsize, bool isunion) @safe pure nothrow
+public uint placeField(Loc loc, ref uint nextoffset, uint memsize, uint memalignsize,
+ structalign_t alignment, ref uint aggsize, ref uint aggalignsize, bool isunion) @trusted nothrow
{
static if (0)
{
bool overflow;
const sz = addu(memsize, actualAlignment, overflow);
addu(ofs, sz, overflow);
- if (overflow) assert(0);
+ if (overflow)
+ {
+ error(loc, "max object size %u exceeded from adding field size %u + alignment adjustment %u + field offset %u when placing field in aggregate",
+ uint.max, memsize, actualAlignment, ofs);
+ return 0;
+ }
// Skip no-op for noreturn without custom aligment
if (memalignsize != 0 || !alignment.isDefault())
{
FuncDeclaration *search_toString(StructDeclaration *sd);
void semanticTypeInfoMembers(StructDeclaration *sd);
+ bool fill(StructDeclaration* sd, const Loc &loc, Expressions &elements, bool ctorinit);
}
enum class ClassKind : uint8_t
virtual Scope *newScope(Scope *sc);
virtual void finalizeSize() = 0;
uinteger_t size(const Loc &loc) override final;
- bool fill(const Loc &loc, Expressions &elements, bool ctorinit);
Type *getType() override final;
bool isDeprecated() const override final; // is aggregate deprecated?
bool isNested() const;
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem;
-import dmd.errors;
+import dmd.dsymbolsem : setScope, addMember, include;
import dmd.expression;
import dmd.func;
import dmd.globals;
this.decl = decl;
}
- Dsymbols* include(Scope* sc)
- {
- if (errors)
- return null;
-
- return decl;
- }
-
/****************************************
* Create a new scope if one or more given attributes
* are different from the sc's.
return sc2;
}
- /****************************************
- * A hook point to supply scope for members.
- * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this.
- */
- Scope* newScope(Scope* sc)
- {
- return sc;
- }
override void addComment(const(char)* comment)
{
//printf("AttribDeclaration::addComment %s\n", comment);
if (comment)
{
- include(null).foreachDsymbol( s => s.addComment(comment) );
+ this.include(null).foreachDsymbol( s => s.addComment(comment) );
}
}
override bool oneMember(out Dsymbol ps, Identifier ident)
{
- Dsymbols* d = include(null);
+ Dsymbols* d = this.include(null);
return Dsymbol.oneMembers(d, ps, ident);
}
override final bool hasPointers()
{
- return include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0;
+ return this.include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0;
}
override final bool hasStaticCtorOrDtor()
{
- return include(null).foreachDsymbol( (s) { return s.hasStaticCtorOrDtor(); } ) != 0;
+ return this.include(null).foreachDsymbol( (s) { return s.hasStaticCtorOrDtor(); } ) != 0;
}
override final void checkCtorConstInit()
{
- include(null).foreachDsymbol( s => s.checkCtorConstInit() );
+ this.include(null).foreachDsymbol( s => s.checkCtorConstInit() );
}
/****************************************
return new StorageClassDeclaration(stc, Dsymbol.arraySyntaxCopy(decl));
}
- override Scope* newScope(Scope* sc)
- {
- StorageClass scstc = sc.stc;
- /* These sets of storage classes are mutually exclusive,
- * so choose the innermost or most recent one.
- */
- if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest))
- scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest);
- if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared))
- scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared);
- if (stc & (STC.const_ | STC.immutable_ | STC.manifest))
- scstc &= ~(STC.const_ | STC.immutable_ | STC.manifest);
- if (stc & (STC.gshared | STC.shared_))
- scstc &= ~(STC.gshared | STC.shared_);
- if (stc & (STC.safe | STC.trusted | STC.system))
- scstc &= ~(STC.safe | STC.trusted | STC.system);
- scstc |= stc;
- //printf("scstc = x%llx\n", scstc);
- return createNewScope(sc, scstc, sc.linkage, sc.cppmangle,
- sc.visibility, sc.explicitVisibility, sc.aligndecl, sc.inlining);
- }
-
override final bool oneMember(out Dsymbol ps, Identifier ident)
{
bool t = Dsymbol.oneMembers(decl, ps, ident);
return new DeprecatedDeclaration(msg.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl));
}
- /**
- * Provides a new scope with `STC.deprecated_` and `Scope.depdecl` set
- *
- * Calls `StorageClassDeclaration.newScope` (as it must be called or copied
- * in any function overriding `newScope`), then set the `Scope`'s depdecl.
- *
- * Returns:
- * Always a new scope, to use for this `DeprecatedDeclaration`'s members.
- */
- override Scope* newScope(Scope* sc)
- {
- auto scx = super.newScope(sc);
- // The enclosing scope is deprecated as well
- if (scx == sc)
- scx = sc.push();
- scx.depdecl = this;
- return scx;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return new LinkDeclaration(loc, linkage, Dsymbol.arraySyntaxCopy(decl));
}
- override Scope* newScope(Scope* sc)
- {
- return createNewScope(sc, sc.stc, this.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility,
- sc.aligndecl, sc.inlining);
- }
override const(char)* toChars() const
{
return new CPPMangleDeclaration(loc, cppmangle, Dsymbol.arraySyntaxCopy(decl));
}
- override Scope* newScope(Scope* sc)
- {
- return createNewScope(sc, sc.stc, LINK.cpp, cppmangle, sc.visibility, sc.explicitVisibility,
- sc.aligndecl, sc.inlining);
- }
-
override const(char)* toChars() const
{
return toString().ptr;
this.loc, this.ident, this.exp, Dsymbol.arraySyntaxCopy(this.decl), this.cppnamespace);
}
- /**
- * Returns:
- * A copy of the parent scope, with `this` as `namespace` and C++ linkage
- */
- override Scope* newScope(Scope* sc)
- {
- auto scx = sc.copy();
- scx.linkage = LINK.cpp;
- scx.namespace = this;
- return scx;
- }
-
override const(char)* toChars() const
{
return toString().ptr;
return new VisibilityDeclaration(this.loc, visibility, Dsymbol.arraySyntaxCopy(decl));
}
- override Scope* newScope(Scope* sc)
- {
- if (pkg_identifiers)
- dsymbolSemantic(this, sc);
- return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, this.visibility, 1, sc.aligndecl, sc.inlining);
- }
-
override const(char)* kind() const
{
return "visibility attribute";
Dsymbol.arraySyntaxCopy(decl));
}
- override Scope* newScope(Scope* sc)
- {
- return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, this, sc.inlining);
- }
-
override void accept(Visitor v)
{
v.visit(this);
return new PragmaDeclaration(loc, ident, Expression.arraySyntaxCopy(args), Dsymbol.arraySyntaxCopy(decl));
}
- override Scope* newScope(Scope* sc)
- {
- if (ident == Id.Pinline)
- {
- // We keep track of this pragma inside scopes,
- // then it's evaluated on demand in function semantic
- return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, sc.aligndecl, this);
- }
- return sc;
- }
-
override const(char)* kind() const
{
return "pragma";
}
}
- // Decide if 'then' or 'else' code should be included
- override Dsymbols* include(Scope* sc)
- {
- //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope);
-
- if (errors)
- return null;
-
- assert(condition);
- return condition.include(_scope ? _scope : sc) ? decl : elsedecl;
- }
-
override final void addComment(const(char)* comment)
{
/* Because addComment is called by the parser, if we called
extern (C++) final class StaticIfDeclaration : ConditionalDeclaration
{
ScopeDsymbol scopesym; /// enclosing symbol (e.g. module) where symbols will be inserted
- private bool addisdone = false; /// true if members have been added to scope
- private bool onStack = false; /// true if a call to `include` is currently active
+ bool addisdone = false; /// true if members have been added to scope
+ bool onStack = false; /// true if a call to `include` is currently active
extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe
{
return new StaticIfDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl));
}
- /****************************************
- * Different from other AttribDeclaration subclasses, include() call requires
- * the completion of addMember and setScope phases.
- */
- override Dsymbols* include(Scope* sc)
- {
- //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope);
-
- if (errors || onStack)
- return null;
- onStack = true;
- scope(exit) onStack = false;
-
- if (sc && condition.inc == Include.notComputed)
- {
- assert(scopesym); // addMember is already done
- assert(_scope); // setScope is already done
- Dsymbols* d = ConditionalDeclaration.include(_scope);
- if (d && !addisdone)
- {
- // Add members lazily.
- d.foreachDsymbol( s => s.addMember(_scope, scopesym) );
-
- // Set the member scopes lazily.
- d.foreachDsymbol( s => s.setScope(_scope) );
-
- addisdone = true;
- }
- return d;
- }
- else
- {
- return ConditionalDeclaration.include(sc);
- }
- }
-
override const(char)* kind() const
{
return "static if";
return false;
}
- override Dsymbols* include(Scope* sc)
- {
- if (errors || onStack)
- return null;
- if (cached)
- {
- assert(!onStack);
- return cache;
- }
- onStack = true;
- scope(exit) onStack = false;
-
- if (_scope)
- {
- sfe.prepare(_scope); // lower static foreach aggregate
- }
- if (!sfe.ready())
- {
- return null; // TODO: ok?
- }
-
- // expand static foreach
- import dmd.statementsem: makeTupleForeach;
- Dsymbols* d = makeTupleForeach(_scope, true, true, sfe.aggrfe, decl, sfe.needExpansion).decl;
- if (d) // process generated declarations
- {
- // Add members lazily.
- d.foreachDsymbol( s => s.addMember(_scope, scopesym) );
-
- // Set the member scopes lazily.
- d.foreachDsymbol( s => s.setScope(_scope) );
- }
- cached = true;
- cache = d;
- return d;
- }
-
override void addComment(const(char)* comment)
{
// do nothing
sym.symtab = new DsymbolTable();
}
- /**************************************
- * Use the ForwardingScopeDsymbol as the parent symbol for members.
- */
- override Scope* newScope(Scope* sc)
- {
- return sc.push(sym);
- }
override inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout
{
}
}
-
/***********************************************************
* Mixin declarations, like:
* mixin("int x");
return new UserAttributeDeclaration(Expression.arraySyntaxCopy(this.atts), Dsymbol.arraySyntaxCopy(decl));
}
- override Scope* newScope(Scope* sc)
- {
- Scope* sc2 = sc;
- if (atts && atts.length)
- {
- // create new one for changes
- sc2 = sc.copy();
- sc2.userAttribDecl = this;
- }
- return sc2;
- }
-
extern (D) static Expressions* concat(Expressions* udas1, Expressions* udas2)
{
Expressions* udas;
{
v.visit(this);
}
-
- /**
- * Check if the provided expression references `core.attribute.gnuAbiTag`
- *
- * This should be called after semantic has been run on the expression.
- * Semantic on UDA happens in semantic2 (see `dmd.semantic2`).
- *
- * Params:
- * e = Expression to check (usually from `UserAttributeDeclaration.atts`)
- *
- * Returns:
- * `true` if the expression references the compiler-recognized `gnuAbiTag`
- */
- static bool isGNUABITag(Expression e)
- {
- if (global.params.cplusplus < CppStdRevision.cpp11)
- return false;
-
- auto ts = e.type ? e.type.isTypeStruct() : null;
- if (!ts)
- return false;
- if (ts.sym.ident != Id.udaGNUAbiTag || !ts.sym.parent)
- return false;
- // Can only be defined in druntime
- Module m = ts.sym.parent.isModule();
- if (!m || !m.isCoreModule(Id.attribute))
- return false;
- return true;
- }
-
- /**
- * Called from a symbol's semantic to check if `gnuAbiTag` UDA
- * can be applied to them
- *
- * Directly emits an error if the UDA doesn't work with this symbol
- *
- * Params:
- * sym = symbol to check for `gnuAbiTag`
- * linkage = Linkage of the symbol (Declaration.link or sc.link)
- */
- static void checkGNUABITag(Dsymbol sym, LINK linkage)
- {
- if (global.params.cplusplus < CppStdRevision.cpp11)
- return;
-
- foreachUdaNoSemantic(sym, (exp) {
- if (isGNUABITag(exp))
- {
- if (sym.isCPPNamespaceDeclaration() || sym.isNspace())
- {
- .error(exp.loc, "`@%s` cannot be applied to namespaces", Id.udaGNUAbiTag.toChars());
- sym.errors = true;
- }
- else if (linkage != LINK.cpp)
- {
- .error(exp.loc, "`@%s` can only apply to C++ symbols", Id.udaGNUAbiTag.toChars());
- sym.errors = true;
- }
- // Only one `@gnuAbiTag` is allowed by semantic2
- return 1; // break
- }
- return 0; // continue
- });
- }
}
/**
return 0;
}
-
/**
* Returns: true if the given expression is an enum from `core.attribute` named `id`
*/
{
public:
Dsymbols *decl; // array of Dsymbol's
-
- virtual Dsymbols *include(Scope *sc);
- virtual Scope *newScope(Scope *sc);
void addComment(const utf8_t *comment) override;
const char *kind() const override;
bool oneMember(Dsymbol *&ps, Identifier *ident) override;
StorageClass stc;
StorageClassDeclaration *syntaxCopy(Dsymbol *s) override;
- Scope *newScope(Scope *sc) override;
bool oneMember(Dsymbol *&ps, Identifier *ident) override final;
StorageClassDeclaration *isStorageClassDeclaration() override { return this; }
const char *msgstr;
DeprecatedDeclaration *syntaxCopy(Dsymbol *s) override;
- Scope *newScope(Scope *sc) override;
void accept(Visitor *v) override { v->visit(this); }
};
static LinkDeclaration *create(const Loc &loc, LINK p, Dsymbols *decl);
LinkDeclaration *syntaxCopy(Dsymbol *s) override;
- Scope *newScope(Scope *sc) override;
const char *toChars() const override;
void accept(Visitor *v) override { v->visit(this); }
};
CPPMANGLE cppmangle;
CPPMangleDeclaration *syntaxCopy(Dsymbol *s) override;
- Scope *newScope(Scope *sc) override;
const char *toChars() const override;
void accept(Visitor *v) override { v->visit(this); }
};
Expression *exp;
CPPNamespaceDeclaration *syntaxCopy(Dsymbol *s) override;
- Scope *newScope(Scope *sc) override;
const char *toChars() const override;
void accept(Visitor *v) override { v->visit(this); }
};
DArray<Identifier*> pkg_identifiers;
VisibilityDeclaration *syntaxCopy(Dsymbol *s) override;
- Scope *newScope(Scope *sc) override;
const char *kind() const override;
const char *toPrettyChars(bool unused) override;
VisibilityDeclaration *isVisibilityDeclaration() override { return this; }
structalign_t salign;
AlignDeclaration *syntaxCopy(Dsymbol *s) override;
- Scope *newScope(Scope *sc) override;
void accept(Visitor *v) override { v->visit(this); }
};
Expressions *args; // array of Expression's
PragmaDeclaration *syntaxCopy(Dsymbol *s) override;
- Scope *newScope(Scope *sc) override;
const char *kind() const override;
void accept(Visitor *v) override { v->visit(this); }
};
ConditionalDeclaration *syntaxCopy(Dsymbol *s) override;
bool oneMember(Dsymbol *&ps, Identifier *ident) override final;
- Dsymbols *include(Scope *sc) override;
void addComment(const utf8_t *comment) override final;
void accept(Visitor *v) override { v->visit(this); }
};
d_bool onStack;
StaticIfDeclaration *syntaxCopy(Dsymbol *s) override;
- Dsymbols *include(Scope *sc) override;
StaticIfDeclaration *isStaticIfDeclaration() override { return this; }
const char *kind() const override;
void accept(Visitor *v) override { v->visit(this); }
StaticForeachDeclaration *syntaxCopy(Dsymbol *s) override;
bool oneMember(Dsymbol *&ps, Identifier *ident) override;
- Dsymbols *include(Scope *sc) override;
void addComment(const utf8_t *comment) override;
const char *kind() const override;
void accept(Visitor *v) override { v->visit(this); }
public:
ForwardingScopeDsymbol *sym;
- Scope *newScope(Scope *sc) override;
ForwardingAttribDeclaration *isForwardingAttribDeclaration() override { return this; }
void accept(Visitor *v) override { v->visit(this); }
};
Expressions *atts;
UserAttributeDeclaration *syntaxCopy(Dsymbol *s) override;
- Scope *newScope(Scope *sc) override;
const char *kind() const override;
void accept(Visitor *v) override { v->visit(this); }
};
void visitExp(ExpStatement s)
{
result = BE.fallthru;
- if (s.exp)
+ if (!s.exp)
+ return;
+
+ if (s.exp.op == EXP.halt)
{
- if (s.exp.op == EXP.halt)
+ result = BE.halt;
+ return;
+ }
+ if (AssertExp a = s.exp.isAssertExp())
+ {
+ if (a.e1.toBool().hasValue(false)) // if it's an assert(0)
{
result = BE.halt;
return;
}
- if (AssertExp a = s.exp.isAssertExp())
- {
- if (a.e1.toBool().hasValue(false)) // if it's an assert(0)
- {
- result = BE.halt;
- return;
- }
- }
- if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn())
- result = BE.halt;
-
- result |= canThrow(s.exp, func, eSink);
}
+ if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn())
+ result = BE.halt;
+ result |= canThrow(s.exp, func, eSink);
}
void visitDtorExp(DtorExpStatement s)
Statement slast = null;
foreach (s; *cs.statements)
{
- if (s)
+ if (!s)
+ continue;
+
+ //printf("result = x%x\n", result);
+ //printf("s: %s\n", s.toChars());
+ if (result & BE.fallthru && slast)
{
- //printf("result = x%x\n", result);
- //printf("s: %s\n", s.toChars());
- if (result & BE.fallthru && slast)
+ slast = slast.last();
+ if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement()))
{
- slast = slast.last();
- if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement()))
+ // Allow if last case/default was empty
+ CaseStatement sc = slast.isCaseStatement();
+ DefaultStatement sd = slast.isDefaultStatement();
+ auto sl = (sc ? sc.statement : (sd ? sd.statement : null));
+
+ if (sl && (!sl.hasCode() || sl.isErrorStatement()))
{
- // Allow if last case/default was empty
- CaseStatement sc = slast.isCaseStatement();
- DefaultStatement sd = slast.isDefaultStatement();
- auto sl = (sc ? sc.statement : (sd ? sd.statement : null));
-
- if (sl && (!sl.hasCode() || sl.isErrorStatement()))
- {
- }
- else if (func.getModule().filetype != FileType.c)
- {
- const(char)* gototype = s.isCaseStatement() ? "case" : "default";
- // https://issues.dlang.org/show_bug.cgi?id=22999
- global.errorSink.error(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype);
- }
+ }
+ else if (func.getModule().filetype != FileType.c)
+ {
+ const(char)* gototype = s.isCaseStatement() ? "case" : "default";
+ // https://issues.dlang.org/show_bug.cgi?id=22999
+ global.errorSink.error(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype);
}
}
+ }
- if ((result & BE.fallthru) || s.comeFrom())
- {
- result &= ~BE.fallthru;
- result |= blockExit(s, func, eSink);
- }
- slast = s;
+ if ((result & BE.fallthru) || s.comeFrom())
+ {
+ result &= ~BE.fallthru;
+ result |= blockExit(s, func, eSink);
}
+ slast = s;
}
}
result = BE.fallthru;
foreach (s; *uls.statements)
{
- if (s)
- {
- int r = blockExit(s, func, eSink);
- result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru);
- if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0)
- result &= ~BE.fallthru;
- }
+ if (!s)
+ continue;
+ int r = blockExit(s, func, eSink);
+ result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru);
+ if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0)
+ result &= ~BE.fallthru;
}
}
import dmd.blockexit : BE, checkThrow;
import dmd.declaration;
import dmd.dsymbol;
+import dmd.dsymbolsem : include;
import dmd.errorsink;
import dmd.expression;
+import dmd.expressionsem : errorSupplementalInferredAttr;
import dmd.func;
import dmd.globals;
import dmd.init;
* Returns:
* merged storage class
*/
-StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure
+StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure @safe
{
if (!f)
return s1;
*/
FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc)
{
- if (Dsymbol assign = search_function(ad, Id.assign))
- {
- /* check identity opAssign exists
- */
- scope er = new NullExp(ad.loc, ad.type); // dummy rvalue
- scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue
- el.type = ad.type;
- const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
- sc = sc.push();
- sc.tinst = null;
- sc.minst = null;
+ Dsymbol assign = search_function(ad, Id.assign);
+ if (!assign)
+ return null;
- auto a = new Expressions(1);
- (*a)[0] = er;
- auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet);
- if (!f)
- {
- (*a)[0] = el;
- f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet);
- }
+ /* check identity opAssign exists
+ */
+ scope er = new NullExp(ad.loc, ad.type); // dummy rvalue
+ scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue
+ el.type = ad.type;
+ const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
+ sc = sc.push();
+ sc.tinst = null;
+ sc.minst = null;
- sc = sc.pop();
- global.endGagging(errors);
- if (f)
- {
- if (f.errors)
- return null;
- auto fparams = f.getParameterList();
- if (fparams.length)
- {
- auto fparam0 = fparams[0];
- if (fparam0.type.toDsymbol(null) != ad)
- f = null;
- }
- }
- // BUGS: This detection mechanism cannot find some opAssign-s like follows:
- // struct S { void opAssign(ref immutable S) const; }
- return f;
+ auto a = new Expressions(1);
+ (*a)[0] = er;
+ auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet);
+ if (!f)
+ {
+ (*a)[0] = el;
+ f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet);
}
- return null;
+
+ sc = sc.pop();
+ global.endGagging(errors);
+ if (!f)
+ return null;
+ if (f.errors)
+ return null;
+ auto fparams = f.getParameterList();
+ if (fparams.length)
+ {
+ auto fparam0 = fparams[0];
+ if (fparam0.type.toDsymbol(null) != ad)
+ f = null;
+ }
+ // BUGS: This detection mechanism cannot find some opAssign-s like follows:
+ // struct S { void opAssign(ref immutable S) const; }
+ return f;
}
/*******************************************
*/
bool needOpEquals(StructDeclaration sd)
{
+ bool dontneed()
+ {
+ //printf("\tdontneed\n");
+ return false;
+ }
+ bool need()
+ {
+ //printf("\tneed\n");
+ return true;
+ }
//printf("StructDeclaration::needOpEquals() %s\n", sd.toChars());
if (sd.isUnionDeclaration())
{
/* If a union has only one field, treat it like a struct
*/
if (sd.fields.length != 1)
- goto Ldontneed;
+ return dontneed();
}
if (sd.hasIdentityEquals)
- goto Lneed;
+ return need();
/* If any of the fields has an opEquals, then we
* need it too.
*/
if (ts.sym.isUnionDeclaration() && ts.sym.fields.length != 1)
continue;
if (needOpEquals(ts.sym))
- goto Lneed;
+ return need();
}
if (tvbase.isFloating())
{
// This is necessray for:
// 1. comparison of +0.0 and -0.0 should be true.
// 2. comparison of NANs should be false always.
- goto Lneed;
+ return need();
}
if (tvbase.ty == Tarray)
- goto Lneed;
+ return need();
if (tvbase.ty == Taarray)
- goto Lneed;
+ return need();
if (tvbase.ty == Tclass)
- goto Lneed;
+ return need();
}
-Ldontneed:
- //printf("\tdontneed\n");
- return false;
-Lneed:
- //printf("\tneed\n");
- return true;
+ return dontneed();
}
/*******************************************
private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc)
{
FuncDeclaration f;
- if (Dsymbol eq = search_function(ad, Id.eq))
+ Dsymbol eq = search_function(ad, Id.eq);
+ if (!eq)
+ return null;
+
+ /* check identity opEquals exists
+ */
+ scope er = new NullExp(ad.loc, null); // dummy rvalue
+ scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue
+ auto a = new Expressions(1);
+
+ bool hasIt(Type tthis)
{
- /* check identity opEquals exists
- */
- scope er = new NullExp(ad.loc, null); // dummy rvalue
- scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue
- auto a = new Expressions(1);
+ const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it
+ sc = sc.push();
+ sc.tinst = null;
+ sc.minst = null;
- bool hasIt(Type tthis)
+ FuncDeclaration rfc(Expression e)
{
- const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it
- sc = sc.push();
- sc.tinst = null;
- sc.minst = null;
-
- FuncDeclaration rfc(Expression e)
- {
- (*a)[0] = e;
- (*a)[0].type = tthis;
- return resolveFuncCall(ad.loc, sc, eq, null, tthis, ArgumentList(a), FuncResolveFlag.quiet);
- }
+ (*a)[0] = e;
+ (*a)[0].type = tthis;
+ return resolveFuncCall(ad.loc, sc, eq, null, tthis, ArgumentList(a), FuncResolveFlag.quiet);
+ }
- f = rfc(er);
- if (!f)
- f = rfc(el);
+ f = rfc(er);
+ if (!f)
+ f = rfc(el);
- sc = sc.pop();
- global.endGagging(errors);
+ sc = sc.pop();
+ global.endGagging(errors);
- return f !is null;
- }
+ return f !is null;
+ }
- if (hasIt(ad.type) ||
- hasIt(ad.type.constOf()) ||
- hasIt(ad.type.immutableOf()) ||
- hasIt(ad.type.sharedOf()) ||
- hasIt(ad.type.sharedConstOf()))
- {
- if (f.errors)
- return null;
- }
+ if (hasIt(ad.type) ||
+ hasIt(ad.type.constOf()) ||
+ hasIt(ad.type.immutableOf()) ||
+ hasIt(ad.type.sharedOf()) ||
+ hasIt(ad.type.sharedConstOf()))
+ {
+ if (f.errors)
+ return null;
}
+
return f;
}
Expression e2 = new IdentifierExp(loc, Id.p);
Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.cmp), e2);
fop.fbody = new ReturnStatement(loc, e);
- uint errors = global.startGagging(); // Do not report errors
+ const errors = global.startGagging(); // Do not report errors
Scope* sc2 = sc.push();
sc2.stc = 0;
sc2.linkage = LINK.d;
*/
private bool needToHash(StructDeclaration sd)
{
+ bool dontneed()
+ {
+ //printf("\tdontneed\n");
+ return false;
+ }
+ bool need()
+ {
+ //printf("\tneed\n");
+ return true;
+ }
//printf("StructDeclaration::needToHash() %s\n", sd.toChars());
if (sd.isUnionDeclaration())
- goto Ldontneed;
+ return dontneed();
if (sd.xhash)
- goto Lneed;
+ return need();
/* If any of the fields has an toHash, then we
* need it too.
if (ts.sym.isUnionDeclaration())
continue;
if (needToHash(ts.sym))
- goto Lneed;
+ return need();
}
if (tvbase.isFloating())
{
/* This is necessary because comparison of +0.0 and -0.0 should be true,
* i.e. not a bit compare.
*/
- goto Lneed;
+ return need();
}
if (tvbase.ty == Tarray)
- goto Lneed;
+ return need();
if (tvbase.ty == Taarray)
- goto Lneed;
+ return need();
if (tvbase.ty == Tclass)
- goto Lneed;
+ return need();
}
-Ldontneed:
- //printf("\tdontneed\n");
- return false;
-Lneed:
- //printf("\tneed\n");
- return true;
+ return dontneed();
}
/******************************************
/**
* Writes a 16 bit value, no reserve check.
*/
- nothrow
+ nothrow @safe
void write16n(int v)
{
auto x = cast(ushort) v;
}
// Position buffer to accept the specified number of bytes at offset
- void position(size_t where, size_t nbytes) nothrow
+ void position(size_t where, size_t nbytes) nothrow @safe
{
if (where + nbytes > data.length)
{
Returns: `true` iff the operation succeeded.
*/
- extern(D) bool moveToFile(const char* filename) @system
+ extern(D) bool moveToFile(const char[] filename) @system
{
bool result = true;
- const bool identical = this[] == FileMapping!(const ubyte)(filename)[];
+ const filenameZ = (filename ~ "\0").ptr;
+ const bool identical = this[] == FileMapping!(const ubyte)(filenameZ)[];
if (fileMapping && fileMapping.active)
{
{
// Resize to fit to get rid of the slack bytes at the end
fileMapping.resize(offset);
- result = fileMapping.moveToFile(filename);
+ result = fileMapping.moveToFile(filenameZ);
}
// Can't call destroy() here because the file mapping is already closed.
data = null;
else
{
if (!identical)
- writeFile(filename, this[]);
+ writeFile(filenameZ, this[]);
destroy();
}
return identical
- ? result && touchFile(filename)
+ ? result && touchFile(filenameZ)
: result;
}
}
import dmd.dsymbol;
import dmd.errors;
import dmd.expression;
-import dmd.expressionsem;
+import dmd.expressionsem : expressionSemantic, evalStaticCondition, resolveProperties;
import dmd.globals;
import dmd.identifier;
import dmd.location;
sc = sc.endCTFE();
el = el.optimize(WANTvalue);
el = el.ctfeInterpret();
- if (el.op == EXP.int64)
+ if (el.op != EXP.int64)
{
- Expressions *es = void;
- if (auto ale = aggr.isArrayLiteralExp())
- {
- // Directly use the elements of the array for the TupleExp creation
- es = ale.elements;
- }
- else
- {
- const length = cast(size_t)el.toInteger();
- es = new Expressions(length);
- foreach (i; 0 .. length)
- {
- auto index = new IntegerExp(loc, i, Type.tsize_t);
- auto value = new IndexExp(aggr.loc, aggr, index);
- (*es)[i] = value;
- }
- }
- aggrfe.aggr = new TupleExp(aggr.loc, es);
- aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc);
- aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue);
- aggrfe.aggr = aggrfe.aggr.ctfeInterpret();
+ aggrfe.aggr = ErrorExp.get();
+ return;
+ }
+
+ Expressions *es;
+ if (auto ale = aggr.isArrayLiteralExp())
+ {
+ // Directly use the elements of the array for the TupleExp creation
+ es = ale.elements;
}
else
{
- aggrfe.aggr = ErrorExp.get();
+ const length = cast(size_t)el.toInteger();
+ es = new Expressions(length);
+ foreach (i; 0 .. length)
+ {
+ auto index = new IntegerExp(loc, i, Type.tsize_t);
+ auto value = new IndexExp(aggr.loc, aggr, index);
+ (*es)[i] = value;
+ }
}
+ aggrfe.aggr = new TupleExp(aggr.loc, es);
+ aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc);
+ aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue);
+ aggrfe.aggr = aggrfe.aggr.ctfeInterpret();
}
/*****************************************
// Run 'typeof' gagged to avoid duplicate errors and if it fails just create
// an empty foreach to expose them.
- uint olderrors = global.startGagging();
+ const olderrors = global.startGagging();
ety = ety.typeSemantic(aloc, sc);
if (global.endGagging(olderrors))
s2.push(createForeach(aloc, pparams[1], null));
override int include(Scope* sc)
{
//printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel);
- if (inc == Include.notComputed)
+ if (inc != Include.notComputed)
{
- inc = Include.no;
- bool definedInModule = false;
- if (ident)
+ return inc == Include.yes;
+ }
+ inc = Include.no;
+ bool definedInModule = false;
+ if (ident)
+ {
+ if (mod.debugids && findCondition(*mod.debugids, ident))
{
- if (mod.debugids && findCondition(*mod.debugids, ident))
- {
- inc = Include.yes;
- definedInModule = true;
- }
- else if (findCondition(global.debugids, ident))
- inc = Include.yes;
- else
- {
- if (!mod.debugidsNot)
- mod.debugidsNot = new Identifiers();
- mod.debugidsNot.push(ident);
- }
+ inc = Include.yes;
+ definedInModule = true;
}
- else if (level <= global.params.debuglevel || level <= mod.debuglevel)
+ else if (findCondition(global.debugids, ident))
inc = Include.yes;
- if (!definedInModule)
- printDepsConditional(sc, this, "depsDebug ");
+ else
+ {
+ if (!mod.debugidsNot)
+ mod.debugidsNot = new Identifiers();
+ mod.debugidsNot.push(ident);
+ }
}
+ else if (level <= global.params.debuglevel || level <= mod.debuglevel)
+ inc = Include.yes;
+ if (!definedInModule)
+ printDepsConditional(sc, this, "depsDebug ");
return (inc == Include.yes);
}
{
//printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel);
//if (ident) printf("\tident = '%s'\n", ident.toChars());
- if (inc == Include.notComputed)
+ if (inc != Include.notComputed)
{
- inc = Include.no;
- bool definedInModule = false;
- if (ident)
+ return inc == Include.yes;
+ }
+
+ inc = Include.no;
+ bool definedInModule = false;
+ if (ident)
+ {
+ if (mod.versionids && findCondition(*mod.versionids, ident))
{
- if (mod.versionids && findCondition(*mod.versionids, ident))
- {
- inc = Include.yes;
- definedInModule = true;
- }
- else if (findCondition(global.versionids, ident))
- inc = Include.yes;
- else
- {
- if (!mod.versionidsNot)
- mod.versionidsNot = new Identifiers();
- mod.versionidsNot.push(ident);
- }
+ inc = Include.yes;
+ definedInModule = true;
}
- else if (level <= global.params.versionlevel || level <= mod.versionlevel)
+ else if (findCondition(global.versionids, ident))
inc = Include.yes;
- if (!definedInModule &&
- (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert)))
+ else
{
- printDepsConditional(sc, this, "depsVersion ");
+ if (!mod.versionidsNot)
+ mod.versionidsNot = new Identifiers();
+ mod.versionidsNot.push(ident);
}
}
+ else if (level <= global.params.versionlevel || level <= mod.versionlevel)
+ inc = Include.yes;
+ if (!definedInModule &&
+ (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert)))
+ {
+ printDepsConditional(sc, this, "depsVersion ");
+ }
return (inc == Include.yes);
}
return 0;
}
- if (inc == Include.notComputed)
+ if (inc != Include.notComputed)
{
- if (!sc)
- {
- error(loc, "`static if` conditional cannot be at global scope");
- inc = Include.no;
- return 0;
- }
+ return inc == Include.yes;
+ }
- import dmd.staticcond;
- bool errors;
+ if (!sc)
+ {
+ error(loc, "`static if` conditional cannot be at global scope");
+ inc = Include.no;
+ return 0;
+ }
- bool result = evalStaticCondition(sc, exp, exp, errors);
+ import dmd.staticcond;
+ bool errors;
- // Prevent repeated condition evaluation.
- // See: fail_compilation/fail7815.d
- if (inc != Include.notComputed)
- return (inc == Include.yes);
- if (errors)
- return errorReturn();
- if (result)
- inc = Include.yes;
- else
- inc = Include.no;
- }
+ bool result = evalStaticCondition(sc, exp, exp, errors);
+
+ // Prevent repeated condition evaluation.
+ // See: fail_compilation/fail7815.d
+ if (inc != Include.notComputed)
+ return (inc == Include.yes);
+ if (errors)
+ return errorReturn();
+ if (result)
+ inc = Include.yes;
+ else
+ inc = Include.no;
return (inc == Include.yes);
}
}
/***********************************************************
- * cppmangle.d
+ * mangle/cpp.d
*/
const(char)* toCppMangleItanium(Dsymbol s)
{
- import dmd.cppmangle;
- return dmd.cppmangle.toCppMangleItanium(s);
+ import dmd.mangle.cpp;
+ return dmd.mangle.cpp.toCppMangleItanium(s);
}
const(char)* cppTypeInfoMangleItanium(Dsymbol s)
{
- import dmd.cppmangle;
- return dmd.cppmangle.cppTypeInfoMangleItanium(s);
+ import dmd.mangle.cpp;
+ return dmd.mangle.cpp.cppTypeInfoMangleItanium(s);
}
const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset)
{
- import dmd.cppmangle;
- return dmd.cppmangle.cppThunkMangleItanium(fd, offset);
+ import dmd.mangle.cpp;
+ return dmd.mangle.cpp.cppThunkMangleItanium(fd, offset);
}
/***********************************************************
}
/***********************************************************
- * dmangle.d
+ * mangle/package.d
*/
const(char)* mangleExact(FuncDeclaration fd)
{
- import dmd.dmangle;
- return dmd.dmangle.mangleExact(fd);
+ import dmd.mangle;
+ return dmd.mangle.mangleExact(fd);
}
void mangleToBuffer(Type t, ref OutBuffer buf)
{
- import dmd.dmangle;
- return dmd.dmangle.mangleToBuffer(t, buf);
+ import dmd.mangle;
+ return dmd.mangle.mangleToBuffer(t, buf);
}
void mangleToBuffer(Expression e, ref OutBuffer buf)
{
- import dmd.dmangle;
- return dmd.dmangle.mangleToBuffer(e, buf);
+ import dmd.mangle;
+ return dmd.mangle.mangleToBuffer(e, buf);
}
void mangleToBuffer(Dsymbol s, ref OutBuffer buf)
{
- import dmd.dmangle;
- return dmd.dmangle.mangleToBuffer(s, buf);
+ import dmd.mangle;
+ return dmd.mangle.mangleToBuffer(s, buf);
}
void mangleToBuffer(TemplateInstance ti, ref OutBuffer buf)
{
- import dmd.dmangle;
- return dmd.dmangle.mangleToBuffer(ti, buf);
+ import dmd.mangle;
+ return dmd.mangle.mangleToBuffer(ti, buf);
}
/***********************************************************
return dmd.dsymbolsem.importAll(d, sc);
}
+Dsymbols* include(Dsymbol d, Scope* sc)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.include(d, sc);
+}
+
/***********************************************************
* dtemplate.d
*/
return dmd.expressionsem.expressionSemantic(e, sc);
}
+bool fill(StructDeclaration sd, const ref Loc loc,
+ ref Expressions elements, bool ctorinit)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.fill(sd, loc, elements, ctorinit);
+}
+
/***********************************************************
* funcsem.d
*/
import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.root.utf;
+import dmd.safe : setUnsafe;
import dmd.tokens;
import dmd.typesem;
return MATCH.nomatch;
m = MATCH.constant;
}
- if (e.hexString && tn.isIntegral && (tn.size == e.sz || (!e.committed && (e.len % tn.size) == 0)))
+ if (e.type != t && e.hexString && tn.isIntegral && (tn.size == e.sz || (!e.committed && (e.len % tn.size) == 0)))
{
m = MATCH.convert;
return m;
import dmd.declaration;
import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem;
+import dmd.dsymbolsem : dsymbolSemantic, addMember, search, setFieldOffset;
import dmd.errors;
import dmd.func;
-import dmd.funcsem;
import dmd.id;
import dmd.identifier;
import dmd.location;
import dmd.objc;
import dmd.root.rmem;
import dmd.target;
-import dmd.typesem;
+import dmd.typesem : covariant, immutableOf, sarrayOf;
import dmd.visitor;
/***********************************************************
final bool isFuncHidden(FuncDeclaration fd)
{
+ import dmd.funcsem : overloadApply;
//printf("ClassDeclaration.isFuncHidden(class = %s, fd = %s)\n", toChars(), fd.toPrettyChars());
Dsymbol s = this.search(Loc.initial, fd.ident, SearchOpt.ignoreAmbiguous | SearchOpt.ignoreErrors);
if (!s)
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
-import dmd.dsymbolsem;
+import dmd.dsymbolsem : dsymbolSemantic, aliasSemantic;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
import dmd.func;
-import dmd.funcsem;
+import dmd.funcsem : overloadApply, getLevelAndCheck;
import dmd.globals;
import dmd.gluelayer;
import dmd.id;
import dmd.identifier;
import dmd.init;
-import dmd.initsem;
+import dmd.initsem : initializerToExpression, initializerSemantic;
import dmd.intrange;
import dmd.location;
import dmd.mtype;
import dmd.root.filename;
import dmd.target;
import dmd.tokens;
-import dmd.typesem;
+import dmd.typesem : toDsymbol, typeSemantic, size, hasPointers;
import dmd.visitor;
version (IN_GCC) {}
else version (IN_LLVM) {}
else version = MARS;
-/************************************
- * Check to see the aggregate type is nested and its context pointer is
- * accessible from the current scope.
- * Returns true if error occurs.
- */
-bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, size_t iStart = 0)
-{
- Dsymbol sparent = ad.toParentLocal();
- Dsymbol sparent2 = ad.toParent2();
- Dsymbol s = sc.func;
- if (ad.isNested() && s)
- {
- //printf("ad = %p %s [%s], parent:%p\n", ad, ad.toChars(), ad.loc.toChars(), ad.parent);
- //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent.toChars(), sparent.loc.toChars(), sparent.parent,toChars());
- //printf("sparent2 = %p %s [%s], parent: %s\n", sparent2, sparent2.toChars(), sparent2.loc.toChars(), sparent2.parent,toChars());
- if (!ensureStaticLinkTo(s, sparent) || sparent != sparent2 && !ensureStaticLinkTo(s, sparent2))
- {
- error(loc, "cannot access frame pointer of `%s`", ad.toPrettyChars());
- return true;
- }
- }
-
- bool result = false;
- for (size_t i = iStart; i < ad.fields.length; i++)
- {
- VarDeclaration vd = ad.fields[i];
- Type tb = vd.type.baseElemOf();
- if (tb.ty == Tstruct)
- {
- result |= checkFrameAccess(loc, sc, (cast(TypeStruct)tb).sym);
- }
- }
- return result;
-}
-
-/***********************************************
- * Mark variable v as modified if it is inside a constructor that var
- * is a field in.
- * Also used to allow immutable globals to be initialized inside a static constructor.
- * Returns:
- * true if it's an initialization of v
- */
-bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1)
-{
- //printf("modifyFieldVar(var = %s)\n", var.toChars());
- Dsymbol s = sc.func;
- while (1)
- {
- FuncDeclaration fd = null;
- if (s)
- fd = s.isFuncDeclaration();
- if (fd &&
- ((fd.isCtorDeclaration() && var.isField()) ||
- ((fd.isStaticCtorDeclaration() || fd.isCrtCtor) && !var.isField())) &&
- fd.toParentDecl() == var.toParent2() &&
- (!e1 || e1.op == EXP.this_))
- {
- bool result = true;
-
- var.ctorinit = true;
- //printf("setting ctorinit\n");
-
- if (var.isField() && sc.ctorflow.fieldinit.length && !sc.intypeof)
- {
- assert(e1);
- auto mustInit = ((var.storage_class & STC.nodefaultctor) != 0 ||
- var.type.needsNested());
-
- const dim = sc.ctorflow.fieldinit.length;
- auto ad = fd.isMemberDecl();
- assert(ad);
- size_t i;
- for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ?
- {
- if (ad.fields[i] == var)
- break;
- }
- assert(i < dim);
- auto fieldInit = &sc.ctorflow.fieldinit[i];
- const fi = fieldInit.csx;
-
- if (fi & CSX.this_ctor)
- {
- if (var.type.isMutable() && e1.type.isMutable())
- result = false;
- else
- {
- const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod);
- .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars());
- .errorSupplemental(fieldInit.loc, "Previous initialization is here.");
- }
- }
- else if (sc.inLoop || (fi & CSX.label))
- {
- if (!mustInit && var.type.isMutable() && e1.type.isMutable())
- result = false;
- else
- {
- const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod);
- .error(loc, "%s field `%s` initialization is not allowed in loops or after labels", modStr, var.toChars());
- }
- }
-
- fieldInit.csx |= CSX.this_ctor;
- fieldInit.loc = e1.loc;
- if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258
- {
- foreach (j, v; ad.fields)
- {
- if (v is var || !var.isOverlappedWith(v))
- continue;
- v.ctorinit = true;
- sc.ctorflow.fieldinit[j].csx = CSX.this_ctor;
- }
- }
- }
- else if (fd != sc.func)
- {
- if (var.type.isMutable())
- result = false;
- else if (sc.func.fes)
- {
- const(char)* p = var.isField() ? "field" : var.kind();
- .error(loc, "%s %s `%s` initialization is not allowed in foreach loop",
- MODtoChars(var.type.mod), p, var.toChars());
- }
- else
- {
- const(char)* p = var.isField() ? "field" : var.kind();
- .error(loc, "%s %s `%s` initialization is not allowed in nested function `%s`",
- MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars());
- }
- }
- else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() &&
- var.type.isImmutable())
- {
- .error(loc, "%s %s `%s` initialization is not allowed in `static this`",
- MODtoChars(var.type.mod), var.kind(), var.toChars());
- errorSupplemental(loc, "Use `shared static this` instead.");
- }
- else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() &&
- var.type.isConst())
- {
- // @@@DEPRECATED_2.116@@@
- // Turn this into an error, merging with the branch above
- .deprecation(loc, "%s %s `%s` initialization is not allowed in `static this`",
- MODtoChars(var.type.mod), var.kind(), var.toChars());
- deprecationSupplemental(loc, "Use `shared static this` instead.");
- }
- return result;
- }
- else
- {
- if (s)
- {
- s = s.toParentP(var.toParent2());
- continue;
- }
- }
- break;
- }
- return false;
-}
-
/******************************************
*/
void ObjectNotFound(Loc loc, Identifier id)
return sz;
}
- /**
- * Issue an error if an attempt to call a disabled method is made
- *
- * If the declaration is disabled but inside a disabled function,
- * returns `true` but do not issue an error message.
- *
- * Params:
- * loc = Location information of the call
- * sc = Scope in which the call occurs
- * isAliasedDeclaration = if `true` searches overload set
- *
- * Returns:
- * `true` if this `Declaration` is `@disable`d, `false` otherwise.
- */
- extern (D) final bool checkDisabled(Loc loc, Scope* sc, bool isAliasedDeclaration = false)
- {
- if (!(storage_class & STC.disable))
- return false;
-
- if (sc.func && sc.func.storage_class & STC.disable)
- return true;
-
- if (auto p = toParent())
- {
- if (auto postblit = isPostBlitDeclaration())
- {
- /* https://issues.dlang.org/show_bug.cgi?id=21885
- *
- * If the generated postblit is disabled, it
- * means that one of the fields has a disabled
- * postblit. Print the first field that has
- * a disabled postblit.
- */
- if (postblit.isGenerated())
- {
- auto sd = p.isStructDeclaration();
- assert(sd);
- for (size_t i = 0; i < sd.fields.length; i++)
- {
- auto structField = sd.fields[i];
- if (structField.overlapped)
- continue;
- Type tv = structField.type.baseElemOf();
- if (tv.ty != Tstruct)
- continue;
- auto sdv = (cast(TypeStruct)tv).sym;
- if (!sdv.postblit)
- continue;
- if (sdv.postblit.isDisabled())
- {
- .error(loc, "%s `%s` is not copyable because field `%s` is not copyable", p.kind, p.toPrettyChars, structField.toChars());
- return true;
- }
- }
- }
- .error(loc, "%s `%s` is not copyable because it has a disabled postblit", p.kind, p.toPrettyChars);
- return true;
- }
- }
-
- // if the function is @disabled, maybe there
- // is an overload in the overload set that isn't
- if (isAliasedDeclaration)
- {
- if (FuncDeclaration fd = isFuncDeclaration())
- {
- for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext)
- if (!(ovl.storage_class & STC.disable))
- return false;
- }
- }
-
- if (auto ctor = isCtorDeclaration())
- {
- if (ctor.isCpCtor && ctor.isGenerated())
- {
- .error(loc, "generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", parent.toPrettyChars());
- return true;
- }
- }
- .error(loc, "%s `%s` cannot be used because it is annotated with `@disable`", kind, toPrettyChars);
- return true;
- }
-
- /*************************************
- * Check to see if declaration can be modified in this context (sc).
- * Issue error if not.
- * Params:
- * loc = location for error messages
- * e1 = `null` or `this` expression when this declaration is a field
- * sc = context
- * flag = if the first bit is set it means do not issue error message for
- * invalid modification; if the second bit is set, it means that
- this declaration is a field and a subfield of it is modified.
- * Returns:
- * Modifiable.yes or Modifiable.initialization
- */
- extern (D) final Modifiable checkModify(Loc loc, Scope* sc, Expression e1, ModifyFlags flag)
- {
- VarDeclaration v = isVarDeclaration();
- if (v && v.canassign)
- return Modifiable.initialization;
-
- if (isParameter() || isResult())
- {
- for (Scope* scx = sc; scx; scx = scx.enclosing)
- {
- if (scx.func == parent && scx.contract != Contract.none)
- {
- const(char)* s = isParameter() && parent.ident != Id.ensure ? "parameter" : "result";
- if (!(flag & ModifyFlags.noError))
- error(loc, "%s `%s` cannot modify %s `%s` in contract", kind, toPrettyChars, s, toChars());
- return Modifiable.initialization; // do not report type related errors
- }
- }
- }
-
- if (e1 && e1.op == EXP.this_ && isField())
- {
- VarDeclaration vthis = e1.isThisExp().var;
- for (Scope* scx = sc; scx; scx = scx.enclosing)
- {
- if (scx.func == vthis.parent && scx.contract != Contract.none)
- {
- if (!(flag & ModifyFlags.noError))
- error(loc, "%s `%s` cannot modify parameter `this` in contract", kind, toPrettyChars);
- return Modifiable.initialization; // do not report type related errors
- }
- }
- }
-
- if (v && (v.isCtorinit() || isField()))
- {
- // It's only modifiable if inside the right constructor
- if ((storage_class & (STC.foreach_ | STC.ref_)) == (STC.foreach_ | STC.ref_))
- return Modifiable.initialization;
- if (flag & ModifyFlags.fieldAssign)
- return Modifiable.yes;
- return modifyFieldVar(loc, sc, v, e1) ? Modifiable.initialization : Modifiable.yes;
- }
- return Modifiable.yes;
- }
-
final bool isStatic() const pure nothrow @nogc @safe
{
return (storage_class & STC.static_) != 0;
assert(this != aliassym);
//static int count; if (++count == 10) *(char*)0=0;
+ Dsymbol err()
+ {
+ // Avoid breaking "recursive alias" state during errors gagged
+ if (global.gag)
+ return this;
+ aliassym = new AliasDeclaration(loc, ident, Type.terror);
+ type = Type.terror;
+ return aliassym;
+ }
// Reading the AliasDeclaration
if (!(adFlags & ignoreRead))
adFlags |= wasRead; // can never assign to this AliasDeclaration again
if (inuse == 1 && type && _scope)
{
inuse = 2;
- uint olderrors = global.errors;
+ const olderrors = global.errors;
Dsymbol s = type.toDsymbol(_scope);
//printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this);
if (global.errors != olderrors)
- goto Lerr;
+ return err();
if (s)
{
s = s.toAlias();
if (global.errors != olderrors)
- goto Lerr;
+ return err();
aliassym = s;
inuse = 0;
}
{
Type t = type.typeSemantic(loc, _scope);
if (t.ty == Terror)
- goto Lerr;
+ return err();
if (global.errors != olderrors)
- goto Lerr;
+ return err();
//printf("t = %s\n", t.toChars());
inuse = 0;
}
if (inuse)
{
.error(loc, "%s `%s` recursive alias declaration", kind, toPrettyChars);
-
- Lerr:
- // Avoid breaking "recursive alias" state during errors gagged
- if (global.gag)
- return this;
- aliassym = new AliasDeclaration(loc, ident, Type.terror);
- type = Type.terror;
- return aliassym;
+ return err();
}
if (semanticRun >= PASS.semanticdone)
auto vbitoffset = v.offset * 8;
// Bitsize of types are overridden by any bit-field widths.
- ulong tbitsize = void;
+ ulong tbitsize;
if (auto bf = isBitFieldDeclaration())
{
bitoffset += bf.bitOffset;
else
tbitsize = tsz * 8;
- ulong vbitsize = void;
+ ulong vbitsize;
if (auto vbf = v.isBitFieldDeclaration())
{
vbitoffset += vbf.bitOffset;
return edtor && !(storage_class & STC.nodtor);
}
- /******************************************
- * If a variable has a scope destructor call, return call for it.
- * Otherwise, return NULL.
- */
- extern (D) final Expression callScopeDtor(Scope* sc)
- {
- //printf("VarDeclaration::callScopeDtor() %s\n", toChars());
-
- // Destruction of STC.field's is handled by buildDtor()
- if (storage_class & (STC.nodtor | STC.ref_ | STC.out_ | STC.field))
- {
- return null;
- }
-
- if (iscatchvar)
- return null; // destructor is built by `void semantic(Catch c, Scope* sc)`, not here
-
- Expression e = null;
- // Destructors for structs and arrays of structs
- Type tv = type.baseElemOf();
- if (tv.ty == Tstruct)
- {
- StructDeclaration sd = (cast(TypeStruct)tv).sym;
- if (!sd.dtor || sd.errors)
- return null;
-
- const sz = type.size();
- assert(sz != SIZE_INVALID);
- if (!sz)
- return null;
-
- if (type.toBasetype().ty == Tstruct)
- {
- // v.__xdtor()
- e = new VarExp(loc, this);
-
- /* This is a hack so we can call destructors on const/immutable objects.
- * Need to add things like "const ~this()" and "immutable ~this()" to
- * fix properly.
- */
- e.type = e.type.mutableOf();
-
- // Enable calling destructors on shared objects.
- // The destructor is always a single, non-overloaded function,
- // and must serve both shared and non-shared objects.
- e.type = e.type.unSharedOf;
-
- e = new DotVarExp(loc, e, sd.dtor, false);
- e = new CallExp(loc, e);
- }
- else
- {
- // __ArrayDtor(v[0 .. n])
- e = new VarExp(loc, this);
-
- const sdsz = sd.type.size();
- assert(sdsz != SIZE_INVALID && sdsz != 0);
- const n = sz / sdsz;
- SliceExp se = new SliceExp(loc, e, new IntegerExp(loc, 0, Type.tsize_t),
- new IntegerExp(loc, n, Type.tsize_t));
-
- // Prevent redundant bounds check
- se.upperIsInBounds = true;
- se.lowerIsLessThanUpper = true;
-
- // This is a hack so we can call destructors on const/immutable objects.
- se.type = sd.type.arrayOf();
-
- e = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se);
- }
- return e;
- }
- // Destructors for classes
- if (storage_class & (STC.auto_ | STC.scope_) && !(storage_class & STC.parameter))
- {
- for (ClassDeclaration cd = type.isClassHandle(); cd; cd = cd.baseClass)
- {
- /* We can do better if there's a way with onstack
- * classes to determine if there's no way the monitor
- * could be set.
- */
- //if (cd.isInterfaceDeclaration())
- // error("interface `%s` cannot be scope", cd.toChars());
-
- if (onstack) // if any destructors
- {
- // delete'ing C++ classes crashes (and delete is deprecated anyway)
- if (cd.classKind == ClassKind.cpp)
- {
- // Don't call non-existant dtor
- if (!cd.dtor)
- break;
-
- e = new VarExp(loc, this);
- e.type = e.type.mutableOf().unSharedOf(); // Hack for mutable ctor on immutable instances
- e = new DotVarExp(loc, e, cd.dtor, false);
- e = new CallExp(loc, e);
- break;
- }
-
- // delete this;
- Expression ec;
- ec = new VarExp(loc, this);
- e = new DeleteExp(loc, ec, true);
- e.type = Type.tvoid;
- break;
- }
- }
- }
- return e;
- }
-
/*******************************************
* If variable has a constant expression initializer, get it.
* Otherwise, return null.
assert(type && _init);
// Ungag errors when not speculative
- uint oldgag = global.gag;
+ const oldgag = global.gag;
if (global.gag)
{
Dsymbol sym = isMember();
return e;
}
- /*******************************************
- * Helper function for the expansion of manifest constant.
- */
- extern (D) final Expression expandInitializer(Loc loc)
- {
- assert((storage_class & STC.manifest) && _init);
-
- auto e = getConstInitializer();
- if (!e)
- {
- .error(loc, "cannot make expression out of initializer for `%s`", toChars());
- return ErrorExp.get();
- }
-
- e = e.copy();
- e.loc = loc; // for better error message
- return e;
- }
-
override final void checkCtorConstInit()
{
version (none)
*/
private Identifier getTypeInfoIdent(Type t)
{
- import dmd.dmangle;
+ import dmd.mangle;
import core.stdc.stdlib;
import dmd.root.rmem;
// _init_10TypeInfo_%s
// Things that should really go into Scope
- // 1 if there's a return exp; statement
- // 2 if there's a throw statement
- // 4 if there's an assert(0)
- // 8 if there's inline asm
- // 16 if there are multiple return statements
- int hasReturnExp;
-
VarDeclaration *nrvo_var; // variable to replace with shidden
Symbol *shidden; // hidden pointer passed to function
bool dllImport(bool v);
bool dllExport() const;
bool dllExport(bool v);
+ bool hasReturnExp() const;
+ bool hasReturnExp(bool v);
+ bool hasInlineAsm() const;
+ bool hasInlineAsm(bool v);
+ bool hasMultipleReturnExp() const;
+ bool hasMultipleReturnExp(bool v);
// Data for a function declaration that is needed for the Objective-C
// integration.
import dmd.arraytypes;
import dmd.dmodule;
import dmd.dsymbol;
-import dmd.errors;
import dmd.identifier;
import dmd.location;
import dmd.visitor;
this.visibility = Visibility.Kind.private_; // default to private
}
- extern (D) void addAlias(Identifier name, Identifier _alias)
- {
- if (isstatic)
- .error(loc, "%s `%s` cannot have an import bind list", kind, toPrettyChars);
- if (!aliasId)
- this.ident = null; // make it an anonymous import
- names.push(name);
- aliases.push(_alias);
- }
-
override const(char)* kind() const
{
return isstatic ? "static import" : "import";
assert(!s);
auto si = new Import(loc, packages, id, aliasId, isstatic);
si.comment = comment;
+ assert(!(isstatic && names.length));
+ if (names.length && !si.aliasId)
+ si.ident = null;
for (size_t i = 0; i < names.length; i++)
{
- si.addAlias(names[i], aliases[i]);
+ si.names.push(names[i]);
+ si.aliases.push(aliases[i]);
}
return si;
}
import dmd.doc;
import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem;
+import dmd.dsymbolsem : dsymbolSemantic, importAll, load, include;
import dmd.errors;
import dmd.errorsink;
import dmd.expression;
const name = srcfile.toString();
if (FileName.equals(name, "object.d"))
{
- ObjectNotFound(loc, ident);
+ ObjectNotFound(Loc.initial, ident);
}
else if (FileName.ext(this.arg) || !loc.isValid())
{
{
const bool doUnittests = global.params.parsingUnittestsRequired();
scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests);
- p.transitionIn = global.params.v.vin;
p.nextToken();
p.parseModuleDeclaration();
md = p.md;
{
enum SourceEncoding { utf16, utf32}
enum Endian { little, big}
- immutable loc = mod.getLoc();
+ immutable loc = mod.loc;
/*
* Convert a buffer from UTF32 to UTF8
* thematic break. If the replacement is made `i` changes to
* point to the closing parenthesis of the `$(HR)` macro.
* iLineStart = the index within `buf` that the thematic break's line starts at
- * loc = the current location within the file
* Returns: whether a thematic break was replaced
*/
-bool replaceMarkdownThematicBreak(ref OutBuffer buf, ref size_t i, size_t iLineStart, const ref Loc loc)
+bool replaceMarkdownThematicBreak(ref OutBuffer buf, ref size_t i, size_t iLineStart)
{
const slice = buf[];
* iEnd = the index within `buf` of the character after the last
* heading character. Is incremented by the length of the
* inserted heading macro when this function ends.
- * loc = the location of the Ddoc within the file
* headingLevel = the level (1-6) of heading to end. Is set to `0` when this
* function ends.
*/
-void endMarkdownHeading(ref OutBuffer buf, size_t iStart, ref size_t iEnd, const ref Loc loc, ref int headingLevel)
+void endMarkdownHeading(ref OutBuffer buf, size_t iStart, ref size_t iEnd, ref int headingLevel)
{
char[5] heading = "$(H0 ";
heading[3] = cast(char) ('0' + headingLevel);
* e.g. `*very* **nice**` becomes `$(EM very) $(STRONG nice)`.
* Params:
* buf = an OutBuffer containing the DDoc
- * loc = the current location within the file
* inlineDelimiters = the collection of delimiters found within a paragraph. When this function returns its length will be reduced to `downToLevel`.
* downToLevel = the length within `inlineDelimiters`` to reduce emphasis to
* Returns: the number of characters added to the buffer by the replacements
*/
-size_t replaceMarkdownEmphasis(ref OutBuffer buf, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, int downToLevel = 0)
+size_t replaceMarkdownEmphasis(ref OutBuffer buf, ref MarkdownDelimiter[] inlineDelimiters, int downToLevel = 0)
{
size_t replaceEmphasisPair(ref MarkdownDelimiter start, ref MarkdownDelimiter end)
{
* i = the index within `buf` of the list item. If this function succeeds `i` will be adjusted to fit the inserted macro.
* iPrecedingBlankLine = the index within `buf` of the preceeding blank line. If non-zero and a new list was started, the preceeding blank line is removed and this value is set to `0`.
* nestedLists = a set of nested lists. If this function succeeds it may contain a new nested list.
- * loc = the location of the Ddoc within the file
* Returns: `true` if a list was created
*/
- bool startItem(ref OutBuffer buf, ref size_t iLineStart, ref size_t i, ref size_t iPrecedingBlankLine, ref MarkdownList[] nestedLists, const ref Loc loc)
+ bool startItem(ref OutBuffer buf, ref size_t iLineStart, ref size_t i, ref size_t iPrecedingBlankLine, ref MarkdownList[] nestedLists)
{
buf.remove(iStart, iContentStart - iStart);
* buf = an OutBuffer containing the DDoc
* i = the index within `buf` that points to the `]` character of the potential link.
* If this function succeeds it will be adjusted to fit the inserted link macro.
- * loc = the current location within the file
* inlineDelimiters = previously parsed Markdown delimiters, including emphasis and link/image starts
* delimiterIndex = the index within `inlineDelimiters` of the nearest link/image starting delimiter
* linkReferences = previously parsed link references. When this function returns it may contain
* additional previously unparsed references.
* Returns: whether a reference link was found and replaced at `i`
*/
- static bool replaceLink(ref OutBuffer buf, ref size_t i, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences)
+ static bool replaceLink(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences)
{
const delimiter = inlineDelimiters[delimiterIndex];
MarkdownLink link;
if (iEnd > i)
{
i = delimiter.iStart;
- link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences, loc);
+ link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences);
inlineDelimiters.length = delimiterIndex;
return true;
}
if (iEnd > i)
{
const label = link.label;
- link = linkReferences.lookupReference(label, buf, i, loc);
+ link = linkReferences.lookupReference(label, buf, i);
// check rightFlanking to avoid replacing things like int[string]
if (!link.href.length && !delimiter.rightFlanking)
link = linkReferences.lookupSymbol(label);
if (iEnd == i)
return false;
- immutable delta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, delimiterIndex);
+ immutable delta = replaceMarkdownEmphasis(buf, inlineDelimiters, delimiterIndex);
iEnd += delta;
i += delta;
link.replaceLink(buf, i, iEnd, delimiter);
* delimiterIndex = the index within `inlineDelimiters` of the nearest link/image starting delimiter
* linkReferences = previously parsed link references. When this function returns it may contain
* additional previously unparsed references.
- * loc = the current location in the file
* Returns: whether a reference link was found and replaced at `i`
*/
- static bool replaceReferenceDefinition(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences, const ref Loc loc)
+ static bool replaceReferenceDefinition(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences)
{
const delimiter = inlineDelimiters[delimiterIndex];
MarkdownLink link;
return false;
i = delimiter.iStart;
- link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences, loc);
+ link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences);
inlineDelimiters.length = delimiterIndex;
return true;
}
* iEnd = the index within `buf` that points just after the end of the definition
* linkReferences = previously parsed link references. When this function returns it may contain
* an additional reference.
- * loc = the current location in the file
*/
- private void storeAndReplaceDefinition(ref OutBuffer buf, ref size_t i, size_t iEnd, ref MarkdownLinkReferences linkReferences, const ref Loc loc)
+ private void storeAndReplaceDefinition(ref OutBuffer buf, ref size_t i, size_t iEnd, ref MarkdownLinkReferences linkReferences)
{
// Remove the definition and trailing whitespace
iEnd = skipChars(buf, iEnd, " \t\r\n");
* label = the label to find the reference for
* buf = an OutBuffer containing the DDoc
* i = the index within `buf` to start searching for references at
- * loc = the current location in the file
* Returns: a link. If the `href` member has a value then the reference is valid.
*/
- MarkdownLink lookupReference(string label, ref OutBuffer buf, size_t i, const ref Loc loc)
+ MarkdownLink lookupReference(string label, ref OutBuffer buf, size_t i)
{
const lowercaseLabel = label.toLowercase();
if (lowercaseLabel !in references)
- extractReferences(buf, i, loc);
+ extractReferences(buf, i);
if (lowercaseLabel in references)
return references[lowercaseLabel];
* Params:
* buf = an OutBuffer containing the DDoc
* i = the index within `buf` to start looking at
- * loc = the current location in the file
* Returns: whether a reference was extracted
*/
- private void extractReferences(ref OutBuffer buf, size_t i, const ref Loc loc)
+ private void extractReferences(ref OutBuffer buf, size_t i)
{
static bool isFollowedBySpace(ref OutBuffer buf, size_t i)
{
break;
case ']':
if (delimiters.length && !inCode &&
- MarkdownLink.replaceReferenceDefinition(buf, i, delimiters, cast(int) delimiters.length - 1, this, loc))
+ MarkdownLink.replaceReferenceDefinition(buf, i, delimiters, cast(int) delimiters.length - 1, this))
--i;
break;
default:
* buf = an OutBuffer containing the DDoc
* iStart = the index within `buf` that the table header row starts at, inclusive
* iEnd = the index within `buf` that the table header row ends at, exclusive
- * loc = the current location in the file
* inQuote = whether the table is inside a quote
* inlineDelimiters = delimiters containing columns separators and any inline emphasis
* columnAlignments = the parsed alignments for each column
* Returns: the number of characters added by starting the table, or `0` if unchanged
*/
-size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, bool inQuote, ref MarkdownDelimiter[] inlineDelimiters, out TableColumnAlignment[] columnAlignments)
+size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, bool inQuote, ref MarkdownDelimiter[] inlineDelimiters, out TableColumnAlignment[] columnAlignments)
{
const iDelimiterRowEnd = parseTableDelimiterRow(buf, iEnd + 1, inQuote, columnAlignments);
if (iDelimiterRowEnd)
{
size_t delta;
- if (replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, true, delta))
+ if (replaceTableRow(buf, iStart, iEnd, inlineDelimiters, columnAlignments, true, delta))
{
buf.remove(iEnd + delta, iDelimiterRowEnd - iEnd);
buf.insert(iEnd + delta, "$(TBODY ");
* buf = an OutBuffer containing the DDoc
* iStart = the index within `buf` that the table row starts at, inclusive
* iEnd = the index within `buf` that the table row ends at, exclusive
- * loc = the current location in the file
* inlineDelimiters = delimiters containing columns separators and any inline emphasis
* columnAlignments = alignments for each column
* headerRow = if `true` then the number of columns will be enforced to match
* delta = the number of characters added by replacing the row, or `0` if unchanged
* Returns: `true` if a table row was found and replaced
*/
-bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow, out size_t delta)
+bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow, out size_t delta)
{
delta = 0;
void replaceTableCell(size_t iCellStart, size_t iCellEnd, int cellIndex, int di)
{
- const eDelta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, di);
+ const eDelta = replaceMarkdownEmphasis(buf, inlineDelimiters, di);
delta += eDelta;
iCellEnd += eDelta;
* buf = an OutBuffer containing the DDoc
* iStart = the index within `buf` that the table row starts at, inclusive
* iEnd = the index within `buf` that the table row ends at, exclusive
- * loc = the current location in the file
* inlineDelimiters = delimiters containing columns separators and any inline emphasis
* columnAlignments = alignments for each column; upon return is set to length `0`
* Returns: the number of characters added by replacing the row, or `0` if unchanged
*/
-size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, ref TableColumnAlignment[] columnAlignments)
+size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, ref MarkdownDelimiter[] inlineDelimiters, ref TableColumnAlignment[] columnAlignments)
{
size_t delta;
- replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, false, delta);
+ replaceTableRow(buf, iStart, iEnd, inlineDelimiters, columnAlignments, false, delta);
delta += endTable(buf, iEnd + delta, columnAlignments);
return delta;
}
}
if (headingLevel)
{
- i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters);
- endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel);
+ i += replaceMarkdownEmphasis(buf, inlineDelimiters);
+ endMarkdownHeading(buf, iParagraphStart, i, headingLevel);
removeBlankLineMacro(buf, iPrecedingBlankLine, i);
++i;
iParagraphStart = skipChars(buf, i, " \t\r\n");
}
if (tableRowDetected && !columnAlignments.length)
- i += startTable(buf, iLineStart, i, loc, lineQuoted, inlineDelimiters, columnAlignments);
+ i += startTable(buf, iLineStart, i, lineQuoted, inlineDelimiters, columnAlignments);
else if (columnAlignments.length)
{
size_t delta;
- if (replaceTableRow(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments, false, delta))
+ if (replaceTableRow(buf, iLineStart, i, inlineDelimiters, columnAlignments, false, delta))
i += delta;
else
i += endTable(buf, i, columnAlignments);
i += endTable(buf, i, columnAlignments);
if (!lineQuoted && quoteLevel)
endAllListsAndQuotes(buf, i, nestedLists, quoteLevel, quoteMacroLevel);
- i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters);
+ i += replaceMarkdownEmphasis(buf, inlineDelimiters);
// if we don't already know about this paragraph break then
// insert a blank line and record the paragraph break
if (quoteLevel < lineQuoteLevel)
{
- i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments);
if (nestedLists.length)
{
const indent = getMarkdownIndent(buf, iLineStart, i);
if (!headingLevel)
break;
- i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments);
if (!lineQuoted && quoteLevel)
i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel);
const list = MarkdownList.parseItem(buf, iLineStart, i);
if (list.isValid)
{
- if (replaceMarkdownThematicBreak(buf, i, iLineStart, loc))
+ if (replaceMarkdownThematicBreak(buf, i, iLineStart))
{
removeBlankLineMacro(buf, iPrecedingBlankLine, i);
iParagraphStart = skipChars(buf, i+1, " \t\r\n");
}
else
{
- i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments);
if (!lineQuoted && quoteLevel)
{
const delta = endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel);
case '_':
{
- if (leadingBlank && !inCode && replaceMarkdownThematicBreak(buf, i, iLineStart, loc))
+ if (leadingBlank && !inCode && replaceMarkdownThematicBreak(buf, i, iLineStart))
{
- i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments);
if (!lineQuoted && quoteLevel)
i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel);
removeBlankLineMacro(buf, iPrecedingBlankLine, i);
break;
}
- i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments);
if (!lineQuoted && quoteLevel)
{
const delta = endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel);
}
list.macroLevel = macroLevel;
- list.startItem(buf, iLineStart, i, iPrecedingBlankLine, nestedLists, loc);
+ list.startItem(buf, iLineStart, i, iPrecedingBlankLine, nestedLists);
break;
}
}
if (leadingBlank)
{
// Check for a thematic break
- if (replaceMarkdownThematicBreak(buf, i, iLineStart, loc))
+ if (replaceMarkdownThematicBreak(buf, i, iLineStart))
{
- i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments);
if (!lineQuoted && quoteLevel)
i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel);
removeBlankLineMacro(buf, iPrecedingBlankLine, i);
if (delimiter.type == '[' || delimiter.type == '!')
{
if (delimiter.isValid &&
- MarkdownLink.replaceLink(buf, i, loc, inlineDelimiters, d, linkReferences))
+ MarkdownLink.replaceLink(buf, i, inlineDelimiters, d, linkReferences))
{
// if we removed a reference link then we're at line start
if (i <= delimiter.iStart)
--downToLevel;
if (headingLevel && headingMacroLevel >= macroLevel)
{
- endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel);
+ endMarkdownHeading(buf, iParagraphStart, i, headingLevel);
removeBlankLineMacro(buf, iPrecedingBlankLine, i);
}
- i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments);
while (nestedLists.length && nestedLists[$-1].macroLevel >= macroLevel)
{
i = buf.insert(i, ")\n)");
}
if (quoteLevel && quoteMacroLevel >= macroLevel)
i += endAllMarkdownQuotes(buf, i, quoteLevel);
- i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters, downToLevel);
+ i += replaceMarkdownEmphasis(buf, inlineDelimiters, downToLevel);
--macroLevel;
quoteMacroLevel = 0;
size_t i = buf.length;
if (headingLevel)
{
- endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel);
+ endMarkdownHeading(buf, iParagraphStart, i, headingLevel);
removeBlankLineMacro(buf, iPrecedingBlankLine, i);
}
- i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
- i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters);
+ i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments);
+ i += replaceMarkdownEmphasis(buf, inlineDelimiters);
endAllListsAndQuotes(buf, i, nestedLists, quoteLevel, quoteMacroLevel);
}
}
/// Copy flags from scope `other`
- extern(D) void copyFlagsFrom(Scope* other)
+ extern(D) void copyFlagsFrom(Scope* other) @safe
{
this.bitFields = other.bitFields;
}
/// Set all scope flags to their initial value
- extern(D) void resetAllFlags()
+ extern(D) void resetAllFlags() @safe
{
this.bitFields = 0;
}
error(loc, "one path skips constructor");
const fies = ctorflow.fieldinit;
- if (this.ctorflow.fieldinit.length && fies.length)
+ if (!this.ctorflow.fieldinit.length || !fies.length)
+ return;
+ FuncDeclaration f = func;
+ if (fes)
+ f = fes.func;
+ auto ad = f.isMemberDecl();
+ assert(ad);
+ foreach (i, v; ad.fields)
{
- FuncDeclaration f = func;
- if (fes)
- f = fes.func;
- auto ad = f.isMemberDecl();
- assert(ad);
- foreach (i, v; ad.fields)
+ bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested());
+ auto fieldInit = &this.ctorflow.fieldinit[i];
+ const fiesCurrent = fies[i];
+ if (fieldInit.loc is Loc.init)
+ fieldInit.loc = fiesCurrent.loc;
+ if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit)
{
- bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested());
- auto fieldInit = &this.ctorflow.fieldinit[i];
- const fiesCurrent = fies[i];
- if (fieldInit.loc is Loc.init)
- fieldInit.loc = fiesCurrent.loc;
- if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit)
- {
- error(loc, "one path skips field `%s`", v.toChars());
- }
+ error(loc, "one path skips field `%s`", v.toChars());
}
}
}
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem;
+import dmd.dsymbolsem : search, setFieldOffset;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
import dmd.opover;
import dmd.target;
import dmd.tokens;
-import dmd.typesem;
+import dmd.typesem : isZeroInit, merge, size, hasPointers;
import dmd.typinf;
import dmd.visitor;
return fd;
}
-/***************************************
- * Request additional semantic analysis for TypeInfo generation.
- * Params:
- * sc = context
- * t = type that TypeInfo is being generated for
- */
-extern (D) void semanticTypeInfo(Scope* sc, Type t)
-{
- if (sc)
- {
- if (sc.intypeof)
- return;
- if (!sc.needsCodegen())
- return;
- }
-
- if (!t)
- return;
-
- void visitVector(TypeVector t)
- {
- semanticTypeInfo(sc, t.basetype);
- }
-
- void visitAArray(TypeAArray t)
- {
- semanticTypeInfo(sc, t.index);
- semanticTypeInfo(sc, t.next);
- }
-
- void visitStruct(TypeStruct t)
- {
- //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars());
- StructDeclaration sd = t.sym;
-
- /* Step 1: create TypeInfoDeclaration
- */
- if (!sc) // inline may request TypeInfo.
- {
- Scope scx;
- scx.eSink = global.errorSink;
- scx._module = sd.getModule();
- getTypeInfoType(sd.loc, t, &scx);
- sd.requestTypeInfo = true;
- }
- else if (!sc.minst)
- {
- // don't yet have to generate TypeInfo instance if
- // the typeid(T) expression exists in speculative scope.
- }
- else
- {
- getTypeInfoType(sd.loc, t, sc);
- sd.requestTypeInfo = true;
-
- // https://issues.dlang.org/show_bug.cgi?id=15149
- // if the typeid operand type comes from a
- // result of auto function, it may be yet speculative.
- // unSpeculative(sc, sd);
- }
-
- /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later.
- * This should be done even if typeid(T) exists in speculative scope.
- * Because it may appear later in non-speculative scope.
- */
- if (!sd.members)
- return; // opaque struct
- if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd))
- return; // none of TypeInfo-specific members
-
- // If the struct is in a non-root module, run semantic3 to get
- // correct symbols for the member function.
- if (sd.semanticRun >= PASS.semantic3)
- {
- // semantic3 is already done
- }
- else if (TemplateInstance ti = sd.isInstantiated())
- {
- if (ti.minst && !ti.minst.isRoot())
- Module.addDeferredSemantic3(sd);
- }
- else
- {
- if (sd.inNonRoot())
- {
- //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot());
- Module.addDeferredSemantic3(sd);
- }
- }
- }
-
- void visitTuple(TypeTuple t)
- {
- if (t.arguments)
- {
- foreach (arg; *t.arguments)
- {
- semanticTypeInfo(sc, arg.type);
- }
- }
- }
-
- /* Note structural similarity of this Type walker to that in isSpeculativeType()
- */
-
- Type tb = t.toBasetype();
- switch (tb.ty)
- {
- case Tvector: visitVector(tb.isTypeVector()); break;
- case Taarray: visitAArray(tb.isTypeAArray()); break;
- case Tstruct: visitStruct(tb.isTypeStruct()); break;
- case Ttuple: visitTuple (tb.isTypeTuple()); break;
-
- case Tclass:
- case Tenum: break;
-
- default: semanticTypeInfo(sc, tb.nextOf()); break;
- }
-}
-
enum StructFlags : int
{
none = 0x0,
// Determine if struct is all zeros or not
zeroInit = true;
+ auto lastOffset = -1;
foreach (vd; fields)
{
+ // First skip zero sized fields
+ if (vd.type.size(vd.loc) == 0)
+ continue;
+
+ // only consider first sized member of an (anonymous) union
+ if (vd.overlapped && vd.offset == lastOffset)
+ continue;
+ lastOffset = vd.offset;
+
if (vd._init)
{
if (vd._init.isVoidInitializer())
*/
continue;
- // Zero size fields are zero initialized
- if (vd.type.size(vd.loc) == 0)
- continue;
-
// Examine init to see if it is all 0s.
auto exp = vd.getConstInitializer();
if (!exp || !_isZeroInit(exp))
final extern (D) this() nothrow @safe
{
//printf("Dsymbol::Dsymbol(%p)\n", this);
- loc = Loc(null, 0, 0);
+ loc = Loc.initial;
}
final extern (D) this(Identifier ident) nothrow @safe
{
//printf("Dsymbol::Dsymbol(%p, ident)\n", this);
- this.loc = Loc(null, 0, 0);
+ this.loc = Loc.initial;
this.ident = ident;
}
return toChars();
}
- final const(Loc) getLoc()
- {
- if (!loc.isValid()) // avoid bug 5861.
- if (const m = getModule())
- return Loc(m.srcfile.toChars(), 0, 0);
- return loc;
- }
-
- final const(char)* locToChars()
- {
- return getLoc().toChars();
- }
-
override bool equals(const RootObject o) const
{
if (this == o)
final Ungag ungagSpeculative() const
{
- uint oldgag = global.gag;
+ const oldgag = global.gag;
if (global.gag && !isSpeculative() && !toParent2().isFuncDeclaration())
global.gag = 0;
return Ungag(oldgag);
}
else
{
- .error(s1.loc, "%s `%s` conflicts with %s `%s` at %s", s1.kind, s1.toPrettyChars, s2.kind(), s2.toPrettyChars(), s2.locToChars());
+ .error(s1.loc, "%s `%s` conflicts with %s `%s` at %s", s1.kind, s1.toPrettyChars, s2.kind(), s2.toPrettyChars(), s2.loc.toChars());
}
}
obj // toObjFile() run
};
-enum
-{
- PASSinit, // initial state
- PASSsemantic, // semantic() started
- PASSsemanticdone, // semantic() done
- PASSsemantic2, // semantic2() started
- PASSsemantic2done, // semantic2() done
- PASSsemantic3, // semantic3() started
- PASSsemantic3done, // semantic3() done
- PASSinline, // inline started
- PASSinlinedone, // inline done
- PASSobj // toObjFile() run
-};
-
/* Flags for symbol search
*/
typedef unsigned SearchOptFlags;
CPPNamespaceDeclaration* cppnamespace(CPPNamespaceDeclaration* ns);
UserAttributeDeclaration* userAttribDecl(UserAttributeDeclaration* uad);
virtual const char *toPrettyCharsHelper(); // helper to print fully qualified (template) arguments
- Loc getLoc();
- const char *locToChars();
bool equals(const RootObject * const o) const override;
bool isAnonymous() const;
Module *getModule();
{
void addMember(Dsymbol *dsym, Scope *sc, ScopeDsymbol *sds);
Dsymbol *search(Dsymbol *d, const Loc &loc, Identifier *ident, SearchOptFlags flags = (SearchOptFlags)SearchOpt::localsOnly);
+ Dsymbols *include(Dsymbol *d, Scope *sc);
void setScope(Dsymbol *d, Scope *sc);
void importAll(Dsymbol *d, Scope *sc);
}
import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.rootobject;
+import dmd.safe;
import dmd.semantic2;
import dmd.semantic3;
import dmd.sideeffect;
Loc loc = e.loc;
Type tthis = (e.op == EXP.type ? e.type : null);
const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag));
- uint olderrors = gag ? global.startGagging() : 0;
+ const olderrors = gag ? global.startGagging() : 0;
e = dotExp(ad.type, sc, e, ad.aliasthis.ident, flags);
if (!e || findOnly)
return gag && global.endGagging(olderrors) ? null : e;
*/
if (!inferred)
{
- uint errors = global.errors;
+ const errors = global.errors;
dsym.inuse++;
// Bug 20549. Don't try this on modules or packages, syntaxCopy
// could crash (inf. recursion) on a mod/pkg referencing itself
{
//printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars());
OutBuffer buf;
- if (expressionsToString(buf, sc, cd.exps))
+ if (expressionsToString(buf, sc, cd.exps, cd.loc, null, true))
return null;
const errors = global.errors;
const bool doUnittests = global.params.parsingUnittestsRequired();
auto loc = adjustLocForMixin(str, cd.loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
- p.transitionIn = global.params.v.vin;
p.nextToken();
auto d = p.parseDeclDefs(0);
tm.argsym.parent = scy.parent;
Scope* argscope = scy.push(tm.argsym);
- uint errorsave = global.errors;
+ const errorsave = global.errors;
// Declare each template parameter as an alias for the argument type
tm.declareParameters(argscope);
ns.semanticRun = PASS.semantic;
ns.parent = sc.parent;
// Link does not matter here, if the UDA is present it will error
- UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp);
+ checkGNUABITag(ns, LINK.cpp);
if (!ns.members)
{
if (sd.semanticRun >= PASS.semanticdone)
return;
- int errors = global.errors;
+ const errors = global.errors;
//printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
Scope* scx = null;
return;
sd.semanticRun = PASS.semantic;
- UserAttributeDeclaration.checkGNUABITag(sd, sc.linkage);
+ checkGNUABITag(sd, sc.linkage);
if (!sd.members) // if opaque declaration
{
Dsymbol scall = sd.search(Loc.initial, Id.call);
if (scall)
{
- uint xerrors = global.startGagging();
+ const xerrors = global.startGagging();
sc = sc.push();
sc.tinst = null;
sc.minst = null;
if (cldec.semanticRun >= PASS.semanticdone)
return;
- int errors = global.errors;
+ const errors = global.errors;
//printf("+ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this);
return;
}
cldec.semanticRun = PASS.semantic;
- UserAttributeDeclaration.checkGNUABITag(cldec, sc.linkage);
+ checkGNUABITag(cldec, sc.linkage);
checkMustUseReserved(cldec);
if (cldec.baseok < Baseok.done)
//printf("InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
if (idec.semanticRun >= PASS.semanticdone)
return;
- int errors = global.errors;
+ const errors = global.errors;
//printf("+InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
if (!idec.baseclasses.length && sc.linkage == LINK.cpp)
idec.classKind = ClassKind.cpp;
idec.cppnamespace = sc.namespace;
- UserAttributeDeclaration.checkGNUABITag(idec, sc.linkage);
+ checkGNUABITag(idec, sc.linkage);
checkMustUseReserved(idec);
if (sc.linkage == LINK.objc)
Scope* sc;
ScopeDsymbol sds;
- this(Scope* sc, ScopeDsymbol sds)
+ this(Scope* sc, ScopeDsymbol sds) @safe
{
this.sc = sc;
this.sds = sds;
visit(cast(Dsymbol)sds);
}
+ override void visit(StructDeclaration sd)
+ {
+ // need to visit auto-generated methods as well
+ if (sd.xeq) visit(sd.xeq);
+ if (sd.xcmp) visit(sd.xcmp);
+ if (sd.xhash) visit(sd.xhash);
+ visit(cast(ScopeDsymbol)sd);
+ }
+
override void visit(AttribDeclaration ad)
{
ad.include(null).foreachDsymbol( s => s.accept(this) );
printf("\timplement template instance %s '%s'\n", tempdecl.parent.toChars(), tempinst.toChars());
printf("\ttempdecl %s\n", tempdecl.toChars());
}
- uint errorsave = global.errors;
+ const errorsave = global.errors;
tempinst.inst = tempinst;
tempinst.parent = tempinst.enclosing ? tempinst.enclosing : tempdecl.parent;
++lines;
}
-/**
- * Check signature of `pragma(printf)` function, print error if invalid.
- *
- * printf/scanf-like functions must be of the form:
- * extern (C/C++) T printf([parameters...], const(char)* format, ...);
- * or:
- * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list);
- *
- * Params:
- * funcdecl = function to check
- * f = function type
- * sc = scope
- */
-void checkPrintfScanfSignature(FuncDeclaration funcdecl, TypeFunction f, Scope* sc)
-{
- static bool isPointerToChar(Parameter p)
- {
- if (auto tptr = p.type.isTypePointer())
- {
- return tptr.next.ty == Tchar;
- }
- return false;
- }
-
- bool isVa_list(Parameter p)
- {
- return p.type.equals(target.va_listType(funcdecl.loc, sc));
- }
-
- const nparams = f.parameterList.length;
- const p = (funcdecl.printf ? Id.printf : Id.scanf).toChars();
- if (!(f.linkage == LINK.c || f.linkage == LINK.cpp))
- {
- .error(funcdecl.loc, "`pragma(%s)` function `%s` must have `extern(C)` or `extern(C++)` linkage,"
- ~" not `extern(%s)`",
- p, funcdecl.toChars(), f.linkage.linkageToChars());
- }
- if (f.parameterList.varargs == VarArg.variadic)
- {
- if (!(nparams >= 1 && isPointerToChar(f.parameterList[nparams - 1])))
- {
- .error(funcdecl.loc, "`pragma(%s)` function `%s` must have"
- ~ " signature `%s %s([parameters...], const(char)*, ...)` not `%s`",
- p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars(), funcdecl.type.toChars());
- }
- }
- else if (f.parameterList.varargs == VarArg.none)
- {
- if(!(nparams >= 2 && isPointerToChar(f.parameterList[nparams - 2]) &&
- isVa_list(f.parameterList[nparams - 1])))
- .error(funcdecl.loc, "`pragma(%s)` function `%s` must have"~
- " signature `%s %s([parameters...], const(char)*, va_list)`",
- p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars());
- }
- else
- {
- .error(funcdecl.loc, "`pragma(%s)` function `%s` must have C-style variadic `...` or `va_list` parameter",
- p, funcdecl.toChars());
- }
-}
-
/*********************************************
* Search for ident as member of d.
* Params:
SearchOptFlags flags;
Dsymbol result;
- this(const ref Loc loc, Identifier ident, SearchOptFlags flags)
+ this(const ref Loc loc, Identifier ident, SearchOptFlags flags) @safe
{
this.loc = loc;
this.ident = ident;
return setResult(m.searchCacheSymbol);
}
- uint errors = global.errors;
+ const errors = global.errors;
m.insearch = true;
visit(cast(ScopeDsymbol)m);
alias visit = typeof(super).visit;
Scope* sc;
- this(Scope* sc)
+ this(Scope* sc) @safe
{
this.sc = sc;
}
alias visit = typeof(super).visit;
Scope* sc;
- this(Scope* sc)
+ this(Scope* sc) @safe
{
this.sc = sc;
}
(*m.members)[0].ident != Id.object ||
(*m.members)[0].isImport() is null))
{
- auto im = new Import(Loc.initial, null, Id.object, null, 0);
+ auto im = new Import(m.loc, null, Id.object, null, 0);
m.members.shift(im);
}
if (!m.symtab)
{
if (p.isPkgMod == PKG.unknown)
{
- uint preverrors = global.errors;
+ const preverrors = global.errors;
imp.mod = Module.load(imp.loc, imp.packages, imp.id);
if (!imp.mod)
p.isPkgMod = PKG.package_;
FieldState* fieldState;
bool isunion;
- this(AggregateDeclaration ad, FieldState* fieldState, bool isunion)
+ this(AggregateDeclaration ad, FieldState* fieldState, bool isunion) @safe
{
this.ad = ad;
this.fieldState = fieldState;
assert(sz != SIZE_INVALID && sz < uint.max);
uint memsize = cast(uint)sz; // size of member
uint memalignsize = target.fieldalign(t); // size of member for alignment purposes
- vd.offset = placeField(
+ vd.offset = placeField(vd.loc,
fieldState.offset,
memsize, memalignsize, vd.alignment,
ad.structsize, ad.alignsize,
error(bfd.loc, "bit field width %d is larger than type", bfd.fieldWidth);
const style = target.c.bitFieldStyle;
+ if (style != TargetC.BitFieldStyle.MS && style != TargetC.BitFieldStyle.Gcc_Clang)
+ assert(0, "unsupported bit-field style");
+
+ const isMicrosoftStyle = style == TargetC.BitFieldStyle.MS;
+ const contributesToAggregateAlignment = target.c.contributesToAggregateAlignment(bfd);
void startNewField()
{
if (log) printf("startNewField()\n");
uint alignsize;
- if (style == TargetC.BitFieldStyle.Gcc_Clang)
+ if (isMicrosoftStyle)
+ alignsize = memsize; // not memalignsize
+ else
{
if (bfd.fieldWidth > 32)
alignsize = memalignsize;
else
alignsize = 1;
}
- else
- alignsize = memsize; // not memalignsize
uint dummy;
- bfd.offset = placeField(
+ bfd.offset = placeField(bfd.loc,
fieldState.offset,
memsize, alignsize, bfd.alignment,
ad.structsize,
- (anon && style == TargetC.BitFieldStyle.Gcc_Clang) ? dummy : ad.alignsize,
+ contributesToAggregateAlignment ? ad.alignsize : dummy,
isunion);
fieldState.inFlight = true;
fieldState.fieldSize = memsize;
}
- if (style == TargetC.BitFieldStyle.Gcc_Clang)
+ if (ad.alignsize == 0)
+ ad.alignsize = 1;
+ if (!isMicrosoftStyle && contributesToAggregateAlignment && ad.alignsize < memalignsize)
+ ad.alignsize = memalignsize;
+
+ if (bfd.fieldWidth == 0)
{
- if (bfd.fieldWidth == 0)
+ if (!isMicrosoftStyle && !isunion)
{
- if (!isunion)
- {
- // Use type of zero width field to align to next field
- fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1);
- ad.structsize = fieldState.offset;
- }
-
- fieldState.inFlight = false;
- return;
+ // Use type of zero width field to align to next field
+ fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1);
+ ad.structsize = fieldState.offset;
}
-
- if (ad.alignsize == 0)
- ad.alignsize = 1;
- if (!anon &&
- ad.alignsize < memalignsize)
- ad.alignsize = memalignsize;
- }
- else if (style == TargetC.BitFieldStyle.MS)
- {
- if (ad.alignsize == 0)
- ad.alignsize = 1;
- if (bfd.fieldWidth == 0)
+ else if (isMicrosoftStyle && fieldState.inFlight && !isunion)
{
- if (fieldState.inFlight && !isunion)
- {
- // documentation says align to next int
- //const alsz = cast(uint)Type.tint32.size();
- const alsz = memsize; // but it really does this
- fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1);
- ad.structsize = fieldState.offset;
- }
-
- fieldState.inFlight = false;
- return;
+ // documentation says align to next int
+ //const alsz = cast(uint)Type.tint32.size();
+ const alsz = memsize; // but it really does this
+ fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1);
+ ad.structsize = fieldState.offset;
}
+
+ fieldState.inFlight = false;
+ return;
}
if (!fieldState.inFlight)
//printf("not in flight\n");
startNewField();
}
- else if (style == TargetC.BitFieldStyle.Gcc_Clang)
+ else if (!isMicrosoftStyle)
{
// If the bit-field spans more units of alignment than its type
// and is at the alignment boundary, start a new field at the
}
}
}
- else if (style == TargetC.BitFieldStyle.MS)
+ else
{
if (memsize != fieldState.fieldSize ||
fieldState.bitOffset + bfd.fieldWidth > fieldState.fieldSize * 8)
startNewField();
}
}
- else
- assert(0);
bfd.offset = fieldState.fieldOffset;
bfd.bitOffset = fieldState.bitOffset;
const pastField = bfd.bitOffset + bfd.fieldWidth;
- if (style == TargetC.BitFieldStyle.Gcc_Clang)
+ if (isMicrosoftStyle)
+ fieldState.fieldSize = memsize;
+ else
{
auto size = (pastField + 7) / 8;
fieldState.fieldSize = size;
else
ad.structsize = bfd.offset + size;
}
- else
- fieldState.fieldSize = memsize;
//printf("at end: ad.structsize = %d\n", cast(int)ad.structsize);
//print(fieldState);
/* Given the anon 'member's size and alignment,
* go ahead and place it.
*/
- anond.anonoffset = placeField(
+ anond.anonoffset = placeField(anond.loc,
fieldState.offset,
anond.anonstructsize, anond.anonalignsize, alignment,
ad.structsize, ad.alignsize,
}
}
}
+
+extern(D) Scope* newScope(Dsymbol d, Scope* sc)
+{
+ scope nsv = new NewScopeVisitor(sc);
+ d.accept(nsv);
+ return nsv.sc;
+}
+
+private extern(C++) class NewScopeVisitor : Visitor
+{
+ alias visit = typeof(super).visit;
+ Scope* sc;
+ this(Scope* sc)
+ {
+ this.sc = sc;
+ }
+
+ /****************************************
+ * A hook point to supply scope for members.
+ * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this.
+ */
+ override void visit(AttribDeclaration dc){}
+
+ override void visit(StorageClassDeclaration swt)
+ {
+ StorageClass scstc = sc.stc;
+ /* These sets of storage classes are mutually exclusive,
+ * so choose the innermost or most recent one.
+ */
+ if (swt.stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest))
+ scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest);
+ if (swt.stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared))
+ scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared);
+ if (swt.stc & (STC.const_ | STC.immutable_ | STC.manifest))
+ scstc &= ~(STC.const_ | STC.immutable_ | STC.manifest);
+ if (swt.stc & (STC.gshared | STC.shared_))
+ scstc &= ~(STC.gshared | STC.shared_);
+ if (swt.stc & (STC.safe | STC.trusted | STC.system))
+ scstc &= ~(STC.safe | STC.trusted | STC.system);
+ scstc |= swt.stc;
+ //printf("scstc = x%llx\n", scstc);
+ sc = swt.createNewScope(sc, scstc, sc.linkage, sc.cppmangle,
+ sc.visibility, sc.explicitVisibility, sc.aligndecl, sc.inlining);
+ }
+
+ /**
+ * Provides a new scope with `STC.deprecated_` and `Scope.depdecl` set
+ *
+ * Calls `StorageClassDeclaration.newScope` (as it must be called or copied
+ * in any function overriding `newScope`), then set the `Scope`'s depdecl.
+ *
+ * Returns:
+ * Always a new scope, to use for this `DeprecatedDeclaration`'s members.
+ */
+ override void visit(DeprecatedDeclaration dpd)
+ {
+ auto oldsc = sc;
+ visit((cast(StorageClassDeclaration)dpd));
+ auto scx = sc;
+ sc = oldsc;
+ // The enclosing scope is deprecated as well
+ if (scx == sc)
+ scx = sc.push();
+ scx.depdecl = dpd;
+ sc = scx;
+ }
+
+ override void visit(LinkDeclaration lid)
+ {
+ sc= lid.createNewScope(sc, sc.stc, lid.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility,
+ sc.aligndecl, sc.inlining);
+ }
+
+ override void visit(CPPMangleDeclaration cpmd)
+ {
+ sc = cpmd.createNewScope(sc, sc.stc, LINK.cpp, cpmd.cppmangle, sc.visibility, sc.explicitVisibility,
+ sc.aligndecl, sc.inlining);
+ }
+
+ /**
+ * Returns:
+ * A copy of the parent scope, with `this` as `namespace` and C++ linkage
+ *///override Scope* visit(Scope* sc)
+ override void visit(CPPNamespaceDeclaration scd)
+ {
+ auto scx = sc.copy();
+ scx.linkage = LINK.cpp;
+ scx.namespace = scd;
+ sc = scx;
+ }
+
+ override void visit(VisibilityDeclaration atbd)
+ {
+ if (atbd.pkg_identifiers)
+ dsymbolSemantic(atbd, sc);
+
+ sc = atbd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, atbd.visibility, 1, sc.aligndecl, sc.inlining);
+ }
+
+ override void visit(AlignDeclaration visd)
+ {
+ sc = visd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility,
+ sc.explicitVisibility, visd, sc.inlining);
+ }
+
+ override void visit(PragmaDeclaration prd)
+ {
+ if (prd.ident == Id.Pinline)
+ {
+ // We keep track of this pragma inside scopes,
+ // then it's evaluated on demand in function semantic
+ sc = prd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, sc.aligndecl, prd); // @suppress(dscanner.style.long_line)
+ }
+ }
+
+ /**************************************
+ * Use the ForwardingScopeDsymbol as the parent symbol for members.
+ */
+ override void visit(ForwardingAttribDeclaration fad)
+ {
+ sc = sc.push(fad.sym);
+ }
+
+ override void visit(UserAttributeDeclaration uac)
+ {
+ Scope* sc2 = sc;
+ if (uac.atts && uac.atts.length)
+ {
+ // create new one for changes
+ sc2 = sc.copy();
+ sc2.userAttribDecl = uac;
+ }
+ sc = sc2;
+ }
+}
+
+
+extern(C++) Dsymbols* include(Dsymbol d, Scope* sc)
+{
+ scope icv = new IncludeVisitor(sc);
+ d.accept(icv);
+ return icv.symbols;
+}
+
+extern(C++) class IncludeVisitor : Visitor
+{
+ alias visit = typeof(super).visit;
+ Scope* sc;
+ Dsymbols* symbols;
+ this(Scope* sc)
+ {
+ this.sc = sc;
+ }
+
+ override void visit(AttribDeclaration ad)
+ {
+ if (ad.errors)
+ {
+ symbols = null;
+ return;
+ }
+ symbols = ad.decl;
+ return;
+ }
+
+// Decide if 'then' or 'else' code should be included
+ override void visit(ConditionalDeclaration cdc)
+ {
+ //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope);
+
+ if (cdc.errors)
+ {
+ symbols = null;
+ return;
+ }
+ assert(cdc.condition);
+ symbols = cdc.condition.include(cdc._scope ? cdc._scope : sc) ? cdc.decl : cdc.elsedecl;
+ }
+
+ override void visit(StaticIfDeclaration sif)
+ {
+ /****************************************
+ * Different from other AttribDeclaration subclasses, include() call requires
+ * the completion of addMember and setScope phases.
+ */
+ //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope);
+ if (sif.errors || sif.onStack)
+ {
+ symbols = null;
+ return;
+ }
+ sif.onStack = true;
+ scope(exit) sif.onStack = false;
+
+ if (sc && sif.condition.inc == Include.notComputed)
+ {
+ assert(sif.scopesym); // addMember is already done
+ assert(sif._scope); // setScope is already done
+
+ Scope* saved_scope = sc;
+ sc = sif._scope;
+ visit(cast(ConditionalDeclaration) sif);
+ Dsymbols* d = symbols;
+ sc = saved_scope;
+
+ if (d && !sif.addisdone)
+ {
+ // Add members lazily.
+ d.foreachDsymbol( s => s.addMember(sif._scope, sif.scopesym) );
+
+ // Set the member scopes lazily.
+ d.foreachDsymbol( s => s.setScope(sif._scope) );
+
+ sif.addisdone = true;
+ }
+ symbols = d;
+ return;
+ }
+ else
+ {
+ visit(cast(ConditionalDeclaration)sif);
+ }
+ }
+
+ override void visit(StaticForeachDeclaration sfd)
+ {
+ if (sfd.errors || sfd.onStack)
+ {
+ symbols = null;
+ return;
+ }
+ if (sfd.cached)
+ {
+ assert(!sfd.onStack);
+ symbols = sfd.cache;
+ return;
+ }
+ sfd.onStack = true;
+ scope(exit) sfd.onStack = false;
+
+ if (sfd._scope)
+ {
+ sfd.sfe.prepare(sfd._scope); // lower static foreach aggregate
+ }
+ if (!sfd.sfe.ready())
+ {
+ symbols = null;// TODO: ok?
+ return;
+ }
+
+ // expand static foreach
+ import dmd.statementsem: makeTupleForeach;
+ Dsymbols* d = makeTupleForeach(sfd._scope, true, true, sfd.sfe.aggrfe, sfd.decl, sfd.sfe.needExpansion).decl;
+ if (d) // process generated declarations
+ {
+ // Add members lazily.
+ d.foreachDsymbol( s => s.addMember(sfd._scope, sfd.scopesym) );
+
+ // Set the member scopes lazily.
+ d.foreachDsymbol( s => s.setScope(sfd._scope) );
+ }
+ sfd.cached = true;
+ sfd.cache = d;
+ symbols = d;
+ }
+}
+
+/**
+ * Called from a symbol's semantic to check if `gnuAbiTag` UDA
+ * can be applied to them
+ *
+ * Directly emits an error if the UDA doesn't work with this symbol
+ *
+ * Params:
+ * sym = symbol to check for `gnuAbiTag`
+ * linkage = Linkage of the symbol (Declaration.link or sc.link)
+ */
+void checkGNUABITag(Dsymbol sym, LINK linkage)
+{
+ if (global.params.cplusplus < CppStdRevision.cpp11)
+ return;
+
+ foreachUdaNoSemantic(sym, (exp) {
+ if (!isGNUABITag(exp))
+ return 0; // continue
+ if (sym.isCPPNamespaceDeclaration() || sym.isNspace())
+ {
+ .error(exp.loc, "`@%s` cannot be applied to namespaces", Id.udaGNUAbiTag.toChars());
+ sym.errors = true;
+ }
+ else if (linkage != LINK.cpp)
+ {
+ .error(exp.loc, "`@%s` can only apply to C++ symbols", Id.udaGNUAbiTag.toChars());
+ sym.errors = true;
+ }
+ // Only one `@gnuAbiTag` is allowed by semantic2
+ return 1; // break
+ });
+}
+
+/**
+ * Check if the provided expression references `core.attribute.gnuAbiTag`
+ *
+ * This should be called after semantic has been run on the expression.
+ * Semantic on UDA happens in semantic2 (see `dmd.semantic2`).
+ *
+ * Params:
+ * e = Expression to check (usually from `UserAttributeDeclaration.atts`)
+ *
+ * Returns:
+ * `true` if the expression references the compiler-recognized `gnuAbiTag`
+ */
+bool isGNUABITag(Expression e)
+{
+ if (global.params.cplusplus < CppStdRevision.cpp11)
+ return false;
+
+ auto ts = e.type ? e.type.isTypeStruct() : null;
+ if (!ts)
+ return false;
+ if (ts.sym.ident != Id.udaGNUAbiTag || !ts.sym.parent)
+ return false;
+ // Can only be defined in druntime
+ Module m = ts.sym.parent.isModule();
+ if (!m || !m.isCoreModule(Id.attribute))
+ return false;
+ return true;
+}
+
+/******************************************
+ * If a variable has a scope destructor call, return call for it.
+ * Otherwise, return NULL.
+ */
+private Expression callScopeDtor(VarDeclaration vd, Scope* sc)
+{
+ //printf("VarDeclaration::callScopeDtor() %s\n", toChars());
+
+ // Destruction of STC.field's is handled by buildDtor()
+ if (vd.storage_class & (STC.nodtor | STC.ref_ | STC.out_ | STC.field))
+ {
+ return null;
+ }
+
+ if (vd.iscatchvar)
+ return null; // destructor is built by `void semantic(Catch c, Scope* sc)`, not here
+
+ Expression e = null;
+ // Destructors for structs and arrays of structs
+ Type tv = vd.type.baseElemOf();
+ if (tv.ty == Tstruct)
+ {
+ StructDeclaration sd = (cast(TypeStruct)tv).sym;
+ if (!sd.dtor || sd.errors)
+ return null;
+
+ const sz = vd.type.size();
+ assert(sz != SIZE_INVALID);
+ if (!sz)
+ return null;
+
+ if (vd.type.toBasetype().ty == Tstruct)
+ {
+ // v.__xdtor()
+ e = new VarExp(vd.loc, vd);
+
+ /* This is a hack so we can call destructors on const/immutable objects.
+ * Need to add things like "const ~this()" and "immutable ~this()" to
+ * fix properly.
+ */
+ e.type = e.type.mutableOf();
+
+ // Enable calling destructors on shared objects.
+ // The destructor is always a single, non-overloaded function,
+ // and must serve both shared and non-shared objects.
+ e.type = e.type.unSharedOf;
+
+ e = new DotVarExp(vd.loc, e, sd.dtor, false);
+ e = new CallExp(vd.loc, e);
+ }
+ else
+ {
+ // __ArrayDtor(v[0 .. n])
+ e = new VarExp(vd.loc, vd);
+
+ const sdsz = sd.type.size();
+ assert(sdsz != SIZE_INVALID && sdsz != 0);
+ const n = sz / sdsz;
+ SliceExp se = new SliceExp(vd.loc, e, new IntegerExp(vd.loc, 0, Type.tsize_t),
+ new IntegerExp(vd.loc, n, Type.tsize_t));
+
+ // Prevent redundant bounds check
+ se.upperIsInBounds = true;
+ se.lowerIsLessThanUpper = true;
+
+ // This is a hack so we can call destructors on const/immutable objects.
+ se.type = sd.type.arrayOf();
+
+ e = new CallExp(vd.loc, new IdentifierExp(vd.loc, Id.__ArrayDtor), se);
+ }
+ return e;
+ }
+ // Destructors for classes
+ if (!(vd.storage_class & (STC.auto_ | STC.scope_) && !(vd.storage_class & STC.parameter)))
+ return null;
+
+ for (ClassDeclaration cd = vd.type.isClassHandle(); cd; cd = cd.baseClass)
+ {
+ /* We can do better if there's a way with onstack
+ * classes to determine if there's no way the monitor
+ * could be set.
+ */
+ //if (cd.isInterfaceDeclaration())
+ // error("interface `%s` cannot be scope", cd.toChars());
+
+ if (!vd.onstack) // if any destructors
+ continue;
+ // delete'ing C++ classes crashes (and delete is deprecated anyway)
+ if (cd.classKind == ClassKind.cpp)
+ {
+ // Don't call non-existant dtor
+ if (!cd.dtor)
+ break;
+
+ e = new VarExp(vd.loc, vd);
+ e.type = e.type.mutableOf().unSharedOf(); // Hack for mutable ctor on immutable instances
+ e = new DotVarExp(vd.loc, e, cd.dtor, false);
+ e = new CallExp(vd.loc, e);
+ break;
+ }
+
+ // delete this;
+ Expression ec;
+ ec = new VarExp(vd.loc, vd);
+ e = new DeleteExp(vd.loc, ec, true);
+ e.type = Type.tvoid;
+ break;
+ }
+ return e;
+}
+
+/***************************************
+ * Collect all instance fields, then determine instance size.
+ * Returns:
+ * false if failed to determine the size.
+ */
+bool determineSize(AggregateDeclaration ad, const ref Loc loc)
+{
+ //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
+
+ // The previous instance size finalizing had:
+ if (ad.type.ty == Terror || ad.errors)
+ return false; // failed already
+ if (ad.sizeok == Sizeok.done)
+ return true; // succeeded
+
+ if (!ad.members)
+ {
+ .error(loc, "%s `%s` unknown size", ad.kind, ad.toPrettyChars);
+ return false;
+ }
+
+ if (ad._scope)
+ dsymbolSemantic(ad, null);
+
+ // Determine the instance size of base class first.
+ if (auto cd = ad.isClassDeclaration())
+ {
+ cd = cd.baseClass;
+ if (cd && !cd.determineSize(loc))
+ goto Lfail;
+ }
+
+ // Determine instance fields when sizeok == Sizeok.none
+ if (!ad.determineFields())
+ goto Lfail;
+ if (ad.sizeok != Sizeok.done)
+ ad.finalizeSize();
+
+ // this aggregate type has:
+ if (ad.type.ty == Terror)
+ return false; // marked as invalid during the finalizing.
+ if (ad.sizeok == Sizeok.done)
+ return true; // succeeded to calculate instance size.
+
+Lfail:
+ // There's unresolvable forward reference.
+ if (ad.type != Type.terror)
+ error(loc, "%s `%s` no size because of forward reference", ad.kind, ad.toPrettyChars);
+ // Don't cache errors from speculative semantic, might be resolvable later.
+ // https://issues.dlang.org/show_bug.cgi?id=16574
+ if (!global.gag)
+ {
+ ad.type = Type.terror;
+ ad.errors = true;
+ }
+ return false;
+}
import dmd.dclass;
import dmd.declaration;
import dmd.dinterpret;
-import dmd.dmangle;
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem;
+import dmd.dsymbolsem : dsymbolSemantic, checkDeprecated, aliasSemantic, search, search_correct, setScope, importAll, include;
import dmd.errors;
import dmd.errorsink;
import dmd.expression;
-import dmd.expressionsem;
+import dmd.expressionsem : resolveLoc, expressionSemantic, resolveProperties;
import dmd.func;
-import dmd.funcsem;
+import dmd.funcsem : functionSemantic, leastAsSpecialized, overloadApply;
import dmd.globals;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.impcnvtab;
import dmd.init;
-import dmd.initsem;
import dmd.location;
+import dmd.mangle;
import dmd.mtype;
import dmd.opover;
import dmd.optimize;
import dmd.root.array;
import dmd.common.outbuffer;
import dmd.rootobject;
-import dmd.semantic2;
-import dmd.semantic3;
-import dmd.templatesem;
+import dmd.semantic3 : semantic3;
+import dmd.templatesem : matchWithInstance, formatParamsWithTiargs, leastAsSpecialized, declareParameter;
import dmd.tokens;
-import dmd.typesem;
+import dmd.typesem : hasPointers, typeSemantic, merge, merge2, resolve, toDsymbol,
+ addStorageClass, isBaseOf, equivalent, sarrayOf, constOf, mutableOf, unSharedOf,
+ unqualify, aliasthisOf, castMod, substWildTo, addMod;
import dmd.visitor;
import dmd.templateparamsem;
edim = s ? getValue(s) : getValue(e);
}
}
- if (tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null) || edim && edim.toInteger() == t.dim.toInteger())
+ if ((tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null)) ||
+ (edim && edim.isIntegerExp() && edim.toInteger() == t.dim.toInteger())
+ )
{
result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
return;
return DYNCAST.templateparameter;
}
- /* Create dummy argument based on parameter.
- */
- abstract RootObject dummyArg();
-
override void accept(Visitor v)
{
v.visit(this);
return defaultType !is null;
}
- override final RootObject dummyArg()
- {
- Type t = specType;
- if (!t)
- {
- // Use this for alias-parameter's too (?)
- if (!tdummy)
- tdummy = new TypeIdentifier(loc, ident);
- t = tdummy;
- }
- return t;
- }
-
override void accept(Visitor v)
{
v.visit(this);
override RootObject defaultArg(const ref Loc instLoc, Scope* sc)
{
Expression e = defaultValue;
- if (e)
- {
- e = e.syntaxCopy();
- Scope* sc2 = sc.push();
- sc2.inDefaultArg = true;
- e = e.expressionSemantic(sc2);
- sc2.pop();
- if (e is null)
- return null;
- if (auto te = e.isTemplateExp())
+ if (!e)
+ return null;
+
+ e = e.syntaxCopy();
+ Scope* sc2 = sc.push();
+ sc2.inDefaultArg = true;
+ e = e.expressionSemantic(sc2);
+ sc2.pop();
+ if (e is null)
+ return null;
+ if (auto te = e.isTemplateExp())
+ {
+ assert(sc && sc.tinst);
+ if (te.td == sc.tinst.tempdecl)
{
- assert(sc && sc.tinst);
- if (te.td == sc.tinst.tempdecl)
- {
- // defaultValue is a reference to its template declaration
- // i.e: `template T(int arg = T)`
- // Raise error now before calling resolveProperties otherwise we'll
- // start looping on the expansion of the template instance.
- auto td = sc.tinst.tempdecl;
- .error(td.loc, "%s `%s` recursive template expansion", td.kind, td.toPrettyChars);
- return ErrorExp.get();
- }
+ // defaultValue is a reference to its template declaration
+ // i.e: `template T(int arg = T)`
+ // Raise error now before calling resolveProperties otherwise we'll
+ // start looping on the expansion of the template instance.
+ auto td = sc.tinst.tempdecl;
+ .error(td.loc, "%s `%s` recursive template expansion", td.kind, td.toPrettyChars);
+ return ErrorExp.get();
}
- if ((e = resolveProperties(sc, e)) is null)
- return null;
- e = e.resolveLoc(instLoc, sc); // use the instantiated loc
- e = e.optimize(WANTvalue);
}
+ if ((e = resolveProperties(sc, e)) is null)
+ return null;
+ e = e.resolveLoc(instLoc, sc); // use the instantiated loc
+ e = e.optimize(WANTvalue);
+
return e;
}
return defaultValue !is null;
}
- override RootObject dummyArg()
- {
- Expression e = specValue;
- if (!e)
- {
- // Create a dummy value
- auto pe = cast(void*)valType in edummies;
- if (!pe)
- {
- e = valType.defaultInit(Loc.initial);
- edummies[cast(void*)valType] = e;
- }
- else
- e = *pe;
- }
- return e;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return defaultAlias !is null;
}
- override RootObject dummyArg()
- {
- RootObject s = specAlias;
- if (!s)
- {
- if (!sdummy)
- sdummy = new Dsymbol();
- s = sdummy;
- }
- return s;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return false;
}
- override RootObject dummyArg()
- {
- return null;
- }
-
override void accept(Visitor v)
{
v.visit(this);
Dsymbol tempdecl; // referenced by foo.bar.abc
Dsymbol enclosing; // if referencing local symbols, this is the context
Dsymbol aliasdecl; // !=null if instance is an alias for its sole member
- TemplateInstance inst; // refer to existing instance
+
+ /**
+ If this is not null and it has a value that is not the current object,
+ then this field points to an existing template instance
+ and that object has been duplicated into us.
+
+ If this object is a duplicate,
+ the ``memberOf`` field will be set to a root module (passed on CLI).
+
+ This information is useful to deduplicate analysis that may occur
+ after semantic 3 has completed.
+
+ See_Also: memberOf
+ */
+ TemplateInstance inst;
+
ScopeDsymbol argsym; // argument symbol table
size_t hash; // cached result of toHash()
TemplateInstances* deferred;
- Module memberOf; // if !null, then this TemplateInstance appears in memberOf.members[]
+ /**
+ If this is not null then this template instance appears in a root module's members.
+
+ Note: This is not useful for determining duplication status of this template instance.
+ Use the field ``inst`` for determining if a template instance has been duplicated into this object.
+
+ See_Also: inst
+ */
+ Module memberOf;
// Used to determine the instance needs code generation.
// Note that these are inaccurate until semantic analysis phase completed.
// Set error here as we don't want it to depend on the number of
// entries that are being printed.
if (cl == Classification.error ||
- (cl == Classification.warning && global.params.warnings == DiagnosticReporting.error) ||
+ (cl == Classification.warning && global.params.useWarnings == DiagnosticReporting.error) ||
(cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error))
cur.errors = true;
if (enclosing != ti.enclosing)
{
//printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : "");
- goto Lnotequals;
+ return false;
}
//printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars());
if (!arrayObjectMatch(tdtypes, ti.tdtypes))
- goto Lnotequals;
+ return false;
/* Template functions may have different instantiations based on
* "auto ref" parameters.
*/
- if (auto fd = ti.toAlias().isFuncDeclaration())
- {
- if (!fd.errors)
- {
- auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs(
- ArgumentList(this.fargs, this.fnames), null);
+ auto fd = ti.toAlias().isFuncDeclaration();
+ if (!fd)
+ return true;
+ if (fd.errors)
+ return true;
- // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d
- // In that case, equalsx returns true to prevent endless template instantiations
- // However, it can also mean the function was explicitly instantiated
- // without function arguments: fail_compilation/fail14669
- // Hence the following check:
- if (this.fargs && !resolvedArgs)
- return true;
+ auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs(
+ ArgumentList(this.fargs, this.fnames), null);
- Expression[] args = resolvedArgs ? (*resolvedArgs)[] : [];
+ // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d
+ // In that case, equalsx returns true to prevent endless template instantiations
+ // However, it can also mean the function was explicitly instantiated
+ // without function arguments: fail_compilation/fail14669
+ // Hence the following check:
+ if (this.fargs && !resolvedArgs)
+ return true;
- auto fparameters = fd.getParameterList();
- size_t nfparams = fparameters.length; // Num function parameters
- for (size_t j = 0; j < nfparams; j++)
- {
- Parameter fparam = fparameters[j];
- if (fparam.storageClass & STC.autoref) // if "auto ref"
- {
- Expression farg = (j < args.length) ? args[j] : fparam.defaultArg;
- // resolveNamedArgs strips trailing nulls / default params
- // when it doesn't anymore, the ternary can be replaced with:
- // assert(j < resolvedArgs.length);
- if (!farg)
- farg = fparam.defaultArg;
- if (!farg)
- goto Lnotequals;
- if (farg.isLvalue())
- {
- if (!(fparam.storageClass & STC.ref_))
- goto Lnotequals; // auto ref's don't match
- }
- else
- {
- if (fparam.storageClass & STC.ref_)
- goto Lnotequals; // auto ref's don't match
- }
- }
- }
+ Expression[] args = resolvedArgs ? (*resolvedArgs)[] : [];
+
+ auto fparameters = fd.getParameterList();
+ size_t nfparams = fparameters.length; // Num function parameters
+ for (size_t j = 0; j < nfparams; j++)
+ {
+ Parameter fparam = fparameters[j];
+ if (!(fparam.storageClass & STC.autoref) ) // if "auto ref"
+ continue;
+
+ Expression farg = (j < args.length) ? args[j] : fparam.defaultArg;
+ // resolveNamedArgs strips trailing nulls / default params
+ // when it doesn't anymore, the ternary can be replaced with:
+ // assert(j < resolvedArgs.length);
+ if (!farg)
+ farg = fparam.defaultArg;
+ if (!farg)
+ return false;
+ if (farg.isLvalue())
+ {
+ if (!(fparam.storageClass & STC.ref_))
+ return false; // auto ref's don't match
+ }
+ else
+ {
+ if (fparam.storageClass & STC.ref_)
+ return false; // auto ref's don't match
}
}
return true;
-
- Lnotequals:
- return false;
}
extern (D) final size_t toHash()
// Elide codegen because there's no instantiation from any root modules.
return false;
}
- else
- {
- // Prefer instantiations from non-root modules, to minimize object code size.
- /* If a TemplateInstance is ever instantiated from a non-root module,
- * we do not have to generate code for it,
- * because it will be generated when the non-root module is compiled.
- *
- * But, if the non-root 'minst' imports any root modules, it might still need codegen.
- *
- * The problem is if A imports B, and B imports A, and both A
- * and B instantiate the same template, does the compilation of A
- * or the compilation of B do the actual instantiation?
- *
- * See https://issues.dlang.org/show_bug.cgi?id=2500.
- *
- * => Elide codegen if there is at least one instantiation from a non-root module
- * which doesn't import any root modules.
- */
- static ThreeState needsCodegenRootOnly(TemplateInstance tithis, TemplateInstance tinst)
+ // Prefer instantiations from non-root modules, to minimize object code size.
+
+ /* If a TemplateInstance is ever instantiated from a non-root module,
+ * we do not have to generate code for it,
+ * because it will be generated when the non-root module is compiled.
+ *
+ * But, if the non-root 'minst' imports any root modules, it might still need codegen.
+ *
+ * The problem is if A imports B, and B imports A, and both A
+ * and B instantiate the same template, does the compilation of A
+ * or the compilation of B do the actual instantiation?
+ *
+ * See https://issues.dlang.org/show_bug.cgi?id=2500.
+ *
+ * => Elide codegen if there is at least one instantiation from a non-root module
+ * which doesn't import any root modules.
+ */
+ static ThreeState needsCodegenRootOnly(TemplateInstance tithis, TemplateInstance tinst)
+ {
+ // If the ancestor isn't speculative,
+ // 1. do codegen if the ancestor needs it
+ // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree)
+ if (tinst && tinst.inst)
{
- // If the ancestor isn't speculative,
- // 1. do codegen if the ancestor needs it
- // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree)
- if (tinst && tinst.inst)
+ tinst = tinst.inst;
+ const needsCodegen = tinst.needsCodegen(); // sets tinst.minst
+ if (tinst.minst) // not speculative
{
- tinst = tinst.inst;
- const needsCodegen = tinst.needsCodegen(); // sets tinst.minst
- if (tinst.minst) // not speculative
- {
- tithis.minst = tinst.minst; // cache result
- return needsCodegen ? ThreeState.yes : ThreeState.no;
- }
+ tithis.minst = tinst.minst; // cache result
+ return needsCodegen ? ThreeState.yes : ThreeState.no;
}
+ }
- // Elide codegen if `this` doesn't need it.
- if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports())
- return ThreeState.no;
+ // Elide codegen if `this` doesn't need it.
+ if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports())
+ return ThreeState.no;
- return ThreeState.none;
- }
+ return ThreeState.none;
+ }
- if (const needsCodegen = needsCodegenRootOnly(this, tinst))
- return needsCodegen == ThreeState.yes ? true : false;
+ if (const needsCodegen = needsCodegenRootOnly(this, tinst))
+ return needsCodegen == ThreeState.yes ? true : false;
- // Elide codegen if a (non-speculative) sibling doesn't need it.
- for (; tnext; tnext = tnext.tnext)
+ // Elide codegen if a (non-speculative) sibling doesn't need it.
+ for (; tnext; tnext = tnext.tnext)
+ {
+ const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst
+ if (tnext.minst) // not speculative
{
- const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst
- if (tnext.minst) // not speculative
+ if (needsCodegen == ThreeState.no)
{
- if (needsCodegen == ThreeState.no)
- {
- minst = tnext.minst; // cache result
- assert(!minst.isRoot() && !minst.rootImports());
- return false;
- }
- else if (!minst)
- {
- minst = tnext.minst; // cache result from non-speculative sibling
- // continue searching
- }
- else if (needsCodegen != ThreeState.none)
- break;
+ minst = tnext.minst; // cache result
+ assert(!minst.isRoot() && !minst.rootImports());
+ return false;
+ }
+ else if (!minst)
+ {
+ minst = tnext.minst; // cache result from non-speculative sibling
+ // continue searching
}
+ else if (needsCodegen != ThreeState.none)
+ break;
}
-
- // Unless `this` is still speculative (=> all further siblings speculative too),
- // do codegen because we found no guaranteed-codegen'd non-root instantiation.
- return minst !is null;
}
+
+ // Unless `this` is still speculative (=> all further siblings speculative too),
+ // do codegen because we found no guaranteed-codegen'd non-root instantiation.
+ return minst !is null;
}
/**********************************************
// The arguments are not treated as part of a default argument,
// because they are evaluated at compile time.
+ const inCondition = sc.condition;
sc = sc.push();
sc.inDefaultArg = false;
+ // https://issues.dlang.org/show_bug.cgi?id=24699
+ sc.condition = inCondition;
+
for (size_t j = 0; j < tiargs.length; j++)
{
RootObject o = (*tiargs)[j];
{
if (ea.checkValue()) // check void expression
ea = ErrorExp.get();
- uint olderrs = global.errors;
+ const olderrs = global.errors;
ea = ea.ctfeInterpret();
if (global.errors != olderrs)
ea = ErrorExp.get();
printf("TemplateInstance.findBestMatch()\n");
}
- uint errs = global.errors;
+ const errs = global.errors;
TemplateDeclaration td_last = null;
Objects dedtypes;
return (errs == global.errors);
}
- /*****************************************************
- * Determine if template instance is really a template function,
- * and that template function needs to infer types from the function
- * arguments.
- *
- * Like findBestMatch, iterate possible template candidates,
- * but just looks only the necessity of type inference.
- */
- extern (D) final bool needsTypeInference(Scope* sc, int flag = 0)
- {
- //printf("TemplateInstance.needsTypeInference() %s\n", toChars());
- if (semanticRun != PASS.initial)
- return false;
-
- uint olderrs = global.errors;
- Objects dedtypes;
- size_t count = 0;
-
- auto tovers = tempdecl.isOverloadSet();
- foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
- {
- Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
- int r = overloadApply(dstart, (Dsymbol s)
- {
- auto td = s.isTemplateDeclaration();
- if (!td)
- return 0;
-
- /* If any of the overloaded template declarations need inference,
- * then return true
- */
- if (!td.onemember)
- return 0;
- if (auto td2 = td.onemember.isTemplateDeclaration())
- {
- if (!td2.onemember || !td2.onemember.isFuncDeclaration())
- return 0;
- if (tiargs.length >= td.parameters.length - (td.isVariadic() ? 1 : 0))
- return 0;
- return 1;
- }
- auto fd = td.onemember.isFuncDeclaration();
- if (!fd || fd.type.ty != Tfunction)
- return 0;
-
- foreach (tp; *td.parameters)
- {
- if (tp.isTemplateThisParameter())
- return 1;
- }
-
- /* Determine if the instance arguments, tiargs, are all that is necessary
- * to instantiate the template.
- */
- //printf("tp = %p, td.parameters.length = %d, tiargs.length = %d\n", tp, td.parameters.length, tiargs.length);
- auto tf = fd.type.isTypeFunction();
- if (tf.parameterList.length)
- {
- auto tp = td.isVariadic();
- if (tp && td.parameters.length > 1)
- return 1;
-
- if (!tp && tiargs.length < td.parameters.length)
- {
- // Can remain tiargs be filled by default arguments?
- foreach (size_t i; tiargs.length .. td.parameters.length)
- {
- if (!(*td.parameters)[i].hasDefaultArg())
- return 1;
- }
- }
-
- foreach (i, fparam; tf.parameterList)
- {
- // 'auto ref' needs inference.
- if (fparam.storageClass & STC.auto_)
- return 1;
- }
- }
-
- if (!flag)
- {
- /* Calculate the need for overload resolution.
- * When only one template can match with tiargs, inference is not necessary.
- */
- dedtypes.setDim(td.parameters.length);
- dedtypes.zero();
- if (td.semanticRun == PASS.initial)
- {
- if (td._scope)
- {
- // Try to fix forward reference. Ungag errors while doing so.
- Ungag ungag = td.ungagSpeculative();
- td.dsymbolSemantic(td._scope);
- }
- if (td.semanticRun == PASS.initial)
- {
- .error(loc, "%s `%s` `%s` forward references template declaration `%s`", kind, toPrettyChars, toChars(), td.toChars());
- return 1;
- }
- }
- MATCH m = matchWithInstance(sc, td, this, dedtypes, ArgumentList(), 0);
- if (m == MATCH.nomatch)
- return 0;
- }
-
- /* If there is more than one function template which matches, we may
- * need type inference (see https://issues.dlang.org/show_bug.cgi?id=4430)
- */
- return ++count > 1 ? 1 : 0;
- });
- if (r)
- return true;
- }
-
- if (olderrs != global.errors)
- {
- if (!global.gag)
- {
- errorSupplemental(loc, "while looking for match for `%s`", toChars());
- semanticRun = PASS.semanticdone;
- inst = this;
- }
- errors = true;
- }
- //printf("false\n");
- return false;
- }
-
/*****************************************
* Determines if a TemplateInstance will need a nested
* generation of the TemplateDeclaration.
/* If a function is really property-like, and then
* it's CTFEable, ei will be a literal expression.
*/
- uint olderrors = global.startGagging();
+ const olderrors = global.startGagging();
ei = resolveProperties(sc, ei);
ei = ei.ctfeInterpret();
if (global.endGagging(olderrors) || ei.op == EXP.error)
}
}
- if (global.params.warnings != DiagnosticReporting.off || canFix)
+ if (global.params.useWarnings != DiagnosticReporting.off || canFix)
{
// Warn about identifiers that are keywords in C++.
if (auto kc = keywordClass(ident))
import dmd.dscope;
import dmd.dsymbol;
import dmd.dsymbolsem;
-import dmd.errors;
import dmd.globals;
import dmd.identifier;
import dmd.location;
import dmd.denum;
import dmd.dimport;
import dmd.dinterpret;
-import dmd.dmangle;
import dmd.dmodule;
import dmd.dscope;
import dmd.dstruct;
ed.cppnamespace = sc.namespace;
ed.semanticRun = PASS.semantic;
- UserAttributeDeclaration.checkGNUABITag(ed, sc.linkage);
+ checkGNUABITag(ed, sc.linkage);
checkMustUseReserved(ed);
if (!ed.members && !ed.memtype) // enum ident;
D_ATTRIBUTE_FORMAT(2, 3) void message(const Loc& loc, const char *format, ...);
D_ATTRIBUTE_FORMAT(1, 2) void tip(const char *format, ...);
-D_ATTRIBUTE_FORMAT(2, 0) void verrorReport(const Loc& loc, const char *format, va_list ap, ErrorKind kind, const char *p1 = nullptr, const char *p2 = nullptr);
-D_ATTRIBUTE_FORMAT(2, 0) void verrorReportSupplemental(const Loc& loc, const char* format, va_list ap, ErrorKind kind);
-
#if defined(__GNUC__) || defined(__clang__)
#define D_ATTRIBUTE_NORETURN __attribute__((noreturn))
#elif _MSC_VER
import dmd.mtype;
import dmd.printast;
import dmd.rootobject;
+import dmd.safe;
import dmd.tokens;
import dmd.typesem : hasPointers, parameterStorageClass;
import dmd.visitor;
enum LOGSEMANTIC = false;
-/// Return value for `checkModifiable`
-enum Modifiable
-{
- /// Not modifiable
- no,
- /// Modifiable (the type is mutable)
- yes,
- /// Modifiable because it is initialization
- initialization,
-}
-/**
- * Specifies how the checkModify deals with certain situations
- */
-enum ModifyFlags
-{
- /// Issue error messages on invalid modifications of the variable
- none,
- /// No errors are emitted for invalid modifications
- noError = 0x1,
- /// The modification occurs for a subfield of the current variable
- fieldAssign = 0x2,
-}
-
/****************************************
* Find the last non-comma expression.
* Params:
dinteger_t toInteger()
{
//printf("Expression %s\n", EXPtoString(op).ptr);
- if (!type.isTypeError())
+ if (!type || !type.isTypeError())
error(loc, "integer constant expression expected instead of `%s`", toChars());
return 0;
}
enum char NoPostfix = 0;
- extern (D) this(const ref Loc loc, InterpolatedSet* set, char postfix = NoPostfix) scope
+ extern (D) this(const ref Loc loc, InterpolatedSet* set, char postfix = NoPostfix) scope @safe
{
super(loc, EXP.interpolated);
this.interpolatedSet = set;
{
Expression lowering; // lowered druntime hook `_d_arrayappend{cTX,T}`
- extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe
{
super(loc, EXP.concatenateAssign, e1, e2);
}
{
ClassDeclaration classDeclaration;
- extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration)
+ extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration) @safe
{
super(loc, EXP.objcClassReference);
this.classDeclaration = classDeclaration;
#define WANTvalue 0 // default
#define WANTexpand 1 // expand const/immutable variables if possible
-/**
- * Specifies how the checkModify deals with certain situations
- */
-enum class ModifyFlags
-{
- /// Issue error messages on invalid modifications of the variable
- none,
- /// No errors are emitted for invalid modifications
- noError = 0x1,
- /// The modification occurs for a subfield of the current variable
- fieldAssign = 0x2,
-};
-
class Expression : public ASTNode
{
public:
import dmd.deps;
import dmd.dimport;
import dmd.dinterpret;
-import dmd.dmangle;
import dmd.dmodule;
import dmd.dstruct;
import dmd.dsymbolsem;
import dmd.inline;
import dmd.intrange;
import dmd.location;
+import dmd.mangle;
import dmd.mtype;
import dmd.mustuse;
import dmd.nspace;
import dmd.sideeffect;
import dmd.safe;
import dmd.target;
+import dmd.templatesem : matchWithInstance;
import dmd.tokens;
import dmd.traits;
import dmd.typesem;
* buf = append generated string to buffer
* sc = context
* exps = array of Expressions
+ * loc = location of the pragma / mixin where this conversion was requested, for supplemental error
+ * fmt = format string for supplemental error. May contain 1 `%s` which prints the faulty expression
+ * expandTuples = whether tuples should be expanded rather than printed as tuple syntax
* Returns:
* true on error
*/
-bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps)
+bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps,
+ Loc loc, const(char)* fmt, bool expandTuples)
{
if (!exps)
return false;
foreach (ex; *exps)
{
+ bool error()
+ {
+ if (loc != Loc.initial && fmt)
+ errorSupplemental(loc, fmt, ex.toChars());
+ return true;
+ }
if (!ex)
continue;
auto sc2 = sc.startCTFE();
// allowed to contain types as well as expressions
auto e4 = ctfeInterpretForPragmaMsg(e3);
if (!e4 || e4.op == EXP.error)
- return true;
+ return error();
// expand tuple
- if (auto te = e4.isTupleExp())
- {
- if (expressionsToString(buf, sc, te.exps))
- return true;
- continue;
- }
+ if (expandTuples)
+ if (auto te = e4.isTupleExp())
+ {
+ if (expressionsToString(buf, sc, te.exps, loc, fmt, true))
+ return error();
+ continue;
+ }
// char literals exp `.toStringExp` return `null` but we cant override it
// because in most contexts we don't want the conversion to succeed.
IntegerExp ie = e4.isIntegerExp();
e4 = new ArrayLiteralExp(ex.loc, tsa, ie);
}
- if (StringExp se = e4.toStringExp())
+ StringExp se = e4.toStringExp();
+
+ if (se && se.type.nextOf().ty.isSomeChar)
buf.writestring(se.toUTF8(sc).peekString());
- else
+ else if (!(se && se.len == 0)) // don't print empty array literal `[]`
buf.writestring(e4.toString());
}
return false;
return null;
}
- auto se = e.toStringExp();
- if (!se)
- {
- error(exp.loc, "`string` expected for %s, not `(%s)` of type `%s`",
- s, exp.toChars(), exp.type.toChars());
- return null;
- }
- return se;
+ if (auto se = e.toStringExp())
+ return se;
+ error(exp.loc, "`string` expected for %s, not `(%s)` of type `%s`",
+ s, exp.toChars(), exp.type.toChars());
+ return null;
}
/****************************************
*/
StringExp toUTF8(StringExp se, Scope* sc)
{
- if (se.sz != 1)
- {
- // Convert to UTF-8 string
- se.committed = false;
- Expression e = castTo(se, sc, Type.tchar.arrayOf());
- e = e.optimize(WANTvalue);
- auto result = e.isStringExp();
- assert(result.sz == 1);
- return result;
- }
- return se;
+ if (se.sz == 1)
+ return se;
+ // Convert to UTF-8 string
+ se.committed = false;
+ Expression e = castTo(se, sc, Type.tchar.arrayOf());
+ e = e.optimize(WANTvalue);
+ auto result = e.isStringExp();
+ assert(result);
+ assert(result.sz == 1);
+ return result;
}
/********************************
* The type for a unary expression is incompatible.
* Returns:
* ErrorExp
*/
-extern (D) Expression incompatibleTypes(UnaExp e)
+private Expression incompatibleTypes(UnaExp e)
{
if (e.e1.type.toBasetype() == Type.terror)
return e.e1;
e2 = e2.castTo(sc, t1);
}
}
- if (op == EXP.mulAssign)
+ if (op == EXP.mulAssign && t2.isFloating())
{
- if (t2.isFloating())
+ if (t1.isReal())
{
- if (t1.isReal())
- {
- if (t2.isImaginary() || t2.isComplex())
- {
- e2 = e2.castTo(sc, t1);
- }
- }
- else if (t1.isImaginary())
+ if (t2.isImaginary() || t2.isComplex())
{
- if (t2.isImaginary() || t2.isComplex())
- {
- switch (t1.ty)
- {
- case Timaginary32:
- t2 = Type.tfloat32;
- break;
-
- case Timaginary64:
- t2 = Type.tfloat64;
- break;
-
- case Timaginary80:
- t2 = Type.tfloat80;
- break;
-
- default:
- assert(0);
- }
- e2 = e2.castTo(sc, t2);
- }
+ e2 = e2.castTo(sc, t1);
}
}
- }
- else if (op == EXP.divAssign)
- {
- if (t2.isImaginary())
+ else if (t1.isImaginary())
{
- if (t1.isReal())
- {
- // x/iv = i(-x/v)
- // Therefore, the result is 0
- e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat.zero, t1));
- e2.type = t1;
- Expression e = new AssignExp(loc, e1, e2);
- e.type = t1;
- return e;
- }
- else if (t1.isImaginary())
+ if (t2.isImaginary() || t2.isComplex())
{
- Type t3;
switch (t1.ty)
{
case Timaginary32:
- t3 = Type.tfloat32;
+ t2 = Type.tfloat32;
break;
case Timaginary64:
- t3 = Type.tfloat64;
+ t2 = Type.tfloat64;
break;
case Timaginary80:
- t3 = Type.tfloat80;
+ t2 = Type.tfloat80;
break;
default:
assert(0);
}
- e2 = e2.castTo(sc, t3);
- Expression e = new AssignExp(loc, e1, e2);
- e.type = t1;
- return e;
+ e2 = e2.castTo(sc, t2);
+ }
+ }
+ }
+ else if (op == EXP.divAssign && t2.isImaginary())
+ {
+ if (t1.isReal())
+ {
+ // x/iv = i(-x/v)
+ // Therefore, the result is 0
+ e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat.zero, t1));
+ e2.type = t1;
+ Expression e = new AssignExp(loc, e1, e2);
+ e.type = t1;
+ return e;
+ }
+ else if (t1.isImaginary())
+ {
+ Type t3;
+ switch (t1.ty)
+ {
+ case Timaginary32:
+ t3 = Type.tfloat32;
+ break;
+
+ case Timaginary64:
+ t3 = Type.tfloat64;
+ break;
+
+ case Timaginary80:
+ t3 = Type.tfloat80;
+ break;
+
+ default:
+ assert(0);
}
+ e2 = e2.castTo(sc, t3);
+ Expression e = new AssignExp(loc, e1, e2);
+ e.type = t1;
+ return e;
}
}
else if (op == EXP.modAssign)
(*fargs)[0] = ie.lwr;
(*fargs)[1] = ie.upr;
- uint xerrors = global.startGagging();
+ const xerrors = global.startGagging();
sc = sc.push();
FuncDeclaration fslice = resolveFuncCall(ae.loc, sc, slice, tiargs, ae.e1.type, ArgumentList(fargs), FuncResolveFlag.quiet);
sc = sc.pop();
// check them for issues.
Expressions* originalArguments = Expression.arraySyntaxCopy(ce.arguments);
- uint errors = global.startGagging();
+ const errors = global.startGagging();
e = ce.expressionSemantic(sc);
if (!global.endGagging(errors))
return e;
}
}
+/// 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)
+{
+ auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental;
+
+ AttributeViolation* s;
+ const(char)* attr;
+ if (stc & STC.safe)
+ {
+ s = fd.safetyViolation;
+ attr = "@safe";
+ }
+ else if (stc & STC.pure_)
+ {
+ s = fd.pureViolation;
+ attr = "pure";
+ }
+ else if (stc & STC.nothrow_)
+ {
+ s = fd.nothrowViolation;
+ attr = "nothrow";
+ }
+ else if (stc & STC.nogc)
+ {
+ s = fd.nogcViolation;
+ attr = "@nogc";
+ }
+
+ if (!s)
+ return;
+
+ if (s.fmtStr)
+ {
+ errorFunc(s.loc, deprecation ?
+ "which wouldn't be `%s` because of:" :
+ "which wasn't inferred `%s` because of:", attr);
+ 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() : "");
+ }
+ else
+ {
+ errorFunc(s.loc, s.fmtStr,
+ s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : "");
+ }
+ }
+ else if (auto sa = s.arg0.isDsymbol())
+ {
+ if (FuncDeclaration fd2 = sa.isFuncDeclaration())
+ {
+ if (maxDepth > 0)
+ {
+ errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars());
+ errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc);
+ }
+ }
+ }
+}
+
/*******************************************
* Accessing variable v.
* Check for purity and safety violations.
FuncDeclaration ff = s.isFuncDeclaration();
if (!ff)
break;
- if (ff.isNested() || ff.isThis())
- {
- if (ff.type.isImmutable() ||
- ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod))
- {
- OutBuffer ffbuf;
- OutBuffer vbuf;
- MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod);
- MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod);
- error(loc, "%s%s `%s` cannot access %sdata `%s`",
- ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars());
- err = true;
- break;
- }
- continue;
+ if (!ff.isNested() && !ff.isThis())
+ break;
+ if (ff.type.isImmutable() ||
+ ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod))
+ {
+ OutBuffer ffbuf;
+ OutBuffer vbuf;
+ MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod);
+ MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod);
+ error(loc, "%s%s `%s` cannot access %sdata `%s`",
+ ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars());
+ err = true;
+ break;
}
- break;
+ continue;
}
}
if (f.isNogc())
return false;
- if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f))
- {
- if (loc.linnum == 0) // e.g. implicitly generated dtor
- loc = sc.func.loc;
-
- // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
- // so don't print anything to avoid double error messages.
- if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
- || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
- || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT))
- {
- error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
- sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
+ if (isRootTraitsCompilesScope(sc) ? !sc.func.isNogcBypassingInference() : !sc.func.setGCCall(f))
+ return false;
- if (!f.isDtorDeclaration)
- f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc);
- }
+ if (loc.linnum == 0) // e.g. implicitly generated dtor
+ loc = sc.func.loc;
- f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc");
+ // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
+ // so don't print anything to avoid double error messages.
+ if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
+ || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
+ || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT))
+ {
+ error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
- return true;
+ if (!f.isDtorDeclaration)
+ f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc);
}
- return false;
+ f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc");
+
+ return true;
}
/********************************************
return e.expressionSemantic(sc);
}
}
+ for (size_t i = 0; i < os.a.length; i++)
{
- for (size_t i = 0; i < os.a.length; i++)
+ FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet);
+ if (!f)
+ continue;
+ if (f.errors)
+ return ErrorExp.get();
+ fd = f;
+ assert(fd.type.ty == Tfunction);
+ auto tf = fd.type.isTypeFunction();
+ if (!tf.isRef && e2)
{
- if (FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet))
- {
- if (f.errors)
- return ErrorExp.get();
- fd = f;
- assert(fd.type.ty == Tfunction);
- auto tf = fd.type.isTypeFunction();
- if (!tf.isRef && e2)
- {
- error(loc, "%s is not an lvalue", e1.toChars());
- return ErrorExp.get();
- }
- }
+ error(loc, "%s is not an lvalue", e1.toChars());
+ return ErrorExp.get();
}
- if (fd)
+ }
+ if (fd)
+ {
+ Expression e = new CallExp(loc, e1);
+ if (e2)
{
- Expression e = new CallExp(loc, e1);
- if (e2)
+ e = new AssignExp(loc, e, e2);
+ if (saveAtts)
{
- e = new AssignExp(loc, e, e2);
- if (saveAtts)
- {
- (cast(BinExp)e).att1 = saveAtts.att1;
- (cast(BinExp)e).att2 = saveAtts.att2;
- }
+ (cast(BinExp)e).att1 = saveAtts.att1;
+ (cast(BinExp)e).att2 = saveAtts.att2;
}
- return e.expressionSemantic(sc);
}
+ return e.expressionSemantic(sc);
}
if (e2)
goto Leprop;
return e.expressionSemantic(sc);
}
}
+ FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet);
+ if (fd && fd.type)
{
- FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet);
- if (fd && fd.type)
+ if (fd.errors)
+ return ErrorExp.get();
+ TypeFunction tf = fd.type.isTypeFunction();
+ if (!e2 || tf.isRef)
{
- if (fd.errors)
- return ErrorExp.get();
- TypeFunction tf = fd.type.isTypeFunction();
- if (!e2 || tf.isRef)
+ Expression e = new CallExp(loc, e1);
+ if (e2)
{
- Expression e = new CallExp(loc, e1);
- if (e2)
+ e = new AssignExp(loc, e, e2);
+ if (saveAtts)
{
- e = new AssignExp(loc, e, e2);
- if (saveAtts)
- {
- (cast(BinExp)e).att1 = saveAtts.att1;
- (cast(BinExp)e).att2 = saveAtts.att2;
- }
+ (cast(BinExp)e).att1 = saveAtts.att1;
+ (cast(BinExp)e).att2 = saveAtts.att2;
}
- return e.expressionSemantic(sc);
}
+ return e.expressionSemantic(sc);
}
}
- if (FuncDeclaration fd = s.isFuncDeclaration())
+ if (FuncDeclaration fd2 = s.isFuncDeclaration())
{
// Keep better diagnostic message for invalid property usage of functions
- assert(fd.type.ty == Tfunction);
+ assert(fd2.type.ty == Tfunction);
Expression e = new CallExp(loc, e1, e2);
return e.expressionSemantic(sc);
}
{
if (e.op == EXP.error)
return true;
- if (e.op == EXP.variable && e.type.ty != Terror)
- {
- VarExp ve = cast(VarExp)e;
- if (isNeedThisScope(sc, ve.var))
- {
- //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n",
- // sc.intypeof, sc.getStructClassScope(), func, fdthis);
- auto t = ve.var.isThis();
- assert(t);
- error(e.loc, "accessing non-static variable `%s` requires an instance of `%s`", ve.var.toChars(), t.toChars());
- return true;
- }
- }
- return false;
+ if (e.op != EXP.variable || e.type.ty == Terror)
+ return false;
+
+ VarExp ve = cast(VarExp)e;
+ if (!isNeedThisScope(sc, ve.var))
+ return false;
+
+ //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n",
+ // sc.intypeof, sc.getStructClassScope(), func, fdthis);
+ auto t = ve.var.isThis();
+ assert(t);
+ error(e.loc, "accessing non-static variable `%s` requires an instance of `%s`", ve.var.toChars(), t.toChars());
+ return true;
}
Expression resolveProperties(Scope* sc, Expression e)
else
(*arguments)[i] = arg;
}
- else
+ else if (arg.isDefaultInitExp())
{
- if (arg.isDefaultInitExp())
- {
- arg = arg.resolveLoc(loc, sc);
- (*arguments)[i] = arg;
- }
+ arg = arg.resolveLoc(loc, sc);
+ (*arguments)[i] = arg;
}
-
if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic
{
//printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars());
+ if (MATCH m = arg.implicitConvTo(p.type))
{
- MATCH m;
- if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch)
- {
- if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m)
- goto L2;
- else if (nargs != nparams)
- return errorArgs();
- goto L1;
- }
+ if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m)
+ goto L2;
+ else if (nargs != nparams)
+ return errorArgs();
+ goto L1;
}
L2:
Type tb = p.type.toBasetype();
{
if (!e.type)
e.type = Type.tfloat64;
- else if (e.type.isImaginary && sc.inCfile)
+ else if (!e.type.isImaginary || !sc.inCfile)
{
- /* Convert to core.stdc.config.complex
- */
- Type t = getComplexLibraryType(e.loc, sc, e.type.ty);
- if (t.ty == Terror)
- return setError();
+ e.type = e.type.typeSemantic(e.loc, sc);
+ result = e;
+ return;
+ }
- Type tf;
- switch (e.type.ty)
- {
- case Timaginary32: tf = Type.tfloat32; break;
- case Timaginary64: tf = Type.tfloat64; break;
- case Timaginary80: tf = Type.tfloat80; break;
- default:
- assert(0);
- }
+ /* Convert to core.stdc.config.complex
+ */
+ Type t = getComplexLibraryType(e.loc, sc, e.type.ty);
+ if (t.ty == Terror)
+ return setError();
- /* Construct ts{re : 0.0, im : e}
- */
- TypeStruct ts = t.isTypeStruct;
- Expressions* elements = new Expressions(2);
- (*elements)[0] = new RealExp(e.loc, CTFloat.zero, tf);
- (*elements)[1] = new RealExp(e.loc, e.toImaginary(), tf);
- Expression sle = new StructLiteralExp(e.loc, ts.sym, elements);
- result = sle.expressionSemantic(sc);
- return;
+ Type tf;
+ switch (e.type.ty)
+ {
+ case Timaginary32: tf = Type.tfloat32; break;
+ case Timaginary64: tf = Type.tfloat64; break;
+ case Timaginary80: tf = Type.tfloat80; break;
+ default:
+ assert(0);
}
- else
- e.type = e.type.typeSemantic(e.loc, sc);
- result = e;
+
+ /* Construct ts{re : 0.0, im : e}
+ */
+ TypeStruct ts = t.isTypeStruct;
+ Expressions* elements = new Expressions(2);
+ (*elements)[0] = new RealExp(e.loc, CTFloat.zero, tf);
+ (*elements)[1] = new RealExp(e.loc, e.toImaginary(), tf);
+ Expression sle = new StructLiteralExp(e.loc, ts.sym, elements);
+ result = sle.expressionSemantic(sc);
}
override void visit(ComplexExp e)
ClassDeclaration cd;
Dsymbol s;
+ void err()
+ {
+ error(e.loc, "`super` is only allowed in non-static class member functions");
+ result = ErrorExp.get();
+ }
/* Special case for typeof(this) and typeof(super) since both
* should work even if they are not inside a non-static member function
*/
return setError();
}
cd = s.isClassDeclaration();
- if (cd)
+ if (!cd)
+ continue;
+
+ cd = cd.baseClass;
+ if (!cd)
{
- cd = cd.baseClass;
- if (!cd)
- {
- error(e.loc, "class `%s` has no `super`", s.toChars());
- return setError();
- }
- e.type = cd.type;
- result = e;
- return;
+ error(e.loc, "class `%s` has no `super`", s.toChars());
+ return setError();
}
+ e.type = cd.type;
+ result = e;
+ return;
}
}
if (!fd)
- goto Lerr;
+ return err();
e.var = fd.vthis;
assert(e.var && e.var.parent);
cd = s.isClassDeclaration();
//printf("parent is %s %s\n", fd.toParent().kind(), fd.toParent().toChars());
if (!cd)
- goto Lerr;
+ return err();
if (!cd.baseClass)
{
error(e.loc, "no base class for `%s`", cd.toChars());
return setError();
result = e;
- return;
-
- Lerr:
- error(e.loc, "`super` is only allowed in non-static class member functions");
- result = ErrorExp.get();
}
override void visit(NullExp e)
}
else if (auto f = e.var.isFuncDeclaration())
{
- if (f.checkNestedReference(sc, e.loc))
+ if (f.checkNestedFuncReference(sc, e.loc))
return setError();
}
}
else if (fd)
{
- // TODO: If fd isn't yet resolved its overload, the checkNestedReference
+ // TODO: If fd isn't yet resolved its overload, the checkNestedFuncReference
// call would cause incorrect validation.
// Maybe here should be moved in CallExp, or AddrExp for functions.
- if (fd.checkNestedReference(sc, e.loc))
+ if (fd.checkNestedFuncReference(sc, e.loc))
return setError();
}
else if (auto od = e.var.isOverDeclaration())
}
Expression e = exp;
- uint olderrors;
sc = sc.push(); // just create new scope
sc.ctfe = false; // temporary stop CTFE
}
}
+ void done()
+ {
+ sc = sc.pop();
+ result = e;
+ }
+
//printf("td = %p, treq = %p\n", td, fd.treq);
if (exp.td)
{
else
e = ErrorExp.get();
}
- goto Ldone;
+ return done();
}
- olderrors = global.errors;
+ const olderrors = global.errors;
exp.fd.dsymbolSemantic(sc);
if (olderrors == global.errors)
{
if (exp.fd.type && exp.fd.type.ty == Tfunction && !exp.fd.type.nextOf())
(cast(TypeFunction)exp.fd.type).next = Type.terror;
e = ErrorExp.get();
- goto Ldone;
+ return done();
}
// Type is a "delegate to" or "pointer to" the function literal
if (exp.fd.type.isTypeError())
{
e = ErrorExp.get();
- goto Ldone;
+ return done();
}
exp.type = new TypeDelegate(exp.fd.type.isTypeFunction());
exp.type = exp.type.typeSemantic(exp.loc, sc);
}
}
exp.fd.tookAddressOf++;
-
- Ldone:
- sc = sc.pop();
- result = e;
+ done();
}
/**
{
if (tiargs && s.isFuncDeclaration())
continue;
- if (auto f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, argumentList, FuncResolveFlag.quiet))
+ auto f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, argumentList, FuncResolveFlag.quiet);
+ if (!f2)
+ continue;
+ if (f2.errors)
+ return null;
+ if (!f)
{
- if (f2.errors)
- return null;
- if (f)
- {
- /* Match in more than one overload set,
- * even if one is a 'better' match than the other.
- */
- if (f.isCsymbol() && f2.isCsymbol())
- {
- /* C has global name space, so just pick one, such as f.
- * If f and f2 are not compatible, that's how C rolls.
- */
- }
- else
- ScopeDsymbol.multiplyDefined(loc, f, f2); // issue error
- }
- else
- f = f2;
+ f = f2;
+ continue;
}
- }
- if (!f)
- {
- .error(loc, "no overload matches for `%s`", exp.toChars());
- errorSupplemental(loc, "Candidates are:");
- foreach (s; os.a)
+ /* Match in more than one overload set,
+ * even if one is a 'better' match than the other.
+ */
+ if (f.isCsymbol() && f2.isCsymbol())
{
- overloadApply(s, (ds){
- if (auto fd = ds.isFuncDeclaration())
- .errorSupplemental(ds.loc, "%s%s", fd.toChars(),
- fd.type.toTypeFunction().parameterList.parametersTypeToChars());
- else
- .errorSupplemental(ds.loc, "%s", ds.toChars());
- return 0;
- });
+ /* C has global name space, so just pick one, such as f.
+ * If f and f2 are not compatible, that's how C rolls.
+ */
}
+ else
+ ScopeDsymbol.multiplyDefined(loc, f, f2); // issue error
}
- else if (f.errors)
- f = null;
- return f;
+ if (f && f.errors)
+ return null;
+ if (f)
+ return f;
+ .error(loc, "no overload matches for `%s`", exp.toChars());
+ errorSupplemental(loc, "Candidates are:");
+ foreach (s; os.a)
+ {
+ overloadApply(s, (ds){
+ if (auto fd = ds.isFuncDeclaration())
+ .errorSupplemental(ds.loc, "%s%s", fd.toChars(),
+ fd.type.toTypeFunction().parameterList.parametersTypeToChars());
+ else
+ .errorSupplemental(ds.loc, "%s", ds.toChars());
+ return 0;
+ });
+ }
+ return f;
}
bool isSuper = false;
exp.f.checkPurity(exp.loc, sc);
exp.f.checkSafety(exp.loc, sc);
exp.f.checkNogc(exp.loc, sc);
- if (exp.f.checkNestedReference(sc, exp.loc))
+ if (exp.f.checkNestedFuncReference(sc, exp.loc))
return setError();
}
else if (sc.func && sc.intypeof != 1 && !(sc.ctfe || sc.debug_))
if (exp.f.needThis())
{
// Change the ancestor lambdas to delegate before hasThis(sc) call.
- if (exp.f.checkNestedReference(sc, exp.loc))
+ if (exp.f.checkNestedFuncReference(sc, exp.loc))
return setError();
auto memberFunc = hasThis(sc);
checkFunctionAttributes(exp, sc, exp.f);
checkAccess(exp.loc, sc, null, exp.f);
- if (exp.f.checkNestedReference(sc, exp.loc))
+ if (exp.f.checkNestedFuncReference(sc, exp.loc))
return setError();
ethis = null;
printf("DeclarationExp::semantic() %s\n", e.toChars());
}
- uint olderrors = global.errors;
+ const olderrors = global.errors;
/* This is here to support extern(linkage) declaration,
* where the extern(linkage) winds up being an AttribDeclaration
// Handle this in the glue layer
Expression e = new TypeidExp(exp.loc, ta);
- bool genObjCode = true;
-
- // https://issues.dlang.org/show_bug.cgi?id=23650
- // We generate object code for typeinfo, required
- // by typeid, only if in non-speculative context
- if (sc.traitsCompiles)
- {
- genObjCode = false;
- }
-
- e.type = getTypeInfoType(exp.loc, ta, sc, genObjCode);
+ e.type = getTypeInfoType(exp.loc, ta, sc);
semanticTypeInfo(sc, ta);
if (ea)
//printf("targ = %s, %s\n", e.targ.toChars(), e.targ.deco);
//printf("tspec = %s, %s\n", e.tspec.toChars(), e.tspec.deco);
- if (e.tok == TOK.colon)
+ if (e.tok != TOK.colon) /* == */
{
- // current scope is itself deprecated, or deprecations are not errors
- const bool deprecationAllowed = sc.isDeprecated
- || global.params.useDeprecated != DiagnosticReporting.error;
- const bool preventAliasThis = e.targ.hasDeprecatedAliasThis && !deprecationAllowed;
+ if (e.targ.equals(e.tspec))
+ return yes();
+ else
+ return no();
+ }
- if (preventAliasThis && e.targ.ty == Tstruct)
- {
- if ((cast(TypeStruct) e.targ).implicitConvToWithoutAliasThis(e.tspec))
- return yes();
- else
- return no();
- }
- else if (preventAliasThis && e.targ.ty == Tclass)
- {
- if ((cast(TypeClass) e.targ).implicitConvToWithoutAliasThis(e.tspec))
- return yes();
- else
- return no();
- }
- else if (e.targ.implicitConvTo(e.tspec))
+ // current scope is itself deprecated, or deprecations are not errors
+ const bool deprecationAllowed = sc.isDeprecated
+ || global.params.useDeprecated != DiagnosticReporting.error;
+ const bool preventAliasThis = e.targ.hasDeprecatedAliasThis && !deprecationAllowed;
+
+ if (preventAliasThis && e.targ.ty == Tstruct)
+ {
+ if ((cast(TypeStruct) e.targ).implicitConvToWithoutAliasThis(e.tspec))
return yes();
else
return no();
}
- else /* == */
+ else if (preventAliasThis && e.targ.ty == Tclass)
{
- if (e.targ.equals(e.tspec))
+ if ((cast(TypeClass) e.targ).implicitConvToWithoutAliasThis(e.tspec))
return yes();
else
return no();
}
+ else if (e.targ.implicitConvTo(e.tspec))
+ return yes();
+ else
+ return no();
}
else if (e.tspec)
{
private Expression compileIt(MixinExp exp, Scope *sc)
{
OutBuffer buf;
- if (expressionsToString(buf, sc, exp.exps))
+ if (expressionsToString(buf, sc, exp.exps, exp.loc, null, true))
return null;
- uint errors = global.errors;
+ const errors = global.errors;
const len = buf.length;
const str = buf.extractChars()[0 .. len];
const bool doUnittests = global.params.parsingUnittestsRequired();
auto loc = adjustLocForMixin(str, exp.loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
- p.transitionIn = global.params.v.vin;
p.nextToken();
//printf("p.loc.linnum = %d\n", p.loc.linnum);
/* This is an `assert(0)` which means halt program execution
*/
FuncDeclaration fd = sc.parent.isFuncDeclaration();
- if (fd)
- fd.hasReturnExp |= 4;
sc.ctorflow.orCSX(CSX.halt);
if (global.params.useAssert == CHECKENABLE.off)
if (e && isDotOpDispatch(e))
{
auto ode = e;
- uint errors = global.startGagging();
+ const errors = global.startGagging();
e = resolvePropertiesX(sc, e);
// Any error or if 'e' is not resolved, go to UFCS
if (global.endGagging(errors) || e is ode)
*/
auto ode = e;
exp.e2 = exp.e2.expressionSemantic(sc);
- uint errors = global.startGagging();
+ const errors = global.startGagging();
e = resolvePropertiesX(sc, e, exp.e2);
// Any error or if 'e' is not resolved, go to UFCS
if (global.endGagging(errors) || e is ode)
}
if (tb1.ty == Tpointer && tb2.ty == Tpointer ||
- tb1.ty == Tnull && tb2.ty == Tnull)
+ tb1.ty == Tnull && tb2.ty == Tnull)
{
result = exp.incompatibleTypes();
return;
Expression trySemantic(Expression exp, Scope* sc)
{
//printf("+trySemantic(%s)\n", exp.toChars());
- uint errors = global.startGagging();
+ const errors = global.startGagging();
Expression e = expressionSemantic(exp, sc);
if (global.endGagging(errors))
{
return exp;
}
-private bool checkDisabled(Dsymbol s, ref Loc loc, Scope* sc)
-{
- if (auto d = s.isDeclaration())
- return d.checkDisabled(loc, sc);
-
- return false;
-}
-
/******************************
* Resolve properties, i.e. `e1.ident`, without seeing UFCS.
* Params:
s = s.toAlias();
s.checkDeprecated(exp.loc, sc);
- s.checkDisabled(exp.loc, sc);
+ if (auto d = s.isDeclaration())
+ d.checkDisabled(exp.loc, sc);
if (auto em = s.isEnumMember())
{
auto die = new DotIdExp(exp.loc, e1, exp.ti.name);
Expression e = die.dotIdSemanticPropX(sc);
+
+ Expression notTemplate()
+ {
+ error(exp.loc, "`%s` isn't a template", e.toChars());
+ return errorExp();
+ }
+
if (e == die)
{
exp.e1 = die.e1; // take back
exp.e1 = dve.e1; // pull semantic() result
if (!exp.findTempDecl(sc))
- goto Lerr;
+ return notTemplate();
if (exp.ti.needsTypeInference(sc))
return exp;
exp.ti.dsymbolSemantic(sc);
.expressionSemantic(sc);
}
-Lerr:
- error(exp.loc, "`%s` isn't a template", e.toChars());
- return errorExp();
+ return notTemplate();
}
MATCH matchType(FuncExp funcExp, Type to, Scope* sc, FuncExp* presult, ErrorSink eSink)
* Returns:
* `true` if ok, `false` for error
*/
-bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v)
+private bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v)
{
//printf("checkAddressVar(exp: %s, v: %s)\n", exp.toChars(), v.toChars());
if (v is null)
* newly created variable such that a closure is made for the variable when
* the address of `fd` is taken.
*/
-VarDeclaration makeThis2Argument(const ref Loc loc, Scope* sc, FuncDeclaration fd)
+private VarDeclaration makeThis2Argument(const ref Loc loc, Scope* sc, FuncDeclaration fd)
{
Type tthis2 = Type.tvoidptr.sarrayOf(2);
VarDeclaration vthis2 = new VarDeclaration(loc, tthis2, Identifier.generateId("__this"), null);
}
return impl(e);
}
+
+/************************************
+ * Check to see the aggregate type is nested and its context pointer is
+ * accessible from the current scope.
+ * Returns true if error occurs.
+ */
+bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, size_t iStart = 0)
+{
+ Dsymbol sparent = ad.toParentLocal();
+ Dsymbol sparent2 = ad.toParent2();
+ Dsymbol s = sc.func;
+ if (ad.isNested() && s)
+ {
+ //printf("ad = %p %s [%s], parent:%p\n", ad, ad.toChars(), ad.loc.toChars(), ad.parent);
+ //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent.toChars(), sparent.loc.toChars(), sparent.parent,toChars());
+ //printf("sparent2 = %p %s [%s], parent: %s\n", sparent2, sparent2.toChars(), sparent2.loc.toChars(), sparent2.parent,toChars());
+ if (!ensureStaticLinkTo(s, sparent) || sparent != sparent2 && !ensureStaticLinkTo(s, sparent2))
+ {
+ error(loc, "cannot access frame pointer of `%s`", ad.toPrettyChars());
+ return true;
+ }
+ }
+
+ bool result = false;
+ for (size_t i = iStart; i < ad.fields.length; i++)
+ {
+ VarDeclaration vd = ad.fields[i];
+ Type tb = vd.type.baseElemOf();
+ if (tb.ty == Tstruct)
+ {
+ result |= checkFrameAccess(loc, sc, (cast(TypeStruct)tb).sym);
+ }
+ }
+ return result;
+}
+
+/// Return value for `checkModifiable`
+enum Modifiable
+{
+ /// Not modifiable
+ no,
+ /// Modifiable (the type is mutable)
+ yes,
+ /// Modifiable because it is initialization
+ initialization,
+}
+
+/**
+ * Specifies how the checkModify deals with certain situations
+ */
+enum ModifyFlags
+{
+ /// Issue error messages on invalid modifications of the variable
+ none,
+ /// No errors are emitted for invalid modifications
+ noError = 0x1,
+ /// The modification occurs for a subfield of the current variable
+ fieldAssign = 0x2,
+}
+
+/*************************************
+ * Check to see if declaration can be modified in this context (sc).
+ * Issue error if not.
+ * Params:
+ * loc = location for error messages
+ * e1 = `null` or `this` expression when this declaration is a field
+ * sc = context
+ * flag = if the first bit is set it means do not issue error message for
+ * invalid modification; if the second bit is set, it means that
+ this declaration is a field and a subfield of it is modified.
+ * Returns:
+ * Modifiable.yes or Modifiable.initialization
+ */
+private Modifiable checkModify(Declaration d, Loc loc, Scope* sc, Expression e1, ModifyFlags flag)
+{
+ VarDeclaration v = d.isVarDeclaration();
+ if (v && v.canassign)
+ return Modifiable.initialization;
+
+ if (d.isParameter() || d.isResult())
+ {
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ if (scx.func == d.parent && scx.contract != Contract.none)
+ {
+ const(char)* s = d.isParameter() && d.parent.ident != Id.ensure ? "parameter" : "result";
+ if (!(flag & ModifyFlags.noError))
+ error(loc, "%s `%s` cannot modify %s `%s` in contract", d.kind, d.toPrettyChars, s, d.toChars());
+ return Modifiable.initialization; // do not report type related errors
+ }
+ }
+ }
+
+ if (e1 && e1.op == EXP.this_ && d.isField())
+ {
+ VarDeclaration vthis = e1.isThisExp().var;
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ if (scx.func == vthis.parent && scx.contract != Contract.none)
+ {
+ if (!(flag & ModifyFlags.noError))
+ error(loc, "%s `%s` cannot modify parameter `this` in contract", d.kind, d.toPrettyChars);
+ return Modifiable.initialization; // do not report type related errors
+ }
+ }
+ }
+
+ if (v && (v.isCtorinit() || d.isField()))
+ {
+ // It's only modifiable if inside the right constructor
+ if ((d.storage_class & (STC.foreach_ | STC.ref_)) == (STC.foreach_ | STC.ref_))
+ return Modifiable.initialization;
+ if (flag & ModifyFlags.fieldAssign)
+ return Modifiable.yes;
+ return modifyFieldVar(loc, sc, v, e1) ? Modifiable.initialization : Modifiable.yes;
+ }
+ return Modifiable.yes;
+}
+
+/***********************************************
+ * Mark variable v as modified if it is inside a constructor that var
+ * is a field in.
+ * Also used to allow immutable globals to be initialized inside a static constructor.
+ * Returns:
+ * true if it's an initialization of v
+ */
+private bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1)
+{
+ //printf("modifyFieldVar(var = %s)\n", var.toChars());
+ Dsymbol s = sc.func;
+ while (1)
+ {
+ FuncDeclaration fd = null;
+ if (s)
+ fd = s.isFuncDeclaration();
+ if (fd &&
+ ((fd.isCtorDeclaration() && var.isField()) ||
+ ((fd.isStaticCtorDeclaration() || fd.isCrtCtor) && !var.isField())) &&
+ fd.toParentDecl() == var.toParent2() &&
+ (!e1 || e1.op == EXP.this_))
+ {
+ bool result = true;
+
+ var.ctorinit = true;
+ //printf("setting ctorinit\n");
+
+ if (var.isField() && sc.ctorflow.fieldinit.length && !sc.intypeof)
+ {
+ assert(e1);
+ auto mustInit = ((var.storage_class & STC.nodefaultctor) != 0 ||
+ var.type.needsNested());
+
+ const dim = sc.ctorflow.fieldinit.length;
+ auto ad = fd.isMemberDecl();
+ assert(ad);
+ size_t i;
+ for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ?
+ {
+ if (ad.fields[i] == var)
+ break;
+ }
+ assert(i < dim);
+ auto fieldInit = &sc.ctorflow.fieldinit[i];
+ const fi = fieldInit.csx;
+
+ if (fi & CSX.this_ctor)
+ {
+ if (var.type.isMutable() && e1.type.isMutable())
+ result = false;
+ else
+ {
+ const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod);
+ .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars());
+ .errorSupplemental(fieldInit.loc, "Previous initialization is here.");
+ }
+ }
+ else if (sc.inLoop || (fi & CSX.label))
+ {
+ if (!mustInit && var.type.isMutable() && e1.type.isMutable())
+ result = false;
+ else
+ {
+ const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod);
+ .error(loc, "%s field `%s` initialization is not allowed in loops or after labels", modStr, var.toChars());
+ }
+ }
+
+ fieldInit.csx |= CSX.this_ctor;
+ fieldInit.loc = e1.loc;
+ if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258
+ {
+ foreach (j, v; ad.fields)
+ {
+ if (v is var || !var.isOverlappedWith(v))
+ continue;
+ v.ctorinit = true;
+ sc.ctorflow.fieldinit[j].csx = CSX.this_ctor;
+ }
+ }
+ }
+ else if (fd != sc.func)
+ {
+ if (var.type.isMutable())
+ result = false;
+ else if (sc.func.fes)
+ {
+ const(char)* p = var.isField() ? "field" : var.kind();
+ .error(loc, "%s %s `%s` initialization is not allowed in foreach loop",
+ MODtoChars(var.type.mod), p, var.toChars());
+ }
+ else
+ {
+ const(char)* p = var.isField() ? "field" : var.kind();
+ .error(loc, "%s %s `%s` initialization is not allowed in nested function `%s`",
+ MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars());
+ }
+ }
+ else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() &&
+ var.type.isImmutable())
+ {
+ .error(loc, "%s %s `%s` initialization is not allowed in `static this`",
+ MODtoChars(var.type.mod), var.kind(), var.toChars());
+ errorSupplemental(loc, "Use `shared static this` instead.");
+ }
+ else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() &&
+ var.type.isConst())
+ {
+ // @@@DEPRECATED_2.116@@@
+ // Turn this into an error, merging with the branch above
+ .deprecation(loc, "%s %s `%s` initialization is not allowed in `static this`",
+ MODtoChars(var.type.mod), var.kind(), var.toChars());
+ deprecationSupplemental(loc, "Use `shared static this` instead.");
+ }
+ return result;
+ }
+ else
+ {
+ if (s)
+ {
+ s = s.toParentP(var.toParent2());
+ continue;
+ }
+ }
+ break;
+ }
+ return false;
+}
+
+/***************************************
+ * Request additional semantic analysis for TypeInfo generation.
+ * Params:
+ * sc = context
+ * t = type that TypeInfo is being generated for
+ */
+void semanticTypeInfo(Scope* sc, Type t)
+{
+ if (sc)
+ {
+ if (sc.intypeof)
+ return;
+ if (!sc.needsCodegen())
+ return;
+ }
+
+ if (!t)
+ return;
+
+ void visitVector(TypeVector t)
+ {
+ semanticTypeInfo(sc, t.basetype);
+ }
+
+ void visitAArray(TypeAArray t)
+ {
+ semanticTypeInfo(sc, t.index);
+ semanticTypeInfo(sc, t.next);
+ }
+
+ void visitStruct(TypeStruct t)
+ {
+ //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars());
+ StructDeclaration sd = t.sym;
+
+ /* Step 1: create TypeInfoDeclaration
+ */
+ if (!sc) // inline may request TypeInfo.
+ {
+ Scope scx;
+ scx.eSink = global.errorSink;
+ scx._module = sd.getModule();
+ getTypeInfoType(sd.loc, t, &scx);
+ sd.requestTypeInfo = true;
+ }
+ else if (!sc.minst)
+ {
+ // don't yet have to generate TypeInfo instance if
+ // the typeid(T) expression exists in speculative scope.
+ }
+ else
+ {
+ getTypeInfoType(sd.loc, t, sc);
+ sd.requestTypeInfo = true;
+
+ // https://issues.dlang.org/show_bug.cgi?id=15149
+ // if the typeid operand type comes from a
+ // result of auto function, it may be yet speculative.
+ // unSpeculative(sc, sd);
+ }
+
+ /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later.
+ * This should be done even if typeid(T) exists in speculative scope.
+ * Because it may appear later in non-speculative scope.
+ */
+ if (!sd.members)
+ return; // opaque struct
+ if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd))
+ return; // none of TypeInfo-specific members
+
+ // If the struct is in a non-root module, run semantic3 to get
+ // correct symbols for the member function.
+ if (sd.semanticRun >= PASS.semantic3)
+ {
+ // semantic3 is already done
+ }
+ else if (TemplateInstance ti = sd.isInstantiated())
+ {
+ if (ti.minst && !ti.minst.isRoot())
+ Module.addDeferredSemantic3(sd);
+ }
+ else
+ {
+ if (sd.inNonRoot())
+ {
+ //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot());
+ Module.addDeferredSemantic3(sd);
+ }
+ }
+ }
+
+ void visitTuple(TypeTuple t)
+ {
+ if (t.arguments)
+ {
+ foreach (arg; *t.arguments)
+ {
+ semanticTypeInfo(sc, arg.type);
+ }
+ }
+ }
+
+ /* Note structural similarity of this Type walker to that in isSpeculativeType()
+ */
+
+ Type tb = t.toBasetype();
+ switch (tb.ty)
+ {
+ case Tvector: visitVector(tb.isTypeVector()); break;
+ case Taarray: visitAArray(tb.isTypeAArray()); break;
+ case Tstruct: visitStruct(tb.isTypeStruct()); break;
+ case Ttuple: visitTuple (tb.isTypeTuple()); break;
+
+ case Tclass:
+ case Tenum: break;
+
+ default: semanticTypeInfo(sc, tb.nextOf()); break;
+ }
+}
+
+/**
+ * Issue an error if an attempt to call a disabled method is made
+ *
+ * If the declaration is disabled but inside a disabled function,
+ * returns `true` but do not issue an error message.
+ *
+ * Params:
+ * d = Declaration to check
+ * loc = Location information of the call
+ * sc = Scope in which the call occurs
+ * isAliasedDeclaration = if `true` searches overload set
+ *
+ * Returns:
+ * `true` if this `Declaration` is `@disable`d, `false` otherwise.
+ */
+bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration = false)
+{
+ if (!(d.storage_class & STC.disable))
+ return false;
+
+ if (sc.func && sc.func.storage_class & STC.disable)
+ return true;
+
+ if (auto p = d.toParent())
+ {
+ if (auto postblit = d.isPostBlitDeclaration())
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=21885
+ *
+ * If the generated postblit is disabled, it
+ * means that one of the fields has a disabled
+ * postblit. Print the first field that has
+ * a disabled postblit.
+ */
+ if (postblit.isGenerated())
+ {
+ auto sd = p.isStructDeclaration();
+ assert(sd);
+ for (size_t i = 0; i < sd.fields.length; i++)
+ {
+ auto structField = sd.fields[i];
+ if (structField.overlapped)
+ continue;
+ Type tv = structField.type.baseElemOf();
+ if (tv.ty != Tstruct)
+ continue;
+ auto sdv = (cast(TypeStruct)tv).sym;
+ if (!sdv.postblit)
+ continue;
+ if (sdv.postblit.isDisabled())
+ {
+ .error(loc, "%s `%s` is not copyable because field `%s` is not copyable", p.kind, p.toPrettyChars, structField.toChars());
+ return true;
+ }
+ }
+ }
+ .error(loc, "%s `%s` is not copyable because it has a disabled postblit", p.kind, p.toPrettyChars);
+ return true;
+ }
+ }
+
+ // if the function is @disabled, maybe there
+ // is an overload in the overload set that isn't
+ if (isAliasedDeclaration)
+ {
+ if (FuncDeclaration fd = d.isFuncDeclaration())
+ {
+ for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext)
+ if (!(ovl.storage_class & STC.disable))
+ return false;
+ }
+ }
+
+ if (auto ctor = d.isCtorDeclaration())
+ {
+ if (ctor.isCpCtor && ctor.isGenerated())
+ {
+ .error(loc, "generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", d.parent.toPrettyChars());
+ return true;
+ }
+ }
+ .error(loc, "%s `%s` cannot be used because it is annotated with `@disable`", d.kind, d.toPrettyChars);
+ return true;
+}
+
+/*******************************************
+ * Helper function for the expansion of manifest constant.
+ */
+private Expression expandInitializer(VarDeclaration vd, Loc loc)
+{
+ assert((vd.storage_class & STC.manifest) && vd._init);
+
+ auto e = vd.getConstInitializer();
+ if (!e)
+ {
+ .error(loc, "cannot make expression out of initializer for `%s`", vd.toChars());
+ return ErrorExp.get();
+ }
+
+ e = e.copy();
+ e.loc = loc; // for better error message
+ return e;
+}
+
+/*****************************************************
+ * Determine if template instance is really a template function,
+ * and that template function needs to infer types from the function
+ * arguments.
+ *
+ * Like findBestMatch, iterate possible template candidates,
+ * but just looks only the necessity of type inference.
+ */
+private bool needsTypeInference(TemplateInstance ti, Scope* sc, int flag = 0)
+{
+ //printf("TemplateInstance.needsTypeInference() %s\n", toChars());
+ if (ti.semanticRun != PASS.initial)
+ return false;
+
+ const olderrs = global.errors;
+ Objects dedtypes;
+ size_t count = 0;
+
+ auto tovers = ti.tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
+ {
+ Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempdecl;
+ int r = overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+
+ /* If any of the overloaded template declarations need inference,
+ * then return true
+ */
+ if (!td.onemember)
+ return 0;
+ if (auto td2 = td.onemember.isTemplateDeclaration())
+ {
+ if (!td2.onemember || !td2.onemember.isFuncDeclaration())
+ return 0;
+ if (ti.tiargs.length >= td.parameters.length - (td.isVariadic() ? 1 : 0))
+ return 0;
+ return 1;
+ }
+ auto fd = td.onemember.isFuncDeclaration();
+ if (!fd || fd.type.ty != Tfunction)
+ return 0;
+
+ foreach (tp; *td.parameters)
+ {
+ if (tp.isTemplateThisParameter())
+ return 1;
+ }
+
+ /* Determine if the instance arguments, tiargs, are all that is necessary
+ * to instantiate the template.
+ */
+ //printf("tp = %p, td.parameters.length = %d, tiargs.length = %d\n", tp, td.parameters.length, tiargs.length);
+ auto tf = fd.type.isTypeFunction();
+ if (tf.parameterList.length)
+ {
+ auto tp = td.isVariadic();
+ if (tp && td.parameters.length > 1)
+ return 1;
+
+ if (!tp && ti.tiargs.length < td.parameters.length)
+ {
+ // Can remain tiargs be filled by default arguments?
+ foreach (size_t i; ti.tiargs.length .. td.parameters.length)
+ {
+ if (!(*td.parameters)[i].hasDefaultArg())
+ return 1;
+ }
+ }
+
+ foreach (i, fparam; tf.parameterList)
+ {
+ // 'auto ref' needs inference.
+ if (fparam.storageClass & STC.auto_)
+ return 1;
+ }
+ }
+
+ if (!flag)
+ {
+ /* Calculate the need for overload resolution.
+ * When only one template can match with tiargs, inference is not necessary.
+ */
+ dedtypes.setDim(td.parameters.length);
+ dedtypes.zero();
+ if (td.semanticRun == PASS.initial)
+ {
+ if (td._scope)
+ {
+ // Try to fix forward reference. Ungag errors while doing so.
+ Ungag ungag = td.ungagSpeculative();
+ td.dsymbolSemantic(td._scope);
+ }
+ if (td.semanticRun == PASS.initial)
+ {
+ .error(ti.loc, "%s `%s` `%s` forward references template declaration `%s`",
+ ti.kind, ti.toPrettyChars, ti.toChars(), td.toChars());
+ return 1;
+ }
+ }
+ MATCH m = matchWithInstance(sc, td, ti, dedtypes, ArgumentList(), 0);
+ if (m == MATCH.nomatch)
+ return 0;
+ }
+
+ /* If there is more than one function template which matches, we may
+ * need type inference (see https://issues.dlang.org/show_bug.cgi?id=4430)
+ */
+ return ++count > 1 ? 1 : 0;
+ });
+ if (r)
+ return true;
+ }
+
+ if (olderrs != global.errors)
+ {
+ if (!global.gag)
+ {
+ errorSupplemental(ti.loc, "while looking for match for `%s`", ti.toChars());
+ ti.semanticRun = PASS.semanticdone;
+ ti.inst = ti;
+ }
+ ti.errors = true;
+ }
+ //printf("false\n");
+ return false;
+}
+
+/***************************************
+ * Fill out remainder of elements[] with default initializers for fields[].
+ * Params:
+ * sd = struct
+ * loc = location
+ * elements = explicit arguments which given to construct object.
+ * ctorinit = true if the elements will be used for default initialization.
+ * Returns:
+ * false if any errors occur.
+ * Otherwise, returns true and the missing arguments will be pushed in elements[].
+ */
+bool fill(StructDeclaration sd, const ref Loc loc, ref Expressions elements, bool ctorinit)
+{
+ //printf("AggregateDeclaration::fill() %s\n", toChars());
+ assert(sd.sizeok == Sizeok.done);
+ const nfields = sd.nonHiddenFields();
+ bool errors = false;
+
+ size_t dim = elements.length;
+ elements.setDim(nfields);
+ foreach (size_t i; dim .. nfields)
+ elements[i] = null;
+
+ // Fill in missing any elements with default initializers
+ foreach (i; 0 .. nfields)
+ {
+ if (elements[i])
+ continue;
+
+ auto vd = sd.fields[i];
+ auto vx = vd;
+ if (vd._init && vd._init.isVoidInitializer())
+ vx = null;
+
+ // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
+ size_t fieldi = i;
+ foreach (j; 0 .. nfields)
+ {
+ if (i == j)
+ continue;
+ auto v2 = sd.fields[j];
+ if (!vd.isOverlappedWith(v2))
+ continue;
+
+ if (elements[j])
+ {
+ vx = null;
+ break;
+ }
+ if (v2._init && v2._init.isVoidInitializer())
+ continue;
+
+ version (all)
+ {
+ /* Prefer first found non-void-initialized field
+ * union U { int a; int b = 2; }
+ * U u; // Error: overlapping initialization for field a and b
+ */
+ if (!vx)
+ {
+ vx = v2;
+ fieldi = j;
+ }
+ else if (v2._init)
+ {
+ .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
+ errors = true;
+ }
+ }
+ else
+ {
+ // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always
+
+ /* Prefer explicitly initialized field
+ * union U { int a; int b = 2; }
+ * U u; // OK (u.b == 2)
+ */
+ if (!vx || !vx._init && v2._init)
+ {
+ vx = v2;
+ fieldi = j;
+ }
+ else if (vx != vd && !vx.isOverlappedWith(v2))
+ {
+ // Both vx and v2 fills vd, but vx and v2 does not overlap
+ }
+ else if (vx._init && v2._init)
+ {
+ .error(loc, "overlapping default initialization for field `%s` and `%s`",
+ v2.toChars(), vd.toChars());
+ errors = true;
+ }
+ else
+ assert(vx._init || !vx._init && !v2._init);
+ }
+ }
+ if (!vx)
+ continue;
+
+ Expression e;
+ if (vx.type.size() == 0)
+ {
+ e = null;
+ }
+ else if (vx._init)
+ {
+ assert(!vx._init.isVoidInitializer());
+ if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057
+ {
+ .error(loc, "%s `%s` recursive initialization of field", vx.kind(), vx.toPrettyChars());
+ errors = true;
+ }
+ else
+ e = vx.getConstInitializer(false);
+ }
+ else
+ {
+ if ((vx.storage_class & STC.nodefaultctor) && !ctorinit)
+ {
+ .error(loc, "field `%s.%s` must be initialized because it has no default constructor",
+ sd.type.toChars(), vx.toChars());
+ errors = true;
+ }
+ /* https://issues.dlang.org/show_bug.cgi?id=12509
+ * Get the element of static array type.
+ */
+ Type telem = vx.type;
+ if (telem.ty == Tsarray)
+ {
+ /* We cannot use Type::baseElemOf() here.
+ * If the bottom of the Tsarray is an enum type, baseElemOf()
+ * will return the base of the enum, and its default initializer
+ * would be different from the enum's.
+ */
+ TypeSArray tsa;
+ while ((tsa = telem.toBasetype().isTypeSArray()) !is null)
+ telem = tsa.next;
+ if (telem.ty == Tvoid)
+ telem = Type.tuns8.addMod(telem.mod);
+ }
+ if (telem.needsNested() && ctorinit)
+ e = telem.defaultInit(loc);
+ else
+ e = telem.defaultInitLiteral(loc);
+ }
+ elements[fieldi] = e;
+ }
+ foreach (e; elements)
+ {
+ if (e && e.op == EXP.error)
+ return false;
+ }
+
+ return !errors;
+}
import dmd.dclass;
import dmd.declaration;
import dmd.delegatize;
-import dmd.dinterpret;
import dmd.dmodule;
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
-import dmd.errors;
import dmd.escape;
import dmd.expression;
+import dmd.funcsem : overloadApply;
import dmd.globals;
import dmd.hdrgen;
import dmd.id;
import dmd.rootobject;
import dmd.root.string;
import dmd.root.stringtable;
-import dmd.semantic2;
-import dmd.semantic3;
import dmd.statement;
import dmd.tokens;
import dmd.visitor;
bool computedEscapingSiblings; /// `hasEscapingSiblings` has been computed
bool dllImport; /// __declspec(dllimport)
bool dllExport; /// __declspec(dllexport)
+
+ bool hasReturnExp; /// Has return exp; statement
+ bool hasInlineAsm; /// Has asm{} statement
+ bool hasMultipleReturnExp; /// Has multiple return exp; statements
}
/***********************************************************
// Things that should really go into Scope
- /// 1 if there's a return exp; statement
- /// 2 if there's a throw statement
- /// 4 if there's an assert(0)
- /// 8 if there's inline asm
- /// 16 if there are multiple return statements
- int hasReturnExp;
-
VarDeclaration nrvo_var; /// variable to replace with shidden
Symbol* shidden; /// hidden pointer passed to function
return bitFields;
}
- final bool isSafe()
- {
- if (safetyInprocess)
- setUnsafe();
- return type.toTypeFunction().trust == TRUST.safe;
- }
-
- extern (D) final bool isSafeBypassingInference()
- {
- return !(safetyInprocess) && isSafe();
- }
-
- final bool isTrusted()
- {
- if (safetyInprocess)
- setUnsafe();
- return type.toTypeFunction().trust == TRUST.trusted;
- }
-
- /**************************************
- * The function is doing something unsafe, so mark it as unsafe.
- *
- * Params:
- * gag = surpress error message (used in escape.d)
- * loc = location of error
- * fmt = 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) final bool setUnsafe(
- bool gag = false, Loc loc = Loc.init, const(char)* fmt = null,
- RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
- {
- if (safetyInprocess)
- {
- safetyInprocess = false;
- type.toTypeFunction().trust = TRUST.system;
- if (fmt || arg0)
- safetyViolation = new AttributeViolation(loc, fmt, arg0, arg1, arg2);
-
- if (fes)
- fes.func.setUnsafe();
- }
- else if (isSafe())
- {
- if (!gag && fmt)
- .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
-
- return true;
- }
- return false;
- }
-
- /**************************************
- * The function is calling `@system` function `f`, so mark it as unsafe.
- *
- * Params:
- * f = function being called (needed for diagnostic of inferred functions)
- * Returns: whether there's a safe error
- */
- extern (D) final bool setUnsafeCall(FuncDeclaration f)
- {
- return setUnsafe(false, f.loc, null, f, null);
- }
/**************************************
* The function is doing something that may throw an exception, register that in case nothrow is being inferred
}
}
-/***************************************************
- * Visit each overloaded function/template in turn, and call dg(s) on it.
- * Exit when no more, or dg(s) returns nonzero.
- *
- * Params:
- * fstart = symbol to start from
- * dg = the delegate to be called on the overload
- * sc = context used to check if symbol is accessible (and therefore visible),
- * can be null
- *
- * Returns:
- * ==0 continue
- * !=0 done (and the return value from the last dg() call)
- */
-extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc = null)
-{
- Dsymbols visited;
-
- int overloadApplyRecurse(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc)
- {
- // Detect cyclic calls.
- if (visited.contains(fstart))
- return 0;
- visited.push(fstart);
-
- Dsymbol next;
- for (auto d = fstart; d; d = next)
- {
- import dmd.access : checkSymbolAccess;
- if (auto od = d.isOverDeclaration())
- {
- /* The scope is needed here to check whether a function in
- an overload set was added by means of a private alias (or a
- selective import). If the scope where the alias is created
- is imported somewhere, the overload set is visible, but the private
- alias is not.
- */
- if (sc)
- {
- if (checkSymbolAccess(sc, od))
- {
- if (int r = overloadApplyRecurse(od.aliassym, dg, sc))
- return r;
- }
- }
- else if (int r = overloadApplyRecurse(od.aliassym, dg, sc))
- return r;
- next = od.overnext;
- }
- else if (auto fa = d.isFuncAliasDeclaration())
- {
- if (fa.hasOverloads)
- {
- if (int r = overloadApplyRecurse(fa.funcalias, dg, sc))
- return r;
- }
- else if (auto fd = fa.toAliasFunc())
- {
- if (int r = dg(fd))
- return r;
- }
- else
- {
- .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars);
- break;
- }
- next = fa.overnext;
- }
- else if (auto ad = d.isAliasDeclaration())
- {
- if (sc)
- {
- if (checkSymbolAccess(sc, ad))
- next = ad.toAlias();
- }
- else
- next = ad.toAlias();
- if (next == ad)
- break;
- if (next == fstart)
- break;
- }
- else if (auto td = d.isTemplateDeclaration())
- {
- if (int r = dg(td))
- return r;
- next = td.overnext;
- }
- else if (auto fd = d.isFuncDeclaration())
- {
- if (int r = dg(fd))
- return r;
- next = fd.overnext;
- }
- else if (auto os = d.isOverloadSet())
- {
- foreach (ds; os.a)
- if (int r = dg(ds))
- return r;
- }
- else
- {
- .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars);
- break;
- // BUG: should print error message?
- }
- }
- return 0;
- }
- return overloadApplyRecurse(fstart, dg, sc);
-}
-
/**
Checks for mismatching modifiers between `lhsMod` and `rhsMod` and prints the
mismatching modifiers to `buf`.
/// ditto
RootObject arg2 = null;
}
-
-/// 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
-void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc)
-{
- auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental;
-
- AttributeViolation* s;
- const(char)* attr;
- if (stc & STC.safe)
- {
- s = fd.safetyViolation;
- attr = "@safe";
- }
- else if (stc & STC.pure_)
- {
- s = fd.pureViolation;
- attr = "pure";
- }
- else if (stc & STC.nothrow_)
- {
- s = fd.nothrowViolation;
- attr = "nothrow";
- }
- else if (stc & STC.nogc)
- {
- s = fd.nogcViolation;
- attr = "@nogc";
- }
-
- if (!s)
- return;
-
- if (s.fmtStr)
- {
- errorFunc(s.loc, deprecation ?
- "which wouldn't be `%s` because of:" :
- "which wasn't inferred `%s` because of:", attr);
- 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() : "");
- }
- else
- {
- errorFunc(s.loc, s.fmtStr,
- s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : "");
- }
- }
- else if (auto sa = s.arg0.isDsymbol())
- {
- if (FuncDeclaration fd2 = sa.isFuncDeclaration())
- {
- if (maxDepth > 0)
- {
- errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars());
- errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc);
- }
- }
- }
-}
import dmd.root.filename;
import dmd.root.string;
import dmd.root.stringtable;
+import dmd.safe;
import dmd.semantic2;
import dmd.semantic3;
import dmd.statement;
funcdecl.visibility = sc.visibility;
funcdecl.userAttribDecl = sc.userAttribDecl;
- UserAttributeDeclaration.checkGNUABITag(funcdecl, funcdecl._linkage);
+ checkGNUABITag(funcdecl, funcdecl._linkage);
checkMustUseReserved(funcdecl);
if (!funcdecl.originalType)
if (!fd.originalType) // semantic not yet run
{
TemplateInstance spec = fd.isSpeculative();
- uint olderrs = global.errors;
- uint oldgag = global.gag;
+ const olderrs = global.errors;
+ const oldgag = global.gag;
if (global.gag && !spec)
global.gag = 0;
dsymbolSemantic(fd, fd._scope);
* we need to temporarily ungag errors.
*/
TemplateInstance spec = fd.isSpeculative();
- uint olderrs = global.errors;
- uint oldgag = global.gag;
+ const olderrs = global.errors;
+ const oldgag = global.gag;
if (global.gag && !spec)
global.gag = 0;
semantic3(fd, fd._scope);
return null;
}
+ import dmd.expressionsem : checkDisabled;
// remove when deprecation period of class allocators and deallocators is over
if (fd.isNewDeclaration() && fd.checkDisabled(loc, sc))
return null;
}
/*********************************************
- * In the current function, we are calling 'this' function.
- * 1. Check to see if the current function can call 'this' function, issue error if not.
- * 2. If the current function is not the parent of 'this' function, then add
- * the current function to the list of siblings of 'this' function.
+ * In the current function 'sc.func', we are calling 'fd'.
+ * 1. Check to see if the current function can call 'fd' , issue error if not.
+ * 2. If the current function is not the parent of 'fd' , then add
+ * the current function to the list of siblings of 'fd' .
* 3. If the current function is a literal, and it's accessing an uplevel scope,
* then mark it as a delegate.
* Returns true if error occurs.
*/
-bool checkNestedReference(FuncDeclaration fd, Scope* sc, const ref Loc loc)
+bool checkNestedFuncReference(FuncDeclaration fd, Scope* sc, const ref Loc loc)
{
- //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
+ //printf("FuncDeclaration::checkNestedFuncReference() %s\n", toPrettyChars());
if (auto fld = fd.isFuncLiteralDeclaration())
{
if (fld.tok == TOK.reserved)
* Returns: `true` if the provided scope is the root
* of the traits compiles list of scopes.
*/
-bool isRootTraitsCompilesScope(Scope* sc)
+bool isRootTraitsCompilesScope(Scope* sc) @safe
{
return (sc.traitsCompiles) && !sc.func.skipCodegen;
}
-/**************************************
- * A statement / expression in this scope is not `@safe`,
- * so mark the enclosing function as `@system`
- *
- * Params:
- * 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
- * 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
- */
-bool setUnsafe(Scope* sc,
- bool gag = false, Loc loc = Loc.init, const(char)* fmt = null,
- RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
-{
- if (sc.intypeof)
- return false; // typeof(cast(int*)0) is safe
-
- if (sc.debug_) // debug {} scopes are permissive
- return false;
-
- if (!sc.func)
- {
- if (sc.varDecl)
- {
- if (sc.varDecl.storage_class & STC.safe)
- {
- .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
- return true;
- }
- else if (!(sc.varDecl.storage_class & STC.trusted))
- {
- sc.varDecl.storage_class |= STC.system;
- sc.varDecl.systemInferred = true;
- }
- }
- return false;
- }
-
-
- if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x)
- {
- if (sc.func.isSafeBypassingInference())
- {
- // 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() : "");
- return true;
- }
- return false;
- }
-
- return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2);
-}
-
-/***************************************
- * Like `setUnsafe`, but for safety errors still behind preview switches
- *
- * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables,
- * the behavior changes based on the setting:
- *
- * - In case of `-revert=fs`, it does nothing.
- * - In case of `-preview=fs`, it's the same as `setUnsafe`
- * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions.
- *
- * Params:
- * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope
- * fs = feature state from the preview flag
- * gag = surpress error message
- * loc = location of error
- * msg = 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,
- RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
-{
- //printf("setUnsafePreview() fs:%d %s\n", fs, msg);
- with (FeatureState) final switch (fs)
- {
- case disabled:
- return false;
-
- case enabled:
- return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2);
-
- case default_:
- if (!sc.func)
- return false;
- if (sc.func.isSafeBypassingInference())
- {
- if (!gag && !sc.isDeprecated())
- {
- deprecation(loc, msg, 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);
- }
- return false;
- }
-}
-
/+
+ Checks the parameter and return types iff this is a `main` function.
+
return true;
}
}
+
+/**
+ * Check signature of `pragma(printf)` function, print error if invalid.
+ *
+ * printf/scanf-like functions must be of the form:
+ * extern (C/C++) T printf([parameters...], const(char)* format, ...);
+ * or:
+ * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list);
+ *
+ * Params:
+ * funcdecl = function to check
+ * f = function type
+ * sc = scope
+ */
+private void checkPrintfScanfSignature(FuncDeclaration funcdecl, TypeFunction f, Scope* sc)
+{
+ static bool isPointerToChar(Parameter p)
+ {
+ if (auto tptr = p.type.isTypePointer())
+ {
+ return tptr.next.ty == Tchar;
+ }
+ return false;
+ }
+
+ bool isVa_list(Parameter p)
+ {
+ return p.type.equals(target.va_listType(funcdecl.loc, sc));
+ }
+
+ const nparams = f.parameterList.length;
+ const p = (funcdecl.printf ? Id.printf : Id.scanf).toChars();
+ if (!(f.linkage == LINK.c || f.linkage == LINK.cpp))
+ {
+ .error(funcdecl.loc, "`pragma(%s)` function `%s` must have `extern(C)` or `extern(C++)` linkage,"
+ ~" not `extern(%s)`",
+ p, funcdecl.toChars(), f.linkage.linkageToChars());
+ }
+ if (f.parameterList.varargs == VarArg.variadic)
+ {
+ if (!(nparams >= 1 && isPointerToChar(f.parameterList[nparams - 1])))
+ {
+ .error(funcdecl.loc, "`pragma(%s)` function `%s` must have"
+ ~ " signature `%s %s([parameters...], const(char)*, ...)` not `%s`",
+ p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars(), funcdecl.type.toChars());
+ }
+ }
+ else if (f.parameterList.varargs == VarArg.none)
+ {
+ if(!(nparams >= 2 && isPointerToChar(f.parameterList[nparams - 2]) &&
+ isVa_list(f.parameterList[nparams - 1])))
+ .error(funcdecl.loc, "`pragma(%s)` function `%s` must have"~
+ " signature `%s %s([parameters...], const(char)*, va_list)`",
+ p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars());
+ }
+ else
+ {
+ .error(funcdecl.loc, "`pragma(%s)` function `%s` must have C-style variadic `...` or `va_list` parameter",
+ p, funcdecl.toChars());
+ }
+}
+
+/***************************************************
+ * Visit each overloaded function/template in turn, and call dg(s) on it.
+ * Exit when no more, or dg(s) returns nonzero.
+ *
+ * Params:
+ * fstart = symbol to start from
+ * dg = the delegate to be called on the overload
+ * sc = context used to check if symbol is accessible (and therefore visible),
+ * can be null
+ *
+ * Returns:
+ * ==0 continue
+ * !=0 done (and the return value from the last dg() call)
+ */
+extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc = null)
+{
+ Dsymbols visited;
+
+ int overloadApplyRecurse(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc)
+ {
+ // Detect cyclic calls.
+ if (visited.contains(fstart))
+ return 0;
+ visited.push(fstart);
+
+ Dsymbol next;
+ for (auto d = fstart; d; d = next)
+ {
+ import dmd.access : checkSymbolAccess;
+ if (auto od = d.isOverDeclaration())
+ {
+ /* The scope is needed here to check whether a function in
+ an overload set was added by means of a private alias (or a
+ selective import). If the scope where the alias is created
+ is imported somewhere, the overload set is visible, but the private
+ alias is not.
+ */
+ if (sc)
+ {
+ if (checkSymbolAccess(sc, od))
+ {
+ if (int r = overloadApplyRecurse(od.aliassym, dg, sc))
+ return r;
+ }
+ }
+ else if (int r = overloadApplyRecurse(od.aliassym, dg, sc))
+ return r;
+ next = od.overnext;
+ }
+ else if (auto fa = d.isFuncAliasDeclaration())
+ {
+ if (fa.hasOverloads)
+ {
+ if (int r = overloadApplyRecurse(fa.funcalias, dg, sc))
+ return r;
+ }
+ else if (auto fd = fa.toAliasFunc())
+ {
+ if (int r = dg(fd))
+ return r;
+ }
+ else
+ {
+ .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars);
+ break;
+ }
+ next = fa.overnext;
+ }
+ else if (auto ad = d.isAliasDeclaration())
+ {
+ if (sc)
+ {
+ if (checkSymbolAccess(sc, ad))
+ next = ad.toAlias();
+ }
+ else
+ next = ad.toAlias();
+ if (next == ad)
+ break;
+ if (next == fstart)
+ break;
+ }
+ else if (auto td = d.isTemplateDeclaration())
+ {
+ if (int r = dg(td))
+ return r;
+ next = td.overnext;
+ }
+ else if (auto fd = d.isFuncDeclaration())
+ {
+ if (int r = dg(fd))
+ return r;
+ next = fd.overnext;
+ }
+ else if (auto os = d.isOverloadSet())
+ {
+ foreach (ds; os.a)
+ if (int r = dg(ds))
+ return r;
+ }
+ else
+ {
+ .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars);
+ break;
+ // BUG: should print error message?
+ }
+ }
+ return 0;
+ }
+ return overloadApplyRecurse(fstart, dg, sc);
+}
uint errorLimit = 20;
uint errorSupplementLimit = 6; // Limit the number of supplemental messages for each error (0 means unlimited)
- uint errorSupplementCount()
+ uint errorSupplementCount() @safe
{
if (verbose)
return uint.max;
bool useInline = false; // inline expand functions
bool release; // build release version
bool preservePaths; // true means don't strip path from source file
- DiagnosticReporting warnings = DiagnosticReporting.off; // how compiler warnings are handled
+ DiagnosticReporting useWarnings = DiagnosticReporting.off; // how compiler warnings are handled
bool cov; // generate code coverage data
ubyte covPercent; // 0..100 code coverage percentage required
bool ctfe_cov = false; // generate coverage data for ctfe
const(char)* timeTraceFile; /// File path of output file
///
- bool parsingUnittestsRequired()
+ bool parsingUnittestsRequired() @safe
{
return useUnitTests || ddoc.doOutput || dihdr.doOutput;
}
{
compileEnv.vendor = "GNU D";
}
- compileEnv.versionNumber = parseVersionNumber(_version);
+ else version (IN_LLVM)
+ {
+ compileEnv.vendor = "LDC";
+
+ import dmd.console : detectTerminal;
+ params.v.color = detectTerminal();
+ }
+
+ compileEnv.versionNumber = parseVersionNumber(versionString());
/* Initialize date, time, and timestamp
*/
enum class MessageStyle : unsigned char
{
digitalmars, // file(line,column): message
- gnu // file:line:column: message
+ gnu, // file:line:column: message
+ sarif // JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
};
// The state of array bounds checking
d_bool useInline; // inline expand functions
d_bool release; // build release version
d_bool preservePaths; // true means don't strip path from source file
- Diagnostic warnings;
+ Diagnostic useWarnings;
d_bool cov; // generate code coverage data
unsigned char covPercent; // 0..100 code coverage percentage required
d_bool ctfe_cov; // generate coverage data for ctfe
DString vendor;
DString timestamp;
d_bool previewIn;
+ d_bool transitionIn;
d_bool ddocOutput;
d_bool masm;
IdentifierCharLookup cCharLookupTable;
//printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars());
if (stcToBuffer(buf, f.storage_class))
buf.writeByte(' ');
+ typeToBuffer(f.type, f.ident, buf, hgs);
auto tf = f.type.isTypeFunction();
- typeToBuffer(tf, f.ident, buf, hgs);
- if (hgs.hdrgen)
+ if (hgs.hdrgen && tf)
{
// if the return type is missing (e.g. ref functions or auto)
// https://issues.dlang.org/show_bug.cgi?id=20090
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
buf.writestring("invariant");
- if(auto es = d.fbody.isExpStatement())
+ auto es = d.fbody.isExpStatement();
+ if (es && es.exp && es.exp.op == EXP.assert_)
{
- assert(es.exp && es.exp.op == EXP.assert_);
buf.writestring(" (");
(cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
buf.writestring(");");
}
else if (Expression e = isExpression(oarg))
{
- if (e.op == EXP.int64 || e.op == EXP.float64 || e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.this_)
+ if (!(e.type && e.type.isTypeEnum()) && e.op == EXP.int64 || e.op == EXP.float64 ||
+ e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.this_)
{
toCBuffer(e, buf, hgs);
return;
return null;
// Assume assembler code takes care of setting the return value
- sc.func.hasReturnExp |= 8;
+ sc.func.hasInlineAsm = true;
version (NoBackend)
{
import dmd.statement;
import dmd.statementsem;
+/***********************************
+ * Parse and run semantic analysis on a GccAsmStatement.
+ * Params:
+ * s = gcc asm statement being parsed
+ * sc = the scope where the asm statement is located
+ * Returns:
+ * the completed gcc asm statement, or null if errors occurred
+ */
+public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
+{
+ //printf("GccAsmStatement.semantic()\n");
+ const bool doUnittests = global.params.parsingUnittestsRequired();
+ scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests);
+
+ // Make a safe copy of the token list before parsing.
+ Token *toklist = null;
+ Token **ptoklist = &toklist;
+
+ for (Token *token = s.tokens; token; token = token.next)
+ {
+ *ptoklist = p.allocateToken();
+ memcpy(*ptoklist, token, Token.sizeof);
+ ptoklist = &(*ptoklist).next;
+ *ptoklist = null;
+ }
+ p.token = *toklist;
+ p.scanloc = s.loc;
+
+ // Parse the gcc asm statement.
+ const errors = global.errors;
+ s = p.parseGccAsm(s);
+ if (errors != global.errors)
+ return null;
+ s.stc = sc.stc;
+
+ // Fold the instruction template string.
+ s.insn = semanticString(sc, s.insn, "asm instruction template");
+
+ if (s.labels && s.outputargs)
+ error(s.loc, "extended asm statements with labels cannot have output constraints");
+
+ // Analyse all input and output operands.
+ if (s.args)
+ {
+ foreach (i; 0 .. s.args.length)
+ {
+ Expression e = (*s.args)[i];
+ e = e.expressionSemantic(sc);
+ // Check argument is a valid lvalue/rvalue.
+ if (i < s.outputargs)
+ e = e.modifiableLvalue(sc);
+ else if (e.checkValue())
+ e = ErrorExp.get();
+ (*s.args)[i] = e;
+
+ e = (*s.constraints)[i];
+ e = e.expressionSemantic(sc);
+ assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1);
+ (*s.constraints)[i] = e;
+ }
+ }
+
+ // Analyse all clobbers.
+ if (s.clobbers)
+ {
+ foreach (i; 0 .. s.clobbers.length)
+ {
+ Expression e = (*s.clobbers)[i];
+ e = e.expressionSemantic(sc);
+ assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1);
+ (*s.clobbers)[i] = e;
+ }
+ }
+
+ // Analyse all goto labels.
+ if (s.labels)
+ {
+ foreach (i; 0 .. s.labels.length)
+ {
+ Identifier ident = (*s.labels)[i];
+ GotoStatement gs = new GotoStatement(s.loc, ident);
+ if (!s.gotos)
+ s.gotos = new GotoStatements();
+ s.gotos.push(gs);
+ gs.statementSemantic(sc);
+ }
+ }
+
+ return s;
+}
+
+/***********************************
+ * Run semantic analysis on an CAsmDeclaration.
+ * Params:
+ * ad = asm declaration
+ * sc = the scope where the asm declaration is located
+ */
+public void gccAsmSemantic(CAsmDeclaration ad, Scope *sc)
+{
+ import dmd.typesem : pointerTo;
+ ad.code = semanticString(sc, ad.code, "asm definition");
+ ad.code.type = ad.code.type.nextOf().pointerTo();
+
+ // Asm definition always needs emitting into the root module.
+ import dmd.dmodule : Module;
+ if (sc._module && sc._module.isRoot())
+ return;
+ if (Module m = Module.rootModule)
+ m.members.push(ad);
+}
+
private:
/***********************************
return s;
}
-/***********************************
- * Parse and run semantic analysis on a GccAsmStatement.
- * Params:
- * s = gcc asm statement being parsed
- * sc = the scope where the asm statement is located
- * Returns:
- * the completed gcc asm statement, or null if errors occurred
- */
-public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
-{
- //printf("GccAsmStatement.semantic()\n");
- const bool doUnittests = global.params.parsingUnittestsRequired();
- scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests);
-
- // Make a safe copy of the token list before parsing.
- Token *toklist = null;
- Token **ptoklist = &toklist;
-
- for (Token *token = s.tokens; token; token = token.next)
- {
- *ptoklist = p.allocateToken();
- memcpy(*ptoklist, token, Token.sizeof);
- ptoklist = &(*ptoklist).next;
- *ptoklist = null;
- }
- p.token = *toklist;
- p.scanloc = s.loc;
-
- // Parse the gcc asm statement.
- const errors = global.errors;
- s = p.parseGccAsm(s);
- if (errors != global.errors)
- return null;
- s.stc = sc.stc;
-
- // Fold the instruction template string.
- s.insn = semanticString(sc, s.insn, "asm instruction template");
-
- if (s.labels && s.outputargs)
- error(s.loc, "extended asm statements with labels cannot have output constraints");
-
- // Analyse all input and output operands.
- if (s.args)
- {
- foreach (i; 0 .. s.args.length)
- {
- Expression e = (*s.args)[i];
- e = e.expressionSemantic(sc);
- // Check argument is a valid lvalue/rvalue.
- if (i < s.outputargs)
- e = e.modifiableLvalue(sc);
- else if (e.checkValue())
- e = ErrorExp.get();
- (*s.args)[i] = e;
-
- e = (*s.constraints)[i];
- e = e.expressionSemantic(sc);
- assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1);
- (*s.constraints)[i] = e;
- }
- }
-
- // Analyse all clobbers.
- if (s.clobbers)
- {
- foreach (i; 0 .. s.clobbers.length)
- {
- Expression e = (*s.clobbers)[i];
- e = e.expressionSemantic(sc);
- assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1);
- (*s.clobbers)[i] = e;
- }
- }
-
- // Analyse all goto labels.
- if (s.labels)
- {
- foreach (i; 0 .. s.labels.length)
- {
- Identifier ident = (*s.labels)[i];
- GotoStatement gs = new GotoStatement(s.loc, ident);
- if (!s.gotos)
- s.gotos = new GotoStatements();
- s.gotos.push(gs);
- gs.statementSemantic(sc);
- }
- }
-
- return s;
-}
-
-/***********************************
- * Run semantic analysis on an CAsmDeclaration.
- * Params:
- * ad = asm declaration
- * sc = the scope where the asm declaration is located
- */
-public void gccAsmSemantic(CAsmDeclaration ad, Scope *sc)
-{
- import dmd.typesem : pointerTo;
- ad.code = semanticString(sc, ad.code, "asm definition");
- ad.code.type = ad.code.type.nextOf().pointerTo();
-
- // Asm definition always needs emitting into the root module.
- import dmd.dmodule : Module;
- if (sc._module && sc._module.isRoot())
- return;
- if (Module m = Module.rootModule)
- m.members.push(ad);
-}
-
unittest
{
import dmd.mtype : TypeBasic;
if (!global.errorSink)
global.errorSink = new ErrorSinkCompiler;
- uint errors = global.startGagging();
+ const errors = global.startGagging();
scope(exit) global.endGagging(errors);
// If this check fails, then Type._init() was called before reaching here,
import dmd.mtype;
import dmd.opover;
import dmd.optimize;
+import dmd.safe : setUnsafe;
import dmd.statement;
import dmd.target;
import dmd.tokens;
import dmd.dimport;
import dmd.dmodule;
import dmd.dsymbol;
+import dmd.dsymbolsem : include;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
import dmd.dtemplate;
import dmd.expression;
import dmd.func;
-import dmd.dmangle;
import dmd.hdrgen;
+import dmd.mangle;
import dmd.mtype;
import dmd.common.outbuffer;
import dmd.root.rmem;
const(char)[] timestamp; /// __TIMESTAMP__
bool previewIn; /// `in` means `[ref] scope const`, accepts rvalues
+ bool transitionIn; /// `-transition=in` is active, `in` parameters are listed
bool ddocOutput; /// collect embedded documentation comments
bool masm; /// use MASM inline asm syntax
import dmd.common.outbuffer;
import dmd.root.array;
import dmd.root.filename;
+import dmd.root.string: toDString;
version (DMDLIB)
{
{
digitalmars, /// filename.d(line): message
gnu, /// filename.d:line: message, see https://www.gnu.org/prep/standards/html_node/Errors.html
+ sarif /// JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
}
-
/**
A source code location
MessageStyle messageStyle = Loc.messageStyle) const nothrow
{
OutBuffer buf;
- if (fileIndex)
- {
- buf.writestring(filename);
- }
- if (linnum)
- {
- final switch (messageStyle)
- {
- case MessageStyle.digitalmars:
- buf.writeByte('(');
- buf.print(linnum);
- if (showColumns && charnum)
- {
- buf.writeByte(',');
- buf.print(charnum);
- }
- buf.writeByte(')');
- break;
- case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html
- buf.writeByte(':');
- buf.print(linnum);
- if (showColumns && charnum)
- {
- buf.writeByte(':');
- buf.print(charnum);
- }
- break;
- }
- }
+ writeSourceLoc(buf, SourceLoc(this), showColumns, messageStyle);
return buf.extractChars();
}
return fileIndex != 0;
}
}
+
+/**
+ * Format a source location for error messages
+ *
+ * Params:
+ * buf = buffer to write string into
+ * loc = source location to write
+ * showColumns = include column number in message
+ * messageStyle = select error message format
+ */
+void writeSourceLoc(ref OutBuffer buf,
+ SourceLoc loc,
+ bool showColumns,
+ MessageStyle messageStyle) nothrow
+{
+ buf.writestring(loc.filename);
+ if (loc.line == 0)
+ return;
+
+ final switch (messageStyle)
+ {
+ case MessageStyle.digitalmars:
+ buf.writeByte('(');
+ buf.print(loc.line);
+ if (showColumns && loc.column)
+ {
+ buf.writeByte(',');
+ buf.print(loc.column);
+ }
+ buf.writeByte(')');
+ break;
+ case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html
+ buf.writeByte(':');
+ buf.print(loc.line);
+ if (showColumns && loc.column)
+ {
+ buf.writeByte(':');
+ buf.print(loc.column);
+ }
+ break;
+ case MessageStyle.sarif: // https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
+ // No formatting needed here for SARIF
+ break;
+ }
+}
+
+/**
+ * Describes a location in the source code as a file + line number + column number
+ *
+ * While `Loc` is a compact opaque location meant to be stored in the AST,
+ * this struct has simple modifiable fields and is used for printing.
+ */
+struct SourceLoc
+{
+ const(char)[] filename; /// name of source file
+ uint line; /// line number (starts at 1)
+ uint column; /// column number (starts at 1)
+
+ // aliases for backwards compatibility
+ alias linnum = line;
+ alias charnum = column;
+
+ this(const(char)[] filename, uint line, uint column) nothrow
+ {
+ this.filename = filename;
+ this.line = line;
+ this.column = column;
+ }
+
+ this(Loc loc) nothrow
+ {
+ this.filename = loc.filename.toDString();
+ this.line = loc.linnum;
+ this.column = loc.charnum;
+ }
+}
* Documentation: https://dlang.org/phobos/dmd_basicmangle.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/basicmangle.d
*/
-module dmd.basicmangle;
+module dmd.mangle.basic;
import dmd.astenums;
import dmd.common.outbuffer : OutBuffer;
* enter `C++, mangling` as the keywords.
*/
-module dmd.cppmangle;
+module dmd.mangle.cpp;
import core.stdc.stdio;
import dmd.attrib;
import dmd.declaration;
import dmd.dsymbol;
+import dmd.dsymbolsem : isGNUABITag;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
foreach (exp; *s.userAttribDecl.atts)
{
- if (UserAttributeDeclaration.isGNUABITag(exp))
+ if (isGNUABITag(exp))
return (*exp.isStructLiteralExp().elements)[0]
.isArrayLiteralExp();
}
* References: https://dlang.org/blog/2017/12/20/ds-newfangled-name-mangling/
*/
-module dmd.dmangle;
+module dmd.mangle;
/******************************************************************************
}
/// Returns: `true` if the given character is a valid mangled character
-package bool isValidMangling(dchar c) nothrow
+package(dmd) bool isValidMangling(dchar c) nothrow
{
import dmd.common.charactertables;
import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
-import dmd.basicmangle;
+import dmd.mangle.basic;
import dmd.dclass;
import dmd.declaration;
import dmd.dinterpret;
continue;
}
// Now that we know it is not an alias, we MUST obtain a value
- uint olderr = global.errors;
+ const olderr = global.errors;
ea = ea.ctfeInterpret();
if (ea.op == EXP.error || olderr != global.errors)
continue;
import dmd.enumsem;
import dmd.errors;
import dmd.expression;
+import dmd.dsymbolsem : determineSize;
import dmd.globals;
import dmd.hdrgen;
import dmd.id;
static Type merge(Type t)
{
- import dmd.basicmangle : tyToDecoBuffer;
+ import dmd.mangle.basic : tyToDecoBuffer;
OutBuffer buf;
buf.reserve(3);
Type t = this;
if (t.isImmutable())
{
+ return t;
}
else if (stc & STC.immutable_)
{
t = t.makeImmutable();
+ return t;
}
- else
+
+ if ((stc & STC.shared_) && !t.isShared())
{
- if ((stc & STC.shared_) && !t.isShared())
+ if (t.isWild())
+ {
+ if (t.isConst())
+ t = t.makeSharedWildConst();
+ else
+ t = t.makeSharedWild();
+ }
+ else
+ {
+ if (t.isConst())
+ t = t.makeSharedConst();
+ else
+ t = t.makeShared();
+ }
+ }
+ if ((stc & STC.const_) && !t.isConst())
+ {
+ if (t.isShared())
{
if (t.isWild())
- {
- if (t.isConst())
- t = t.makeSharedWildConst();
- else
- t = t.makeSharedWild();
- }
+ t = t.makeSharedWildConst();
else
- {
- if (t.isConst())
- t = t.makeSharedConst();
- else
- t = t.makeShared();
- }
+ t = t.makeSharedConst();
}
- if ((stc & STC.const_) && !t.isConst())
+ else
{
- if (t.isShared())
- {
- if (t.isWild())
- t = t.makeSharedWildConst();
- else
- t = t.makeSharedConst();
- }
+ if (t.isWild())
+ t = t.makeWildConst();
else
- {
- if (t.isWild())
- t = t.makeWildConst();
- else
- t = t.makeConst();
- }
+ t = t.makeConst();
}
- if ((stc & STC.wild) && !t.isWild())
+ }
+ if ((stc & STC.wild) && !t.isWild())
+ {
+ if (t.isShared())
{
- if (t.isShared())
- {
- if (t.isConst())
- t = t.makeSharedWildConst();
- else
- t = t.makeSharedWild();
- }
+ if (t.isConst())
+ t = t.makeSharedWildConst();
else
- {
- if (t.isConst())
- t = t.makeWildConst();
- else
- t = t.makeWild();
- }
+ t = t.makeSharedWild();
+ }
+ else
+ {
+ if (t.isConst())
+ t = t.makeWildConst();
+ else
+ t = t.makeWild();
}
}
+
return t;
}
return t;
}
- /*******************************************
- * Compute number of elements for a (possibly multidimensional) static array,
- * or 1 for other types.
- * Params:
- * loc = for error message
- * Returns:
- * number of elements, uint.max on overflow
- */
- final uint numberOfElems(const ref Loc loc)
- {
- //printf("Type::numberOfElems()\n");
- uinteger_t n = 1;
- Type tb = this;
- while ((tb = tb.toBasetype()).ty == Tsarray)
- {
- bool overflow = false;
- n = mulu(n, (cast(TypeSArray)tb).dim.toUInteger(), overflow);
- if (overflow || n >= uint.max)
- {
- error(loc, "static array `%s` size overflowed to %llu", toChars(), cast(ulong)n);
- return uint.max;
- }
- tb = (cast(TypeSArray)tb).next;
- }
- return cast(uint)n;
- }
-
/****************************************
* Return the mask that an integral type will
* fit into.
return newArgs;
}
- extern (D) bool checkRetType(const ref Loc loc)
- {
- Type tb = next.toBasetype();
- if (tb.ty == Tfunction)
- {
- error(loc, "functions cannot return a function");
- next = Type.terror;
- }
- if (tb.ty == Ttuple)
- {
- error(loc, "functions cannot return a sequence (use `std.typecons.Tuple`)");
- next = Type.terror;
- }
- if (!isRef && (tb.ty == Tstruct || tb.ty == Tsarray))
- {
- if (auto ts = tb.baseElemOf().isTypeStruct())
- {
- if (!ts.sym.members)
- {
- error(loc, "functions cannot return opaque type `%s` by value", tb.toChars());
- next = Type.terror;
- }
- }
- }
- if (tb.ty == Terror)
- return true;
- return false;
- }
-
-
/// Returns: `true` the function is `isInOutQual` or `isInOutParam` ,`false` otherwise.
bool iswild() const pure nothrow @safe @nogc
{
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
-import dmd.dmangle;
import dmd.dmodule;
import dmd.dscope;
import dmd.dstruct;
import dmd.id;
import dmd.identifier;
import dmd.location;
+import dmd.mangle;
import dmd.mtype;
import dmd.root.array;
import dmd.common.outbuffer;
{
//printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars());
//static int xx; if (xx++ == 10) assert(0);
+ auto ve = e1.isVarExp();
+ if (!ve)
+ return e1;
+
Expression e = e1;
- if (auto ve = e1.isVarExp())
+ VarDeclaration v = ve.var.isVarDeclaration();
+ e = expandVar(result, v);
+ if (!e)
+ return e1;
+
+ // If it is a comma expression involving a declaration, we mustn't
+ // perform a copy -- we'd get two declarations of the same variable.
+ // See https://issues.dlang.org/show_bug.cgi?id=4465.
+ if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp())
+ e = e1;
+ else if (e.type != e1.type && e1.type && e1.type.ty != Tident)
{
- VarDeclaration v = ve.var.isVarDeclaration();
- e = expandVar(result, v);
- if (e)
- {
- // If it is a comma expression involving a declaration, we mustn't
- // perform a copy -- we'd get two declarations of the same variable.
- // See https://issues.dlang.org/show_bug.cgi?id=4465.
- if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp())
- e = e1;
- else if (e.type != e1.type && e1.type && e1.type.ty != Tident)
- {
- // Type 'paint' operation
- e = e.copy();
- e.type = e1.type;
- }
- e.loc = e1.loc;
- }
- else
- {
- e = e1;
- }
+ // Type 'paint' operation
+ e = e.copy();
+ e.type = e1.type;
}
+ e.loc = e1.loc;
+
return e;
}
bool doUnittests; // parse unittest blocks
}
- bool transitionIn = false; /// `-transition=in` is active, `in` parameters are listed
-
/*********************
* Use this constructor for string mixins.
* Input:
break;
case TOK.new_:
- s = parseNew(pAttrs);
+ s = parseNewDeclaration(pAttrs);
break;
case TOK.colon:
AST.Expression constraint = tpl ? parseConstraint() : null;
- AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto
+ AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // ReturnType -> auto
tf = tf.addSTC(stc);
auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf);
* @disable new();
* Current token is 'new'.
*/
- private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs)
+ private AST.Dsymbol parseNewDeclaration(PrefixAttributes!AST* pAttrs)
{
const loc = token.loc;
StorageClass stc = getStorageClass!AST(pAttrs);
// Don't call nextToken again.
}
case TOK.in_:
- if (transitionIn)
+ if (compileEnv.transitionIn)
eSink.message(scanloc, "Usage of 'in' on parameter");
stc = STC.in_;
if (compileEnv.previewIn)
name = _alias;
_alias = null;
}
- s.addAlias(name, _alias);
+ if (s.isstatic)
+ error(loc, "static import `%s` cannot have an import bind list", s.toPrettyChars());
+ if (!s.aliasId)
+ s.ident = null; // make it an anonymous import
+ s.names.push(name);
+ s.aliases.push(_alias);
}
while (token.value == TOK.comma);
break; // no comma-separated imports of this form
}
/*******************************************
+ * Params:
+ * thisexp = If not null, it is the `this` reference for the creation
+ * of an inner class.
+ * https://dlang.org/spec/class.html#nested-explicit
+ * https://dlang.org/spec/expression.html#postfix_expressions
+ * If null, then it is a NewExpression.
+ * https://dlang.org/spec/expression.html#NewExpression
+ * Returns:
+ * NewExpression
*/
private AST.Expression parseNewExp(AST.Expression thisexp)
{
EXP.assign : PREC.assign,
EXP.construct : PREC.assign,
EXP.blit : PREC.assign,
+ EXP.loweredAssignExp : PREC.assign,
EXP.addAssign : PREC.assign,
EXP.minAssign : PREC.assign,
EXP.concatenateAssign : PREC.assign,
import dmd.dinterpret;
import dmd.dscope;
import dmd.dsymbol;
+import dmd.dsymbolsem : include;
import dmd.errors;
import dmd.expression;
import dmd.expressionsem;
{
import dmd.aggregate;
import dmd.common.outbuffer;
- import dmd.dmangle;
import dmd.dmodule;
import dmd.dsymbolsem;
import dmd.identifier;
+ import dmd.mangle : isValidMangling;
import dmd.root.rmem;
import dmd.root.utf;
import dmd.target;
private bool pragmaMsgSemantic(Loc loc, Scope* sc, Expressions* args)
{
import dmd.tokens;
+ import dmd.common.outbuffer;
if (!args)
return true;
- foreach (arg; *args)
- {
- sc = sc.startCTFE();
- auto e = arg.expressionSemantic(sc);
- e = resolveProperties(sc, e);
- sc = sc.endCTFE();
- // pragma(msg) is allowed to contain types as well as expressions
- e = ctfeInterpretForPragmaMsg(e);
- if (e.op == EXP.error)
- {
- errorSupplemental(loc, "while evaluating `pragma(msg, %s)`", arg.toChars());
- return false;
- }
- if (auto se = e.toStringExp())
- {
- const slice = se.toUTF8(sc).peekString();
- fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr);
- }
- else
- fprintf(stderr, "%s", e.toChars());
+ OutBuffer buf;
+ if (expressionsToString(buf, sc, args, loc, "while evaluating `pragma(msg, %s)`", false))
+ return false;
+ else
+ {
+ buf.writestring("\n");
+ fprintf(stderr, buf.extractChars);
}
- fprintf(stderr, "\n");
return true;
}
* Returns:
* the slice
*/
- extern (D) static const(char)[] sansExt(const char[] filename)
+ extern (D) static const(char)[] sansExt(const char[] filename) @safe
{
auto e = ext(filename);
size_t length = e.length;
return str.ptr;
}
- const(char)[] toString() const pure nothrow @nogc @trusted
+ const(char)[] toString() const pure nothrow @nogc @safe
{
return str;
}
return s ? s[0 .. strlen(s)] : null;
}
+private struct FTuple(T...)
+{
+ T expand;
+}
+
+/// Returns: a (length, ptr) tuple for passing a D string to `printf`-style functions with the format string `%.*s`
+auto fTuple(const(char)[] str)
+{
+ return FTuple!(int, const(char)*)(cast(int) str.length, str.ptr);
+}
+
+///
+unittest
+{
+ import core.stdc.stdio: snprintf;
+ char[6] buf = '.';
+ const(char)[] str = "cutoff"[0..4];
+ snprintf(buf.ptr, buf.length, "%.*s", str.fTuple.expand);
+ assert(buf[] == "cuto\0.");
+}
+
/**
Compare two slices for equality, in a case-insensitive way
a `FindSplit` object that casts to `true` iff `needle` was found inside `str`.
In that case, `split[1]` is the needle, and `split[0]`/`split[2]` are before/after the needle.
*/
-FindSplit findSplit(return scope const(char)[] str, scope const(char)[] needle)
+FindSplit findSplit(return scope const(char)[] str, scope const(char)[] needle) @safe
{
if (needle.length > str.length)
return FindSplit([str, null, null]);
Returns:
substring of `str` inbetween `l` and `r`
*/
-const(char)[] findBetween(const(char)[] str, const(char)[] l, const(char)[] r)
+const(char)[] findBetween(const(char)[] str, const(char)[] l, const(char)[] r) @safe
{
if (auto s0 = str.findSplit(l))
if (auto s1 = s0[2].findSplit(r))
import dmd.dclass;
import dmd.declaration;
import dmd.dscope;
+import dmd.dsymbolsem : determineSize;
+import dmd.errors;
import dmd.expression;
+import dmd.func;
+import dmd.funcsem : isRootTraitsCompilesScope;
+import dmd.globals : FeatureState;
import dmd.id;
import dmd.identifier;
+import dmd.location;
import dmd.mtype;
+import dmd.rootobject;
import dmd.target;
import dmd.tokens;
import dmd.typesem : hasPointers, arrayOf, size;
-import dmd.funcsem : setUnsafe, setUnsafePreview;
/*************************************************************
* Check for unsafe access in @safe code:
}
return false;
}
+
+bool isSafe(FuncDeclaration fd)
+{
+ if (fd.safetyInprocess)
+ fd.setUnsafe();
+ return fd.type.toTypeFunction().trust == TRUST.safe;
+}
+
+extern (D) bool isSafeBypassingInference(FuncDeclaration fd)
+{
+ return !(fd.safetyInprocess) && fd.isSafe();
+}
+
+bool isTrusted(FuncDeclaration fd)
+{
+ if (fd.safetyInprocess)
+ fd.setUnsafe();
+ return fd.type.toTypeFunction().trust == TRUST.trusted;
+}
+
+/**************************************
+ * The function is doing something unsafe, so mark it as 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
+ * 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)
+{
+ 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() : "");
+
+ return true;
+ }
+ return false;
+}
+
+/**************************************
+ * The function is calling `@system` function `f`, so mark it as unsafe.
+ *
+ * Params:
+ * fd = caller
+ * f = function being called (needed for diagnostic of inferred functions)
+ * Returns: whether there's a safe error
+ */
+extern (D) bool setUnsafeCall(FuncDeclaration fd, FuncDeclaration f)
+{
+ return fd.setUnsafe(false, f.loc, null, f, null);
+}
+
+/**************************************
+ * A statement / expression in this scope is not `@safe`,
+ * so mark the enclosing function as `@system`
+ *
+ * Params:
+ * 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
+ * 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
+ */
+bool setUnsafe(Scope* sc,
+ bool gag = false, Loc loc = Loc.init, const(char)* fmt = null,
+ RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
+{
+ if (sc.intypeof)
+ return false; // typeof(cast(int*)0) is safe
+
+ if (sc.debug_) // debug {} scopes are permissive
+ return false;
+
+ if (!sc.func)
+ {
+ if (sc.varDecl)
+ {
+ if (sc.varDecl.storage_class & STC.safe)
+ {
+ .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
+ return true;
+ }
+ else if (!(sc.varDecl.storage_class & STC.trusted))
+ {
+ sc.varDecl.storage_class |= STC.system;
+ sc.varDecl.systemInferred = true;
+ }
+ }
+ return false;
+ }
+
+
+ if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x)
+ {
+ if (sc.func.isSafeBypassingInference())
+ {
+ // 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() : "");
+ return true;
+ }
+ return false;
+ }
+
+ return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2);
+}
+
+/***************************************
+ * Like `setUnsafe`, but for safety errors still behind preview switches
+ *
+ * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables,
+ * the behavior changes based on the setting:
+ *
+ * - In case of `-revert=fs`, it does nothing.
+ * - In case of `-preview=fs`, it's the same as `setUnsafe`
+ * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions.
+ *
+ * Params:
+ * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope
+ * fs = feature state from the preview flag
+ * gag = surpress error message
+ * loc = location of error
+ * msg = 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,
+ RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
+{
+ //printf("setUnsafePreview() fs:%d %s\n", fs, msg);
+ with (FeatureState) final switch (fs)
+ {
+ case disabled:
+ return false;
+
+ case enabled:
+ return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2);
+
+ case default_:
+ if (!sc.func)
+ return false;
+ if (sc.func.isSafeBypassingInference())
+ {
+ if (!gag && !sc.isDeprecated())
+ {
+ deprecation(loc, msg, 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);
+ }
+ return false;
+ }
+}
sc.tinst = tempinst;
sc.minst = tempinst.minst;
- int needGagging = (tempinst.gagged && !global.gag);
- uint olderrors = global.errors;
- int oldGaggedErrors = -1; // dead-store to prevent spurious warning
- if (needGagging)
- oldGaggedErrors = global.startGagging();
+ const needGagging = (tempinst.gagged && !global.gag);
+ const olderrors = global.errors;
+ const oldGaggedErrors = needGagging ? global.startGagging() : -1;
for (size_t i = 0; i < tempinst.members.length; i++)
{
return;
}
- UserAttributeDeclaration.checkGNUABITag(vd, vd._linkage);
+ checkGNUABITag(vd, vd._linkage);
if (vd._init && !vd.toParent().isFuncDeclaration())
{
return;
TypeFunction f = cast(TypeFunction) fd.type;
- UserAttributeDeclaration.checkGNUABITag(fd, fd._linkage);
+ checkGNUABITag(fd, fd._linkage);
//semantic for parameters' UDAs
foreach (i, param; f.parameterList)
{
printf("+Nspace::semantic2('%s')\n", ns.toChars());
scope(exit) printf("-Nspace::semantic2('%s')\n", ns.toChars());
}
- UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp);
+ checkGNUABITag(ns, LINK.cpp);
if (!ns.members)
return;
override void visit(CPPNamespaceDeclaration decl)
{
- UserAttributeDeclaration.checkGNUABITag(decl, LINK.cpp);
+ checkGNUABITag(decl, LINK.cpp);
visit(cast(AttribDeclaration)decl);
}
}
// Handles compiler-recognized `core.attribute.gnuAbiTag`
- if (UserAttributeDeclaration.isGNUABITag(e))
+ if (isGNUABITag(e))
doGNUABITagSemantic(e, lastTag);
}
}
return;
}
- UserAttributeDeclaration.checkGNUABITag(
- ad, ad.classKind == ClassKind.cpp ? LINK.cpp : LINK.d);
+ checkGNUABITag(ad, ad.classKind == ClassKind.cpp ? LINK.cpp : LINK.d);
auto sc2 = ad.newScope(sc);
*/
private void doGNUABITagSemantic(ref Expression e, ref Expression* lastTag)
{
- import dmd.dmangle;
-
+ import dmd.mangle : isValidMangling;
// When `@gnuAbiTag` is used, the type will be the UDA, not the struct literal
if (e.op == EXP.type)
{
sc.minst = tempinst.minst;
bool needGagging = tempinst.gagged && !global.gag;
- uint olderrors = global.errors;
- int oldGaggedErrors = -1; // dead-store to prevent spurious warning
+ const olderrors = global.errors;
+ const oldGaggedErrors = needGagging ? global.startGagging() : -1;
/* If this is a gagged instantiation, gag errors.
* Future optimisation: If the results are actually needed, errors
* would already be gagged, so we don't really need to run semantic
* on the members.
*/
- if (needGagging)
- oldGaggedErrors = global.startGagging();
for (size_t i = 0; i < tempinst.members.length; i++)
{
sc = sc.push(tmix.argsym);
sc = sc.push(tmix);
- uint olderrors = global.errors;
+ const olderrors = global.errors;
for (size_t i = 0; i < tmix.members.length; i++)
{
* For generated opAssign function, any errors
* from its body need to be gagged.
*/
- uint oldErrors = global.startGagging();
+ const oldErrors = global.startGagging();
++funcdecl.inuse;
funcdecl.semantic3(sc);
--funcdecl.inuse;
return;
}
- uint oldErrors = global.errors;
+ const oldErrors = global.errors;
auto fds = FuncDeclSem3(funcdecl,sc);
fds.checkInContractOverrides();
Statement s = new ReturnStatement(funcdecl.loc, null);
s = s.statementSemantic(sc2);
funcdecl.fbody = new CompoundStatement(funcdecl.loc, funcdecl.fbody, s);
- funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1);
+ funcdecl.hasMultipleReturnExp = funcdecl.hasReturnExp;
+ funcdecl.hasReturnExp = true;
}
}
else if (funcdecl.fes)
Expression e = IntegerExp.literal!0;
Statement s = new ReturnStatement(Loc.initial, e);
funcdecl.fbody = new CompoundStatement(Loc.initial, funcdecl.fbody, s);
- funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1);
+ funcdecl.hasMultipleReturnExp = funcdecl.hasReturnExp;
+ funcdecl.hasReturnExp = true;
}
assert(!funcdecl.returnLabel);
}
}
else
{
- const(bool) inlineAsm = (funcdecl.hasReturnExp & 8) != 0;
- if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !inlineAsm && !sc.inCfile)
+ if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !funcdecl.hasInlineAsm && !sc.inCfile)
{
if (!funcdecl.hasReturnExp)
.error(funcdecl.loc, "%s `%s` has no `return` statement, but is expected to return a value of type `%s`", funcdecl.kind, funcdecl.toPrettyChars, f.next.toChars());
{
if (v.isReference() || (v.storage_class & STC.lazy_))
continue;
- if (v.needsScopeDtor())
- {
- v.storage_class |= STC.nodtor;
- if (!paramsNeedDtor)
- continue;
-
- // same with ExpStatement.scopeCode()
- Statement s = new DtorExpStatement(Loc.initial, v.edtor, v);
+ if (!v.needsScopeDtor())
+ continue;
+ v.storage_class |= STC.nodtor;
+ if (!paramsNeedDtor)
+ continue;
- s = s.statementSemantic(sc2);
+ // same with ExpStatement.scopeCode()
+ Statement s = new DtorExpStatement(Loc.initial, v.edtor, v);
- const blockexit = s.blockExit(funcdecl, isNothrow ? global.errorSink : null);
- if (blockexit & BE.throw_)
- {
- funcdecl.hasNoEH = false;
- if (isNothrow)
- error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars());
- else if (funcdecl.nothrowInprocess)
- f.isNothrow = false;
- }
+ s = s.statementSemantic(sc2);
- if (sbody.blockExit(funcdecl, f.isNothrow ? global.errorSink : null) == BE.fallthru)
- sbody = new CompoundStatement(Loc.initial, sbody, s);
- else
- sbody = new TryFinallyStatement(Loc.initial, sbody, s);
+ const blockexit = s.blockExit(funcdecl, isNothrow ? global.errorSink : null);
+ if (blockexit & BE.throw_)
+ {
+ funcdecl.hasNoEH = false;
+ if (isNothrow)
+ error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars());
+ else if (funcdecl.nothrowInprocess)
+ f.isNothrow = false;
}
+
+ if (sbody.blockExit(funcdecl, f.isNothrow ? global.errorSink : null) == BE.fallthru)
+ sbody = new CompoundStatement(Loc.initial, sbody, s);
+ else
+ sbody = new TryFinallyStatement(Loc.initial, sbody, s);
}
}
// from this point on all possible 'throwers' are checked
sd.xeq._scope &&
sd.xeq.semanticRun < PASS.semantic3done)
{
- uint errors = global.startGagging();
+ const errors = global.startGagging();
sd.xeq.semantic3(sd.xeq._scope);
if (global.endGagging(errors))
sd.xeq = sd.xerreq;
sd.xcmp._scope &&
sd.xcmp.semanticRun < PASS.semantic3done)
{
- uint errors = global.startGagging();
+ const errors = global.startGagging();
sd.xcmp.semantic3(sd.xcmp._scope);
if (global.endGagging(errors))
sd.xcmp = sd.xerrcmp;
import dmd.arraytypes;
import dmd.astenums;
import dmd.ast_node;
-import dmd.errors;
import dmd.cond;
import dmd.declaration;
import dmd.dsymbol;
import dmd.parse;
import dmd.common.outbuffer;
import dmd.root.string;
+import dmd.safe : isSafe, setUnsafe;
import dmd.semantic2;
import dmd.sideeffect;
import dmd.statement;
}
else if (rs.exp)
{
- fd.hasReturnExp |= (fd.hasReturnExp & 1 ? 16 : 1);
+ fd.hasMultipleReturnExp = fd.hasReturnExp;
+ fd.hasReturnExp = true;
FuncLiteralDeclaration fld = fd.isFuncLiteralDeclaration();
if (tret)
return false;
}
- if (FuncDeclaration fd = sc.parent.isFuncDeclaration())
- fd.hasReturnExp |= 2;
-
if (auto ne = exp.isNewExp())
{
ne.thrownew = true;
OutBuffer buf;
- if (expressionsToString(buf, sc, cs.exps))
+ if (expressionsToString(buf, sc, cs.exps, cs.loc, null, true))
return errorStatements();
const errors = global.errors;
const bool doUnittests = global.params.parsingUnittestsRequired();
auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
- p.transitionIn = global.params.v.vin;
p.nextToken();
auto a = new Statements();
*/
struct TargetC
{
+ import dmd.declaration : BitFieldDeclaration;
+
enum Runtime : ubyte
{
Unspecified,
ubyte wchar_tsize; /// size of a C `wchar_t` type
Runtime runtime; /// vendor of the C runtime to link against
BitFieldStyle bitFieldStyle; /// different C compilers do it differently
+
+ /**
+ * Indicates whether the specified bit-field contributes to the alignment
+ * of the containing aggregate.
+ * E.g., (not all) ARM ABIs do NOT ignore anonymous (incl. 0-length)
+ * bit-fields.
+ */
+ extern (C++) bool contributesToAggregateAlignment(BitFieldDeclaration bfd);
}
////////////////////////////////////////////////////////////////////////////////
#include "globals.h"
#include "tokens.h"
+class BitFieldDeclaration;
class ClassDeclaration;
class Dsymbol;
class Expression;
uint8_t wchar_tsize; // size of a C 'wchar_t' type
Runtime runtime;
BitFieldStyle bitFieldStyle; // different C compilers do it differently
+
+ bool contributesToAggregateAlignment(BitFieldDeclaration *bfd);
};
struct TargetCPP
DYNCAST dyncast() const override { return DYNCAST_TEMPLATEPARAMETER; }
- /* Create dummy argument based on parameter.
- */
- virtual RootObject *dummyArg() = 0;
void accept(Visitor *v) override { v->visit(this); }
};
RootObject *specialization() override final;
RootObject *defaultArg(const Loc &instLoc, Scope *sc) override final;
bool hasDefaultArg() override final;
- RootObject *dummyArg() override final;
void accept(Visitor *v) override { v->visit(this); }
};
RootObject *specialization() override;
RootObject *defaultArg(const Loc &instLoc, Scope *sc) override;
bool hasDefaultArg() override;
- RootObject *dummyArg() override;
void accept(Visitor *v) override { v->visit(this); }
};
RootObject *specialization() override;
RootObject *defaultArg(const Loc &instLoc, Scope *sc) override;
bool hasDefaultArg() override;
- RootObject *dummyArg() override;
void accept(Visitor *v) override { v->visit(this); }
};
RootObject *specialization() override;
RootObject *defaultArg(const Loc &instLoc, Scope *sc) override;
bool hasDefaultArg() override;
- RootObject *dummyArg() override;
void accept(Visitor *v) override { v->visit(this); }
};
import dmd.dclass;
import dmd.declaration;
import dmd.dinterpret;
-import dmd.dmangle;
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_);
tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_);
- UserAttributeDeclaration.checkGNUABITag(tempdecl, sc.linkage);
+ checkGNUABITag(tempdecl, sc.linkage);
if (!tempdecl.isstatic)
{
// Resolve parameter types and 'auto ref's.
tf.inferenceArguments = argumentList;
- uint olderrors = global.startGagging();
+ const olderrors = global.startGagging();
fd.type = tf.typeSemantic(td.loc, paramscope);
global.endGagging(olderrors);
if (fd.type.ty != Tfunction)
Dsymbol s;
Scope *sco;
- uint errors = global.startGagging();
+ const errors = global.startGagging();
/* ref: https://issues.dlang.org/show_bug.cgi?id=11118
* The parameter isn't part of the template
* ones, let's try to find it in the
m.last = MATCH.nomatch;
}
}
+/* Create dummy argument based on parameter.
+ */
+private RootObject dummyArg(TemplateParameter tp)
+{
+ scope v = new DummyArgVisitor();
+ tp.accept(v);
+ return v.result;
+}
+private extern(C++) class DummyArgVisitor : Visitor
+{
+ RootObject result;
+
+ alias visit = typeof(super).visit;
+ override void visit(TemplateTypeParameter ttp)
+ {
+ Type t = ttp.specType;
+ if (t)
+ {
+ result = t;
+ return;
+ }
+ // Use this for alias-parameter's too (?)
+ if (!ttp.tdummy)
+ ttp.tdummy = new TypeIdentifier(ttp.loc, ttp.ident);
+ result = ttp.tdummy;
+ }
+
+ override void visit(TemplateValueParameter tvp)
+ {
+ Expression e = tvp.specValue;
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ // Create a dummy value
+ auto pe = cast(void*)tvp.valType in tvp.edummies;
+ if (pe)
+ {
+ result = *pe;
+ return;
+ }
+
+ e = tvp.valType.defaultInit(Loc.initial);
+ tvp.edummies[cast(void*)tvp.valType] = e;
+ result = e;
+ }
+
+ override void visit(TemplateAliasParameter tap)
+ {
+ RootObject s = tap.specAlias;
+ if (s)
+ {
+ result = s;
+ return;
+ }
+ if (!tap.sdummy)
+ tap.sdummy = new Dsymbol();
+ result = tap.sdummy;
+ }
+
+ override void visit(TemplateTupleParameter tap)
+ {
+ result = null;
+ }
+}
import dmd.declaration;
import dmd.dimport;
import dmd.dinterpret;
-import dmd.dmangle;
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
import dmd.id;
import dmd.identifier;
import dmd.location;
+import dmd.mangle : decoToType;
import dmd.mtype;
import dmd.nogc;
import dmd.optimize;
e.ident == Id.getVirtualMethods ||
e.ident == Id.getOverloads)
{
- uint errors = global.errors;
+ const errors = global.errors;
Expression eorig = ex;
ex = ex.expressionSemantic(scx);
if (errors < global.errors)
foreach (o; *e.args)
{
- uint errors = global.startGagging();
+ const errors = global.startGagging();
Scope* sc2 = sc.push();
sc2.tinst = null;
sc2.minst = null; // this is why code for these are not emitted to object file
return dimError(1);
auto arg0 = (*e.args)[0];
Dsymbol s = getDsymbolWithoutExpCtx(arg0);
- if (!s || !s.loc.isValid())
+ if (!s || !s.loc.isValid() || s.isModule())
{
- error(e.loc, "can only get the location of a symbol, not `%s`", arg0.toChars());
+ error(e.loc, "can only get the location of a symbol, not `%s`", s ? s.toPrettyChars() : arg0.toChars());
return ErrorExp.get();
}
import dmd.denum;
import dmd.dimport;
import dmd.dinterpret;
-import dmd.dmangle;
import dmd.dmodule;
import dmd.dscope;
import dmd.dstruct;
import dmd.location;
import dmd.visitor;
import dmd.mtype;
+import dmd.mangle;
import dmd.nogc;
import dmd.objc;
import dmd.opover;
}
Type t = s.getType(); // type symbol, type alias, or type tuple?
- uint errorsave = global.errors;
+ const errorsave = global.errors;
SearchOptFlags flags = t is null ? SearchOpt.localsOnly : SearchOpt.ignorePrivateImports;
Dsymbol sm = s.searchX(loc, sc, id, flags);
L1:
if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams) // if last varargs param
{
- auto trailingArgs = args[u .. $];
+ Expression[] trailingArgs;
+ if (args.length >= u)
+ trailingArgs = args[u .. $];
if (auto vmatch = matchTypeSafeVarArgs(tf, p, trailingArgs, pMessage))
return vmatch < match ? vmatch : match;
// Error message was already generated in `matchTypeSafeVarArgs`
*/
OutBuffer buf;
auto callExp = e.isCallExp();
- void nocpctor()
+
+ bool nocpctor()
{
buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies",
argStruct.toChars(), arg.type.toChars(), tprm.toChars());
- }
- auto f = callExp.f;
- if (!f)
- {
- nocpctor();
*pMessage = buf.extractChars();
return false;
}
- char[] s;
- if (!f.isPure && sc.func.setImpure())
- s ~= "pure ";
- if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe())
- s ~= "@safe ";
- if (!f.isNogc && sc.func.setGC(arg.loc, null))
- s ~= "nogc ";
+
+ auto f = callExp.f;
+ if (!f)
+ return nocpctor();
+
if (f.isDisabled() && !f.isGenerated())
{
/* https://issues.dlang.org/show_bug.cgi?id=24301
*/
buf.printf("`%s` copy constructor cannot be used because it is annotated with `@disable`",
f.type.toChars());
+ *pMessage = buf.extractChars();
+ return false;
}
- else if (s)
+
+ bool bpure = !f.isPure && sc.func.setImpure();
+ bool bsafe = !f.isSafe() && !f.isTrusted() && sc.setUnsafe();
+ bool bnogc = !f.isNogc && sc.func.setGC(arg.loc, null);
+ if (bpure | bsafe | bnogc)
{
- s[$-1] = '\0';
- buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr);
+ const nullptr = "".ptr;
+ buf.printf("`%s` copy constructor cannot be called from a `%s%s%s` context",
+ f.type.toChars(),
+ bpure ? "pure " .ptr : nullptr,
+ bsafe ? "@safe ".ptr : nullptr,
+ bnogc ? "nogc" .ptr : nullptr);
}
else if (f.isGenerated() && f.isDisabled())
{
* i.e: `inout` constructor creates `const` object, not mutable.
* Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202.
*/
- nocpctor();
+ return nocpctor();
}
*pMessage = buf.extractChars();
which isn't a particularly good error message (x is a variable?).
*/
Dsymbol varDecl = mtype.toDsymbol(sc);
- const(Loc) varDeclLoc = varDecl.getLoc();
Module varDeclModule = varDecl.getModule(); //This can be null
.error(loc, "variable `%s` is used as a type", mtype.toChars());
//Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574
if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported
{
- const(Loc) varDeclModuleImportLoc = varDeclModule.getLoc();
.errorSupplemental(
- varDeclModuleImportLoc,
+ varDeclModule.loc,
"variable `%s` is imported here from: `%s`",
varDecl.toChars,
varDeclModule.toPrettyChars,
);
}
- .errorSupplemental(varDeclLoc, "variable `%s` is declared here", varDecl.toChars);
+ .errorSupplemental(varDecl.loc, "variable `%s` is declared here", varDecl.toChars);
}
else
.error(loc, "`%s` is used as a type", mtype.toChars());
* e.g.
* template opDispatch(name) if (isValid!name) { ... }
*/
- uint errors = gagError ? global.startGagging() : 0;
+ const errors = gagError ? global.startGagging() : 0;
e = dti.dotTemplateSemanticProp(sc, DotExpFlag.none);
if (gagError && global.endGagging(errors))
e = null;
*/
auto die = new DotIdExp(e.loc, alias_e, ident);
- auto errors = gagError ? 0 : global.startGagging();
+ const errors = gagError ? 0 : global.startGagging();
auto exp = die.dotIdSemanticProp(sc, gagError);
if (!gagError)
{
return MATCH.nomatch;
}
+/*******************************************
+ * Compute number of elements for a (possibly multidimensional) static array,
+ * or 1 for other types.
+ * Params:
+ * t = static array type
+ * loc = for error message
+ * Returns:
+ * number of elements, uint.max on overflow
+ */
+uint numberOfElems(Type t, const ref Loc loc)
+{
+ //printf("Type::numberOfElems()\n");
+ uinteger_t n = 1;
+ Type tb = t;
+ while ((tb = tb.toBasetype()).ty == Tsarray)
+ {
+ bool overflow = false;
+ n = mulu(n, (cast(TypeSArray)tb).dim.toUInteger(), overflow);
+ if (overflow || n >= uint.max)
+ {
+ error(loc, "static array `%s` size overflowed to %llu", t.toChars(), cast(ulong)n);
+ return uint.max;
+ }
+ tb = (cast(TypeSArray)tb).next;
+ }
+ return cast(uint)n;
+}
+
+bool checkRetType(TypeFunction tf, const ref Loc loc)
+{
+ Type tb = tf.next.toBasetype();
+ if (tb.ty == Tfunction)
+ {
+ error(loc, "functions cannot return a function");
+ tf.next = Type.terror;
+ }
+ if (tb.ty == Ttuple)
+ {
+ error(loc, "functions cannot return a sequence (use `std.typecons.Tuple`)");
+ tf.next = Type.terror;
+ }
+ if (!tf.isRef && (tb.ty == Tstruct || tb.ty == Tsarray))
+ {
+ if (auto ts = tb.baseElemOf().isTypeStruct())
+ {
+ if (!ts.sym.members)
+ {
+ error(loc, "functions cannot return opaque type `%s` by value", tb.toChars());
+ tf.next = Type.terror;
+ }
+ }
+ }
+ if (tb.ty == Terror)
+ return true;
+ return false;
+}
/******************************* Private *****************************************/
RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc)
{
OutBuffer buf;
- if (expressionsToString(buf, sc, tm.exps))
+ if (expressionsToString(buf, sc, tm.exps, tm.loc, null, true))
return null;
const errors = global.errors;
const bool doUnittests = global.params.parsingUnittestsRequired();
auto locm = adjustLocForMixin(str, loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(locm, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
- p.transitionIn = global.params.v.vin;
p.nextToken();
//printf("p.loc.linnum = %d\n", p.loc.linnum);
* Returns:
* The type of the `TypeInfo` object associated with `t`
*/
-extern (C++) Type getTypeInfoType(const ref Loc loc, Type t, Scope* sc, bool genObjCode = true);
+extern (C++) Type getTypeInfoType(const ref Loc loc, Type t, Scope* sc);
private TypeInfoDeclaration getTypeInfoDeclaration(Type t)
{
bool isSpeculativeType(Type *t);
bool builtinTypeInfo(Type *t);
}
-Type *getTypeInfoType(const Loc &loc, Type *t, Scope *sc, bool genObjCode = true);
+Type *getTypeInfoType(const Loc &loc, Type *t, Scope *sc);
* size = 1 for ubyte[], 2 for ushort[], 4 for uint[], 8 for ulong[]
* Returns: copy of `data`, with bytes shuffled if compiled for `version(LittleEndian)`
*/
-ubyte[] arrayCastBigEndian(const ubyte[] data, size_t size)
+ubyte[] arrayCastBigEndian(const ubyte[] data, size_t size) @safe
{
ubyte[] impl(T)()
{
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.dtemplate;
-import dmd.errors;
import dmd.expression;
import dmd.func;
import dmd.id;
return decl->cpp_type_info_ptr_sym;
}
-/* Get the exact TypeInfo for TYPE, if it doesn't exist, create it.
- When GENERATE is true, push the TypeInfo as a member of MOD so that it will
- get code generation. */
+/* Get the exact TypeInfo for TYPE, if it doesn't exist, create it. */
void
-create_typeinfo (Type *type, Module *mod, bool generate)
+create_typeinfo (Type *type, Module *mod)
{
if (!Type::dtypeinfo)
create_frontend_tinfo_types ();
/* If this has a custom implementation in rt/typeinfo, then
do not generate a COMDAT for it. */
- if (generate && !builtin_typeinfo_p (t))
+ if (!builtin_typeinfo_p (t))
{
/* Find module that will go all the way to an object file. */
if (mod)
AttribDeclaration *attrib = sym->isAttribDeclaration ();
if (attrib != NULL)
{
- Dsymbols *decls = attrib->include (NULL);
+ Dsymbols *decls = dmd::include (attrib, NULL);
if (decls != NULL)
{
fields += layout_aggregate_members (decls, context, inherited_p);
int f11042a3()() if (__traits(compiles, true || error) == false) { return 0; } enum x11042a3 = f11042a3();
int f11042b3()() if (__traits(compiles, false && error) == false) { return 0; } enum x11042b3 = f11042b3();
+// https://issues.dlang.org/show_bug.cgi?id=24699
+enum T24699(bool cond) = cond;
+enum b24699a = T24699!(true || error);
+enum b24699b = T24699!(false && error);
+
/***************************************************/
// https://issues.dlang.org/show_bug.cgi?id=11554
f();
f();
f();
- f();
+ static assert("1"); // also surpress deprecationSupplemental
}
immutable ubyte[] s0 = import("imp16088.d");
static assert(s0[0 .. expectedStart.length] == "module imports.imp16088;");
+
+// https://issues.dlang.org/show_bug.cgi?id=24687
+
+void foo(string path);
+void foo(const(ubyte[]) data);
+
+void bar1() { foo(import("imp16088.d")); } // matches both
+void bar2() { foo(cast(const(ubyte[])) import("imp16088.d")); } // matches both!
--- /dev/null
+struct O
+{
+ invariant() {}
+}
static assert(__traits(isZeroInit, Holder!(Vector!(int[4]), 0)));
static assert(!__traits(isZeroInit, Holder!(Vector!(int[4]), 1)));
}
+
+// https://issues.dlang.org/show_bug.cgi?id=24776
+struct S6 {
+ union {
+ int i1;
+ float f1;
+ }
+}
+static assert(__traits(isZeroInit, S6));
+
+struct S7
+{
+ union {
+ float f2;
+ int i2;
+ }
+}
+static assert(!__traits(isZeroInit, S7));
+
+// https://issues.dlang.org/show_bug.cgi?id=23841
+union U
+{
+ float x = 0;
+ float y;
+}
+static assert(__traits(isZeroInit, U));
+
+union U2
+{
+ float x;
+ int y;
+}
+static assert(!__traits(isZeroInit, U2));
+
+struct S8 {
+ int[0] dummy; // same offset as anon union, but doesn't overlap; should be ignored anyway for zero-init check
+ union {
+ float f; // is the first member of the anon union and must be checked
+ int i;
+ }
+}
+static assert(!__traits(isZeroInit, S8));
T2
TestStaticForeach2
issue22007
-1 2 '3'
-2 3 '4'
+1 2 3
+2 3 4
0 1
1 2
2 3
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+"ab"w x"11223344556677"
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=24337
+
+immutable ushort[] y = cast(immutable ushort[]) "ab"w;
+immutable ulong[] z = x"00 11 22 33 44 55 66 77";
+pragma(msg, y, " ", z);
--- /dev/null
+// https://issues.dlang.org/show_bug.cgi?id=24760
+
+long f(int e = 0, uint[] optional...) => optional.length;
+long f0() => f(); // compiler segfaults
REQUIRED_ARGS: -vcg-ast -o-
PERMUTE_ARGS:
OUTPUT_FILES: compilable/vcg-ast.d.cg
+EXTRA_FILES: imports/vcg_ast_import.d
TEST_OUTPUT_FILE: extra-files/vcg-ast.d.cg
*/
{
values!wchar_t;
}
+
+// https://issues.dlang.org/show_bug.cgi?id=24764
+
+import imports.vcg_ast_import;
+
+template imported()
+{
+ import imported = imports.vcg_ast_import;
+}
+
+alias myImport = imported!();
--- /dev/null
+/*
+REQUIRED_ARGS: -vcg-ast -o-
+OUTPUT_FILES: compilable/vcg_ast_compilable.d.cg
+TEST_OUTPUT:
+---
+=== compilable/vcg_ast_compilable.d.cg
+import object;
+auto binaryFun(E)(E b)
+{
+ return 'a' == b;
+}
+void find(Element)(Element needle) if (is(typeof(binaryFun(needle))))
+{
+}
+void find()(string needle)
+{
+}
+void splitter()
+{
+ find(3);
+ find("");
+}
+binaryFun!int
+{
+ auto pure nothrow @nogc @safe bool binaryFun(int b)
+ {
+ return 97 == b;
+ }
+
+}
+find!int
+{
+ pure nothrow @nogc @safe void find(int needle)
+ {
+ }
+
+}
+binaryFun!string
+{
+ auto _error_ binaryFun
+ {
+ __error__
+ }
+
+}
+find!()
+{
+ pure nothrow @nogc @safe void find(string needle)
+ {
+ }
+
+}
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=24431
+auto binaryFun(E)(E b)
+{
+ return 'a' == b;
+}
+
+void find(Element)(Element needle) if (is(typeof(binaryFun(needle)))) { }
+void find()(string needle) { }
+
+void splitter()
+{
+ find!int(3);
+ find!()("");
+}
+++ /dev/null
-/*
-TEST_OUTPUT:
----
-fail_compilation/failCopyCtor.d(10): Error: `struct A` may not define both a rvalue constructor and a copy constructor
-fail_compilation/failCopyCtor.d(12): rvalue constructor defined here
-fail_compilation/failCopyCtor.d(13): copy constructor defined here
----
-*/
-
-struct A
-{
- this(immutable A a) {}
- this(ref shared A a) immutable {}
- this(ref A a) {}
-}
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/template_enum_param.d(15): Error: static assert: `false` is false
+fail_compilation/template_enum_param.d(17): instantiated from here: `X!(E.a)`
+---
+*/
+
+enum E
+{
+ a,b,c
+}
+template X(E e)
+{
+ static assert(false);
+}
+alias Y = X!(E.a);
--- /dev/null
+/* TEST_OUTPUT:
+---
+fail_compilation/test21995.d(10): Error: max object size 4294967295 exceeded from adding field size 3 + alignment adjustment 1 + field offset 4294967292 when placing field in aggregate
+---
+*/
+struct S
+{
+ ubyte[0x7ffffffe] a;
+ ubyte[0x7ffffffe] b;
+ ubyte[3] c;
+}
/*
TEST_OUTPUT:
---
-fail_compilation/trait_loc_err.d(13): Error: can only get the location of a symbol, not `trait_loc_err`
-fail_compilation/trait_loc_err.d(14): Error: can only get the location of a symbol, not `stdc`
+fail_compilation/trait_loc_err.d(14): Error: can only get the location of a symbol, not `trait_loc_err`
+fail_compilation/trait_loc_err.d(15): Error: can only get the location of a symbol, not `core.stdc`
+fail_compilation/trait_loc_err.d(16): Error: can only get the location of a symbol, not `core.stdc.stdio`
---
*/
module trait_loc_err;
{
__traits(getLocation, __traits(parent, main));
__traits(getLocation, __traits(parent, core.stdc.stdio));
+ __traits(getLocation, core.stdc.stdio);
}
class Y : Tst!(float) {}
+// https://issues.dlang.org/show_bug.cgi?id=24731
+void test24731()
+{
+ static int solve(size_t N)(ref double[N+1][N])
+ {
+ return N;
+ }
+
+ double[3][2] m;
+ assert(solve(m) == 2);
+ assert(solve!2(m) == 2);
+}
+
+
void main() {
Tst!(int) t = new Tst!(int);
Y u = new Y;
printf("%g\n", i);
}
+ test24731();
}
/*
TEST_OUTPUT:
---
-CT x.offsetof = <
CT y.offsetof = <
0 > y
+CT x.offsetof = <
0 > x
---
*/
+++ /dev/null
-// https://issues.dlang.org/show_bug.cgi?id=23650
-
-__gshared int x;
-
-void main()
-{
-
- static assert(__traits(compiles,
- {
- struct S { int *p = &x; }
- auto t = typeid(S);
- }));
-}
--- /dev/null
+module mod;
+
+struct Variable
+{
+ size_t toHash() const { return 0; }
+}
+
+enum hasInoutConstruction(T) = __traits(compiles, { struct S { T a; } });
+
+struct Algebraic(T)
+{
+ static if (hasInoutConstruction!T)
+ {
+ }
+}
+
+Algebraic!Variable foo();
+
+struct S
+{
+ Variable[] symbols;
+}
+
+void main() {}
--- /dev/null
+import core.stdc.stdio;
+
+pragma(inline, true)
+double sqrt(double x)
+{
+ static import core.math;
+ return core.math.sqrt(x);
+}
+
+int main()
+{
+ double q = -5.0;
+ double r = q + 1.0;
+ double result = sqrt(-r);
+ //printf("%f\n", result);
+ assert(result == 2);
+ return 0;
+}
-4ccb01fde535c7ad6ad4bdae2516c99420751814
+2b89c2909de239bd603d6f36379658fe902667db
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
// but third-party compilers could use this
enum uint _WIN32_WINNT = 0x500;
} else {
- enum uint _WIN32_WINNT = 0x501;
+ enum uint _WIN32_WINNT = 0x601;
}
version (IE11) {
SECURITY_SQOS_PRESENT = 0x00100000,
SECURITY_VALID_SQOS_FLAGS = 0x001F0000;
+// for GetFinalPathNameByHandle()
+enum DWORD
+ VOLUME_NAME_DOS = 0x0,
+ VOLUME_NAME_GUID = 0x1,
+ VOLUME_NAME_NT = 0x2,
+ VOLUME_NAME_NONE = 0x4,
+ FILE_NAME_NORMALIZED = 0x0,
+ FILE_NAME_OPENED = 0x8;
// Thread exit code
enum DWORD STILL_ACTIVE = 0x103;
GetFileExMaxInfoLevel
}
+import core.sys.windows.sdkddkver : NTDDI_VERSION, NTDDI_LONGHORN;
+
+static if (NTDDI_VERSION >= NTDDI_LONGHORN)
+{
+ enum FILE_INFO_BY_HANDLE_CLASS
+ {
+ FileBasicInfo,
+ FileStandardInfo,
+ FileNameInfo,
+ FileRenameInfo,
+ FileDispositionInfo,
+ FileAllocationInfo,
+ FileEndOfFileInfo,
+ FileStreamInfo,
+ FileCompressionInfo,
+ FileAttributeTagInfo,
+ FileIdBothDirectoryInfo,
+ FileIdBothDirectoryRestartInfo,
+ FileIoPriorityHintInfo,
+ FileRemoteProtocolInfo,
+ FileFullDirectoryInfo,
+ FileFullDirectoryRestartInfo,
+// static if (NTDDI_VERSION >= NTDDI_WIN8)
+// {
+ FileStorageInfo,
+ FileAlignmentInfo,
+ FileIdInfo,
+ FileIdExtdDirectoryInfo,
+ FileIdExtdDirectoryRestartInfo,
+// }
+// static if (NTDDI_VERSION >= NTDDI_WIN10_RS1)
+// {
+ FileDispositionInfoEx,
+ FileRenameInfoEx,
+// }
+// static if (NTDDI_VERSION >= NTDDI_WIN10_19H1)
+// {
+ FileCaseSensitiveInfo,
+ FileNormalizedNameInfo,
+// }
+ MaximumFileInfoByHandleClass
+ }
+ alias PFILE_INFO_BY_HANDLE_CLASS = FILE_INFO_BY_HANDLE_CLASS*;
+}
+
struct SYSTEM_INFO {
union {
DWORD dwOemId;
alias DWORD EXECUTION_STATE;
}
-// CreateSymbolicLink
+// CreateSymbolicLink, GetFileInformationByHandleEx
static if (_WIN32_WINNT >= 0x600) {
enum {
SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1,
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2
}
+
+ struct FILE_BASIC_INFO
+ {
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ DWORD FileAttributes;
+ }
+ alias PFILE_BASIC_INFO = FILE_BASIC_INFO*;
+
+ struct FILE_STANDARD_INFO
+ {
+ LARGE_INTEGER AllocationSize;
+ LARGE_INTEGER EndOfFile;
+ DWORD NumberOfLinks;
+ BOOLEAN DeletePending;
+ BOOLEAN Directory;
+ }
+ alias PFILE_STANDARD_INFO = FILE_STANDARD_INFO*;
}
// Callbacks
DWORD GetFileSize(HANDLE, PDWORD);
BOOL GetFileTime(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME);
DWORD GetFileType(HANDLE);
+ DWORD GetFinalPathNameByHandleA(HANDLE, LPSTR, DWORD, DWORD);
+ DWORD GetFinalPathNameByHandleW(HANDLE, LPWSTR, DWORD, DWORD);
DWORD GetFullPathNameA(LPCSTR, DWORD, LPSTR, LPSTR*);
DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*);
DWORD GetLastError() @trusted;
static if (_WIN32_WINNT >= 0x600) {
BOOL CreateSymbolicLinkA(LPCSTR, LPCSTR, DWORD);
BOOL CreateSymbolicLinkW(LPCWSTR, LPCWSTR, DWORD);
+ BOOL GetFileInformationByHandleEx(HANDLE, FILE_INFO_BY_HANDLE_CLASS, LPVOID, DWORD);
}
}
Given an array of length `size` that needs to be expanded to `newlength`,
compute a new capacity.
-Better version by Dave Fladebo:
+Better version by Dave Fladebo, enhanced by Steven Schveighoffer:
This uses an inverse logorithmic algorithm to pre-allocate a bit more
space for larger arrays.
-- Arrays smaller than PAGESIZE bytes are left as-is, so for the most
-common cases, memory allocation is 1 to 1. The small overhead added
-doesn't affect small array perf. (it's virtually the same as
-current).
-- Larger arrays have some space pre-allocated.
+- The maximum "extra" space is about 80% of the requested space. This is for
+PAGE size and smaller.
- As the arrays grow, the relative pre-allocated space shrinks.
-- The logorithmic algorithm allocates relatively more space for
-mid-size arrays, making it very fast for medium arrays (for
-mid-to-large arrays, this turns out to be quite a bit faster than the
-equivalent realloc() code in C, on Linux at least. Small arrays are
-just as fast as GCC).
- Perhaps most importantly, overall memory usage and stress on the GC
is decreased significantly for demanding environments.
+- The algorithm is tuned to avoid any division at runtime.
Params:
newlength = new `.length`
- size = old `.length`
+ elemsize = size of the element in the new array
Returns: new capacity for array
*/
-size_t newCapacity(size_t newlength, size_t size)
+size_t newCapacity(size_t newlength, size_t elemsize)
{
- version (none)
- {
- size_t newcap = newlength * size;
- }
- else
- {
- size_t newcap = newlength * size;
- size_t newext = 0;
+ size_t newcap = newlength * elemsize;
- if (newcap > PAGESIZE)
+ /*
+ * Max growth factor numerator is 234, so allow for multiplying by 256.
+ * But also, the resulting size cannot be more than 2x, so prevent
+ * growing if 2x would fill up the address space (for 32-bit)
+ */
+ enum largestAllowed = (ulong.max >> 8) & (size_t.max >> 1);
+ if (!newcap || (newcap & ~largestAllowed))
+ return newcap;
+
+ /*
+ * The calculation for "extra" space depends on the requested capacity.
+ * We use an inverse logarithm of the new capacity to add an extra 15%
+ * to 83% capacity. Note that normally we humans think in terms of
+ * percent, but using 128 instead of 100 for the denominator means we
+ * can avoid all division by simply bit-shifthing. Since there are only
+ * 64 bits in a long, the bsr of a size_t is going to be 0 - 63. Using
+ * a lookup table allows us to precalculate the multiplier based on the
+ * inverse logarithm. The formula rougly is:
+ *
+ * newcap = request * (1.0 + min(0.83, 10.0 / (log(request) + 1)))
+ */
+ import core.bitop;
+ static immutable multTable = (){
+ assert(__ctfe);
+ ulong[size_t.sizeof * 8] result;
+ foreach (i; 0 .. result.length)
{
- //double mult2 = 1.0 + (size / log10(pow(newcap * 2.0,2.0)));
-
- // redo above line using only integer math
+ auto factor = 128 + 1280 / (i + 1);
+ result[i] = factor > 234 ? 234 : factor;
+ }
+ return result;
+ }();
- /*static int log2plus1(size_t c)
- { int i;
+ auto mult = multTable[bsr(newcap)];
- if (c == 0)
- i = -1;
- else
- for (i = 1; c >>= 1; i++)
- {
- }
- return i;
- }*/
-
- /* The following setting for mult sets how much bigger
- * the new size will be over what is actually needed.
- * 100 means the same size, more means proportionally more.
- * More means faster but more memory consumption.
- */
- //long mult = 100 + (1000L * size) / (6 * log2plus1(newcap));
- //long mult = 100 + (1000L * size) / log2plus1(newcap);
- import core.bitop;
- long mult = 100 + (1000L) / (bsr(newcap) + 1);
-
- // testing shows 1.02 for large arrays is about the point of diminishing return
- //
- // Commented out because the multipler will never be < 102. In order for it to be < 2,
- // then 1000L / (bsr(x) + 1) must be > 2. The highest bsr(x) + 1
- // could be is 65 (64th bit set), and 1000L / 64 is much larger
- // than 2. We need 500 bit integers for 101 to be achieved :)
- /*if (mult < 102)
- mult = 102;*/
- /*newext = cast(size_t)((newcap * mult) / 100);
- newext -= newext % size;*/
- // This version rounds up to the next element, and avoids using
- // mod.
- newext = cast(size_t)((newlength * mult + 99) / 100) * size;
- debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/100.0,newext / cast(double)size);
- }
- newcap = newext > newcap ? newext : newcap;
- debug(PRINTF) printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size);
- }
+ // if this were per cent, then the code would look like:
+ // ((newlength * mult + 99) / 100) * elemsize
+ newcap = cast(size_t)((newlength * mult + 127) >> 7) * elemsize;
+ debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/128.0,newcap / cast(double)elemsize);
+ debug(PRINTF) printf("newcap = %d, newlength = %d, elemsize = %d\n", newcap, newlength, elemsize);
return newcap;
}
-eab6595ade1dab9a757bc0efe2f4e92f39cab0f7
+bdedad3bf8090808e41726e7b9f8e928f6cb10bd
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
if (isExpressionTuple!values)
{
uint among(Value)(Value value)
- if (!is(CommonType!(Value, values) == void))
+ if (!is(CommonType!(Value, values) == void))
{
switch (value)
{
A range with each fun applied to all the elements. If there is more than one
fun, the element type will be `Tuple` containing one element for each fun.
*/
- auto map(Range)(Range r) if (isInputRange!(Unqual!Range))
+ auto map(Range)(Range r)
+ if (isInputRange!(Unqual!Range))
{
import std.meta : AliasSeq, staticMap;
A range containing only elements `x` in `range` for
which `predicate(x)` returns `true`.
*/
- auto filter(Range)(Range range) if (isInputRange!(Unqual!Range))
+ auto filter(Range)(Range range)
+ if (isInputRange!(Unqual!Range))
{
return FilterResult!(unaryFun!predicate, Range)(range);
}
Returns:
A range containing only the elements in `r` for which `pred` returns `true`.
*/
- auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range))
+ auto filterBidirectional(Range)(Range r)
+ if (isBidirectionalRange!(Unqual!Range))
{
return FilterBidiResult!(unaryFun!pred, Range)(r);
}
lhs = Data to be swapped with `rhs`.
rhs = Data to be swapped with `lhs`.
*/
+void swap(T)(ref T lhs, ref T rhs)
+if (is(typeof(lhs.proxySwap(rhs))))
+{
+ lhs.proxySwap(rhs);
+}
+
+/// ditto
void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow @nogc
if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs))))
{
swap(a3, a4);
}
-/// ditto
-void swap(T)(ref T lhs, ref T rhs)
-if (is(typeof(lhs.proxySwap(rhs))))
-{
- lhs.proxySwap(rhs);
-}
-
/**
Swaps two elements in-place of a range `r`,
specified by their indices `i1` and `i2`.
// can't use `temp.length` if there's no default constructor
static if (__traits(compiles, { T defaultConstructed; cast(void) defaultConstructed; }))
{
- if (__ctfe) temp.length = newSize;
- else temp = () @trusted { return uninitializedArray!(T[])(newSize); }();
+
+ static if (hasElaborateAssign!T)
+ temp.length = newSize;
+ else
+ {
+ if (__ctfe) temp.length = newSize;
+ else temp = () @trusted { return uninitializedArray!(T[])(newSize); }();
+ }
}
else
{
+ static assert(!hasElaborateAssign!T,
+ "Structs which have opAssign but cannot be default-initialized " ~
+ "do not currently work with stable sort: " ~
+ "https://issues.dlang.org/show_bug.cgi?id=24810");
temp = () @trusted { return uninitializedArray!(T[])(newSize); }();
}
}
array.sort!((a, b) => false, SwapStrategy.stable);
}
+// https://issues.dlang.org/show_bug.cgi?id=24809
+@safe unittest
+{
+ static struct E
+ {
+ int value;
+ int valid = 42;
+
+ ~this()
+ {
+ assert(valid == 42);
+ }
+ }
+
+ import std.array : array;
+ import std.range : chain, only, repeat;
+ auto arr = chain(repeat(E(41), 18),
+ only(E(39)),
+ repeat(E(41), 16),
+ only(E(1)),
+ repeat(E(42), 33),
+ only(E(33)),
+ repeat(E(42), 16),
+ repeat(E(43), 27),
+ only(E(33)),
+ repeat(E(43), 34),
+ only(E(34)),
+ only(E(43)),
+ only(E(63)),
+ repeat(E(44), 42),
+ only(E(27)),
+ repeat(E(44), 11),
+ repeat(E(45), 64),
+ repeat(E(46), 3),
+ only(E(11)),
+ repeat(E(46), 7),
+ only(E(4)),
+ repeat(E(46), 34),
+ only(E(36)),
+ repeat(E(46), 17),
+ repeat(E(47), 36),
+ only(E(39)),
+ repeat(E(47), 26),
+ repeat(E(48), 17),
+ only(E(21)),
+ repeat(E(48), 5),
+ only(E(39)),
+ repeat(E(48), 14),
+ only(E(58)),
+ repeat(E(48), 24),
+ repeat(E(49), 13),
+ only(E(40)),
+ repeat(E(49), 38),
+ only(E(18)),
+ repeat(E(49), 11),
+ repeat(E(50), 6)).array();
+
+ arr.sort!((a, b) => a.value < b.value, SwapStrategy.stable)();
+}
// schwartzSort
/**
* Params:
* item = the single item to append
*/
- void put(U)(U item) if (canPutItem!U)
+ void put(U)(U item)
+ if (canPutItem!U)
{
static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof)
{
}
// Const fixing hack.
- void put(Range)(Range items) if (canPutConstRange!Range)
+ void put(Range)(Range items)
+ if (canPutConstRange!Range)
{
alias p = put!(Unqual!Range);
p(items);
* Params:
* items = the range of items to append
*/
- void put(Range)(Range items) if (canPutRange!Range)
+ void put(Range)(Range items)
+ if (canPutRange!Range)
{
// note, we disable this branch for appending one type of char to
// another because we can't trust the length portion.
/**
* ditto
*/
- char[] encode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 &&
- is(ElementType!R1 : ubyte) && hasLength!R1 &&
- is(R2 == char[]))
+ char[] encode(R1, R2)(R1 source, R2 buffer)
+ if (!isArray!R1 && isInputRange!R1 &&
+ is(ElementType!R1 : ubyte) && hasLength!R1 &&
+ is(R2 == char[]))
in
{
assert(buffer.length >= encodeLength(source.length), "Insufficient buffer for encoding");
* ditto
*/
size_t encode(R1, R2)(R1 source, auto ref R2 range)
- if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : ubyte) &&
- hasLength!R1 && !is(R2 == char[]) && isOutputRange!(R2, char))
+ if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : ubyte) &&
+ hasLength!R1 && !is(R2 == char[]) && isOutputRange!(R2, char))
{
immutable srcLen = source.length;
if (srcLen == 0)
* A newly-allocated `char[]` buffer containing the encoded string.
*/
@safe
- pure char[] encode(Range)(Range source) if (isArray!Range && is(ElementType!Range : ubyte))
+ pure char[] encode(Range)(Range source)
+ if (isArray!Range && is(ElementType!Range : ubyte))
{
return encode(source, new char[encodeLength(source.length)]);
}
/**
* ditto
*/
- char[] encode(Range)(Range source) if (!isArray!Range && isInputRange!Range &&
- is(ElementType!Range : ubyte) && hasLength!Range)
+ char[] encode(Range)(Range source)
+ if (!isArray!Range && isInputRange!Range &&
+ is(ElementType!Range : ubyte) && hasLength!Range)
{
return encode(source, new char[encodeLength(source.length)]);
}
* Note: This struct is not intended to be created in user code directly;
* use the $(LREF encoder) function instead.
*/
- struct Encoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(ubyte)[]) ||
- is(ElementType!Range : const(char)[])))
+ struct Encoder(Range)
+ if (isInputRange!Range && (is(ElementType!Range : const(ubyte)[]) ||
+ is(ElementType!Range : const(char)[])))
{
private:
Range range_;
* Note: This struct is not intended to be created in user code directly;
* use the $(LREF encoder) function instead.
*/
- struct Encoder(Range) if (isInputRange!Range && is(ElementType!Range : ubyte))
+ struct Encoder(Range)
+ if (isInputRange!Range && is(ElementType!Range : ubyte))
{
private:
Range range_;
* }
* -----
*/
- Encoder!(Range) encoder(Range)(Range range) if (isInputRange!Range)
+ Encoder!(Range) encoder(Range)(Range range)
+ if (isInputRange!Range)
{
return typeof(return)(range);
}
* base alphabet of the current Base64 encoding scheme.
*/
@trusted
- pure ubyte[] decode(R1, R2)(in R1 source, return scope R2 buffer) if (isArray!R1 && is(ElementType!R1 : dchar) &&
- is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
+ pure ubyte[] decode(R1, R2)(in R1 source, return scope R2 buffer)
+ if (isArray!R1 && is(ElementType!R1 : dchar) &&
+ is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
in
{
assert(buffer.length >= realDecodeLength(source), "Insufficient buffer for decoding");
/**
* ditto
*/
- ubyte[] decode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 &&
- is(ElementType!R1 : dchar) && hasLength!R1 &&
- is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
+ ubyte[] decode(R1, R2)(R1 source, R2 buffer)
+ if (!isArray!R1 && isInputRange!R1 &&
+ is(ElementType!R1 : dchar) && hasLength!R1 &&
+ is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
in
{
assert(buffer.length >= decodeLength(source.length), "Insufficient buffer for decoding");
* base alphabet of the current Base64 encoding scheme.
*/
size_t decode(R1, R2)(in R1 source, auto ref R2 range)
- if (isArray!R1 && is(ElementType!R1 : dchar) &&
- !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
+ if (isArray!R1 && is(ElementType!R1 : dchar) &&
+ !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
out(result)
{
immutable expect = realDecodeLength(source);
* ditto
*/
size_t decode(R1, R2)(R1 source, auto ref R2 range)
- if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : dchar) &&
- hasLength!R1 && !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
+ if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : dchar) &&
+ hasLength!R1 && !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
out(result)
{
// @@@BUG@@@ Workaround for DbC problem.
* A newly-allocated `ubyte[]` buffer containing the decoded string.
*/
@safe
- pure ubyte[] decode(Range)(Range source) if (isArray!Range && is(ElementType!Range : dchar))
+ pure ubyte[] decode(Range)(Range source)
+ if (isArray!Range && is(ElementType!Range : dchar))
{
return decode(source, new ubyte[decodeLength(source.length)]);
}
/**
* ditto
*/
- ubyte[] decode(Range)(Range source) if (!isArray!Range && isInputRange!Range &&
- is(ElementType!Range : dchar) && hasLength!Range)
+ ubyte[] decode(Range)(Range source)
+ if (!isArray!Range && isInputRange!Range &&
+ is(ElementType!Range : dchar) && hasLength!Range)
{
return decode(source, new ubyte[decodeLength(source.length)]);
}
* Note: This struct is not intended to be created in user code directly;
* use the $(LREF decoder) function instead.
*/
- struct Decoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(char)[]) ||
- is(ElementType!Range : const(ubyte)[])))
+ struct Decoder(Range)
+ if (isInputRange!Range && (is(ElementType!Range : const(char)[]) ||
+ is(ElementType!Range : const(ubyte)[])))
{
private:
Range range_;
* Note: This struct is not intended to be created in user code directly;
* use the $(LREF decoder) function instead.
*/
- struct Decoder(Range) if (isInputRange!Range && is(ElementType!Range : char))
+ struct Decoder(Range)
+ if (isInputRange!Range && is(ElementType!Range : char))
{
private:
Range range_;
* }
* -----
*/
- Decoder!(Range) decoder(Range)(Range range) if (isInputRange!Range)
+ Decoder!(Range) decoder(Range)(Range range)
+ if (isInputRange!Range)
{
return typeof(return)(range);
}
* Throws:
* $(REF ConvException, std,conv) if the string doesn't represent a valid number
*/
- this(Range)(Range s) if (
- isBidirectionalRange!Range &&
+ this(Range)(Range s)
+ if (isBidirectionalRange!Range &&
isSomeChar!(ElementType!Range) &&
!isInfinite!Range &&
!isNarrowString!Range)
* (ignored when magnitude is zero)
* magnitude = a finite range of unsigned integers
*/
- this(Range)(bool isNegative, Range magnitude) if (
- isInputRange!Range &&
+ this(Range)(bool isNegative, Range magnitude)
+ if (isInputRange!Range &&
isUnsigned!(ElementType!Range) &&
(hasLength!Range || isForwardRange!Range) &&
!isInfinite!Range)
}
/// Construct a `BigInt` from a built-in integral type.
- this(T)(T x) pure nothrow @safe if (isIntegral!T)
+ this(T)(T x) pure nothrow @safe
+ if (isIntegral!T)
{
data = data.init; // @@@: Workaround for compiler bug
opAssign(x);
}
/// Construct a `BigInt` from another `BigInt`.
- this(T)(T x) pure nothrow @safe if (is(immutable T == immutable BigInt))
+ this(T)(T x) pure nothrow @safe
+ if (is(immutable T == immutable BigInt))
{
opAssign(x);
}
}
/// Assignment from built-in integer types.
- BigInt opAssign(T)(T x) pure nothrow @safe if (isIntegral!T)
+ BigInt opAssign(T)(T x) pure nothrow @safe
+ if (isIntegral!T)
{
data = cast(ulong) absUnsign(x);
sign = (x < 0);
* `BigInt op= integer`.
*/
BigInt opOpAssign(string op, T)(T y) pure nothrow @safe return scope
- if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%"
- || op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T)
+ if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%"
+ || op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T)
{
ulong u = absUnsign(y);
* Implements assignment operators of the form `BigInt op= BigInt`.
*/
BigInt opOpAssign(string op, T)(T y) pure nothrow @safe return scope
- if ((op=="+" || op== "-" || op=="*" || op=="|" || op=="&" || op=="^" || op=="/" || op=="%")
- && is (T: BigInt))
+ if ((op=="+" || op== "-" || op=="*" || op=="|" || op=="&" || op=="^" || op=="/" || op=="%") && is (T: BigInt))
{
static if (op == "+")
{
* Implements binary operators between `BigInt`s.
*/
BigInt opBinary(string op, T)(T y) pure nothrow @safe const return scope
- if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" ||
- op=="/" || op=="%")
- && is (T: BigInt))
+ if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" ||
+ op=="/" || op=="%") && is (T: BigInt))
{
BigInt r = this;
return r.opOpAssign!(op)(y);
* Implements binary operators between `BigInt`'s and built-in integers.
*/
BigInt opBinary(string op, T)(T y) pure nothrow @safe const return scope
- if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" ||
- op=="^"|| op==">>" || op=="<<" || op=="^^")
- && isIntegral!T)
+ if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" ||
+ op=="^"|| op==">>" || op=="<<" || op=="^^")
+ && isIntegral!T)
{
BigInt r = this;
r.opOpAssign!(op)(y);
)
*/
auto opBinary(string op, T)(T y) pure nothrow @safe const
- if (op == "%" && isIntegral!T)
+ if (op == "%" && isIntegral!T)
{
assert(y != 0, "% 0 not allowed");
`BigInt` on the right-hand side.
*/
BigInt opBinaryRight(string op, T)(T y) pure nothrow @safe const
- if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T)
+ if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T)
{
return opBinary!(op)(y);
}
// BigInt = integer op BigInt
/// ditto
BigInt opBinaryRight(string op, T)(T y) pure nothrow @safe const
- if (op == "-" && isIntegral!T)
+ if (op == "-" && isIntegral!T)
{
ulong u = absUnsign(y);
BigInt r;
// integer = integer op BigInt
/// ditto
T opBinaryRight(string op, T)(T x) pure nothrow @safe const
- if ((op=="%" || op=="/") && isIntegral!T)
+ if ((op=="%" || op=="/") && isIntegral!T)
{
checkDivByZero();
/**
Implements `BigInt` unary operators.
*/
- BigInt opUnary(string op)() pure nothrow @safe const if (op=="+" || op=="-" || op=="~")
+ BigInt opUnary(string op)() pure nothrow @safe const
+ if (op=="+" || op=="-" || op=="~")
{
static if (op=="-")
{
// non-const unary operations
/// ditto
- BigInt opUnary(string op)() pure nothrow @safe if (op=="++" || op=="--")
+ BigInt opUnary(string op)() pure nothrow @safe
+ if (op=="++" || op=="--")
{
static if (op=="++")
{
}
/// ditto
- bool opEquals(T)(const T y) const pure nothrow @nogc @safe if (isIntegral!T)
+ bool opEquals(T)(const T y) const pure nothrow @nogc @safe
+ if (isIntegral!T)
{
if (sign != (y<0))
return 0;
}
/// ditto
- bool opEquals(T)(const T y) const pure nothrow @nogc if (isFloatingPoint!T)
+ bool opEquals(T)(const T y) const pure nothrow @nogc
+ if (isFloatingPoint!T)
{
return 0 == opCmp(y);
}
/**
Implements casting to floating point types.
*/
- T opCast(T)() @safe nothrow @nogc const if (isFloatingPoint!T)
+ T opCast(T)() @safe nothrow @nogc const
+ if (isFloatingPoint!T)
{
return toFloat!(T, "nearest");
}
}
/// ditto
- int opCmp(T)(const T y) pure nothrow @nogc @safe const if (isIntegral!T)
+ int opCmp(T)(const T y) pure nothrow @nogc @safe const
+ if (isIntegral!T)
{
if (sign != (y<0) )
return sign ? -1 : 1;
return sign? -cmp: cmp;
}
/// ditto
- int opCmp(T)(const T y) nothrow @nogc @safe const if (isFloatingPoint!T)
+ int opCmp(T)(const T y) nothrow @nogc @safe const
+ if (isFloatingPoint!T)
{
import core.bitop : bsr;
import std.math.operations : cmp;
* Support for unary operator ~ for `BitArray`.
*/
BitArray opUnary(string op)() const pure nothrow
- if (op == "~")
+ if (op == "~")
{
auto dim = this.dim;
* Support for binary bitwise operators for `BitArray`.
*/
BitArray opBinary(string op)(const BitArray e2) const pure nothrow
- if (op == "-" || op == "&" || op == "|" || op == "^")
+ if (op == "-" || op == "&" || op == "|" || op == "^")
in
{
assert(e2.length == _len, "e2 must have the same length as this");
* Support for operator op= for `BitArray`.
*/
BitArray opOpAssign(string op)(const BitArray e2) @nogc pure nothrow return scope
- if (op == "-" || op == "&" || op == "|" || op == "^")
+ if (op == "-" || op == "&" || op == "|" || op == "^")
in
{
assert(e2.length == _len, "e2 must have the same length as this");
* concatenation semantics are not followed)
*/
BitArray opOpAssign(string op)(bool b) pure nothrow return scope
- if (op == "~")
+ if (op == "~")
{
length = _len + 1;
this[_len - 1] = b;
* ditto
*/
BitArray opOpAssign(string op)(BitArray b) pure nothrow return scope
- if (op == "~")
+ if (op == "~")
{
auto istart = _len;
length = _len + b.length;
* Support for binary operator ~ for `BitArray`.
*/
BitArray opBinary(string op)(bool b) const pure nothrow
- if (op == "~")
+ if (op == "~")
{
BitArray r;
/** ditto */
BitArray opBinaryRight(string op)(bool b) const pure nothrow
- if (op == "~")
+ if (op == "~")
{
BitArray r;
/** ditto */
BitArray opBinary(string op)(BitArray b) const pure nothrow
- if (op == "~")
+ if (op == "~")
{
BitArray r;
* preserve bits past the end of the array.)
*/
void opOpAssign(string op)(size_t nbits) @nogc pure nothrow
- if (op == "<<")
+ if (op == "<<")
{
size_t wordsToShift = nbits / bitsPerSizeT;
size_t bitsToShift = nbits % bitsPerSizeT;
* preserve bits past the end of the array.)
*/
void opOpAssign(string op)(size_t nbits) @nogc pure nothrow
- if (op == ">>")
+ if (op == ">>")
{
size_t wordsToShift = nbits / bitsPerSizeT;
size_t bitsToShift = nbits % bitsPerSizeT;
}
-private union EndianSwapper(T)
-if (canSwapEndianness!T)
-{
- T value;
- ubyte[T.sizeof] array;
-
- static if (is(immutable FloatingPointTypeOf!(T) == immutable float))
- uint intValue;
- else static if (is(immutable FloatingPointTypeOf!(T) == immutable double))
- ulong intValue;
-
-}
-
-// Can't use EndianSwapper union during CTFE.
-private auto ctfeRead(T)(const ubyte[T.sizeof] array)
-if (__traits(isIntegral, T))
-{
- Unqual!T result;
- version (LittleEndian)
- foreach_reverse (b; array)
- result = cast() cast(T) ((result << 8) | b);
- else
- foreach (b; array)
- result = cast() cast(T) ((result << 8) | b);
- return cast(T) result;
-}
-
-// Can't use EndianSwapper union during CTFE.
-private auto ctfeBytes(T)(const T value)
-if (__traits(isIntegral, T))
-{
- ubyte[T.sizeof] result;
- Unqual!T tmp = value;
- version (LittleEndian)
- {
- foreach (i; 0 .. T.sizeof)
- {
- result[i] = cast(ubyte) tmp;
- tmp = cast() cast(T) (tmp >>> 8);
- }
- }
- else
- {
- foreach_reverse (i; 0 .. T.sizeof)
- {
- result[i] = cast(ubyte) tmp;
- tmp = cast()(T) (tmp >>> 8);
- }
- }
- return result;
-}
-
/++
Converts the given value from the native endianness to big endian and
returns it as a `ubyte[n]` where `n` is the size of the given type.
and therefore could vary from machine to machine (which could make it
unusable if you tried to transfer it to another machine).
+/
-auto nativeToBigEndian(T)(const T val) @safe pure nothrow @nogc
+auto nativeToBigEndian(T)(const T val) @trusted pure nothrow @nogc
if (canSwapEndianness!T)
{
- version (LittleEndian)
- return nativeToEndianImpl!true(val);
+ static if (isFloatOrDouble!T)
+ return nativeToBigEndian(*cast(const UnsignedOfSize!(T.sizeof)*) &val);
else
- return nativeToEndianImpl!false(val);
+ {
+ enum len = T.sizeof;
+ ubyte[len] retval;
+
+ static foreach (i; 0 .. len)
+ retval[i] = cast(ubyte)(val >> (len - i - 1) * 8);
+
+ return retval;
+ }
}
///
assert(cd == bigEndianToNative!double(swappedCD));
}
-private auto nativeToEndianImpl(bool swap, T)(const T val) @safe pure nothrow @nogc
-if (__traits(isIntegral, T))
-{
- if (!__ctfe)
- {
- static if (swap)
- return EndianSwapper!T(swapEndian(val)).array;
- else
- return EndianSwapper!T(val).array;
- }
- else
- {
- // Can't use EndianSwapper in CTFE.
- static if (swap)
- return ctfeBytes(swapEndian(val));
- else
- return ctfeBytes(val);
- }
-}
-
@safe unittest
{
import std.meta;
because the FPU will mess up any swapped floating point values. So, you
can't actually have swapped floating point values as floating point values).
+/
-T bigEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+T bigEndianToNative(T, size_t n)(ubyte[n] val) @trusted pure nothrow @nogc
if (canSwapEndianness!T && n == T.sizeof)
{
- version (LittleEndian)
- return endianToNativeImpl!(true, T, n)(val);
+ static if (isFloatOrDouble!T)
+ {
+ auto retval = bigEndianToNative!(UnsignedOfSize!(T.sizeof))(val);
+ return *cast(const T*) &retval;
+ }
else
- return endianToNativeImpl!(false, T, n)(val);
+ {
+ enum len = T.sizeof;
+ alias U = UnsignedOfSize!len;
+ U retval;
+
+ static foreach (i; 0 .. len)
+ retval |= (cast(U) val[i]) << (len - i - 1) * 8;
+
+ return cast(T) retval;
+ }
}
///
because the FPU will mess up any swapped floating point values. So, you
can't actually have swapped floating point values as floating point values).
+/
-auto nativeToLittleEndian(T)(const T val) @safe pure nothrow @nogc
+auto nativeToLittleEndian(T)(const T val) @trusted pure nothrow @nogc
if (canSwapEndianness!T)
{
- version (BigEndian)
- return nativeToEndianImpl!true(val);
+ static if (isFloatOrDouble!T)
+ return nativeToLittleEndian(*cast(const UnsignedOfSize!(T.sizeof)*) &val);
else
- return nativeToEndianImpl!false(val);
+ {
+ enum len = T.sizeof;
+ ubyte[len] retval;
+
+ static foreach (i; 0 .. len)
+ retval[i] = cast(ubyte)(val >> i * 8);
+
+ return retval;
+ }
}
///
ubyte[4] swappedI = nativeToLittleEndian(i);
assert(i == littleEndianToNative!int(swappedI));
+ float f = 123.45f;
+ ubyte[4] swappedF = nativeToLittleEndian(f);
+ assert(f == littleEndianToNative!float(swappedF));
+
+ const float cf = 123.45f;
+ ubyte[4] swappedCF = nativeToLittleEndian(cf);
+ assert(cf == littleEndianToNative!float(swappedCF));
+
double d = 123.45;
ubyte[8] swappedD = nativeToLittleEndian(d);
assert(d == littleEndianToNative!double(swappedD));
+
+ const double cd = 123.45;
+ ubyte[8] swappedCD = nativeToLittleEndian(cd);
+ assert(cd == littleEndianToNative!double(swappedCD));
}
@safe unittest
and therefore could vary from machine to machine (which could make it
unusable if you tried to transfer it to another machine).
+/
-T littleEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+T littleEndianToNative(T, size_t n)(ubyte[n] val) @trusted pure nothrow @nogc
if (canSwapEndianness!T && n == T.sizeof)
{
- version (BigEndian)
- return endianToNativeImpl!(true, T, n)(val);
+ static if (isFloatOrDouble!T)
+ {
+ auto retval = littleEndianToNative!(UnsignedOfSize!(T.sizeof))(val);
+ return *cast(const T*) &retval;
+ }
else
- return endianToNativeImpl!(false, T, n)(val);
+ {
+ enum len = T.sizeof;
+ alias U = UnsignedOfSize!len;
+ U retval;
+
+ static foreach (i; 0 .. len)
+ retval |= (cast(U) val[i]) << i * 8;
+
+ return cast(T) retval;
+ }
}
///
assert(c == littleEndianToNative!dchar(swappedC));
}
-private T endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @nogc nothrow pure @trusted
-if (__traits(isIntegral, T) && n == T.sizeof)
-{
- if (!__ctfe)
- {
- EndianSwapper!T es = { array: val };
- static if (swap)
- return swapEndian(es.value);
- else
- return es.value;
- }
- else
- {
- static if (swap)
- return swapEndian(ctfeRead!T(val));
- else
- return ctfeRead!T(val);
- }
-}
-
-private auto nativeToEndianImpl(bool swap, T)(const T val) @trusted pure nothrow @nogc
-if (isFloatOrDouble!T)
-{
- if (!__ctfe)
- {
- EndianSwapper!T es = EndianSwapper!T(val);
- static if (swap)
- es.intValue = swapEndian(es.intValue);
- return es.array;
- }
- else
- {
- static if (T.sizeof == 4)
- uint intValue = *cast(const uint*) &val;
- else static if (T.sizeof == 8)
- ulong intValue = *cast(const ulong*) & val;
- static if (swap)
- intValue = swapEndian(intValue);
- return ctfeBytes(intValue);
- }
-}
-
-private auto endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @trusted pure nothrow @nogc
-if (isFloatOrDouble!T && n == T.sizeof)
-{
- if (!__ctfe)
- {
- EndianSwapper!T es = { array: val };
- static if (swap)
- es.intValue = swapEndian(es.intValue);
- return es.value;
- }
- else
- {
- static if (n == 4)
- uint x = ctfeRead!uint(val);
- else static if (n == 8)
- ulong x = ctfeRead!ulong(val);
- static if (swap)
- x = swapEndian(x);
- return *cast(T*) &x;
- }
-}
-
private template isFloatOrDouble(T)
{
enum isFloatOrDouble = isFloatingPoint!T &&
}
}
+private template UnsignedOfSize(size_t n)
+{
+ static if (n == 8)
+ alias UnsignedOfSize = ulong;
+ else static if (n == 4)
+ alias UnsignedOfSize = uint;
+ else static if (n == 2)
+ alias UnsignedOfSize = ushort;
+ else static if (n == 1)
+ alias UnsignedOfSize = ubyte;
+ else
+ alias UnsignedOfSize = void;
+}
+
+@safe unittest
+{
+ static assert(is(UnsignedOfSize!(byte.sizeof) == ubyte));
+ static assert(is(UnsignedOfSize!(ubyte.sizeof) == ubyte));
+ static assert(is(UnsignedOfSize!(short.sizeof) == ushort));
+ static assert(is(UnsignedOfSize!(ushort.sizeof) == ushort));
+ static assert(is(UnsignedOfSize!(int.sizeof) == uint));
+ static assert(is(UnsignedOfSize!(uint.sizeof) == uint));
+ static assert(is(UnsignedOfSize!(long.sizeof) == ulong));
+ static assert(is(UnsignedOfSize!(ulong.sizeof) == ulong));
+
+ static assert(is(UnsignedOfSize!(bool.sizeof) == ubyte));
+ static assert(is(UnsignedOfSize!(char.sizeof) == ubyte));
+ static assert(is(UnsignedOfSize!(wchar.sizeof) == ushort));
+ static assert(is(UnsignedOfSize!(dchar.sizeof) == uint));
+
+ static assert(is(UnsignedOfSize!(float.sizeof) == uint));
+ static assert(is(UnsignedOfSize!(double.sizeof) == ulong));
+
+ static assert(is(UnsignedOfSize!10 == void));
+}
+
/++
Takes a range of `ubyte`s and converts the first `T.sizeof` bytes to
`T`. The value returned is converted from the given endianness to the
static struct Hook1
{
uint calls;
- auto hookOpUnary(string op, T)(T value) if (op == "-")
+ auto hookOpUnary(string op, T)(T value)
+ if (op == "-")
{
++calls;
return T(42);
}
- auto hookOpUnary(string op, T)(T value) if (op == "~")
+ auto hookOpUnary(string op, T)(T value)
+ if (op == "~")
{
++calls;
return T(43);
static struct Hook2
{
uint calls;
- void hookOpUnary(string op, T)(ref T value) if (op == "++")
+ void hookOpUnary(string op, T)(ref T value)
+ if (op == "++")
{
++calls;
--value;
}
- void hookOpUnary(string op, T)(ref T value) if (op == "--")
+ void hookOpUnary(string op, T)(ref T value)
+ if (op == "--")
{
++calls;
++value;
/// ditto
void toString(Writer, Char)(scope Writer w, scope const ref FormatSpec!Char formatSpec) const
- if (isOutputRange!(Writer, const(Char)[]))
+ if (isOutputRange!(Writer, const(Char)[]))
{
import std.format.write : formatValue;
import std.math.traits : signbit;
// +complex
Complex opUnary(string op)() const
- if (op == "+")
+ if (op == "+")
{
return this;
}
// -complex
Complex opUnary(string op)() const
- if (op == "-")
+ if (op == "-")
{
return Complex(-re, -im);
}
// complex op numeric
Complex!(CommonType!(T,R)) opBinary(string op, R)(const R r) const
- if (isNumeric!R)
+ if (isNumeric!R)
{
alias C = typeof(return);
auto w = C(this.re, this.im);
// numeric + complex, numeric * complex
Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const
- if ((op == "+" || op == "*") && (isNumeric!R))
+ if ((op == "+" || op == "*") && (isNumeric!R))
{
return opBinary!(op)(r);
}
// numeric - complex
Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const
- if (op == "-" && isNumeric!R)
+ if (op == "-" && isNumeric!R)
{
return Complex(r - re, -im);
}
// numeric / complex
Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const
- if (op == "/" && isNumeric!R)
+ if (op == "/" && isNumeric!R)
{
version (FastMath)
{
// numeric ^^ complex
Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R lhs) const
- if (op == "^^" && isNumeric!R)
+ if (op == "^^" && isNumeric!R)
{
import core.math : cos, sin;
import std.math.exponential : exp, log;
// complex += complex, complex -= complex
ref Complex opOpAssign(string op, C)(const C z)
- if ((op == "+" || op == "-") && is(C R == Complex!R))
+ if ((op == "+" || op == "-") && is(C R == Complex!R))
{
mixin ("re "~op~"= z.re;");
mixin ("im "~op~"= z.im;");
// complex *= complex
ref Complex opOpAssign(string op, C)(const C z)
- if (op == "*" && is(C R == Complex!R))
+ if (op == "*" && is(C R == Complex!R))
{
auto temp = re*z.re - im*z.im;
im = im*z.re + re*z.im;
// complex /= complex
ref Complex opOpAssign(string op, C)(const C z)
- if (op == "/" && is(C R == Complex!R))
+ if (op == "/" && is(C R == Complex!R))
{
version (FastMath)
{
// complex ^^= complex
ref Complex opOpAssign(string op, C)(const C z)
- if (op == "^^" && is(C R == Complex!R))
+ if (op == "^^" && is(C R == Complex!R))
{
import core.math : cos, sin;
import std.math.exponential : exp, log;
// complex += numeric, complex -= numeric
ref Complex opOpAssign(string op, U : T)(const U a)
- if (op == "+" || op == "-")
+ if (op == "+" || op == "-")
{
mixin ("re "~op~"= a;");
return this;
// complex *= numeric, complex /= numeric
ref Complex opOpAssign(string op, U : T)(const U a)
- if (op == "*" || op == "/")
+ if (op == "*" || op == "/")
{
mixin ("re "~op~"= a;");
mixin ("im "~op~"= a;");
// complex ^^= real
ref Complex opOpAssign(string op, R)(const R r)
- if (op == "^^" && isFloatingPoint!R)
+ if (op == "^^" && isFloatingPoint!R)
{
import core.math : cos, sin;
immutable ab = abs(this)^^r;
// complex ^^= int
ref Complex opOpAssign(string op, U)(const U i)
- if (op == "^^" && isIntegral!U)
+ if (op == "^^" && isIntegral!U)
{
switch (i)
{
MsgType type;
Variant data;
- this(T...)(MsgType t, T vals) if (T.length > 0)
+ this(T...)(MsgType t, T vals)
+ if (T.length > 0)
{
static if (T.length == 1)
{
/**
Constructor taking a number of nodes
*/
- this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
+ this(U)(U[] values...)
+ if (isImplicitlyConvertible!(U, T))
{
insertBack(values);
}
/**
$(D k in container) returns true if the given key is in the container.
*/
- bool opBinaryRight(string op)(KeyType k) if (op == "in")
+ bool opBinaryRight(string op)(KeyType k)
+ if (op == "in")
{
assert(0, "Not implemented");
}
Complexity: $(BIGOH n + m), where m is the number of elements in $(D
stuff)
*/
- TotalContainer opBinary(string op)(Stuff rhs) if (op == "~")
+ TotalContainer opBinary(string op)(Stuff rhs)
+ if (op == "~")
{
assert(0, "Not implemented");
}
/// ditto
- TotalContainer opBinaryRight(string op)(Stuff lhs) if (op == "~")
+ TotalContainer opBinaryRight(string op)(Stuff lhs)
+ if (op == "~")
{
assert(0, "Not implemented");
}
/**
Forwards to $(D insertAfter(this[], stuff)).
*/
- void opOpAssign(string op)(Stuff stuff) if (op == "~")
+ void opOpAssign(string op)(Stuff stuff)
+ if (op == "~")
{
assert(0, "Not implemented");
}
Complexity: $(BIGOH log(n))
+/
- bool opBinaryRight(string op)(Elem e) const if (op == "in")
+ bool opBinaryRight(string op)(Elem e) const
+ if (op == "in")
{
return _find(e) !is null;
}
*
* Complexity: $(BIGOH log(n))
*/
- size_t stableInsert(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, Elem))
+ size_t stableInsert(Stuff)(Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, Elem))
{
static if (allowDuplicates)
{
* Complexity: $(BIGOH m * log(n))
*/
size_t stableInsert(Stuff)(scope Stuff stuff)
- if (isInputRange!Stuff &&
- isImplicitlyConvertible!(ElementType!Stuff, Elem))
+ if (isInputRange!Stuff &&
+ isImplicitlyConvertible!(ElementType!Stuff, Elem))
{
size_t result = 0;
static if (allowDuplicates)
--------------------
+/
size_t removeKey(U...)(U elems)
- if (allSatisfy!(isImplicitlyConvertibleToElem, U))
+ if (allSatisfy!(isImplicitlyConvertibleToElem, U))
{
Elem[U.length] toRemove = [elems];
return removeKey(toRemove[]);
/++ Ditto +/
size_t removeKey(U)(scope U[] elems)
- if (isImplicitlyConvertible!(U, Elem))
+ if (isImplicitlyConvertible!(U, Elem))
{
immutable lenBefore = length;
/++ Ditto +/
size_t removeKey(Stuff)(Stuff stuff)
- if (isInputRange!Stuff &&
- isImplicitlyConvertible!(ElementType!Stuff, Elem) &&
- !isDynamicArray!Stuff)
+ if (isInputRange!Stuff &&
+ isImplicitlyConvertible!(ElementType!Stuff, Elem) &&
+ !isDynamicArray!Stuff)
{
import std.array : array;
//We use array in case stuff is a Range from this RedBlackTree - either
/**
* Constructor. Pass in a range of elements to initialize the tree with.
*/
- this(Stuff)(Stuff stuff) if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem))
+ this(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem))
{
_setup();
stableInsert(stuff);
/**
Constructor taking a number of nodes
*/
- this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
+ this(U)(U[] values...)
+ if (isImplicitlyConvertible!(U, T))
{
insertFront(values);
}
import std.traits : isDynamicArray;
auto make(Range)(Range range)
- if (!isDynamicArray!Range && isInputRange!Range && !isInfinite!Range)
+ if (!isDynamicArray!Range && isInputRange!Range && !isInfinite!Range)
{
import std.range : ElementType;
return .make!(Container!(ElementType!Range, Args))(range);
}
auto make(T)(T[] items...)
- if (!isInfinite!T)
+ if (!isInfinite!T)
{
return .make!(Container!(T, Args))(items);
}
template to(T)
{
T to(A...)(A args)
- if (A.length > 0)
+ if (A.length > 0)
{
return toImpl!T(args);
}
// Fix https://issues.dlang.org/show_bug.cgi?id=6175
T to(S)(ref S arg)
- if (isStaticArray!S)
+ if (isStaticArray!S)
{
return toImpl!T(arg);
}
// Fix https://issues.dlang.org/show_bug.cgi?id=16108
T to(S)(ref S arg)
- if (isAggregateType!S && !isCopyable!S)
+ if (isAggregateType!S && !isCopyable!S)
{
return toImpl!T(arg);
}
auto result = ()@trusted{ return cast(T) value; }();
if (!result && value)
{
- throw new ConvException("Cannot convert object of static type "
- ~S.classinfo.name~" and dynamic type "~value.classinfo.name
- ~" to type "~T.classinfo.name);
+ string name(TypeInfo ti) @trusted
+ {
+ while (auto tc = (cast(TypeInfo_Const) ti))
+ {
+ ti = tc.base;
+ }
+ if (auto tinf = cast(TypeInfo_Interface) ti)
+ {
+ ti = tinf.info;
+ }
+ TypeInfo_Class tc = cast(TypeInfo_Class) ti;
+ assert(tc);
+ return tc.name;
+ }
+ throw new ConvException("Cannot convert object of static type " ~
+ name(typeid(S)) ~ " and dynamic type " ~ name(typeid(value)) ~ " to type " ~ name(typeid(T)));
}
return result;
}
+/
ref DateTime add(string units)
(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
- if (units == "years" || units == "months")
+ if (units == "years" || units == "months")
{
_date.add!units(value, allowOverflow);
return this;
+/
ref DateTime roll(string units)
(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
- if (units == "years" || units == "months")
+ if (units == "years" || units == "months")
{
_date.roll!units(value, allowOverflow);
return this;
A reference to the `DateTime` (`this`).
+/
ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
- if (units == "days")
+ if (units == "days")
{
_date.roll!"days"(value);
return this;
/// ditto
ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
- if (units == "hours" ||
- units == "minutes" ||
- units == "seconds")
+ if (units == "hours" ||
+ units == "minutes" ||
+ units == "seconds")
{
_tod.roll!units(value);
return this;
this $(LREF DateTime).
+/
DateTime opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
- if (op == "+" || op == "-")
+ if (op == "+" || op == "-")
{
DateTime retval = this;
immutable seconds = duration.total!"seconds";
$(LREF DateTime).
+/
ref DateTime opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
- if (op == "+" || op == "-")
+ if (op == "+" || op == "-")
{
import core.time : convert;
import std.format : format;
)
+/
Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc
- if (op == "-")
+ if (op == "-")
{
immutable dateResult = _date - rhs.date;
immutable todResult = _tod - rhs._tod;
be valid.
+/
static DateTime fromISOString(S)(scope const S isoString) @safe pure
- if (isSomeString!S)
+ if (isSomeString!S)
{
import std.algorithm.searching : countUntil;
import std.exception : enforce;
would not be valid.
+/
static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure
- if (isSomeString!(S))
+ if (isSomeString!(S))
{
import std.algorithm.searching : countUntil;
import std.exception : enforce;
would not be valid.
+/
static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure
- if (isSomeString!(S))
+ if (isSomeString!(S))
{
import std.algorithm.searching : countUntil;
import std.exception : enforce;
+/
@safe pure nothrow @nogc
ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
- if (units == "years")
+ if (units == "years")
{
_year += value;
// Shares documentation with "years" version.
@safe pure nothrow @nogc
ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
- if (units == "months")
+ if (units == "months")
{
auto years = months / 12;
months %= 12;
+/
@safe pure nothrow @nogc
ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
- if (units == "years")
+ if (units == "years")
{
return add!"years"(value, allowOverflow);
}
// Shares documentation with "years" version.
@safe pure nothrow @nogc
ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
- if (units == "months")
+ if (units == "months")
{
months %= 12;
auto newMonth = _month + months;
A reference to the `Date` (`this`).
+/
ref Date roll(string units)(long days) @safe pure nothrow @nogc
- if (units == "days")
+ if (units == "days")
{
immutable limit = maxDay(_year, _month);
days %= limit;
this $(LREF Date).
+/
Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
- if (op == "+" || op == "-")
+ if (op == "+" || op == "-")
{
Date retval = this;
immutable days = duration.total!"days";
this $(LREF Date).
+/
ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
- if (op == "+" || op == "-")
+ if (op == "+" || op == "-")
{
immutable days = duration.total!"days";
mixin("return _addDays(" ~ op ~ "days);");
)
+/
Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc
- if (op == "-")
+ if (op == "-")
{
import core.time : dur;
return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal);
valid.
+/
static Date fromISOString(S)(scope const S isoString) @safe pure
- if (isSomeString!S)
+ if (isSomeString!S)
{
import std.algorithm.searching : startsWith;
import std.conv : to, text, ConvException;
would not be valid.
+/
static Date fromISOExtString(S)(scope const S isoExtString) @safe pure
- if (isSomeString!(S))
+ if (isSomeString!(S))
{
import std.algorithm.searching : startsWith;
import std.conv : to, ConvException;
be valid.
+/
static Date fromSimpleString(S)(scope const S simpleString) @safe pure
- if (isSomeString!(S))
+ if (isSomeString!(S))
{
import std.algorithm.searching : startsWith;
import std.conv : to, ConvException;
A reference to the `TimeOfDay` (`this`).
+/
ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
- if (units == "hours")
+ if (units == "hours")
{
import core.time : dur;
return this += dur!"hours"(value);
/// ditto
ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
- if (units == "minutes" || units == "seconds")
+ if (units == "minutes" || units == "seconds")
{
import std.format : format;
this $(LREF TimeOfDay).
+/
TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
- if (op == "+" || op == "-")
+ if (op == "+" || op == "-")
{
TimeOfDay retval = this;
immutable seconds = duration.total!"seconds";
this $(LREF TimeOfDay).
+/
ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
- if (op == "+" || op == "-")
+ if (op == "+" || op == "-")
{
immutable seconds = duration.total!"seconds";
mixin("return _addSeconds(" ~ op ~ "seconds);");
rhs = The $(LREF TimeOfDay) to subtract from this one.
+/
Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc
- if (op == "-")
+ if (op == "-")
{
immutable lhsSec = _hour * 3600 + _minute * 60 + _second;
immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second;
not be valid.
+/
static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure
- if (isSomeString!S)
+ if (isSomeString!S)
{
import std.conv : to, text, ConvException;
import std.exception : enforce;
would not be valid.
+/
static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure
- if (isSomeString!S)
+ if (isSomeString!S)
{
import std.conv : ConvException, text, to;
import std.string : strip;
--------------------
+/
this(U)(scope const TP begin, scope const U end) pure
- if (is(immutable TP == immutable U))
+ if (is(immutable TP == immutable U))
{
if (!_valid(begin, end))
throw new DateTimeException("Arguments would result in an invalid Interval.");
--------------------
+/
this(D)(scope const TP begin, scope const D duration) pure
- if (__traits(compiles, begin + duration))
+ if (__traits(compiles, begin + duration))
{
_begin = cast(TP) begin;
_end = begin + duration;
--------------------
+/
void shift(D)(D duration) pure
- if (__traits(compiles, begin + duration))
+ if (__traits(compiles, begin + duration))
{
_enforceNotEmpty();
--------------------
+/
void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
- if (isIntegral!T)
+ if (isIntegral!T)
{
_enforceNotEmpty();
--------------------
+/
void expand(D)(D duration, Direction dir = Direction.both) pure
- if (__traits(compiles, begin + duration))
+ if (__traits(compiles, begin + duration))
{
_enforceNotEmpty();
--------------------
+/
void shift(D)(D duration) pure nothrow
- if (__traits(compiles, begin + duration))
+ if (__traits(compiles, begin + duration))
{
_begin += duration;
}
--------------------
+/
void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
- if (isIntegral!T)
+ if (isIntegral!T)
{
auto begin = _begin;
--------------------
+/
void expand(D)(D duration) pure nothrow
- if (__traits(compiles, begin + duration))
+ if (__traits(compiles, begin + duration))
{
_begin -= duration;
}
--------------------
+/
void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
- if (isIntegral!T)
+ if (isIntegral!T)
{
auto begin = _begin;
--------------------
+/
void shift(D)(D duration) pure nothrow
- if (__traits(compiles, end + duration))
+ if (__traits(compiles, end + duration))
{
_end += duration;
}
--------------------
+/
void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
- if (isIntegral!T)
+ if (isIntegral!T)
{
auto end = _end;
--------------------
+/
void expand(D)(D duration) pure nothrow
- if (__traits(compiles, end + duration))
+ if (__traits(compiles, end + duration))
{
_end += duration;
}
--------------------
+/
void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
- if (isIntegral!T)
+ if (isIntegral!T)
{
auto end = _end;
this SysTime.
+/
T toUnixTime(T = time_t)() @safe const pure nothrow scope
- if (is(T == int) || is(T == long))
+ if (is(T == int) || is(T == long))
{
return stdTimeToUnixTime!T(_stdTime);
}
causing the month to increment.
+/
ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope
- if (units == "years" || units == "months")
+ if (units == "years" || units == "months")
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
$(LREF SysTime).
+/
ref SysTime roll(string units)(long value) @safe nothrow scope
- if (units == "days")
+ if (units == "days")
{
auto hnsecs = adjTime;
auto gdays = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
// Shares documentation with "days" version.
ref SysTime roll(string units)(long value) @safe nothrow scope
- if (units == "hours" || units == "minutes" || units == "seconds")
+ if (units == "hours" || units == "minutes" || units == "seconds")
{
try
{
// Shares documentation with "days" version.
ref SysTime roll(string units)(long value) @safe nothrow scope
- if (units == "msecs" || units == "usecs" || units == "hnsecs")
+ if (units == "msecs" || units == "usecs" || units == "hnsecs")
{
auto hnsecs = adjTime;
immutable days = splitUnitsFromHNSecs!"days"(hnsecs);
this $(LREF SysTime).
+/
SysTime opBinary(string op)(Duration duration) @safe const pure nothrow return scope
- if (op == "+" || op == "-")
+ if (op == "+" || op == "-")
{
SysTime retval = SysTime(this._stdTime, this._timezone);
immutable hnsecs = duration.total!"hnsecs";
this $(LREF SysTime).
+/
ref SysTime opOpAssign(string op)(Duration duration) @safe pure nothrow scope
- if (op == "+" || op == "-")
+ if (op == "+" || op == "-")
{
immutable hnsecs = duration.total!"hnsecs";
mixin("_stdTime " ~ op ~ "= hnsecs;");
)
+/
Duration opBinary(string op)(SysTime rhs) @safe const pure nothrow scope
- if (op == "-")
+ if (op == "-")
{
return dur!"hnsecs"(_stdTime - rhs._stdTime);
}
Returns a $(REF Date,std,datetime,date) equivalent to this $(LREF SysTime).
+/
Date opCast(T)() @safe const nothrow scope
- if (is(immutable T == immutable Date))
+ if (is(immutable T == immutable Date))
{
return Date(dayOfGregorianCal);
}
$(LREF SysTime).
+/
DateTime opCast(T)() @safe const nothrow scope
- if (is(immutable T == immutable DateTime))
+ if (is(immutable T == immutable DateTime))
{
try
{
$(LREF SysTime).
+/
TimeOfDay opCast(T)() @safe const nothrow scope
- if (is(immutable T == immutable TimeOfDay))
+ if (is(immutable T == immutable TimeOfDay))
{
try
{
// should be allowed, and it doesn't work without this opCast() since opCast()
// has already been defined for other types.
SysTime opCast(T)() @safe const pure nothrow scope
- if (is(immutable T == immutable SysTime))
+ if (is(immutable T == immutable SysTime))
{
return SysTime(_stdTime, _timezone);
}
be valid.
+/
static SysTime fromISOString(S)(scope const S isoString, immutable TimeZone tz = null) @safe
- if (isSomeString!S)
+ if (isSomeString!S)
{
import std.algorithm.searching : startsWith, find;
import std.conv : to;
be valid.
+/
static SysTime fromISOExtString(S)(scope const S isoExtString, immutable TimeZone tz = null) @safe
- if (isSomeString!(S))
+ if (isSomeString!(S))
{
import std.algorithm.searching : countUntil, find;
import std.conv : to;
be valid.
+/
static SysTime fromSimpleString(S)(scope const S simpleString, immutable TimeZone tz = null) @safe
- if (isSomeString!(S))
+ if (isSomeString!(S))
{
import std.algorithm.searching : find;
import std.conv : to;
isoString = A string which represents a time zone in the ISO format.
+/
static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure
- if (isSomeString!S)
+ if (isSomeString!S)
{
import std.algorithm.searching : startsWith;
import std.conv : text, to, ConvException;
isoExtString = A string which represents a time zone in the ISO format.
+/
static immutable(SimpleTimeZone) fromISOExtString(S)(scope S isoExtString) @safe pure
- if (isSomeString!S)
+ if (isSomeString!S)
{
import std.algorithm.searching : startsWith;
import std.conv : ConvException, to;
Reads an int from a TZ file.
+/
static T readVal(T)(ref File tzFile) @trusted
- if ((isIntegral!T || isSomeChar!T) || is(immutable T == immutable bool))
+ if ((isIntegral!T || isSomeChar!T) || is(immutable T == immutable bool))
{
import std.bitmanip : bigEndianToNative;
T[1] buff;
Reads an array of values from a TZ file.
+/
static T readVal(T)(ref File tzFile, size_t length) @trusted
- if (isArray!T)
+ if (isArray!T)
{
auto buff = new T(length);
Reads a `TempTTInfo` from a TZ file.
+/
static T readVal(T)(ref File tzFile) @safe
- if (is(T == TempTTInfo))
+ if (is(T == TempTTInfo))
{
return TempTTInfo(readVal!int(tzFile),
readVal!bool(tzFile),
@system unittest
{
import std.format : format;
- assert("%s".format.ifThrown!Exception(e => e.classinfo.name) == "std.format.FormatException");
+ assert("%s".format.ifThrown!Exception(e => typeid(e).name) == "std.format.FormatException");
}
//Verify Examples
static assert(!__traits(compiles, (new Object()).ifThrown(1)));
//Use a lambda to get the thrown object.
- assert("%s".format().ifThrown(e => e.classinfo.name) == "std.format.FormatException");
+ assert("%s".format().ifThrown(e => typeid(e).name) == "std.format.FormatException");
}
@system unittest
static assert(stateSize!C3 == 2 * size_t.sizeof + char.sizeof);
}
+/**
+State of an allocator `A`.
+
+`AllocatorState!(A).sizeof` is zero for `A` being `NullAllocator`, `Mallocator`,
+`GCAllocator`, and `MMapAllocator` and typically non-zero for the other.
+ */
+mixin template AllocatorState(A)
+if (isAllocator!A)
+{
+ static if (stateSize!A == 0)
+ alias allocator = A.instance;
+ else
+ A allocator;
+}
+
+///
+@safe @nogc nothrow pure
+unittest
+{
+ import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.experimental.allocator.mmap_allocator : MmapAllocator;
+ struct S
+ {
+ mixin AllocatorState!NullAllocator n;
+ mixin AllocatorState!GCAllocator g;
+ mixin AllocatorState!Mallocator m;
+ mixin AllocatorState!MmapAllocator p;
+ }
+ static assert(S.sizeof == 1);
+}
+
/**
Returns `true` if the `Allocator` has the alignment known at compile time;
otherwise it returns `false`.
version (CRuntime_Microsoft)
{
- @nogc nothrow private extern(C) void* _aligned_malloc(size_t, size_t);
- @nogc nothrow private extern(C) void _aligned_free(void *memblock);
- @nogc nothrow private extern(C) void* _aligned_realloc(void *, size_t, size_t);
+ @nogc nothrow pure private extern(C) void* _aligned_malloc(size_t, size_t);
+ @nogc nothrow pure private extern(C) void _aligned_free(void *memblock);
+ @nogc nothrow pure private extern(C) void* _aligned_realloc(void *, size_t, size_t);
}
/**
/**
Forwards to $(D alignedAllocate(bytes, platformAlignment)).
*/
- @trusted @nogc nothrow
+ @trusted @nogc nothrow pure
void[] allocate(size_t bytes) shared
{
if (!bytes) return null;
`__aligned_malloc`) on Windows.
*/
version (Posix)
- @trusted @nogc nothrow
+ @trusted @nogc nothrow pure
void[] alignedAllocate(size_t bytes, uint a) shared
{
import core.stdc.errno : ENOMEM, EINVAL;
return result[0 .. bytes];
}
else version (Windows)
- @trusted @nogc nothrow
+ @trusted @nogc nothrow pure
void[] alignedAllocate(size_t bytes, uint a) shared
{
auto result = _aligned_malloc(bytes, a);
`__aligned_free(b.ptr)`) on Windows.
*/
version (Posix)
- @system @nogc nothrow
+ @system @nogc nothrow pure
bool deallocate(void[] b) shared
{
- import core.stdc.stdlib : free;
- free(b.ptr);
+ import core.memory : pureFree;
+ pureFree(b.ptr);
return true;
}
else version (Windows)
- @system @nogc nothrow
+ @system @nogc nothrow pure
bool deallocate(void[] b) shared
{
_aligned_free(b.ptr);
Should be used with blocks obtained with `allocate` otherwise the custom
alignment passed with `alignedAllocate` can be lost.
*/
- @system @nogc nothrow
+ @system @nogc nothrow pure
bool reallocate(ref void[] b, size_t newSize) shared
{
return alignedReallocate(b, newSize, alignment);
$(D __aligned_realloc(b.ptr, newSize, a))).
*/
version (Windows)
- @system @nogc nothrow
+ @system @nogc nothrow pure
bool alignedReallocate(ref void[] b, size_t s, uint a) shared
{
if (!s)
/// ditto
version (Posix)
- @system @nogc nothrow
+ @system @nogc nothrow pure
bool alignedReallocate(ref void[] b, size_t s, uint a) shared
{
if (!s)
}
///
-@nogc @system nothrow unittest
+pure @nogc @system nothrow unittest
{
auto buffer = AlignedMallocator.instance.alignedAllocate(1024 * 1024 * 4,
128);
}
version (Posix)
-@nogc @system nothrow unittest
+pure @nogc @system nothrow unittest
{
// https://issues.dlang.org/show_bug.cgi?id=16398
// test the "pseudo" alignedReallocate for Posix
}
this(R)(R pathname, SpanMode mode, bool followSymlink)
- if (isSomeFiniteCharInputRange!R)
+ if (isSomeFiniteCharInputRange!R)
{
_mode = mode;
_followSymlink = followSymlink;
static struct G
{
string toString() {return "";}
- void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, string)) {}
+ void toString(Writer)(ref Writer w)
+ if (isOutputRange!(Writer, string)) {}
}
static struct H
{
}
static struct I
{
- void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, string)) {}
+ void toString(Writer)(ref Writer w)
+ if (isOutputRange!(Writer, string)) {}
void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt)
if (isOutputRange!(Writer, string))
{}
static struct G
{
string toString() const {return "";}
- void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {}
+ void toString(Writer)(ref Writer w) const
+ if (isOutputRange!(Writer, string)) {}
}
static struct H
{
}
static struct I
{
- void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {}
+ void toString(Writer)(ref Writer w) const
+ if (isOutputRange!(Writer, string)) {}
void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const
if (isOutputRange!(Writer, string))
{}
{
int n = 0;
alias n this;
- T opCast(T) () if (is(T == Frop))
+ T opCast(T) ()
+ if (is(T == Frop))
{
return Frop();
}
{
import std.traits : isIntegral;
private bool unsafeOp(ElementType1, ElementType2)(ElementType1 a, ElementType2 b) pure
- if (isIntegral!ElementType1 && isIntegral!ElementType2)
+ if (isIntegral!ElementType1 && isIntegral!ElementType2)
{
import std.traits : CommonType;
alias T = CommonType!(ElementType1, ElementType2);
* Returns: lvalue of result
*/
Int128 opUnary(string op)() const
- if (op == "+")
+ if (op == "+")
{
return this;
}
* Returns: lvalue of result
*/
Int128 opUnary(string op)() const
- if (op == "-" || op == "~")
+ if (op == "-" || op == "~")
{
static if (op == "-")
return Int128(neg(this.data));
* Returns: lvalue of result
*/
Int128 opUnary(string op)()
- if (op == "++" || op == "--")
+ if (op == "++" || op == "--")
{
static if (op == "++")
this.data = inc(this.data);
* Returns: value after the operation is applied
*/
Int128 opBinary(string op)(Int128 op2) const
- if (op == "+" || op == "-" ||
- op == "*" || op == "/" || op == "%" ||
- op == "&" || op == "|" || op == "^")
+ if (op == "+" || op == "-" ||
+ op == "*" || op == "/" || op == "%" ||
+ op == "&" || op == "|" || op == "^")
{
static if (op == "+")
return Int128(add(this.data, op2.data));
/// ditto
Int128 opBinary(string op, Int)(const Int op2) const
- if ((op == "+" || op == "-" ||
- op == "*" || op == "/" || op == "%" ||
- op == "&" || op == "|" || op == "^") &&
- is(Int : long) && __traits(isIntegral, Int))
+ if ((op == "+" || op == "-" ||
+ op == "*" || op == "/" || op == "%" ||
+ op == "&" || op == "|" || op == "^") &&
+ is(Int : long) && __traits(isIntegral, Int))
{
static if (__traits(isUnsigned, Int))
return mixin("this " ~ op ~ " Int128(0, op2)");
/// ditto
Int128 opBinary(string op, IntLike)(auto ref IntLike op2) const
- if ((op == "+" || op == "-" ||
- op == "*" || op == "/" || op == "%" ||
- op == "&" || op == "|" || op == "^") &&
- is(IntLike : long) && !__traits(isIntegral, IntLike))
+ if ((op == "+" || op == "-" ||
+ op == "*" || op == "/" || op == "%" ||
+ op == "&" || op == "|" || op == "^") &&
+ is(IntLike : long) && !__traits(isIntegral, IntLike))
{
return opBinary!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0]));
}
/// ditto
Int128 opBinaryRight(string op, Int)(const Int op2) const
- if ((op == "+" || op == "-" ||
- op == "*" || op == "/" || op == "%" ||
- op == "&" || op == "|" || op == "^") &&
- is(Int : long) && __traits(isIntegral, Int))
+ if ((op == "+" || op == "-" ||
+ op == "*" || op == "/" || op == "%" ||
+ op == "&" || op == "|" || op == "^") &&
+ is(Int : long) && __traits(isIntegral, Int))
{
static if (__traits(isUnsigned, Int))
mixin("return Int128(0, op2) " ~ op ~ " this;");
/// ditto
Int128 opBinaryRight(string op, IntLike)(auto ref IntLike op2) const
- if ((op == "+" || op == "-" ||
- op == "*" || op == "/" || op == "%" ||
- op == "&" || op == "|" || op == "^") &&
- is(IntLike : long) && !__traits(isIntegral, IntLike))
+ if ((op == "+" || op == "-" ||
+ op == "*" || op == "/" || op == "%" ||
+ op == "&" || op == "|" || op == "^") &&
+ is(IntLike : long) && !__traits(isIntegral, IntLike))
{
return opBinaryRight!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0]));
}
/// ditto
Int128 opBinary(string op)(long op2) const
- if (op == "<<")
+ if (op == "<<")
{
return Int128(shl(this.data, cast(uint) op2));
}
/// ditto
Int128 opBinary(string op)(long op2) const
- if (op == ">>")
+ if (op == ">>")
{
return Int128(sar(this.data, cast(uint) op2));
}
/// ditto
Int128 opBinary(string op)(long op2) const
- if (op == ">>>")
+ if (op == ">>>")
{
return Int128(shr(this.data, cast(uint) op2));
}
* Returns: lvalue of updated left operand
*/
ref Int128 opOpAssign(string op)(Int128 op2)
- if (op == "+" || op == "-" ||
- op == "*" || op == "/" || op == "%" ||
- op == "&" || op == "|" || op == "^" ||
- op == "<<" || op == ">>" || op == ">>>")
+ if (op == "+" || op == "-" ||
+ op == "*" || op == "/" || op == "%" ||
+ op == "&" || op == "|" || op == "^" ||
+ op == "<<" || op == ">>" || op == ">>>")
{
mixin("this = this " ~ op ~ " op2;");
return this;
/// ditto
ref Int128 opOpAssign(string op, Int)(auto ref Int op2)
- if ((op == "+" || op == "-" ||
- op == "*" || op == "/" || op == "%" ||
- op == "&" || op == "|" || op == "^" ||
- op == "<<" || op == ">>" || op == ">>>")
- && is(Int : long))
+ if ((op == "+" || op == "-" ||
+ op == "*" || op == "/" || op == "%" ||
+ op == "&" || op == "|" || op == "^" ||
+ op == "<<" || op == ">>" || op == ">>>")
+ && is(Int : long))
{
mixin("this = this " ~ op ~ " op2;");
return this;
data = x;
}
package(std) // used from: std.bigint
- this(T)(T x) pure nothrow @safe scope if (isIntegral!T)
+ this(T)(T x) pure nothrow @safe scope
+ if (isIntegral!T)
{
opAssign(x);
}
}
///
- void opAssign(Tulong)(Tulong u) pure nothrow @safe scope if (is (Tulong == ulong))
+ void opAssign(Tulong)(Tulong u) pure nothrow @safe scope
+ if (is (Tulong == ulong))
{
if (u == 0) data = ZERO;
else if (u == 1) data = ONE;
}
///
- int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe scope if (is (Tulong == ulong))
+ int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe scope
+ if (is (Tulong == ulong))
{
if (data.length > maxBigDigits!Tulong)
return 1;
}
// return false if invalid character found
- bool fromHexString(Range)(Range s) scope if (
- isBidirectionalRange!Range && isSomeChar!(ElementType!Range))
+ bool fromHexString(Range)(Range s) scope
+ if (isBidirectionalRange!Range && isSomeChar!(ElementType!Range))
{
import std.range : walkLength;
}
// return true if OK; false if erroneous characters found
- bool fromDecimalString(Range)(Range s) scope if (
- isForwardRange!Range && isSomeChar!(ElementType!Range))
+ bool fromDecimalString(Range)(Range s) scope
+ if (isForwardRange!Range && isSomeChar!(ElementType!Range))
{
import std.range : walkLength;
}
void fromMagnitude(Range)(Range magnitude) scope
- if (isInputRange!Range
- && (isForwardRange!Range || hasLength!Range)
- && isUnsigned!(ElementType!Range))
+ if (isInputRange!Range
+ && (isForwardRange!Range || hasLength!Range)
+ && isUnsigned!(ElementType!Range))
{
while (!magnitude.empty && magnitude.front == 0)
magnitude.popFront;
// return x >> y
BigUint opBinary(string op, Tulong)(Tulong y) pure nothrow @safe const return scope
- if (op == ">>" && is (Tulong == ulong))
+ if (op == ">>" && is (Tulong == ulong))
{
assert(y > 0, "Can not right shift BigUint by 0");
uint bits = cast(uint) y & BIGDIGITSHIFTMASK;
// return x << y
BigUint opBinary(string op, Tulong)(Tulong y) pure nothrow @safe const scope
- if (op == "<<" && is (Tulong == ulong))
+ if (op == "<<" && is (Tulong == ulong))
{
assert(y > 0, "Can not left shift BigUint by 0");
if (isZero()) return this;
// If wantSub is false, return x + y, leaving sign unchanged
// If wantSub is true, return abs(x - y), negating sign if x < y
- static BigUint addOrSubInt(Tulong)(const scope BigUint x, Tulong y,
- bool wantSub, ref bool sign) pure nothrow @safe if (is(Tulong == ulong))
+ static BigUint addOrSubInt(Tulong)(const scope BigUint x, Tulong y, bool wantSub, ref bool sign) pure nothrow @safe
+ if (is(Tulong == ulong))
{
BigUint r;
if (wantSub)
}
// return x % y
- static uint modInt(T)(scope BigUint x, T y_) pure if ( is(immutable T == immutable uint) )
+ static uint modInt(T)(scope BigUint x, T y_) pure
+ if ( is(immutable T == immutable uint) )
{
import core.memory : GC;
uint y = y_;
// return x op y
static BigUint bitwiseOp(string op)(scope BigUint x, scope BigUint y, bool xSign, bool ySign, ref bool resultSign)
- pure nothrow @safe if (op == "|" || op == "^" || op == "&")
+ pure nothrow @safe
+ if (op == "|" || op == "^" || op == "&")
{
auto d1 = includeSign(x.data, y.uintLength, xSign);
auto d2 = includeSign(y.data, x.uintLength, ySign);
{
import std.array : array;
- this(Range)(Range r) if (isInputRange!Range) {_payload = array(r);}
- final @property ref T front(){return _payload.front;}
- final void popFront(){_payload.popFront();}
- final @property bool empty(){return _payload.empty;}
+ this(Range)(Range r)
+ if (isInputRange!Range) {_payload = array(r);}
+ final @property ref T front() {return _payload.front;}
+ final void popFront() {_payload.popFront();}
+ final @property bool empty() {return _payload.empty;}
protected T[] _payload;
}
class ReferenceInfiniteInputRange(T)
{
this(T first = T.init) {_val = first;}
- final @property T front(){return _val;}
- final void popFront(){++_val;}
+ final @property T front() {return _val;}
+ final void popFront() {++_val;}
enum bool empty = false;
protected T _val;
}
*/
class ReferenceForwardRange(T) : ReferenceInputRange!T
{
- this(Range)(Range r) if (isInputRange!Range) {super(r);}
+ this(Range)(Range r)
+ if (isInputRange!Range) {super(r);}
final @property auto save(this This)() {return new This( _payload);}
}
*/
class ReferenceBidirectionalRange(T) : ReferenceForwardRange!T
{
- this(Range)(Range r) if (isInputRange!Range) {super(r);}
- final @property ref T back(){return _payload.back;}
- final void popBack(){_payload.popBack();}
+ this(Range)(Range r)
+ if (isInputRange!Range) {super(r);}
+ final @property ref T back() {return _payload.back;}
+ final void popBack() {_payload.popBack();}
}
@safe unittest
escapeNonAsciiChars = 0x2, /// Encode non-ASCII characters with a Unicode escape sequence
doNotEscapeSlashes = 0x4, /// Do not escape slashes ('/')
strictParsing = 0x8, /// Strictly follow RFC-8259 grammar when parsing
+ preserveObjectOrder = 0x16, /// Preserve order of object keys when parsing
}
/**
{
import std.exception : enforce;
+ import std.typecons : Tuple;
+
+ alias OrderedObjectMember = Tuple!(
+ string, "key",
+ JSONValue, "value",
+ );
+
union Store
{
+ struct Object
+ {
+ bool isOrdered;
+ union
+ {
+ JSONValue[string] unordered;
+ OrderedObjectMember[] ordered;
+ }
+ }
+
string str;
long integer;
ulong uinteger;
double floating;
- JSONValue[string] object;
+ Object object;
JSONValue[] array;
}
private Store store;
}
/***
- * Value getter/setter for `JSONType.object`.
+ * Value getter/setter for unordered `JSONType.object`.
* Throws: `JSONException` for read access if `type` is not
- * `JSONType.object`.
+ * `JSONType.object` or the object is ordered.
* Note: This is @system because of the following pattern:
---
auto a = &(json.object());
{
enforce!JSONException(type == JSONType.object,
"JSONValue is not an object");
- return store.object;
+ enforce!JSONException(!store.object.isOrdered,
+ "JSONValue object is ordered, cannot return by ref");
+ return store.object.unordered;
}
/// ditto
@property JSONValue[string] object(return scope JSONValue[string] v) pure nothrow @nogc @trusted // TODO make @safe
}
/***
- * Value getter for `JSONType.object`.
+ * Value getter for unordered `JSONType.object`.
* Unlike `object`, this retrieves the object by value
* and can be used in @safe code.
*
{
enforce!JSONException(type == JSONType.object,
"JSONValue is not an object");
- return store.object;
+ if (store.object.isOrdered)
+ {
+ // Convert to unordered
+ JSONValue[string] result;
+ foreach (pair; store.object.ordered)
+ result[pair.key] = pair.value;
+ return cast(inout) result;
+ }
+ else
+ return store.object.unordered;
+ }
+
+ /***
+ * Value getter/setter for ordered `JSONType.object`.
+ * Throws: `JSONException` for read access if `type` is not
+ * `JSONType.object` or the object is unordered.
+ * Note: This is @system because of the following pattern:
+ ---
+ auto a = &(json.orderedObject());
+ json.uinteger = 0; // overwrite AA pointer
+ (*a)["hello"] = "world"; // segmentation fault
+ ---
+ */
+ @property ref inout(OrderedObjectMember[]) orderedObject() inout pure @system return
+ {
+ enforce!JSONException(type == JSONType.object,
+ "JSONValue is not an object");
+ enforce!JSONException(store.object.isOrdered,
+ "JSONValue object is unordered, cannot return by ref");
+ return store.object.ordered;
+ }
+ /// ditto
+ @property OrderedObjectMember[] orderedObject(return scope OrderedObjectMember[] v) pure nothrow @nogc @trusted // TODO make @safe
+ {
+ assign(v);
+ return v;
+ }
+
+ /***
+ * Value getter for ordered `JSONType.object`.
+ * Unlike `orderedObject`, this retrieves the object by value
+ * and can be used in @safe code.
+ */
+ @property inout(OrderedObjectMember[]) orderedObjectNoRef() inout pure @trusted
+ {
+ enforce!JSONException(type == JSONType.object,
+ "JSONValue is not an object");
+ if (store.object.isOrdered)
+ return store.object.ordered;
+ else
+ {
+ // Convert to ordered
+ OrderedObjectMember[] result;
+ foreach (key, value; store.object.unordered)
+ result ~= OrderedObjectMember(key, value);
+ return cast(inout) result;
+ }
+ }
+
+ /// Returns `true` if the order of keys of the represented object is being preserved.
+ @property bool isOrdered() const pure @trusted
+ {
+ enforce!JSONException(type == JSONType.object,
+ "JSONValue is not an object");
+ return store.object.isOrdered;
}
/***
static if (is(Value : JSONValue))
{
JSONValue[string] t = arg;
- () @trusted { store.object = t; }();
+ () @trusted {
+ store.object.isOrdered = false;
+ store.object.unordered = t;
+ }();
}
else
{
JSONValue[string] aa;
foreach (key, value; arg)
aa[key] = JSONValue(value);
- () @trusted { store.object = aa; }();
+ () @trusted {
+ store.object.isOrdered = false;
+ store.object.unordered = aa;
+ }();
}
}
+ else static if (is(T : OrderedObjectMember[]))
+ {
+ type_tag = JSONType.object;
+ () @trusted {
+ store.object.isOrdered = true;
+ store.object.ordered = arg;
+ }();
+ }
else static if (isArray!T)
{
type_tag = JSONType.array;
}
}
- private void assignRef(T)(ref T arg) if (isStaticArray!T)
+ private void assignRef(T)(ref T arg)
+ if (isStaticArray!T)
{
type_tag = JSONType.array;
static if (is(ElementEncodingType!T : JSONValue))
* and `K` i.e. a JSON object, any array or `bool`. The type will
* be set accordingly.
*/
- this(T)(T arg) if (!isStaticArray!T)
+ this(T)(T arg)
+ if (!isStaticArray!T)
{
assign(arg);
}
/// Ditto
- this(T)(ref T arg) if (isStaticArray!T)
+ this(T)(ref T arg)
+ if (isStaticArray!T)
{
assignRef(arg);
}
assert(obj1 != obj2);
}
+ /**
+ * An enum value that can be used to obtain a `JSONValue` representing
+ * an empty JSON object.
+ * Unlike `emptyObject`, the order of inserted keys is preserved.
+ */
+ enum emptyOrderedObject = {
+ JSONValue v = void;
+ v.orderedObject = null;
+ return v;
+ }();
+ ///
+ @system unittest
+ {
+ JSONValue obj = JSONValue.emptyOrderedObject;
+ assert(obj.type == JSONType.object);
+ assert(obj.isOrdered);
+ obj["b"] = JSONValue(2);
+ obj["a"] = JSONValue(1);
+ assert(obj["a"] == JSONValue(1));
+ assert(obj["b"] == JSONValue(2));
+
+ string[] keys;
+ foreach (string k, JSONValue v; obj)
+ keys ~= k;
+ assert(keys == ["b", "a"]);
+ }
+
/**
* An enum value that can be used to obtain a `JSONValue` representing
* an empty JSON array.
assert(arr1 != arr2);
}
- void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue))
+ void opAssign(T)(T arg)
+ if (!isStaticArray!T && !is(T : JSONValue))
{
assign(arg);
}
- void opAssign(T)(ref T arg) if (isStaticArray!T)
+ void opAssign(T)(ref T arg)
+ if (isStaticArray!T)
{
assignRef(arg);
}
*/
void opIndexAssign(T)(auto ref T value, string key)
{
- enforce!JSONException(type == JSONType.object || type == JSONType.null_,
- "JSONValue must be object or null");
- JSONValue[string] aa = null;
- if (type == JSONType.object)
+ enforce!JSONException(
+ type == JSONType.object ||
+ type == JSONType.null_,
+ "JSONValue must be object or null");
+ if (type == JSONType.object && isOrdered)
{
- aa = this.objectNoRef;
+ auto arr = this.orderedObjectNoRef;
+ foreach (ref pair; arr)
+ if (pair.key == key)
+ {
+ pair.value = value;
+ return;
+ }
+ arr ~= OrderedObjectMember(key, JSONValue(value));
+ this.orderedObject = arr;
}
+ else
+ {
+ JSONValue[string] aa = null;
+ if (type == JSONType.object)
+ {
+ aa = this.objectNoRef;
+ }
- aa[key] = value;
- this.object = aa;
+ aa[key] = value;
+ this.object = aa;
+ }
}
///
@safe unittest
/// ditto
bool opEquals(ref const JSONValue rhs) const @nogc nothrow pure @trusted
{
+ import std.algorithm.searching : canFind;
+
// Default doesn't work well since store is a union. Compare only
// what should be in store.
// This is @trusted to remain nogc, nothrow, fast, and usable from @safe code.
case JSONType.string:
return type_tag == rhs.type_tag && store.str == rhs.store.str;
case JSONType.object:
- return type_tag == rhs.type_tag && store.object == rhs.store.object;
+ if (rhs.type_tag != JSONType.object)
+ return false;
+ if (store.object.isOrdered)
+ {
+ if (rhs.store.object.isOrdered)
+ {
+ if (store.object.ordered.length != rhs.store.object.ordered.length)
+ return false;
+ foreach (ref pair; store.object.ordered)
+ if (!rhs.store.object.ordered.canFind(pair))
+ return false;
+ return true;
+ }
+ else
+ {
+ if (store.object.ordered.length != rhs.store.object.unordered.length)
+ return false;
+ foreach (ref pair; store.object.ordered)
+ if (pair.key !in rhs.store.object.unordered ||
+ rhs.store.object.unordered[pair.key] != pair.value)
+ return false;
+ return true;
+ }
+ }
+ else
+ {
+ if (rhs.store.object.isOrdered)
+ {
+ if (store.object.unordered.length != rhs.store.object.ordered.length)
+ return false;
+ foreach (ref pair; rhs.store.object.ordered)
+ if (pair.key !in store.object.unordered ||
+ store.object.unordered[pair.key] != pair.value)
+ return false;
+ return true;
+ }
+ else
+ return store.object.unordered == rhs.store.object.unordered;
+ }
case JSONType.array:
return type_tag == rhs.type_tag && store.array == rhs.store.array;
case JSONType.true_:
int opApply(scope int delegate(string key, ref JSONValue) dg) @system
{
enforce!JSONException(type == JSONType.object,
- "JSONValue is not an object");
+ "JSONValue is not an object");
+
int result;
- foreach (string key, ref value; object)
+ if (isOrdered)
{
- result = dg(key, value);
- if (result)
- break;
+ foreach (ref pair; orderedObject)
+ {
+ result = dg(pair.key, pair.value);
+ if (result)
+ break;
+ }
+ }
+ else
+ {
+ foreach (string key, ref value; object)
+ {
+ result = dg(key, value);
+ if (result)
+ break;
+ }
}
return result;
Nullable!Char next;
int line = 1, pos = 0;
immutable bool strict = (options & JSONOptions.strictParsing) != 0;
+ immutable bool ordered = (options & JSONOptions.preserveObjectOrder) != 0;
void error(string msg)
{
switch (c)
{
case '{':
- if (testChar('}'))
+ if (ordered)
{
- value.object = null;
- break;
- }
+ if (testChar('}'))
+ {
+ value.orderedObject = null;
+ break;
+ }
- JSONValue[string] obj;
- do
+ JSONValue.OrderedObjectMember[] obj;
+ do
+ {
+ skipWhitespace();
+ if (!strict && peekChar() == '}')
+ {
+ break;
+ }
+ checkChar('"');
+ string name = parseString();
+ checkChar(':');
+ JSONValue member;
+ parseValue(member);
+ obj ~= JSONValue.OrderedObjectMember(name, member);
+ }
+ while (testChar(','));
+ value.orderedObject = obj;
+
+ checkChar('}');
+ }
+ else
{
- skipWhitespace();
- if (!strict && peekChar() == '}')
+ if (testChar('}'))
{
+ value.object = null;
break;
}
- checkChar('"');
- string name = parseString();
- checkChar(':');
- JSONValue member;
- parseValue(member);
- obj[name] = member;
- }
- while (testChar(','));
- value.object = obj;
- checkChar('}');
+ JSONValue[string] obj;
+ do
+ {
+ skipWhitespace();
+ if (!strict && peekChar() == '}')
+ {
+ break;
+ }
+ checkChar('"');
+ string name = parseString();
+ checkChar(':');
+ JSONValue member;
+ parseValue(member);
+ obj[name] = member;
+ }
+ while (testChar(','));
+ value.object = obj;
+
+ checkChar('}');
+ }
break;
case '[':
final switch (value.type)
{
case JSONType.object:
- auto obj = value.objectNoRef;
- if (!obj.length)
+ if (value.isOrdered)
{
- json.put("{}");
- }
- else
- {
- putCharAndEOL('{');
- bool first = true;
-
- void emit(R)(R names)
+ auto obj = value.orderedObjectNoRef;
+ if (!obj.length)
{
- foreach (name; names)
+ json.put("{}");
+ }
+ else
+ {
+ putCharAndEOL('{');
+ bool first = true;
+
+ foreach (pair; obj)
{
- auto member = obj[name];
if (!first)
putCharAndEOL(',');
first = false;
putTabs(1);
- toString(name);
+ toString(pair.key);
json.put(':');
if (pretty)
json.put(' ');
- toValueImpl(member, indentLevel + 1);
+ toValueImpl(pair.value, indentLevel + 1);
}
- }
- import std.algorithm.sorting : sort;
- // https://issues.dlang.org/show_bug.cgi?id=14439
- // auto names = obj.keys; // aa.keys can't be called in @safe code
- auto names = new string[obj.length];
- size_t i = 0;
- foreach (k, v; obj)
+ putEOL();
+ putTabs();
+ json.put('}');
+ }
+ }
+ else
+ {
+ auto obj = value.objectNoRef;
+ if (!obj.length)
{
- names[i] = k;
- i++;
+ json.put("{}");
}
- sort(names);
- emit(names);
+ else
+ {
+ putCharAndEOL('{');
+ bool first = true;
- putEOL();
- putTabs();
- json.put('}');
+ void emit(R)(R names)
+ {
+ foreach (name; names)
+ {
+ auto member = obj[name];
+ if (!first)
+ putCharAndEOL(',');
+ first = false;
+ putTabs(1);
+ toString(name);
+ json.put(':');
+ if (pretty)
+ json.put(' ');
+ toValueImpl(member, indentLevel + 1);
+ }
+ }
+
+ import std.algorithm.sorting : sort;
+ // https://issues.dlang.org/show_bug.cgi?id=14439
+ // auto names = obj.keys; // aa.keys can't be called in @safe code
+ auto names = new string[obj.length];
+ size_t i = 0;
+ foreach (k, v; obj)
+ {
+ names[i] = k;
+ i++;
+ }
+ sort(names);
+ emit(names);
+
+ putEOL();
+ putTabs();
+ json.put('}');
+ }
}
break;
assert(app.data == s, app.data);
}
+
+// https://issues.dlang.org/show_bug.cgi?id=24823 - JSONOptions.preserveObjectOrder
+@safe unittest
+{
+ import std.array : appender;
+
+ string s = `{"b":2,"a":1}`;
+ JSONValue j = parseJSON(s, -1, JSONOptions.preserveObjectOrder);
+
+ auto app = appender!string();
+ j.toString(app);
+
+ assert(app.data == s, app.data);
+}
string funcName = __FUNCTION__,
string prettyFuncName = __PRETTY_FUNCTION__,
string moduleName = __MODULE__, A...)(lazy A args)
- if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0)
+ if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0)
{
stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName,
prettyFuncName, moduleName)(args);
}
void put(T)(T msg) @safe
- if (isSomeString!T)
+ if (isSomeString!T)
{
log.logMsgPart(msg);
}
string funcName = __FUNCTION__,
string prettyFuncName = __PRETTY_FUNCTION__,
string moduleName = __MODULE__, A...)(lazy A args)
- if (args.length == 0 || (args.length > 0 && !is(A[0] : bool)))
+ if (args.length == 0 || (args.length > 0 && !is(A[0] : bool)))
{
synchronized (mutex)
{
string prettyFuncName = __PRETTY_FUNCTION__,
string moduleName = __MODULE__, A...)(const LogLevel ll,
lazy bool condition, lazy A args)
- if (args.length != 1)
+ if (args.length != 1)
{
synchronized (mutex)
{
string funcName = __FUNCTION__,
string prettyFuncName = __PRETTY_FUNCTION__,
string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args)
- if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0)
+ if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0)
{
synchronized (mutex)
{
string funcName = __FUNCTION__,
string prettyFuncName = __PRETTY_FUNCTION__,
string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
- if (args.length != 1)
+ if (args.length != 1)
{
synchronized (mutex)
{
string funcName = __FUNCTION__,
string prettyFuncName = __PRETTY_FUNCTION__,
string moduleName = __MODULE__, A...)(lazy A args)
- if ((args.length > 1
- && !is(Unqual!(A[0]) : bool)
- && !is(immutable A[0] == immutable LogLevel))
- || args.length == 0)
+ if ((args.length > 1
+ && !is(Unqual!(A[0]) : bool)
+ && !is(immutable A[0] == immutable LogLevel))
+ || args.length == 0)
{
synchronized (mutex)
{
static @property CustomFloat im() { return CustomFloat(0.0f); }
/// Initialize from any `real` compatible type.
- this(F)(F input) if (__traits(compiles, cast(real) input ))
+ this(F)(F input)
+ if (__traits(compiles, cast(real) input ))
{
this = input;
}
/// Assigns from any `real` compatible type.
void opAssign(F)(F input)
- if (__traits(compiles, cast(real) input))
+ if (__traits(compiles, cast(real) input))
{
import std.conv : text;
/// Fetches the stored value either as a `float`, `double` or `real`.
@property F get(F)()
- if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0)
+ if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0)
{
import std.conv : text;
/// Convert the CustomFloat to a real and perform the relevant operator on the result
real opUnary(string op)()
- if (__traits(compiles, mixin(op~`(get!real)`)) || op=="++" || op=="--")
+ if (__traits(compiles, mixin(op~`(get!real)`)) || op=="++" || op=="--")
{
static if (op=="++" || op=="--")
{
// do not match equally, which is disallowed by the spec:
// https://dlang.org/spec/operatoroverloading.html#binary
real opBinary(string op,T)(T b)
- if (__traits(compiles, mixin(`get!real`~op~`b.get!real`)))
- {
- return mixin(`get!real`~op~`b.get!real`);
- }
+ 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)
- if ( __traits(compiles, mixin(`get!real`~op~`b`)) &&
- !__traits(compiles, mixin(`get!real`~op~`b.get!real`)))
+ if ( __traits(compiles, mixin(`get!real`~op~`b`)) &&
+ !__traits(compiles, mixin(`get!real`~op~`b.get!real`)))
{
return mixin(`get!real`~op~`b`);
}
/// ditto
real opBinaryRight(string op,T)(T a)
- if ( __traits(compiles, mixin(`a`~op~`get!real`)) &&
- !__traits(compiles, mixin(`get!real`~op~`b`)) &&
- !__traits(compiles, mixin(`get!real`~op~`b.get!real`)))
+ if ( __traits(compiles, mixin(`a`~op~`get!real`)) &&
+ !__traits(compiles, mixin(`get!real`~op~`b`)) &&
+ !__traits(compiles, mixin(`get!real`~op~`b.get!real`)))
{
return mixin(`a`~op~`get!real`);
}
/// ditto
int opCmp(T)(auto ref T b)
- if (__traits(compiles, cast(real) b))
+ if (__traits(compiles, cast(real) b))
{
auto x = get!real;
auto y = cast(real) b;
/// ditto
void opOpAssign(string op, T)(auto ref T b)
- if (__traits(compiles, mixin(`get!real`~op~`cast(real) b`)))
+ if (__traits(compiles, mixin(`get!real`~op~`cast(real) b`)))
{
return mixin(`this = this `~op~` cast(real) b`);
}
* i.e., output[j] := sum[ exp(-2 PI i j k / N) input[k] ].
*/
Complex!F[] fft(F = double, R)(R range) const
- if (isFloatingPoint!F && isRandomAccessRange!R)
+ if (isFloatingPoint!F && isRandomAccessRange!R)
{
enforceSize(range);
Complex!F[] ret;
* property that can be both read and written and are floating point numbers.
*/
void fft(Ret, R)(R range, Ret buf) const
- if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret)
+ if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret)
{
assert(buf.length == range.length);
enforceSize(range);
* output[j] := (1 / N) sum[ exp(+2 PI i j k / N) input[k] ].
*/
Complex!F[] inverseFft(F = double, R)(R range) const
- if (isRandomAccessRange!R && isComplexLike!(ElementType!R) && isFloatingPoint!F)
+ if (isRandomAccessRange!R && isComplexLike!(ElementType!R) && isFloatingPoint!F)
{
enforceSize(range);
Complex!F[] ret;
* must be some complex-like type.
*/
void inverseFft(Ret, R)(R range, Ret buf) const
- if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret)
+ if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret)
{
enforceSize(range);
call to `popFront` or, if thrown during construction, simply
allowed to propagate to the caller.
*/
- auto asyncBuf(S)(S source, size_t bufSize = 100) if (isInputRange!S)
+ auto asyncBuf(S)(S source, size_t bufSize = 100)
+ if (isInputRange!S)
{
static final class AsyncBuf
{
of segments to assemble the path from.
Returns: The assembled path.
*/
-immutable(ElementEncodingType!(ElementType!Range))[]
- buildPath(Range)(scope Range segments)
- if (isInputRange!Range && !isInfinite!Range && isSomeString!(ElementType!Range))
+immutable(ElementEncodingType!(ElementType!Range))[] buildPath(Range)(scope Range segments)
+if (isInputRange!Range && !isInfinite!Range && isSomeString!(ElementType!Range))
{
if (segments.empty) return null;
}
else
{
+ import core.memory : pureMalloc, pureFree;
C[] pattmp;
+ scope(exit) if (pattmp !is null) (() @trusted => pureFree(pattmp.ptr))();
+
for (size_t pi = 0; pi < pattern.length; pi++)
{
const pc = pattern[pi];
* pattern[pi0 .. pi-1] ~ pattern[piRemain..$]
*/
if (pattmp is null)
+ {
// Allocate this only once per function invocation.
- // Should do it with malloc/free, but that would make it impure.
- pattmp = new C[pattern.length];
+ pattmp = (() @trusted =>
+ (cast(C*) pureMalloc(C.sizeof * pattern.length))[0 .. pattern.length])
+ ();
+ }
const len1 = pi - 1 - pi0;
pattmp[0 .. len1] = pattern[pi0 .. pi - 1];
}
///
-@safe unittest
+@safe @nogc unittest
{
assert(globMatch("foo.bar", "*"));
assert(globMatch("foo.bar", "*.*"));
static if (!__traits(compiles, closefrom))
{
- // FIXME: This implementation crashes the system when RLIMIT_NOFILE
- // has a big value. For a possible solution see:
- // https://github.com/dlang/phobos/pull/8990
- void fallback (int lowfd) {
- // NOTE: malloc() and getrlimit() are not on the POSIX async
- // signal safe functions list, but practically this should
- // not be a problem. Java VM and CPython also use malloc()
- // in its own implementation via opendir().
- import core.stdc.stdlib : malloc;
- import core.sys.posix.poll : pollfd, poll, POLLNVAL;
+ void fallback (int lowfd)
+ {
+ import core.sys.posix.dirent : dirent, opendir, readdir, closedir, DIR;
+ import core.sys.posix.unistd : close;
+ import core.sys.posix.stdlib : atoi, malloc, free;
import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE;
// Get the maximum number of file descriptors that could be open.
rlimit r;
if (getrlimit(RLIMIT_NOFILE, &r) != 0)
- {
abortOnError(forkPipeOut, InternalError.getrlimit, .errno);
- }
+
immutable maxDescriptors = cast(int) r.rlim_cur;
- immutable maxToClose = maxDescriptors - lowfd;
+ // Missing druntime declaration
+ pragma(mangle, "dirfd")
+ extern(C) nothrow @nogc int dirfd(DIR* dir);
- // Call poll() to see which ones are actually open:
- auto pfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose);
- if (pfds is null)
- {
- abortOnError(forkPipeOut, InternalError.malloc, .errno);
- }
- foreach (i; 0 .. maxToClose)
+ DIR* dir = null;
+
+ // We read from /dev/fd or /proc/self/fd only if the limit is high enough
+ if (maxDescriptors > 128*1024)
{
- pfds[i].fd = i + lowfd;
- pfds[i].events = 0;
- pfds[i].revents = 0;
+ // Try to open the directory /dev/fd or /proc/self/fd
+ dir = opendir("/dev/fd");
+ if (dir is null) dir = opendir("/proc/self/fd");
}
- if (poll(pfds, maxToClose, 0) >= 0)
+
+ // If we have a directory, close all file descriptors except stdin, stdout, and stderr
+ if (dir)
{
- foreach (i; 0 .. maxToClose)
+ scope(exit) closedir(dir);
+
+ int opendirfd = dirfd(dir);
+
+ // Iterate over all file descriptors
+ while (true)
{
- // POLLNVAL will be set if the file descriptor is invalid.
- if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd);
+ dirent* entry = readdir(dir);
+
+ if (entry is null) break;
+ if (entry.d_name[0] == '.') continue;
+
+ int fd = atoi(cast(char*) entry.d_name);
+
+ // Don't close stdin, stdout, stderr, or the directory file descriptor
+ if (fd < lowfd || fd == opendirfd) continue;
+
+ close(fd);
}
}
else
{
- // Fall back to closing everything.
- foreach (i; lowfd .. maxDescriptors)
+ // This is going to allocate 8 bytes for each possible file descriptor from lowfd to r.rlim_cur
+ if (maxDescriptors <= 128*1024)
{
- close(i);
+ // NOTE: malloc() and getrlimit() are not on the POSIX async
+ // signal safe functions list, but practically this should
+ // not be a problem. Java VM and CPython also use malloc()
+ // in its own implementation via opendir().
+ import core.stdc.stdlib : malloc;
+ import core.sys.posix.poll : pollfd, poll, POLLNVAL;
+
+ immutable maxToClose = maxDescriptors - lowfd;
+
+ // Call poll() to see which ones are actually open:
+ auto pfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose);
+ if (pfds is null)
+ {
+ abortOnError(forkPipeOut, InternalError.malloc, .errno);
+ }
+
+ foreach (i; 0 .. maxToClose)
+ {
+ pfds[i].fd = i + lowfd;
+ pfds[i].events = 0;
+ pfds[i].revents = 0;
+ }
+
+ if (poll(pfds, maxToClose, 0) < 0)
+ // couldn't use poll, use the slow path.
+ goto LslowClose;
+
+ foreach (i; 0 .. maxToClose)
+ {
+ // POLLNVAL will be set if the file descriptor is invalid.
+ if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd);
+ }
+ }
+ else
+ {
+ LslowClose:
+ // Fall back to closing everything.
+ foreach (i; lowfd .. maxDescriptors)
+ {
+ close(i);
+ }
}
}
}
// Until we find a way to perform this check we will try to use dlsym to
// check for the function. See: https://github.com/dlang/phobos/pull/9048
version (CRuntime_Glibc)
+ {
void closefrom (int lowfd) {
static bool tryGlibcClosefrom (int lowfd) {
import core.sys.posix.dlfcn : dlopen, dlclose, dlsym, dlerror, RTLD_LAZY;
if (!tryGlibcClosefrom(lowfd))
fallback(lowfd);
}
+ }
else
alias closefrom = fallback;
}
`Exception` if the InputRange didn't provide enough elements to seed the generator.
The number of elements required is the 'n' template parameter of the MersenneTwisterEngine struct.
*/
- void seed(T)(T range) if (isInputRange!T && is(immutable ElementType!T == immutable UIntType))
+ void seed(T)(T range)
+ if (isInputRange!T && is(immutable ElementType!T == immutable UIntType))
{
this.seedImpl(range, this.state);
}
which can be used with an arbitrary `State` instance
*/
private static void seedImpl(T)(T range, ref State mtState)
- if (isInputRange!T && is(immutable ElementType!T == immutable UIntType))
+ if (isInputRange!T && is(immutable ElementType!T == immutable UIntType))
{
size_t j;
for (j = 0; j < n && !range.empty; ++j, range.popFront())
Hence, our condition to reroll is
`bucketFront > (UpperType.max - (upperDist - 1))`
+/
+/// ditto
auto uniform(string boundaries = "[)", T1, T2, RandomGen)
(T1 a, T2 b, ref RandomGen rng)
if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) &&
return cast(ResultType)(lower + offset);
}
+///
@safe unittest
{
import std.conv : to;
+ import std.meta : AliasSeq;
+ import std.range.primitives : isForwardRange;
+ import std.traits : isIntegral, isSomeChar;
+
auto gen = Mt19937(123_456_789);
static assert(isForwardRange!(typeof(gen)));
auto c = uniform(0.0, 1.0);
assert(0 <= c && c < 1);
- static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort,
+ static foreach (T; AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort,
int, uint, long, ulong, float, double, real))
{{
T lo = 0, hi = 100;
auto reproRng = Xorshift(239842);
- static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short,
+ static foreach (T; AliasSeq!(char, wchar, dchar, byte, ubyte, short,
ushort, int, uint, long, ulong))
{{
T lo = T.min + 10, hi = T.max - 10;
{
indexDef = q{
size_t index = ranges[0].length - 1;
- enforce(this.stoppingPolicy == StoppingPolicy.requireSameLength,
- "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength");
+ enforce(
+ this.stoppingPolicy == StoppingPolicy.requireSameLength,
+ "Indexed lockstep can only be used with foreach_reverse when " ~
+ "stoppingPolicy == requireSameLength");
foreach (range; ranges[1 .. $])
enforce(range.length == ranges[0].length);
bool opEquals(Cyclic c) const { return current == c.current; }
bool opEquals(int i) const { return current == i; }
- void opUnary(string op)() if (op == "++")
+ void opUnary(string op)()
+ if (op == "++")
{
current = (current + 1) % wrapAround;
}
return (*_range).front;
}
- static if (is(typeof((*(cast(const R*)_range)).front))) @property auto front() const
+ static if (is(typeof(((const R* r) => (*r).front)(null)))) @property auto front() const
{
return (*_range).front;
}
return (*_range).empty;
}
- static if (is(typeof((*cast(const R*)_range).empty))) @property bool empty() const
+ static if (is(typeof(((const R* r) => (*r).empty)(null)))) @property bool empty() const
{
return (*_range).empty;
}
else static if (isForwardRange!R)
{
import std.traits : isSafe;
- private alias S = typeof((*_range).save);
+ private alias S = typeof((() => (*_range).save)());
+
+ static if (is(typeof(((const R* r) => (*r).save)(null))))
+ private alias CS = typeof(((const R* r) => (*r).save)(null));
- static if (is(typeof((*cast(const R*)_range).save)))
- private alias CS = typeof((*cast(const R*)_range).save);
static if (isSafe!((R* r) => (*r).save))
{
mixin(_genSave());
}
- static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() @trusted const
+ static if (is(typeof(((const R* r) => (*r).save)(null)))) @property RefRange!CS save() @trusted const
{
mixin(_genSave());
}
mixin(_genSave());
}
- static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() const
+ static if (is(typeof(((const R* r) => (*r).save)(null)))) @property RefRange!CS save() const
{
mixin(_genSave());
}
private static string _genSave() @safe pure nothrow
{
return `import core.lifetime : emplace;` ~
- `alias S = typeof((*_range).save);` ~
+ `alias S = typeof((() => (*_range).save)());` ~
`static assert(isForwardRange!S, S.stringof ~ " is not a forward range.");` ~
`auto mem = new void[S.sizeof];` ~
`emplace!S(mem, cast(S)(*_range).save);` ~
return (*_range).back;
}
- static if (is(typeof((*(cast(const R*)_range)).back))) @property auto back() const
+ static if (is(typeof(((const R* r) => (*r).back)(null)))) @property auto back() const
{
return (*_range).back;
}
else static if (isRandomAccessRange!R)
{
auto ref opIndex(IndexType)(IndexType index)
- if (is(typeof((*_range)[index])))
+ if (is(typeof((*_range)[index])))
{
return (*_range)[index];
}
auto ref opIndex(IndexType)(IndexType index) const
- if (is(typeof((*cast(const R*)_range)[index])))
+ if (is(typeof((*cast(const R*)_range)[index])))
{
return (*_range)[index];
}
{
return (*_range).length;
}
- static if (is(typeof((*cast(const R*)_range).length))) @property auto length() const
+ static if (is(typeof(((const R* r) => (*r).length)(null)))) @property auto length() const
{
return (*_range).length;
}
RefRange!T opSlice(IndexType1, IndexType2)
(IndexType1 begin, IndexType2 end)
- if (is(typeof((*_range)[begin .. end])))
+ if (is(typeof((*_range)[begin .. end])))
{
mixin(_genOpSlice());
}
RefRange!CT opSlice(IndexType1, IndexType2)
(IndexType1 begin, IndexType2 end) const
- if (is(typeof((*cast(const R*)_range)[begin .. end])))
+ if (is(typeof((*cast(const R*)_range)[begin .. end])))
{
mixin(_genOpSlice());
}
auto rr2 = refRange(&r2);
}
+// https://issues.dlang.org/show_bug.cgi?id=24801
+@safe unittest
+{
+
+ {
+ static struct R
+ {
+ int front() => 0;
+ void popFront() {}
+ bool empty() => false;
+ }
+ R range;
+ auto r = RefRange!R(&range);
+ }
+
+ {
+ static struct R
+ {
+ size_t start, end;
+ size_t length() => end - start;
+ int opIndex(size_t i) => 0;
+
+
+ int front() => this[0];
+ int back() => this[length-1];
+ void popFront() { start++; }
+ void popBack() { end--; }
+ bool empty() => length == 0;
+ R save() const => R();
+ }
+
+ R range;
+ auto r = RefRange!R(&range);
+ }
+
+
+}
+
/// ditto
auto refRange(R)(R* range)
if (isInputRange!R)
{
string data;
- void put(C)(C c) if (isSomeChar!C)
+ void put(C)(C c)
+ if (isSomeChar!C)
{
data ~= c;
}
}
void stackPush(T)(T val)
- if (!isDynamicArray!T)
+ if (!isDynamicArray!T)
{
*cast(T*)&memory[lastState] = val;
enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof;
}
void stackPop(T)(ref T val)
- if (!isDynamicArray!T)
+ if (!isDynamicArray!T)
{
enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof;
lastState -= delta;
Generator g;
@trusted this(S)(R pattern, S flags)
- if (isSomeString!S)
+ if (isSomeString!S)
{
pat = origin = pattern;
//reserve slightly more then avg as sampled from unittests
}
static bool op(IR code)(E e, S* state)
- if (code == IR.RepeatEnd || code == IR.RepeatQEnd)
+ if (code == IR.RepeatEnd || code == IR.RepeatQEnd)
{
with(e) with(state)
{
}
static bool op(IR code)(E e, S* state)
- if (code == IR.InfiniteEnd || code == IR.InfiniteQEnd)
+ if (code == IR.InfiniteEnd || code == IR.InfiniteQEnd)
{
with(e) with(state)
{
}
static bool op(IR code)(E e, S* state)
- if (code == IR.InfiniteBloomEnd)
+ if (code == IR.InfiniteBloomEnd)
{
with(e) with(state)
{
static bool op(IR code)(E e, S* state)
- if (code == IR.LookbehindStart || code == IR.NeglookbehindStart)
+ if (code == IR.LookbehindStart || code == IR.NeglookbehindStart)
{
with(e) with(state)
{
}
static bool op(IR code)(E e, S* state)
- if (code == IR.LookaheadStart || code == IR.NeglookaheadStart)
+ if (code == IR.LookaheadStart || code == IR.NeglookaheadStart)
{
with(e) with(state)
{
}
static bool op(IR code)(E e, S* state)
- if (code == IR.LookaheadEnd || code == IR.NeglookaheadEnd ||
- code == IR.LookbehindEnd || code == IR.NeglookbehindEnd)
+ if (code == IR.LookaheadEnd || code == IR.NeglookaheadEnd ||
+ code == IR.LookbehindEnd || code == IR.NeglookbehindEnd)
{
with(e) with(state)
{
@trusted:
// can't match these without input
static bool op(IR code)(E e, S* state)
- if (code == IR.Char || code == IR.OrChar || code == IR.CodepointSet
+ if (code == IR.Char || code == IR.OrChar || code == IR.CodepointSet
|| code == IR.Trie || code == IR.Char || code == IR.Any)
{
return state.popState(e);
// forward all control flow to normal versions
static bool op(IR code)(E e, S* state)
- if (code != IR.Char && code != IR.OrChar && code != IR.CodepointSet
+ if (code != IR.Char && code != IR.OrChar && code != IR.CodepointSet
&& code != IR.Trie && code != IR.Char && code != IR.Any && code != IR.Backref)
{
return ThompsonOps!(E,S,true).op!code(e,state);
----
+/
R opIndex(String)(String i) /*const*/ //@@@BUG@@@
- if (isSomeString!String)
+ if (isSomeString!String)
{
size_t index = lookupNamedGroup(_names, i);
return getMatch(index);
// must synchronize across all threads
private bool getHost(string opMixin, T)(T param) @system
{
- synchronized(this.classinfo)
+ synchronized(typeid(this))
return getHostNoSync!(opMixin, T)(param);
}
}
/// ditto
this(R1, R2)(R1 name)
- if (isSomeFiniteCharInputRange!R1)
+ if (isSomeFiniteCharInputRange!R1)
{
import std.conv : to;
this(name.to!string, "rb");
/// ditto
this(R1, R2)(R1 name, R2 mode)
- if (isSomeFiniteCharInputRange!R1 &&
- isSomeFiniteCharInputRange!R2)
+ if (isSomeFiniteCharInputRange!R1 &&
+ isSomeFiniteCharInputRange!R2)
{
import std.conv : to;
this(name.to!string, mode.to!string);
Notice that neither example accesses the line data returned by
`front` after the corresponding `popFront` call is made (because
the contents may well have changed).
+----
+
+Windows specific Example:
+----
+import std.stdio;
+
+version (Windows)
+void main()
+{
+
+ foreach (line; File("file.txt").byLine(No.keepTerminator, "\r\n"))
+ {
+ writeln("|"~line~"|");
+ if (line == "HelloWorld")
+ writeln("^This Line is here.");
+ }
+
+}
*/
auto byLine(Terminator = char, Char = char)
(KeepTerminator keepTerminator = No.keepTerminator,
/// Range primitive implementations.
void put(A)(scope A writeme)
- if ((isSomeChar!(ElementType!A) ||
- is(ElementType!A : const(ubyte))) &&
- isInputRange!A &&
- !isInfinite!A)
+ if ((isSomeChar!(ElementType!A) ||
+ is(ElementType!A : const(ubyte))) &&
+ isInputRange!A &&
+ !isInfinite!A)
{
import std.exception : errnoEnforce;
}
/// ditto
- void put(C)(scope C c) @safe if (isSomeChar!C || is(C : const(ubyte)))
+ void put(C)(scope C c) @safe
+ if (isSomeChar!C || is(C : const(ubyte)))
{
import std.utf : decodeFront, encode, stride;
alias CaseSensitive = Flag!"caseSensitive";
/++
- Searches for character in range.
+ Searches for a character in a string or range.
Params:
- s = string or InputRange of characters to search in correct UTF format
- c = character to search for
- startIdx = starting index to a well-formed code point
- cs = `Yes.caseSensitive` or `No.caseSensitive`
+ s = string or InputRange of characters to search for `c` in
+ c = character to search for in `s`
+ startIdx = index to a well-formed code point in `s` to start
+ searching from; defaults to 0
+ cs = specifies whether comparisons are case-sensitive
+ (`Yes.caseSensitive`) or not (`No.caseSensitive`).
Returns:
- the index of the first occurrence of `c` in `s` with
- respect to the start index `startIdx`. If `c`
- is not found, then `-1` is returned.
- If `c` is found the value of the returned index is at least
- `startIdx`.
- If the parameters are not valid UTF, the result will still
- be in the range [-1 .. s.length], but will not be reliable otherwise.
+ If `c` is found in `s`, then the index of its first occurrence is
+ returned. If `c` is not found or `startIdx` is greater than or equal to
+ `s.length`, then -1 is returned. If the parameters are not valid UTF,
+ the result will still be either -1 or in the range [`startIdx` ..
+ `s.length`], but will not be reliable otherwise.
Throws:
- If the sequence starting at `startIdx` does not represent a well
- formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
+ If the sequence starting at `startIdx` does not represent a well-formed
+ code point, then a $(REF UTFException, std,utf) may be thrown.
See_Also: $(REF countUntil, std,algorithm,searching)
+/
}
/++
- Searches for substring in `s`.
+ Searches for a substring in a string or range.
Params:
- s = string or ForwardRange of characters to search in correct UTF format
- sub = substring to search for
- startIdx = the index into s to start searching from
- cs = `Yes.caseSensitive` (default) or `No.caseSensitive`
+ s = string or ForwardRange of characters to search for `sub` in
+ sub = substring to search for in `s`
+ startIdx = index to a well-formed code point in `s` to start
+ searching from; defaults to 0
+ cs = specifies whether comparisons are case-sensitive
+ (`Yes.caseSensitive`) or not (`No.caseSensitive`)
Returns:
- the index of the first occurrence of `sub` in `s` with
- respect to the start index `startIdx`. If `sub` is not found,
- then `-1` is returned.
- If the arguments are not valid UTF, the result will still
- be in the range [-1 .. s.length], but will not be reliable otherwise.
- If `sub` is found the value of the returned index is at least
- `startIdx`.
+ The index of the first occurrence of `sub` in `s`. If `sub` is not found
+ or `startIdx` is greater than or equal to `s.length`, then -1 is
+ returned. If the arguments are not valid UTF, the result will still be
+ either -1 or in the range [`startIdx` .. `s.length`], but will not be
+ reliable otherwise.
Throws:
- If the sequence starting at `startIdx` does not represent a well
- formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
+ If the sequence starting at `startIdx` does not represent a well-formed
+ code point, then a $(REF UTFException, std,utf) may be thrown.
Bugs:
- Does not work with case insensitive strings where the mapping of
- tolower and toupper is not 1:1.
+ Does not work with case-insensitive strings where the mapping of
+ $(REF toLower, std,uni) and $(REF toUpper, std,uni) is not 1:1.
+/
ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub)
if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
}
/++
+ Searches for the last occurrence of a character in a string.
+
Params:
- s = string to search
- c = character to search for
- startIdx = the index into s to start searching from
- cs = `Yes.caseSensitive` or `No.caseSensitive`
+ s = string to search for `c` in
+ c = character to search for in `s`
+ startIdx = index of a well-formed code point in `s` to start searching
+ from; defaults to 0
+ cs = specifies whether comparisons are case-sensitive
+ (`Yes.caseSensitive`) or not (`No.caseSensitive`)
Returns:
- The index of the last occurrence of `c` in `s`. If `c` is not
- found, then `-1` is returned. The `startIdx` slices `s` in
- the following way $(D s[0 .. startIdx]). `startIdx` represents a
- codeunit index in `s`.
+ If `c` is found in `s`, then the index of its last occurrence is
+ returned. If `c` is not found or `startIdx` is greater than or equal to
+ `s.length`, then -1 is returned. If the parameters are not valid UTF,
+ the result will still be either -1 or in the range [`startIdx` ..
+ `s.length`], but will not be reliable otherwise.
Throws:
- If the sequence ending at `startIdx` does not represent a well
- formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
-
- `cs` indicates whether the comparisons are case sensitive.
+ If the sequence ending at `startIdx` does not represent a well-formed
+ code point, then a $(REF UTFException, std,utf) may be thrown.
+/
ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c,
in CaseSensitive cs = Yes.caseSensitive) @safe pure
}
/++
+ Searches for the last occurrence of a substring in a string.
+
Params:
- s = string to search
- sub = substring to search for
- startIdx = the index into s to start searching from
- cs = `Yes.caseSensitive` or `No.caseSensitive`
+ s = string to search for `sub` in
+ sub = substring to search for in `s`
+ startIdx = index to a well-formed code point in `s` to start
+ searching from; defaults to 0
+ cs = specifies whether comparisons are case-sensitive
+ (`Yes.caseSensitive`) or not (`No.caseSensitive`)
Returns:
- the index of the last occurrence of `sub` in `s`. If `sub` is
- not found, then `-1` is returned. The `startIdx` slices `s`
- in the following way $(D s[0 .. startIdx]). `startIdx` represents a
- codeunit index in `s`.
+ The index of the last occurrence of `sub` in `s`. If `sub` is not found
+ or `startIdx` is greater than or equal to `s.length`, then -1 is
+ returned. If the parameters are not valid UTF, the result will still be
+ either -1 or in the range [`startIdx` .. `s.length`], but will not be
+ reliable otherwise.
Throws:
- If the sequence ending at `startIdx` does not represent a well
- formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
+ If the sequence starting at `startIdx` does not represent a well-formed
+ code point, then a $(REF UTFException, std,utf) may be thrown.
- `cs` indicates whether the comparisons are case sensitive.
+ Bugs:
+ Does not work with case-insensitive strings where the mapping of
+ $(REF toLower, std,uni) and $(REF toUpper, std,uni) is not 1:1.
+/
ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub,
in CaseSensitive cs = Yes.caseSensitive) @safe pure
}
/**
- Returns the index of the first occurrence of any of the elements in $(D
- needles) in `haystack`. If no element of `needles` is found,
- then `-1` is returned. The `startIdx` slices `haystack` in the
- following way $(D haystack[startIdx .. $]). `startIdx` represents a
- codeunit index in `haystack`. If the sequence ending at `startIdx`
- does not represent a well formed codepoint, then a $(REF UTFException, std,utf)
- may be thrown.
+ Searches the string `haystack` for one of the characters in `needles`
+ starting at index `startIdx`. If `startIdx` is not given, it defaults to 0.
Params:
- haystack = String to search for needles in.
- needles = Strings to search for in haystack.
- startIdx = slices haystack like this $(D haystack[startIdx .. $]). If
- the startIdx is greater than or equal to the length of haystack the
- functions returns `-1`.
- cs = Indicates whether the comparisons are case sensitive.
+ haystack = string to search for needles in
+ needles = characters to search for in `haystack`
+ startIdx = index of a well-formed code point in `haystack` to start
+ searching from; defaults to 0
+ cs = specifies whether comparisons are case-sensitive
+ (`Yes.caseSensitive`) or not (`No.caseSensitive`)
+
+ Returns:
+ The index of the first occurrence of any of the elements of `needles` in
+ `haystack`. If no element of `needles` is found or `startIdx` is greater
+ than or equal to `haystack.length`, then -1 is returned. If the
+ parameters are not valid UTF, the result will still be either -1 or in
+ the range [`startIdx` .. `haystack.length`], but will not be reliable
+ otherwise.
+
+ Throws:
+ If the sequence starting at `startIdx` does not represent a well-formed
+ code point, then a $(REF UTFException, std,utf) may be thrown.
*/
ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles,
in CaseSensitive cs = Yes.caseSensitive) @safe pure
}
/**
- Returns the index of the last occurrence of any of the elements in $(D
- needles) in `haystack`. If no element of `needles` is found,
- then `-1` is returned. The `stopIdx` slices `haystack` in the
- following way $(D s[0 .. stopIdx]). `stopIdx` represents a codeunit
- index in `haystack`. If the sequence ending at `startIdx` does not
- represent a well formed codepoint, then a $(REF UTFException, std,utf) may be
- thrown.
+ Searches `haystack` for the last occurrence of any of the
+ characters in `needles`.
Params:
- haystack = String to search for needles in.
- needles = Strings to search for in haystack.
- stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]). If
- the stopIdx is greater than or equal to the length of haystack the
- functions returns `-1`.
- cs = Indicates whether the comparisons are case sensitive.
+ haystack = string to search needles in
+ needles = characters to search for in `haystack`
+ stopIdx = index in `haystack` to stop searching at (exclusive); defaults
+ to `haystack.length`
+ cs = specifies whether comparisons are case-sensitive
+ (`Yes.caseSensitive`) or not (`No.caseSensitive`)
+
+ Returns:
+ The index of the last occurrence of any of the characters of `needles`
+ in `haystack`. If no character of `needles` is found or `stopIdx` is 0,
+ then -1 is returned. If the parameters are not valid UTF, the result
+ will still be in the range [-1 .. `stopIdx`], but will not be reliable
+ otherwise.
*/
ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack,
const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive)
}
/**
- Returns the index of the first occurrence of any character not an elements
- in `needles` in `haystack`. If all element of `haystack` are
- element of `needles` `-1` is returned.
+ Searches `haystack` for a character not in `needles`.
Params:
- haystack = String to search for needles in.
- needles = Strings to search for in haystack.
- startIdx = slices haystack like this $(D haystack[startIdx .. $]). If
- the startIdx is greater than or equal to the length of haystack the
- functions returns `-1`.
- cs = Indicates whether the comparisons are case sensitive.
+ haystack = string to search for needles in
+ needles = characters to search for in `haystack`
+ startIdx = index of a well-formed code point in `haystack` to start
+ searching from; defaults to 0
+ cs = specifies whether comparisons are case-sensitive
+ (`Yes.caseSensitive`) or not (`No.caseSensitive`)
+
+ Returns:
+ The index of the first character in `haystack` that is not an element of
+ `needles`. If all characters of `haystack` are elements of `needles` or
+ `startIdx` is greater than or equal to `haystack.length`, then -1 is
+ returned. If the parameters are not valid UTF, the result will still be
+ either -1 or in the range [`startIdx` .. `haystack.length`], but will
+ not be reliable otherwise.
+
+ Throws:
+ If the sequence starting at `startIdx` does not represent a well-formed
+ code point, then a $(REF UTFException, std,utf) may be thrown.
*/
ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack,
const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive)
}
/**
- Returns the last index of the first occurence of any character that is not
- an elements in `needles` in `haystack`. If all element of
- `haystack` are element of `needles` `-1` is returned.
+ Searches for the last character in `haystack` that is not in `needles`.
Params:
- haystack = String to search for needles in.
- needles = Strings to search for in haystack.
- stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]) If
- the stopIdx is greater than or equal to the length of haystack the
- functions returns `-1`.
- cs = Indicates whether the comparisons are case sensitive.
+ haystack = string to search for needles in
+ needles = characters to search for in `haystack`
+ stopIdx = index in `haystack` to stop searching at (exclusive);
+ defaults to `haystack.length`
+ cs = specifies whether comparisons are case-sensitive
+ (`Yes.caseSensitive`) or not (`No.caseSensitive`)
+
+ Returns:
+ The index of the last character in `haystack` that is not an element of
+ `needles`. If all characters of `haystack` are in `needles` or `stopIdx`
+ is 0, then -1 is returned. If the parameters are not valid UTF, the
+ result will still be in the range [-1 .. `stopIdx`], but will not be
+ reliable otherwise.
*/
ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack,
const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive)
static struct A(T = void)
{
// doesn't work as expected, only accepts A when T = void
- void func(B)(B b) if (isInstanceOf!(A, B)) {}
+ void func(B)(B b)
+ if (isInstanceOf!(A, B)) {}
// correct behavior
- void method(B)(B b) if (isInstanceOf!(TemplateOf!(A), B)) {}
+ void method(B)(B b)
+ if (isInstanceOf!(TemplateOf!(A), B)) {}
}
A!(void) a1;
// e.g. Tuple!(int, "x", string, "y")
template Interleave(A...)
{
- template and(B...) if (B.length == 1)
+ template and(B...)
+ if (B.length == 1)
{
alias and = AliasSeq!(A[0], B[0]);
}
- template and(B...) if (B.length != 1)
+ template and(B...)
+ if (B.length != 1)
{
alias and = AliasSeq!(A[0], B[0],
Interleave!(A[1..$]).and!(B[1..$]));
non-release mode.
*/
void opAssign()(T value)
- if (isAssignable!T) //@@@9416@@@
+ if (isAssignable!T) //@@@9416@@@
{
enum message = "Called `opAssign' on null NullableRef!" ~ T.stringof ~ ".";
assert(!isNull, message);
}
}
-// / ditto
+/// ditto
class NotImplementedError : Error
{
+ ///
this(string method) nothrow pure @safe
{
super(method ~ " is not implemented");
}
}
+///
@system unittest
{
import std.exception : assertThrown;
Postcondition: `refCountedStore.isInitialized`
*/
- this(A...)(auto ref A args) if (A.length > 0)
+ this(A...)(auto ref A args)
+ if (A.length > 0)
out
{
assert(refCountedStore.isInitialized);
{
import std.functional : unaryFun;
- auto ref borrow(RC)(RC refCount) if
+ auto ref borrow(RC)(RC refCount)
+ if
(
isInstanceOf!(SafeRefCounted, RC)
&& is(typeof(unaryFun!fun(refCount.refCountedPayload)))
}
bool opEquals(T)(T b)
- if (is(ValueType : T) || is(typeof(a.opEquals(b))) || is(typeof(b.opEquals(a))))
+ if (is(ValueType : T) || is(typeof(a.opEquals(b))) || is(typeof(b.opEquals(a))))
{
static if (is(typeof(a.opEquals(b))))
return a.opEquals(b);
}
int opCmp(T)(auto ref const T b)
- if (is(ValueType : T) || is(typeof(a.opCmp(b))) || is(typeof(b.opCmp(a))))
+ if (is(ValueType : T) || is(typeof(a.opCmp(b))) || is(typeof(b.opCmp(a))))
{
static if (is(typeof(a.opCmp(b))))
return a.opCmp(b);
}
}
- auto ref opAssign (this X, V )(auto ref V v) if (!is(V == typeof(this))) { return a = v; }
+ auto ref opAssign (this X, V )(auto ref V v)
+ if (!is(V == typeof(this))) { return a = v; }
auto ref opIndexAssign(this X, V, D...)(auto ref V v, auto ref D i) { return a[i] = v; }
auto ref opSliceAssign(this X, V )(auto ref V v) { return a[] = v; }
auto ref opSliceAssign(this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return a[b .. e] = v; }
*/
struct Yes
{
+ ///
template opDispatch(string name)
{
enum opDispatch = Flag!name.yes;
/// Ditto
struct No
{
+ ///
template opDispatch(string name)
{
enum opDispatch = Flag!name.no;
}
this(T...)(T flags)
- if (allSatisfy!(isBaseEnumType, T))
+ if (allSatisfy!(isBaseEnumType, T))
{
this = flags;
}
}
Base opCast(B)() const
- if (is(Base : B))
+ if (is(Base : B))
{
return mValue;
}
auto opUnary(string op)() const
- if (op == "~")
+ if (op == "~")
{
return BitFlags(cast(E) cast(Base) ~mValue);
}
auto ref opAssign(T...)(T flags)
- if (allSatisfy!(isBaseEnumType, T))
+ if (allSatisfy!(isBaseEnumType, T))
{
mValue = 0;
foreach (E flag; flags)
}
auto opBinary(string op)(BitFlags flags) const
- if (op == "|" || op == "&")
+ if (op == "|" || op == "&")
{
BitFlags result = this;
result.opOpAssign!op(flags);
}
auto opBinary(string op)(E flag) const
- if (op == "|" || op == "&")
+ if (op == "|" || op == "&")
{
BitFlags result = this;
result.opOpAssign!op(flag);
}
auto opBinaryRight(string op)(E flag) const
- if (op == "|" || op == "&")
+ if (op == "|" || op == "&")
{
return opBinary!op(flag);
}
$(TR $(TD `unknown`) $(TD `unknown`) $(TD) $(TD `unknown`) $(TD `unknown`) $(TD `unknown`))
)
*/
- Ternary opUnary(string s)() if (s == "~")
+ Ternary opUnary(string s)()
+ if (s == "~")
{
return make((386 >> value) & 6);
}
/// ditto
- Ternary opBinary(string s)(Ternary rhs) if (s == "|")
+ Ternary opBinary(string s)(Ternary rhs)
+ if (s == "|")
{
return make((25_512 >> (value + rhs.value)) & 6);
}
/// ditto
- Ternary opBinary(string s)(Ternary rhs) if (s == "&")
+ Ternary opBinary(string s)(Ternary rhs)
+ if (s == "&")
{
return make((26_144 >> (value + rhs.value)) & 6);
}
/// ditto
- Ternary opBinary(string s)(Ternary rhs) if (s == "^")
+ Ternary opBinary(string s)(Ternary rhs)
+ if (s == "^")
{
return make((26_504 >> (value + rhs.value)) & 6);
}
return _refCounted;
}
- this(A...)(auto ref A args) if (A.length > 0)
+ this(A...)(auto ref A args)
+ if (A.length > 0)
out
{
assert(refCountedStore.isInitialized);
$(LREF byGrapheme)
$(LREF decodeGrapheme)
$(LREF graphemeStride)
+ $(LREF popGrapheme)
))
$(TR $(TD Comparison) $(TD
$(LREF icmp)
import std.range.primitives : back, ElementEncodingType, ElementType, empty,
front, hasLength, hasSlicing, isForwardRange, isInputRange,
isRandomAccessRange, popFront, put, save;
-import std.traits : isConvertibleToString, isIntegral, isSomeChar,
- isSomeString, Unqual, isDynamicArray;
+import std.traits : isAutodecodableString, isConvertibleToString, isIntegral,
+ isSomeChar, isSomeString, Unqual, isDynamicArray;
// debug = std_uni;
import std.internal.unicode_tables; // generated file
}
void store(OutRange)(scope OutRange sink) const
- if (isOutputRange!(OutRange, char))
+ if (isOutputRange!(OutRange, char))
{
import std.format.write : formattedWrite;
formattedWrite(sink, "[%( 0x%x, %)]", offsets[]);
template sharMethod(alias uniLowerBound)
{
size_t sharMethod(alias _pred="a<b", Range, T)(Range range, T needle)
- if (is(T : ElementType!Range))
+ if (is(T : ElementType!Range))
{
import std.functional : binaryFun;
import std.math.algebraic : nextPow2, truncPow2;
}
static void append(T, V)(ref T[] arr, V value)
- if (!isInputRange!V)
+ if (!isInputRange!V)
{
arr ~= force!T(value);
}
static void append(T, V)(ref T[] arr, V value)
- if (isInputRange!V)
+ if (isInputRange!V)
{
insertInPlace(arr, arr.length, value);
}
static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000
- if (isDynamicArray!T && is(Unqual!T == T))
+ if (isDynamicArray!T && is(Unqual!T == T))
{
debug
{
}
static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000
- if (isDynamicArray!T && !is(Unqual!T == T))
+ if (isDynamicArray!T && !is(Unqual!T == T))
{
arr = null;
}
}
static void append(T, V)(ref T[] arr, V value)
- if (!isInputRange!V)
+ if (!isInputRange!V)
{
if (arr.length == size_t.max) assert(0);
arr = realloc(arr, arr.length+1);
}
static void append(T, V)(ref T[] arr, V value)
- if (isInputRange!V && hasLength!V)
+ if (isInputRange!V && hasLength!V)
{
import core.checkedint : addu;
bool overflow;
Construct from another code point set of any type.
*/
this(Set)(Set set) pure
- if (isCodepointSet!Set)
+ if (isCodepointSet!Set)
{
uint[] arr;
foreach (v; set.byInterval)
Construct a set from a forward range of code point intervals.
*/
this(Range)(Range intervals) pure
- if (isForwardRange!Range && isIntegralPair!(ElementType!Range))
+ if (isForwardRange!Range && isIntegralPair!(ElementType!Range))
{
uint[] arr;
foreach (v; intervals)
)
*/
This opBinary(string op, U)(U rhs)
- if (isCodepointSet!U || is(U:dchar))
+ if (isCodepointSet!U || is(U:dchar))
{
static if (op == "&" || op == "|" || op == "~")
{// symmetric ops thus can swap arguments to reuse r-value
/// The 'op=' versions of the above overloaded operators.
ref This opOpAssign(string op, U)(U rhs)
- if (isCodepointSet!U || is(U:dchar))
+ if (isCodepointSet!U || is(U:dchar))
{
static if (op == "|") // union
{
the same as $(LREF opIndex).
*/
bool opBinaryRight(string op: "in", U)(U ch) const
- if (is(U : dchar))
+ if (is(U : dchar))
{
return this[ch];
}
package(std) // used from: std.regex.internal.parser
ref intersect(U)(U rhs)
- if (isCodepointSet!U)
+ if (isCodepointSet!U)
{
Marker mark;
foreach ( i; rhs.byInterval)
// same as the above except that skip & drop parts are swapped
package(std) // used from: std.regex.internal.parser
ref sub(U)(U rhs)
- if (isCodepointSet!U)
+ if (isCodepointSet!U)
{
Marker mark;
foreach (i; rhs.byInterval)
package(std) // used from: std.regex.internal.parse
ref add(U)(U rhs)
- if (isCodepointSet!U)
+ if (isCodepointSet!U)
{
Marker start;
foreach (i; rhs.byInterval)
}
this(Range)(Range range)
- if (isInputRange!Range && hasLength!Range)
+ if (isInputRange!Range && hasLength!Range)
{
import std.algorithm.mutation : copy;
length = range.length;
}
this(Range)(Range range)
- if (isForwardRange!Range && !hasLength!Range)
+ if (isForwardRange!Range && !hasLength!Range)
{
import std.algorithm.mutation : copy;
import std.range.primitives : walkLength;
}
void append(Range)(Range range)
- if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint))
+ if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint))
{
size_t nl = length + range.length;
length = nl;
template mapTrieIndex(Prefix...)
{
size_t mapTrieIndex(Key)(Key key)
- if (isValidPrefixForTrie!(Key, Prefix))
+ if (isValidPrefixForTrie!(Key, Prefix))
{
alias p = Prefix;
size_t idx;
///
void store(OutRange)(scope OutRange sink) const
- if (isOutputRange!(OutRange, char))
+ if (isOutputRange!(OutRange, char))
{
_table.store(sink);
}
if (sumOfIntegerTuple!sizes == 21)
{
auto codepointSetTrie(Set)(Set set)
- if (isCodepointSet!Set)
+ if (isCodepointSet!Set)
{
auto builder = TrieBuilder!(bool, dchar, lastDchar+1, GetBitSlicing!(21, sizes))(false);
foreach (ival; set.byInterval)
static if (is(TypeOfBitPacked!T == bool))
{
auto codepointTrie(Set)(const scope Set set)
- if (isCodepointSet!Set)
+ if (isCodepointSet!Set)
{
return codepointSetTrie(set);
}
// unsorted range of pairs
///
auto codepointTrie(R)(R range, T defValue=T.init)
- if (isInputRange!R
- && is(typeof(ElementType!R.init[0]) : T)
- && is(typeof(ElementType!R.init[1]) : dchar))
+ if (isInputRange!R
+ && is(typeof(ElementType!R.init[0]) : T)
+ && is(typeof(ElementType!R.init[1]) : dchar))
{
// build from unsorted array of pairs
// TODO: expose index sorting functions for Trie
$(REF setUnion, std,_algorithm).
*/
auto buildTrie(Range)(Range range, Value filler=Value.init)
- if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value)
- && is(typeof(Range.init.front[1]) : Key))
+ if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value)
+ && is(typeof(Range.init.front[1]) : Key))
{
auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
foreach (v; range)
and `filler` is false.
*/
auto buildTrie(Range)(Range range, Value filler=Value.init)
- if (is(TypeOfBitPacked!Value == bool)
- && isInputRange!Range && is(typeof(Range.init.front[0]) : Key)
- && is(typeof(Range.init.front[1]) : Key))
+ if (is(TypeOfBitPacked!Value == bool)
+ && isInputRange!Range && is(typeof(Range.init.front[0]) : Key)
+ && is(typeof(Range.init.front[1]) : Key))
{
auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
foreach (ival; range)
}
auto buildTrie(Range)(Range range, Value filler, bool unsorted)
- if (isInputRange!Range
- && is(typeof(Range.init.front[0]) : Value)
- && is(typeof(Range.init.front[1]) : Key))
+ if (isInputRange!Range
+ && is(typeof(Range.init.front[0]) : Value)
+ && is(typeof(Range.init.front[1]) : Key))
{
import std.algorithm.sorting : multiSort;
alias Comps = GetComparators!(Prefix.length);
If no filler provided keys map to true, and `filler` is false.
*/
auto buildTrie(Range)(Range range, Value filler=Value.init)
- if (is(TypeOfBitPacked!Value == bool)
- && isInputRange!Range && is(typeof(Range.init.front) : Key))
+ if (is(TypeOfBitPacked!Value == bool)
+ && isInputRange!Range && is(typeof(Range.init.front) : Key))
{
auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
foreach (v; range)
of values where array index serves as key.
*/
auto buildTrie()(Value[] array, Value filler=Value.init)
- if (isUnsigned!Key)
+ if (isUnsigned!Key)
{
auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
foreach (idx, v; array)
of the result of test.)
*/
public bool match(Range)(ref Range inp)
- if (isRandomAccessRange!Range && is(ElementType!Range : char))
+ if (isRandomAccessRange!Range && is(ElementType!Range : char))
{
assert(false);
}
///ditto
public bool skip(Range)(ref Range inp)
- if (isRandomAccessRange!Range && is(ElementType!Range : char))
+ if (isRandomAccessRange!Range && is(ElementType!Range : char))
{
assert(false);
}
///ditto
public bool test(Range)(ref Range inp)
- if (isRandomAccessRange!Range && is(ElementType!Range : char))
+ if (isRandomAccessRange!Range && is(ElementType!Range : char))
{
assert(false);
}
}
static auto encode(size_t sz)(dchar ch)
- if (sz > 1)
+ if (sz > 1)
{
import std.utf : encodeUTF = encode;
char[4] buf;
enum dispatch = genDispatch();
public bool match(Range)(ref Range inp) const
- if (isRandomAccessRange!Range && is(ElementType!Range : char) &&
- !isDynamicArray!Range)
+ if (isRandomAccessRange!Range && is(ElementType!Range : char) &&
+ !isDynamicArray!Range)
{
enum mode = Mode.skipOnMatch;
assert(!inp.empty);
static if (Sizes.length == 4) // can skip iff can detect all encodings
{
public bool skip(Range)(ref Range inp) const
- if (isRandomAccessRange!Range && is(ElementType!Range : char) &&
- !isDynamicArray!Range)
+ if (isRandomAccessRange!Range && is(ElementType!Range : char) &&
+ !isDynamicArray!Range)
{
enum mode = Mode.alwaysSkip;
assert(!inp.empty);
}
public bool test(Range)(ref Range inp) const
- if (isRandomAccessRange!Range && is(ElementType!Range : char) &&
- !isDynamicArray!Range)
+ if (isRandomAccessRange!Range && is(ElementType!Range : char) &&
+ !isDynamicArray!Range)
{
enum mode = Mode.neverSkip;
assert(!inp.empty);
}
bool match(C)(ref C[] str) const
- if (isSomeChar!C)
+ if (isSomeChar!C)
{
return fwdStr!"match"(str);
}
bool skip(C)(ref C[] str) const
- if (isSomeChar!C)
+ if (isSomeChar!C)
{
return fwdStr!"skip"(str);
}
bool test(C)(ref C[] str) const
- if (isSomeChar!C)
+ if (isSomeChar!C)
{
return fwdStr!"test"(str);
}
mixin template DefMatcher()
{
public bool match(Range)(ref Range inp) const
- if (isRandomAccessRange!Range && is(ElementType!Range : wchar) &&
- !isDynamicArray!Range)
+ if (isRandomAccessRange!Range && is(ElementType!Range : wchar) &&
+ !isDynamicArray!Range)
{
enum mode = Mode.skipOnMatch;
assert(!inp.empty);
static if (Sizes.length == 2)
{
public bool skip(Range)(ref Range inp) const
- if (isRandomAccessRange!Range && is(ElementType!Range : wchar) &&
- !isDynamicArray!Range)
+ if (isRandomAccessRange!Range && is(ElementType!Range : wchar) &&
+ !isDynamicArray!Range)
{
enum mode = Mode.alwaysSkip;
assert(!inp.empty);
}
public bool test(Range)(ref Range inp) const
- if (isRandomAccessRange!Range && is(ElementType!Range : wchar) &&
- !isDynamicArray!Range)
+ if (isRandomAccessRange!Range && is(ElementType!Range : wchar) &&
+ !isDynamicArray!Range)
{
enum mode = Mode.neverSkip;
assert(!inp.empty);
}
bool match(C)(ref C[] str) const
- if (isSomeChar!C)
+ if (isSomeChar!C)
{
return fwdStr!"match"(str);
}
bool skip(C)(ref C[] str) const
- if (isSomeChar!C)
+ if (isSomeChar!C)
{
return fwdStr!"skip"(str);
}
bool test(C)(ref C[] str) const
- if (isSomeChar!C)
+ if (isSomeChar!C)
{
return fwdStr!"test"(str);
}
}
struct Impl(Sizes...)
- if (Sizes.length >= 1 && Sizes.length <= 2)
+ if (Sizes.length >= 1 && Sizes.length <= 2)
{
private:
import std.meta : allSatisfy;
}
struct CherryPick(I, Sizes...)
- if (Sizes.length >= 1 && Sizes.length <= 2)
+ if (Sizes.length >= 1 && Sizes.length <= 2)
{
private:
import std.meta : allSatisfy;
{
/// Run-time checked search.
static auto opCall(C)(const scope C[] name)
- if (is(C : dchar))
+ if (is(C : dchar))
{
import std.conv : to;
CodepointSet set;
sets.
*/
static auto opCall(C)(const scope C[] name)
- if (is(C : dchar))
+ if (is(C : dchar))
{
return loadAny(name);
}
TransformRes.goOn
];
-template genericDecodeGrapheme(bool getValue)
-{
- static if (getValue)
+enum GraphemeRet { none, step, value }
+
+template genericDecodeGrapheme(GraphemeRet retType)
+{ alias Ret = GraphemeRet;
+
+ static if (retType == Ret.value)
alias Value = Grapheme;
- else
+ else static if (retType == Ret.step)
+ alias Value = size_t;
+ else static if (retType == Ret.none)
alias Value = void;
Value genericDecodeGrapheme(Input)(ref Input range)
{
- static if (getValue)
- Grapheme grapheme;
+ static if (retType == Ret.value)
+ Grapheme result;
+ else static if (retType == Ret.step)
+ size_t result = 0;
+
auto state = GraphemeState.Start;
dchar ch;
with(TransformRes)
{
case goOn:
- static if (getValue)
- grapheme ~= ch;
+ static if (retType == Ret.value)
+ result ~= ch;
+ else static if (retType == Ret.step)
+ result++;
range.popFront();
continue;
goto rerun;
case retInclude:
- static if (getValue)
- grapheme ~= ch;
+ static if (retType == Ret.value)
+ result ~= ch;
+ else static if (retType == Ret.step)
+ result++;
range.popFront();
break outer;
}
}
- static if (getValue)
- return grapheme;
+ static if (retType != Ret.none)
+ return result;
}
}
{
auto src = input[index..$];
auto n = src.length;
- genericDecodeGrapheme!(false)(src);
+ genericDecodeGrapheme!(GraphemeRet.none)(src);
return n - src.length;
}
Grapheme decodeGrapheme(Input)(ref Input inp)
if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar))
{
- return genericDecodeGrapheme!true(inp);
+ return genericDecodeGrapheme!(GraphemeRet.value)(inp);
}
@safe unittest
assert(equal(decodeGrapheme(s)[], "\U0001F1EC\U0001F1E7"));
}
+/++
+ Reads one full grapheme cluster from an
+ $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of dchar `inp`,
+ but doesn't return it. Instead returns the number of code units read.
+ This differs from number of code points read only if `input` is an
+ autodecodable string.
+
+ Note:
+ This function modifies `inp` and thus `inp`
+ must be an L-value.
++/
+size_t popGrapheme(Input)(ref Input inp)
+if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar))
+{
+ static if (isAutodecodableString!Input || hasLength!Input)
+ {
+ // Why count each step in the decoder when you can just
+ // measure the grapheme in one go?
+ auto n = inp.length;
+ genericDecodeGrapheme!(GraphemeRet.none)(inp);
+ return n - inp.length;
+ }
+ else return genericDecodeGrapheme!(GraphemeRet.step)(inp);
+}
+
+///
+@safe pure unittest
+{
+ // Two Union Jacks of the Great Britain in each
+ string s = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7";
+ wstring ws = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7";
+ dstring ds = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7";
+
+ // String pop length in code units, not points.
+ assert(s.popGrapheme() == 8);
+ assert(ws.popGrapheme() == 4);
+ assert(ds.popGrapheme() == 2);
+
+ assert(s == "\U0001F1EC\U0001F1E7");
+ assert(ws == "\U0001F1EC\U0001F1E7");
+ assert(ds == "\U0001F1EC\U0001F1E7");
+
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter;
+
+ // Also works for non-random access ranges as long as the
+ // character type is 32-bit.
+ auto testPiece = "\r\nhello!"d.filter!(x => !x.isAlpha);
+ // Windows-style line ending is two code points in a single grapheme.
+ assert(testPiece.popGrapheme() == 2);
+ assert(testPiece.equal("!"d));
+}
+
+// Attribute compliance test. Should be nothrow `@nogc` when
+// no autodecoding needed.
+@safe pure nothrow @nogc unittest
+{
+ import std.algorithm.iteration : filter;
+
+ auto str = "abcdef"d;
+ assert(str.popGrapheme() == 1);
+
+ // also test with non-random access
+ auto filtered = "abcdef"d.filter!(x => x%2);
+ assert(filtered.popGrapheme() == 1);
+}
+
/++
$(P Iterate a string by $(LREF Grapheme).)
public:
/// Ctor
this(C)(const scope C[] chars...)
- if (is(C : dchar))
+ if (is(C : dchar))
{
this ~= chars;
}
///ditto
this(Input)(Input seq)
- if (!isDynamicArray!Input
- && isInputRange!Input && is(ElementType!Input : dchar))
+ if (!isDynamicArray!Input
+ && isInputRange!Input && is(ElementType!Input : dchar))
{
this ~= seq;
}
/// Append all $(CHARACTERS) from the input range `inp` to this Grapheme.
ref opOpAssign(string op, Input)(scope Input inp)
- if (isInputRange!Input && is(ElementType!Input : dchar))
+ if (isInputRange!Input && is(ElementType!Input : dchar))
{
static if (op == "~")
{
@property bool valid()() /*const*/
{
auto r = this[];
- genericDecodeGrapheme!false(r);
+ genericDecodeGrapheme!(GraphemeRet.none)(r);
return r.length == 0;
}
{
void toCaseInPlaceAlloc(C)(ref C[] s, size_t curIdx,
size_t destIdx) @trusted pure
- if (is(C == char) || is(C == wchar) || is(C == dchar))
+ if (is(C == char) || is(C == wchar) || is(C == dchar))
{
import std.utf : decode;
alias caseLength = toCaseLength!(indexFn, maxIdx, tableFn);
else:
auto ref byUTF(R)(R r)
- if (isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
+ if (isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
{
return byUTF(r.byCodeUnit());
}
auto ref byUTF(R)(R r)
- if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
+ if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
{
static if (is(immutable ElementEncodingType!R == immutable RC, RC) && is(RC == C))
{
* You need to pass exactly 16 ubytes.
*/
@safe pure this(T...)(T uuidData)
- if (uuidData.length == 16 && allSatisfy!(isIntegral, T))
+ if (uuidData.length == 16 && allSatisfy!(isIntegral, T))
{
import std.conv : to;
*
* For a less strict parser, see $(LREF parseUUID)
*/
- this(T)(in T[] uuid) if (isSomeChar!T)
+ this(T)(in T[] uuid)
+ if (isSomeChar!T)
{
import std.conv : to, parse;
if (uuid.length < 36)
/// Allows assignment from a subset algebraic type
this(T : VariantN!(tsize, Types), size_t tsize, Types...)(T value)
- if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types))
+ if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types))
{
opAssign(value);
}
// Allow assignment from another variant which is a subset of this one
VariantN opAssign(T : VariantN!(tsize, Types), size_t tsize, Types...)(T rhs)
- if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types))
+ if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types))
{
// discover which type rhs is actually storing
foreach (V; T.AllowedTypes)
{ return opLogic!(T, op)(lhs); }
///ditto
VariantN opBinary(string op, T)(T rhs)
- if (op == "~")
+ if (op == "~")
{
auto temp = this;
temp ~= rhs;
If the `VariantN` contains an array, applies `dg` to each
element of the array in turn. Otherwise, throws an exception.
*/
- int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate))
+ int opApply(Delegate)(scope Delegate dg)
+ if (is(Delegate == delegate))
{
alias A = Parameters!(Delegate)[0];
if (type == typeid(A[]))
{
///
auto visit(VariantType)(VariantType variant)
- if (isAlgebraic!VariantType)
+ if (isAlgebraic!VariantType)
{
return visitImpl!(true, VariantType, Handlers)(variant);
}
{
///
auto tryVisit(VariantType)(VariantType variant)
- if (isAlgebraic!VariantType)
+ if (isAlgebraic!VariantType)
{
return visitImpl!(false, VariantType, Handlers)(variant);
}