continue;
}
- StringExp *se = e0->toStringExp ();
+ StringExp *se = dmd::toStringExp (e0);
gcc_assert (se->sz == 1);
/* Empty string attribute, just ignore it. */
dtype = Type::basic[i];
/* Search for type matching size and signedness. */
- if (unsignedp != dtype->isUnsigned ()
+ if (unsignedp != dmd::isUnsigned (dtype)
|| size != dmd::size (dtype))
continue;
if (optimize)
VersionCondition::addPredefinedGlobalIdent ("D_Optimized");
+ if (profile_flag)
+ VersionCondition::addPredefinedGlobalIdent ("D_Profile");
VersionCondition::addPredefinedGlobalIdent ("all");
&& dmd::implicitConvTo (tb1, tb2) != MATCH::nomatch)
return true;
- if (tb1->isIntegral () == tb2->isIntegral ()
+ if (dmd::isIntegral (tb1) == dmd::isIntegral (tb2)
&& dmd::size (tb1) == dmd::size (tb2))
return true;
switch (tb->ty)
{
case TY::Tsarray:
- return size_int (tb->isTypeSArray ()->dim->toUInteger ());
+ return size_int (dmd::toUInteger (tb->isTypeSArray ()->dim));
case TY::Tarray:
return d_array_length (exp);
/* Compare inner data structures. */
tcmp = lower_struct_comparison (code, ts->sym, t1ref, t2ref);
}
- else if (type->ty != TY::Tvector && type->isIntegral ())
+ else if (type->ty != TY::Tvector && dmd::isIntegral (type))
{
/* Integer comparison, no special handling required. */
tcmp = build_boolop (code, t1ref, t2ref);
}
- else if (type->ty != TY::Tvector && type->isFloating ())
+ else if (type->ty != TY::Tvector && dmd::isFloating (type))
{
/* Floating-point comparison, don't compare padding in type. */
- if (!type->isComplex ())
+ if (!dmd::isComplex (type))
tcmp = build_float_identity (code, t1ref, t2ref);
else
{
if (TREE_CODE (etype) == ARRAY_TYPE && TREE_TYPE (val) != etype)
val = build_array_from_val (type->nextOf (), val);
- size_t dims = type->isTypeSArray ()->dim->toInteger ();
+ size_t dims = dmd::toInteger (type->isTypeSArray ()->dim);
vec <constructor_elt, va_gc> *elms = NULL;
vec_safe_reserve (elms, dims);
}
/* Returns the TypeFunction class for Type T.
- Assumes T is already ->toBasetype(). */
+ Assumes T is already the main variant type (toBasetype). */
TypeFunction *
get_function_type (Type *t)
DECL_LANG_FRAMEINFO (fds) = ffi;
const bool requiresClosure = fd->requiresClosure;
- if (fd->needsClosure ())
+ if (dmd::needsClosure (fd))
{
/* This can shift due to templates being expanded that access alias
symbols, give it a decent error for now. */
Type *tb = type->toBasetype ();
- if (expr->type->isIntegral ())
- cst = build_integer_cst (expr->toInteger (), build_ctype (expr->type));
- else if (expr->type->isFloating ())
- cst = build_float_cst (expr->toReal (), expr->type);
+ if (dmd::isIntegral (expr->type))
+ cst = build_integer_cst (dmd::toInteger (expr), build_ctype (expr->type));
+ else if (dmd::isFloating (expr->type))
+ cst = build_float_cst (dmd::toReal (expr), expr->type);
else if (expr->op == EXP::arrayLiteral)
{
/* Build array as VECTOR_CST, assumes EXPR is constant. */
for (size_t i = 0; i < elements->length; i++)
{
Expression *e = (*elements)[i];
- if (e->type->isIntegral ())
+ if (dmd::isIntegral (e->type))
{
- tree value = build_integer_cst (e->toInteger (),
+ tree value = build_integer_cst (dmd::toInteger (e),
build_ctype (e->type));
CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value);
}
- else if (e->type->isFloating ())
+ else if (dmd::isFloating (e->type))
{
- tree value = build_float_cst (e->toReal (), e->type);
+ tree value = build_float_cst (dmd::toReal (e), e->type);
CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value);
}
else
}
/* Build vector type. */
- int nunits = expr->type->isTypeSArray ()->dim->toUInteger ();
+ int nunits = dmd::toUInteger (expr->type->isTypeSArray ()->dim);
Type *telem = expr->type->nextOf ();
tree vectype = build_vector_type (build_ctype (telem), nunits);
{
/* Interpret value as a vector of the same size,
then return the array literal. */
- int nunits = type->isTypeSArray ()->dim->toUInteger ();
+ int nunits = dmd::toUInteger (type->isTypeSArray ()->dim);
Type *elem = type->nextOf ();
tree vectype = build_vector_type (build_ctype (elem), nunits);
}
else if (tbtype->ty == TY::Tarray)
{
- dinteger_t dim = ebtype->isTypeSArray ()->dim->toInteger ();
+ dinteger_t dim = dmd::toInteger (ebtype->isTypeSArray ()->dim);
dinteger_t esize = dmd::size (ebtype->nextOf ());
dinteger_t tsize = dmd::size (tbtype->nextOf ());
else if (tbtype->ty == TY::Tsarray)
{
/* Strings are treated as dynamic arrays in D2. */
- if (ebtype->isString () && tbtype->isString ())
+ if (dmd::isString (ebtype) && dmd::isString (tbtype))
return indirect_ref (build_ctype (totype), d_array_ptr (exp));
}
else
default:
/* All casts between imaginary and non-imaginary result in 0.0,
except for casts between complex and imaginary types. */
- if (!ebtype->isComplex () && !tbtype->isComplex ()
- && (ebtype->isImaginary () != tbtype->isImaginary ()))
+ if (!dmd::isComplex (ebtype) && !dmd::isComplex (tbtype)
+ && (dmd::isImaginary (ebtype) != dmd::isImaginary (tbtype)))
{
warning (OPT_Wcast_result,
"cast from %qs to %qs will produce zero result",
tree array = d_save_expr (TREE_OPERAND (ptr, 0));
array = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (expr), array);
- uinteger_t dim = tbtype->isTypeSArray ()->dim->toUInteger ();
+ uinteger_t dim = dmd::toUInteger (tbtype->isTypeSArray ()->dim);
vec <constructor_elt, va_gc> *elms = NULL;
for (uinteger_t i = 0; i < dim; i++)
{
if (same_type_p (telem, ebtype))
{
TypeSArray *sa_type = tbtype->isTypeSArray ();
- uinteger_t count = sa_type->dim->toUInteger ();
+ uinteger_t count = dmd::toUInteger (sa_type->dim);
tree ctor = build_padded_constructor (build_ctype (totype), NULL);
if (count)
/* D Front end uses IntegerExp(0) to mean zero-init an array or structure. */
if ((tbtype->ty == TY::Tsarray || tbtype->ty == TY::Tstruct)
- && ebtype->isIntegral ())
+ && dmd::isIntegral (ebtype))
{
tree ret = build_expr (expr, false, literalp);
gcc_assert (integer_zerop (ret));
/* Convert single element to an array. */
tree expr = build_expr (exp);
- if (!exp->isLvalue ())
+ if (!dmd::isLvalue (exp))
{
tree var = build_local_temp (TREE_TYPE (expr));
expr = compound_expr (modify_expr (var, expr), var);
error count depending on how KIND is treated. */
void D_ATTRIBUTE_FORMAT(2,0) ATTRIBUTE_GCC_DIAG(2,0)
-verrorReport (const SourceLoc loc, const char *format, va_list ap,
- ErrorKind kind, const char *prefix1, const char *prefix2)
+vreportDiagnostic (const SourceLoc loc, const char *format, va_list ap,
+ ErrorKind kind, const char *prefix1, const char *prefix2)
{
enum diagnostics::kind diag_kind = diagnostics::kind::unspecified;
int opt = 0;
else if (kind == ErrorKind::warning)
{
if (global.gag || global.params.useWarnings == DIAGNOSTICoff)
- {
- if (global.gag)
- global.gaggedWarnings++;
-
- return;
- }
+ return;
/* Warnings don't count if not treated as errors. */
if (global.params.useWarnings == DIAGNOSTICerror)
else if (kind == ErrorKind::deprecation)
{
if (global.params.useDeprecated == DIAGNOSTICerror)
- return verrorReport (loc, format, ap, ErrorKind::error, prefix1,
- prefix2);
+ return vreportDiagnostic (loc, format, ap, ErrorKind::error, prefix1,
+ prefix2);
else if (global.gag || global.params.useDeprecated != DIAGNOSTICinform)
{
if (global.gag)
- global.gaggedWarnings++;
+ global.gaggedDeprecations++;
return;
}
explicit location LOC. This doesn't increase the global error count. */
void D_ATTRIBUTE_FORMAT(2,0) ATTRIBUTE_GCC_DIAG(2,0)
-verrorReportSupplemental (const SourceLoc loc, const char* format, va_list ap,
- ErrorKind kind)
+vsupplementalDiagnostic (const SourceLoc loc, const char* format, va_list ap,
+ ErrorKind kind)
{
if (kind == ErrorKind::error)
{
else if (kind == ErrorKind::deprecation)
{
if (global.params.useDeprecated == DIAGNOSTICerror)
- return verrorReportSupplemental (loc, format, ap, ErrorKind::error);
+ return vsupplementalDiagnostic (loc, format, ap, ErrorKind::error);
else if (global.params.useDeprecated != DIAGNOSTICinform || global.gag)
return;
}
d_option.stdinc = false;
break;
+ case OPT_std_d2024:
+ global.params.edition = Edition::v2024;
+ break;
+
+ case OPT_std_d202y:
+ global.params.edition = Edition::v2025;
+ break;
+
case OPT_v:
global.params.v.verbose = value;
break;
}
/* Do deferred semantic analysis. */
- Module::runDeferredSemantic ();
+ dmd::runDeferredSemantic ();
if (Module::deferred.length)
{
dmd::semantic2 (m, NULL);
}
- Module::runDeferredSemantic2 ();
+ dmd::runDeferredSemantic2 ();
if (global.errors)
goto had_errors;
}
}
- Module::runDeferredSemantic3 ();
+ dmd::runDeferredSemantic3 ();
/* Check again, incase semantic3 pass loaded any more modules. */
while (builtin_modules.length != 0)
/* Generate C++ header files. */
if (global.params.cxxhdr.doOutput)
- dmd::genCppHdrFiles (modules);
+ dmd::genCppHdrFiles (modules, global.errorSink);
if (global.errors)
goto had_errors;
Target::fieldalign (Type *type)
{
/* Work out the correct alignment for the field decl. */
- unsigned int align = type->alignsize () * BITS_PER_UNIT;
+ unsigned int align = dmd::alignsize (type) * BITS_PER_UNIT;
#ifdef BIGGEST_FIELD_ALIGNMENT
align = MIN (align, (unsigned) BIGGEST_FIELD_ALIGNMENT);
type = Type::tuns8;
/* No support for non-trivial types, complex types, or booleans. */
- if (!type->isTypeBasic () || type->isComplex () || type->ty == TY::Tbool)
+ if (!type->isTypeBasic () || dmd::isComplex (type) || type->ty == TY::Tbool)
return 2;
/* In [simd/vector extensions], which vector types are supported depends on
return true;
/* Don't support if type is non-scalar, such as __vector(void[]). */
- if (!type->isScalar ())
+ if (!dmd::isScalar (type))
return false;
/* Don't support if expression cannot be represented. */
case EXP::mod:
case EXP::modAssign:
/* fmod() is lowered as a function call. */
- if (type->isFloating ())
+ if (dmd::isFloating (type))
return false;
break;
{
/* Do not store variables we cannot take the address of,
but keep the values for purposes of debugging. */
- if (d->type->isScalar () && !dmd::hasPointers (d->type))
+ if (dmd::isScalar (d->type) && !dmd::hasPointers (d->type))
{
tree decl = get_symbol_decl (d);
d_pushdecl (decl);
doing_semantic_analysis_p = true;
dmd::functionSemantic3 (d);
- Module::runDeferredSemantic3 ();
+ dmd::runDeferredSemantic3 ();
doing_semantic_analysis_p = false;
}
else
d->shidden = resdecl;
+ tree var = NULL_TREE;
+
if (d->isNRVO () && d->nrvo_var)
- {
- tree var = get_symbol_decl (d->nrvo_var);
+ var = get_symbol_decl (d->nrvo_var);
+ else if (d->vresult)
+ var = get_symbol_decl (d->vresult);
+ if (var != NULL_TREE)
+ {
/* Copy name from VAR to RESULT. */
DECL_NAME (resdecl) = DECL_NAME (var);
/* Don't forget that we take its address. */
/* CONST_DECL was initially intended for enumerals and may be used for
scalars in general, but not for aggregates. Here a non-constant
value is generated anyway so as its value can be used. */
- if (!vd->canTakeAddressOf () && !vd->type->isScalar ())
+ if (!vd->canTakeAddressOf () && !dmd::isScalar (vd->type))
{
gcc_assert (vd->_init && !vd->_init->isVoidInitializer ());
Expression *ie = dmd::initializerToExpression (vd->_init);
/* Cannot make an expression out of a void initializer. */
gcc_assert (vd->_init && !vd->_init->isVoidInitializer ());
/* Non-scalar manifest constants have already been dealt with. */
- gcc_assert (vd->type->isScalar ());
+ gcc_assert (dmd::isScalar (vd->type));
Expression *ie = dmd::initializerToExpression (vd->_init);
DECL_INITIAL (decl->csym) = build_expr (ie, true);
-e1f6680f50d147846316c2fa3363461a2aa7ac1d
+24a41073c2dbf456d4f7a6fe6a7965d6ce6fc5cb
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
| [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 |
| [timetrace.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/timetrace.d) | Build time profiling utility |
+| [targetcompiler.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/targetcompiler.d) | Differences in building gdc, ldc and dmd |
### Lexing / parsing
-v2.112.0-beta.1
+v2.112.0
import dmd.aliasthis;
import dmd.arraytypes;
import dmd.astenums;
-import dmd.attrib;
import dmd.declaration;
import dmd.dscope;
-import dmd.dstruct;
import dmd.dsymbol;
-import dmd.dsymbolsem : determineSize;
-import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
import dmd.func;
-import dmd.hdrgen;
-import dmd.id;
import dmd.identifier;
import dmd.location;
import dmd.mtype;
-import dmd.tokens;
import dmd.visitor;
/**
DtorDeclaration fieldDtor; /// function destructing (non-inherited) fields
Expression getRTInfo; /// pointer to GC info generated by object.RTInfo(this)
+ Scope* rtInfoScope; /// scope to be used when evaluating getRTInfo
///
Visibility visibility;
visibility = Visibility(Visibility.Kind.public_);
}
- /***************************************
- * Create a new scope from sc.
- * semantic, semantic2 and semantic3 will use this for aggregate members.
- */
- Scope* newScope(Scope* sc)
- {
- auto sc2 = sc.push(this);
- sc2.stc &= STC.flowThruAggregate;
- sc2.parent = this;
- sc2.inunion = isUnionDeclaration();
- sc2.visibility = Visibility(Visibility.Kind.public_);
- sc2.explicitVisibility = false;
- sc2.aligndecl = null;
- sc2.userAttribDecl = null;
- sc2.namespace = null;
- return sc2;
- }
-
/***************************************
* Returns:
* The total number of fields minus the number of hidden fields.
return fields.length - isNested() - (vthis2 !is null);
}
- override final ulong size(Loc loc)
- {
- //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
- bool ok = determineSize(this, loc);
- //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
- return ok ? structsize : SIZE_INVALID;
- }
-
-
- override final Type getType()
- {
- /* Apply storage classes to forward references. (Issue 22254)
- * Note: Avoid interfaces for now. Implementing qualifiers on interface
- * definitions exposed some issues in their TypeInfo generation in DMD.
- * Related PR: https://github.com/dlang/dmd/pull/13312
- */
- if (semanticRun == PASS.initial && !isInterfaceDeclaration())
- {
- auto stc = storage_class;
- if (_scope)
- stc |= _scope.stc;
- type = type.addSTC(stc);
- }
- return type;
- }
-
// is aggregate deprecated?
override final bool isDeprecated() const
{
return enclosing !is null;
}
- /* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested.
- */
- extern (D) final void makeNested()
- {
- if (enclosing) // if already nested
- return;
- if (sizeok == Sizeok.done)
- return;
- if (isUnionDeclaration() || isInterfaceDeclaration())
- return;
- if (storage_class & STC.static_)
- return;
-
- // If nested struct, add in hidden 'this' pointer to outer scope
- auto s = toParentLocal();
- if (!s)
- s = toParent2();
- if (!s)
- return;
- Type t = null;
- if (auto fd = s.isFuncDeclaration())
- {
- enclosing = fd;
-
- /* https://issues.dlang.org/show_bug.cgi?id=14422
- * If a nested class parent is a function, its
- * context pointer (== `outer`) should be void* always.
- */
- t = Type.tvoidptr;
- }
- else if (auto ad = s.isAggregateDeclaration())
- {
- if (isClassDeclaration() && ad.isClassDeclaration())
- {
- enclosing = ad;
- }
- else if (isStructDeclaration())
- {
- if (auto ti = ad.parent.isTemplateInstance())
- {
- enclosing = ti.enclosing;
- }
- }
- t = ad.handleType();
- }
- if (enclosing)
- {
- //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars());
- assert(t);
- if (t.ty == Tstruct)
- t = Type.tvoidptr; // t should not be a ref type
-
- assert(!vthis);
- vthis = new ThisDeclaration(loc, t);
- //vthis.storage_class |= STC.ref_;
-
- // Emulate vthis.addMember()
- members.push(vthis);
-
- // Emulate vthis.dsymbolSemantic()
- vthis.storage_class |= STC.field;
- vthis.parent = this;
- vthis.visibility = Visibility(Visibility.Kind.public_);
- vthis.alignment = t.alignment();
- vthis.semanticRun = PASS.semanticdone;
-
- if (sizeok == Sizeok.fwd)
- fields.push(vthis);
-
- makeNested2();
- }
- }
-
- /* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer.
- */
- extern (D) final void makeNested2()
- {
- if (vthis2)
- return;
- if (!vthis)
- makeNested(); // can't add second before first
- if (!vthis)
- return;
- if (sizeok == Sizeok.done)
- return;
- if (isUnionDeclaration() || isInterfaceDeclaration())
- return;
- if (storage_class & STC.static_)
- return;
-
- auto s0 = toParentLocal();
- auto s = toParent2();
- if (!s || !s0 || s == s0)
- return;
- auto cd = s.isClassDeclaration();
- Type t = cd ? cd.type : Type.tvoidptr;
-
- vthis2 = new ThisDeclaration(loc, t);
- //vthis2.storage_class |= STC.ref_;
-
- // Emulate vthis2.addMember()
- members.push(vthis2);
-
- // Emulate vthis2.dsymbolSemantic()
- vthis2.storage_class |= STC.field;
- vthis2.parent = this;
- vthis2.visibility = Visibility(Visibility.Kind.public_);
- vthis2.alignment = t.alignment();
- vthis2.semanticRun = PASS.semanticdone;
-
- if (sizeok == Sizeok.fwd)
- fields.push(vthis2);
- }
-
override final bool isExport() const
{
return visibility.kind == Visibility.Kind.export_;
bool fill(StructDeclaration* sd, Loc loc, Expressions &elements, bool ctorinit);
bool isFuncHidden(ClassDeclaration* cd, FuncDeclaration* fd);
Dsymbol* vtblSymbol(ClassDeclaration *cd);
+ bool fillVtbl(BaseClass *bc, ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance);
}
enum class ClassKind : uint8_t
DtorDeclaration *fieldDtor; // function destructing (non-inherited) fields
Expression *getRTInfo; // pointer to GC info generated by object.RTInfo(this)
+ Scope* rtInfoScope; // scope to be used when evaluating getRTInfo
Visibility visibility;
d_bool noDefaultCtor; // no default construction
d_bool disableNew; // disallow allocations using `new`
Sizeok sizeok; // set when structsize contains valid data
- virtual Scope *newScope(Scope *sc);
- uinteger_t size(Loc loc) override final;
- Type *getType() override final;
bool isDeprecated() const override final; // is aggregate deprecated?
bool isNested() const;
bool isExport() const override final;
static StructDeclaration *create(Loc loc, Identifier *id, bool inObject);
StructDeclaration *syntaxCopy(Dsymbol *s) override;
const char *kind() const override;
+ bool hasCopyConstruction();
bool zeroInit() const; // !=0 if initialize with 0 fill
bool zeroInit(bool v);
bool hasIdentityAssign() const; // true if has identity opAssign
DArray<BaseClass> baseInterfaces; // if BaseClass is an interface, these
// are a copy of the InterfaceDeclaration::interfaces
-
- bool fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance);
};
struct ClassFlags
static ClassDeclaration *create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject);
const char *toPrettyChars(bool QualifyTypes = false) override;
ClassDeclaration *syntaxCopy(Dsymbol *s) override;
- Scope *newScope(Scope *sc) override;
#define OFFSET_RUNTIME 0x76543210
#define OFFSET_FWDREF 0x76543211
virtual int vtblOffset() const;
const char *kind() const override;
- void addObjcSymbols(ClassDeclarations *classes, ClassDeclarations *categories) override final;
-
// Back end
Dsymbol *vtblsym;
{
public:
InterfaceDeclaration *syntaxCopy(Dsymbol *s) override;
- Scope *newScope(Scope *sc) override;
bool isBaseOf(ClassDeclaration *cd, int *poffset) override;
const char *kind() const override;
int vtblOffset() const override;
import dmd.dcast : implicitConvTo;
import dmd.declaration;
import dmd.dscope;
-import dmd.dsymbol;
import dmd.errors;
import dmd.expression;
import dmd.expressionsem;
import dmd.mtype;
import dmd.common.outbuffer;
import dmd.tokens;
-import dmd.typesem : isAssignable;
+import dmd.typesem : isAssignable, nextOf, toBasetype;
import dmd.visitor;
/**********************************************
public import dmd.nspace;
public import dmd.statement;
public import dmd.staticassert;
- public import dmd.typesem;
- public import dmd.ctfeexpr;
public import dmd.init : Designator;
+ public import dmd.typesem;
-
+ alias addSTC = dmd.typesem.addSTC;
alias initializerToExpression = dmd.initsem.initializerToExpression;
- alias typeToExpression = dmd.typesem.typeToExpression;
+ alias typeToExpression = dmd.mtype.typeToExpression;
alias UserAttributeDeclaration = dmd.attrib.UserAttributeDeclaration;
alias Ensure = dmd.func.Ensure; // workaround for bug in older DMD frontends
alias ErrorExp = dmd.expression.ErrorExp;
+ alias ArgumentLabel = dmd.expression.ArgumentLabel;
alias MODFlags = dmd.mtype.MODFlags;
alias Type = dmd.mtype.Type;
alias StorageClass = ulong;
/********
- * Determine if it's the ambigous case of where `return` attaches to.
+ * Determine if it's the ambiguous case of where `return` attaches to.
* Params:
* stc = STC flags
* Returns:
private:
ushort value = 0; // unknown
enum STRUCTALIGN_DEFAULT = 1234; // default = match whatever the corresponding C compiler does
- bool pack; // use #pragma pack semantics
+ ubyte flags; // Align semantic flags
+ enum : ubyte
+ {
+ PACK = 0x1, // use #pragma pack semantics
+ ALIGNAS = 0x2, // use _Alignas semantics
+ }
public:
pure @safe @nogc nothrow:
void setUnknown() { value = 0; }
void set(uint value) { this.value = cast(ushort)value; }
uint get() const { return value; }
- bool isPack() const { return pack; }
- void setPack(bool pack) { this.pack = pack; }
+ bool isPack() const { return !!(flags & PACK); }
+ void setPack() { flags |= PACK; }
+ bool fromAlignas() const { return !!(flags & ALIGNAS); }
+ void setAlignas() { flags |= ALIGNAS; }
}
/// Use to return D arrays from C++ functions
module dmd.attrib;
-import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
import dmd.cond;
-import dmd.declaration;
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
import dmd.expression;
-import dmd.func;
import dmd.hdrgen : visibilityToBuffer;
import dmd.id;
import dmd.identifier;
import dmd.location;
-import dmd.mtype;
-import dmd.objc; // for objc.addSymbols
import dmd.common.outbuffer;
-import dmd.root.array; // for each
import dmd.visitor;
/***********************************************************
return "attribute";
}
- /****************************************
- */
- override final void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories)
- {
- objc.addSymbols(this, classes, categories);
- }
-
override void accept(Visitor v)
{
v.visit(this);
import dmd.declaration;
import dmd.errorsink;
import dmd.expression;
+import dmd.expressionsem : toBool;
+import dmd.typesem : toBasetype;
import dmd.func;
import dmd.globals;
import dmd.id;
import dmd.arraytypes;
import dmd.expression;
+import dmd.expressionsem : toInteger, toReal;
+import dmd.typesem : isFloating, toBasetype;
import dmd.func;
import dmd.location;
module dmd.canthrow;
import dmd.aggregate;
-import dmd.arraytypes;
-import dmd.attrib;
import dmd.astenums;
import dmd.blockexit : BE, checkThrow;
-import dmd.declaration;
import dmd.dsymbol;
import dmd.dsymbolsem : include, toAlias;
import dmd.errorsink;
import dmd.expression;
-import dmd.expressionsem : errorSupplementalInferredAttr;
+import dmd.expressionsem;
+import dmd.typesem;
import dmd.func;
import dmd.globals;
-import dmd.init;
import dmd.mtype;
import dmd.tokens;
import dmd.visitor;
import dmd.target;
+/**********************************************
+ * While in general printf is not @safe (and should be marked @system), many uses of printf are safe.
+ * This function determines if a particular call of printf is safe.
+ * Params:
+ * format = printf format string
+ * Returns:
+ * true if @safe
+ */
+public
+bool isFormatSafe(scope const char[] format)
+{
+ //printf("isFormatSafe('%.*s')\n", cast(int)format.length, format.ptr);
+ /* Only need to check the format string, any other errors are checked
+ * for later with checkPrintfFormat()
+ */
+ for (size_t i = 0; i < format.length;)
+ {
+ if (format[i] != '%')
+ {
+ ++i;
+ continue;
+ }
+ bool widthStar;
+ bool precisionStar;
+ size_t j = i;
+ const fmt = parsePrintfFormatSpecifier(format, j, widthStar, precisionStar);
+ i = j;
+ if (fmt == Format.s || fmt == Format.ls || fmt == Format.error)
+ return false;
+ }
+ return true;
+}
+
/******************************************
* Check that arguments to a printf format string are compatible
* with that string. Issue errors for incompatibilities.
public
bool checkPrintfFormat(Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list, ErrorSink eSink)
{
- //printf("checkPrintFormat('%.*s')\n", cast(int)format.length, format.ptr);
+ //printf("checkPrintfFormat('%.*s')\n", cast(int)format.length, format.ptr);
size_t n; // index in args
for (size_t i = 0; i < format.length;)
{
*/
module dmd.common.bitfields;
+nothrow:
+@safe:
+
//version = Has_Bitfields; // does not work (yet) because hashOf doesn't work on bitfields
version(Has_Bitfields)
version = Debugger_friendly; // without Has_Bitfields, this uses more space by using S
{
import core.stdc.wchar_;
import core.sys.windows.winbase;
- import core.sys.windows.winnls : CP_ACP;
+ import core.sys.windows.winnls : CP_UTF8;
import core.sys.windows.winnt;
- enum CodePage = CP_ACP; // assume filenames encoded in system default Windows ANSI code page
+ enum CodePage = CP_UTF8; // assume filenames already gone through Windows ANSI code page -> UTF8 conversion
enum invalidHandle = INVALID_HANDLE_VALUE;
}
else version (Posix)
-
nothrow:
/**
{
int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, (6 << 6) | (4 << 3) | 4);
if (fd == -1)
- goto err;
+ return false;
if (.write(fd, data.ptr, data.length) != data.length)
- goto err2;
+ {
+ close(fd);
+ .remove(name);
+ return false;
+ }
if (close(fd) == -1)
- goto err;
+ return false;
return true;
- err2:
- close(fd);
- .remove(name);
- err:
- return false;
}
else version (Windows)
{
- DWORD numwritten; // here because of the gotos
const nameStr = name[0 .. strlen(name)];
// work around Windows file path length limitation
// (see documentation for extendedPathThen).
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
null));
if (h == INVALID_HANDLE_VALUE)
- goto err;
-
+ return false;
+ bool errorRet()
+ {
+ CloseHandle(h);
+ nameStr.extendedPathThen!(p => DeleteFileW(p.ptr));
+ return false;
+ }
+ DWORD numwritten;
if (WriteFile(h, data.ptr, cast(DWORD)data.length, &numwritten, null) != TRUE)
- goto err2;
+ return errorRet();
if (numwritten != data.length)
- goto err2;
+ return errorRet();
if (!CloseHandle(h))
- goto err;
+ return false;
return true;
- err2:
- CloseHandle(h);
- nameStr.extendedPathThen!(p => DeleteFileW(p.ptr));
- err:
- return false;
}
else
{
size_t charsToWchars(scope const(char)[] narrow, scope wchar[] buffer)
{
// https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
- import core.sys.windows.winnls : MultiByteToWideChar, CP_ACP;
- return MultiByteToWideChar(CP_ACP, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
+ import core.sys.windows.winnls : MultiByteToWideChar, CP_UTF8;
+ return MultiByteToWideChar(CP_UTF8, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
}
- size_t length = charsToWchars(narrow, buffer[]);
- if (length >= buffer.length) // not enough room in buffer[]
+ size_t length = charsToWchars(narrow, buffer[0..$-1]);
+ if (length == 0 && narrow.length > 0) // not enough room in buffer[]
{
+ import core.sys.windows.winbase : GetLastError;
+ import core.sys.windows.winerror : ERROR_INSUFFICIENT_BUFFER;
+ assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
+ length = charsToWchars(narrow, []);
buffer.create(length + 1); // extend buffer length
- length = charsToWchars(narrow, buffer[]); // try again
- assert(length < buffer.length);
+ length = charsToWchars(narrow, buffer[0..length]); // try again
+ assert(length > 0 && length < buffer.length);
}
buffer[length] = 0;
return buffer[0 .. length];
import dmd.arraytypes;
import dmd.astenums;
import dmd.ast_node;
-import dmd.dcast;
-import dmd.dinterpret;
import dmd.dmodule;
import dmd.dscope;
-import dmd.dsymbol;
import dmd.errors;
import dmd.expression;
import dmd.globals;
import dmd.identifier;
import dmd.location;
import dmd.mtype;
-import dmd.optimize;
import dmd.common.outbuffer;
import dmd.rootobject;
import dmd.root.string;
import dmd.tokens;
import dmd.utils;
import dmd.visitor;
-import dmd.id;
import dmd.statement;
import dmd.declaration;
import dmd.dstruct;
auto tf = new TypeFunction(ParameterList(), null, LINK.default_, STC.none);
auto fd = new FuncLiteralDeclaration(loc, loc, tf, TOK.reserved, null);
fd.fbody = s;
+ fd.skipCodegen = true;
auto fe = new FuncExp(loc, fd);
auto ce = new CallExp(loc, fe, new Expressions());
return ce;
{ // TODO: move to druntime?
return new CallExp(loc, new TypeExp(loc, type), e);
}
-
- /*****************************************
- * Returns:
- * `true` iff ready to call `dmd.statementsem.makeTupleForeach`.
- */
- extern(D) bool ready()
- {
- return aggrfe && aggrfe.aggr && aggrfe.aggr.type && aggrfe.aggr.type.toBasetype().ty == Ttuple;
- }
}
/***********************************************************
case "Alpha_HardFloat":
case "Alpha_SoftFloat":
case "Android":
+ case "Apple":
case "ARM":
case "ARM_HardFloat":
case "ARM_SoftFloat":
import dmd.dstruct;
import dmd.errors;
import dmd.expression;
-import dmd.expressionsem : getField;
+import dmd.expressionsem;
import dmd.globals;
import dmd.location;
import dmd.mtype;
import dmd.sideeffect;
import dmd.target;
import dmd.tokens;
-import dmd.typesem : toDsymbol, equivalent, sarrayOf, size;
+import dmd.typesem;
private enum LOG = false;
import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.tokens;
-import dmd.typesem : size;
+import dmd.typesem : size, makeImmutable, nextOf;
/***********************************************************
*/
AST.Dsymbols* symbols; // symbols declared in current scope
bool addFuncName; /// add declaration of __func__ to function symbol table
+ bool refFuncName; // declaration of __FUNCTION__
+ bool pretFuncName; // declaration for __PRETTY_FUNCTION__
bool importBuiltins; /// seen use of C compiler builtins, so import __importc_builtins;
private
return wrap;
}
+ if (token.value == TOK._module)
+ {
+ token.value = TOK.module_;
+ auto oldMd = this.md;
+ parseModuleDeclaration();
+ if (oldMd)
+ {
+ // We only use the first module declaration,
+ // subsequent __module statements should only come from #included files
+ this.md = oldMd;
+ }
+ continue;
+ }
+
/* GNU Extensions
* external-declaration:
* simple-asm-expr ;
{
/* If tokens look like a declaration, assume it is one
*/
- auto tk = &token;
- if (isCDeclaration(tk))
+ if (startsDeclaration(&token))
goto Ldeclaration;
goto Lexp;
}
const id = token.ident.toString();
if (id.length > 2 && id[0] == '_' && id[1] == '_') // leading double underscore
{
+ if (token.ident is Id.FUNCTION)
+ refFuncName = true; // implicitly declare __FUNCTION__
+
+ if (token.ident is Id.PRETTY_FUNCTION)
+ pretFuncName = true; // implicitly declare __PRETTY_FUNCTION__
+
if (token.ident is Id.__func__)
{
addFuncName = true; // implicitly declare __func__
case TOK._assert: // __check(assign-exp) extension
nextToken();
check(TOK.leftParenthesis, "`__check`");
- e = parseAssignExp();
+ e = cparseAssignExp();
check(TOK.rightParenthesis);
e = new AST.AssertExp(loc, e, null);
break;
case TOK._Alignof:
case TOK.sizeof_:
- {
- Identifier id = token.value == TOK.sizeof_? Id.__sizeof : Id.__xalignof;
- nextToken();
- if (token.value == TOK.leftParenthesis)
- {
- auto tk = peek(&token);
- if (isTypeName(tk))
- {
- /* Expression may be either be requesting the sizeof a type-name
- * or a compound literal, which requires checking whether
- * the next token is leftCurly
- */
- nextToken();
- auto t = cparseTypeName();
- check(TOK.rightParenthesis);
- if (token.value == TOK.leftCurly)
- {
- // ( type-name ) { initializer-list }
- auto ci = cparseInitializer();
- e = new AST.CompoundLiteralExp(loc, t, ci);
- e = cparsePostfixOperators(e);
- }
- else
- {
- // ( type-name )
- e = new AST.TypeExp(loc, t);
- }
- }
- else
- {
- // must be an expression
- e = cparseUnaryExp();
- }
- }
- else
- {
- //C11 6.5.3
- e = cparseUnaryExp();
- }
-
- e = new AST.DotIdExp(loc, e, id);
+ e = cparseSizeofOrAlignofExp();
break;
- }
case TOK.andAnd:
/* https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
return e;
}
+ /**************
+ * C11 6.5.3.4
+ * sizeof unary-expression
+ * sizeof ( type-name )
+ * _Alignof ( type-name )
+ * _Alignof unary-expression // gcc extension
+ */
+ private AST.Expression cparseSizeofOrAlignofExp()
+ {
+ Identifier id = token.value == TOK.sizeof_? Id.__sizeof : Id.__xalignof;
+ AST.Expression e;
+ nextToken();
+ if (token.value == TOK.leftParenthesis && startsTypeName(peek(&token)))
+ {
+ /* Expression may be either be requesting the sizeof a type-name
+ * or a compound literal, which requires checking whether
+ * the next token is leftCurly
+ */
+ nextToken();
+ auto t = cparseTypeName();
+ check(TOK.rightParenthesis);
+ if (token.value == TOK.leftCurly)
+ {
+ // ( type-name ) { initializer-list }
+ auto ci = cparseInitializer();
+ e = new AST.CompoundLiteralExp(loc, t, ci);
+ e = cparsePostfixOperators(e);
+ }
+ else
+ {
+ // ( type-name )
+ e = new AST.TypeExp(loc, t);
+ }
+ }
+ else
+ {
+ // C11 6.5.3
+ e = cparseUnaryExp();
+ }
+ return new AST.DotIdExp(loc, e, id);
+ }
+
/**************
* C11 6.5.4
* cast-expression
*/
private AST.Expression cparseCastExp()
{
- if (token.value != TOK.leftParenthesis)
- return cparseUnaryExp();
-
//printf("cparseCastExp()\n");
- auto tk = peek(&token);
- bool iscast;
- bool isexp;
- if (tk.value == TOK.identifier)
- {
- iscast = isTypedef(tk.ident);
- isexp = !iscast;
- }
- if (isexp)
- {
- // ( identifier ) is an expression
- return cparseUnaryExp();
- }
// If ( type-name )
- auto pt = &token;
-
- if (!isCastExpression(pt))
- return cparseUnaryExp();
-
- // Expression may be either a cast or a compound literal, which
- // requires checking whether the next token is leftCurly
- const loc = token.loc;
- nextToken();
- auto t = cparseTypeName();
- check(TOK.rightParenthesis);
- pt = &token;
-
- if (token.value == TOK.leftCurly)
+ if (token.value == TOK.leftParenthesis && startsTypeName(peek(&token)))
{
- // C11 6.5.2.5 ( type-name ) { initializer-list }
- auto ci = cparseInitializer();
- auto ce = new AST.CompoundLiteralExp(loc, t, ci);
- return cparsePostfixOperators(ce);
- }
+ // Expression may be either a cast or a compound literal, which
+ // requires checking whether the next token is leftCurly
+ const loc = token.loc;
+ nextToken();
+ auto t = cparseTypeName();
+ check(TOK.rightParenthesis);
+
+ if (token.value == TOK.leftCurly)
+ {
+ // C11 6.5.2.5 ( type-name ) { initializer-list }
+ auto ci = cparseInitializer();
+ auto ce = new AST.CompoundLiteralExp(loc, t, ci);
+ return cparsePostfixOperators(ce);
+ }
- if (iscast)
- {
// ( type-name ) cast-expression
auto ce = cparseCastExp();
return new AST.CastExp(loc, ce, t);
}
- if (t.isTypeIdentifier() &&
- isexp &&
- token.value == TOK.leftParenthesis &&
- !isCastExpression(pt))
- {
- /* (t)(...)... might be a cast expression or a function call,
- * with different grammars: a cast would be cparseCastExp(),
- * a function call would be cparsePostfixExp(CallExp(cparseArguments())).
- * We can't know until t is known. So, parse it as a function call
- * and let semantic() rewrite the AST as a CastExp if it turns out
- * to be a type.
- */
- auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident);
- ie.parens = true; // let semantic know it might be a CastExp
- AST.Expression e = new AST.CallExp(loc, ie, cparseArguments());
- return cparsePostfixOperators(e);
- }
-
- // ( type-name ) cast-expression
- auto ce = cparseCastExp();
- return new AST.CastExp(loc, ce, t);
+ return cparseUnaryExp();
}
/**************
bool first = true;
while (1)
{
- Identifier id;
+ AST.ArgumentLabel id;
AST.StringExp asmName;
auto dt = cparseDeclarator(DTR.xdirect_fd, tspec, id, specifier);
if (!dt)
}
- // Check alignasExp and not alignExps so that gnu
+ // Check lastAlignas and not alignasExps so that gnu
// __atribute__((aligned())) is silently allowed, matching the
// behavior of other compilers.
- if (specifier.alignasExp && dt.isTypeFunction())
- error(specifier.alignasExp.loc, "no alignment-specifier for function declaration"); // C11 6.7.5-2
- if (specifier.alignasExp && specifier.scw == SCW.xregister)
- error(specifier.alignasExp.loc, "no alignment-specifier for `register` storage class"); // C11 6.7.5-2
+ if (specifier.lastAlignas && dt.isTypeFunction())
+ error(specifier.lastAlignas.loc, "no alignment-specifier for function declaration"); // C11 6.7.5-2
+ if (specifier.lastAlignas && specifier.scw == SCW.xregister)
+ error(specifier.lastAlignas.loc, "no alignment-specifier for `register` storage class"); // C11 6.7.5-2
/* C11 6.9.1 Function Definitions
* function-definition:
*/
auto t = &token;
if (first && // first declarator
- id &&
+ id.name &&
dt.isTypeFunction() && // function type not inherited from a typedef
- isDeclarationList(t) && // optional declaration-list
level == LVL.global && // function definitions only at global scope
- t.value == TOK.leftCurly) // start of compound-statement
+ t.value != TOK.endOfFile)
{
auto s = cparseFunctionDefinition(id, dt.isTypeFunction(), specifier);
typedefTab.setDim(typedefTabLengthSave);
{
if (token.value == TOK.assign)
error("no initializer for typedef declaration");
- if (specifier.alignasExp)
- error(specifier.alignasExp.loc, "no alignment-specifier for typedef declaration"); // C11 6.7.5-2
+ if (specifier.lastAlignas)
+ error(specifier.lastAlignas.loc, "no alignment-specifier for typedef declaration"); // C11 6.7.5-2
if (specifier.vector_size)
{
Identifier idt;
if (auto tt = dt.isTypeTag())
{
- if (!tt.id && id)
+ if (!tt.id && id.name)
/* This applies for enums declared as
* typedef enum {A} E;
* Or for similar structs and unions.
*/
- tt.id = id;
+ tt.id = id.name;
if (tt.members)
{
Specifier spec;
}
if (isalias)
{
- //printf("AliasDeclaration %s %s\n", id.toChars(), dt.toChars());
- auto ad = new AST.AliasDeclaration(token.loc, id, dt);
- if (id == idt)
+ //printf("AliasDeclaration %s %s\n", id.name.toChars(), dt.toChars());
+ auto ad = new AST.AliasDeclaration(id.loc, id.name, dt);
+ if (id.name == idt)
ad.hidden = true; // do not print when generating .di files
s = ad;
}
- insertTypedefToTypedefTab(id, dt); // remember typedefs
+ insertTypedefToTypedefTab(id.name, dt); // remember typedefs
}
- else if (id)
+ else if (id.name)
{
if (auto tt = dt.isTypeTag())
{
initializer = cparseInitializer();
}
// declare the symbol
- assert(id);
+ assert(id.name);
if (isFunctionTypedef(dt))
{
error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
STC stc = specifiersToSTC(level, specifier);
stc &= ~STC.gshared; // no gshared functions
- auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, stc, dt, specifier.noreturn);
+ auto fd = new AST.FuncDeclaration(id.loc, Loc.initial, id.name, stc, dt, specifier.noreturn);
specifiersToFuncDeclaration(fd, specifier);
s = fd;
}
if (!hasInitializer &&
!(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global))
initializer = new AST.VoidInitializer(token.loc);
- auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier));
+ auto vd = new AST.VarDeclaration(id.loc, dt, id.name, initializer, specifiersToSTC(level, specifier));
specifiersToVarDeclaration(vd, specifier);
s = vd;
}
if (level != LVL.global)
- insertIdToTypedefTab(id); // non-typedef declarations can hide typedefs in outer scopes
+ insertIdToTypedefTab(id.name); // non-typedef declarations can hide typedefs in outer scopes
}
if (s !is null)
{
}
symbols.push(s);
}
- if (level == LVL.global && !id)
+ if (level == LVL.global && !id.name)
error("expected identifier for declaration");
first = false;
* Returns:
* Dsymbol for the function
*/
- AST.Dsymbol cparseFunctionDefinition(Identifier id, AST.TypeFunction ft, ref Specifier specifier)
+ AST.Dsymbol cparseFunctionDefinition(AST.ArgumentLabel id, AST.TypeFunction ft, ref Specifier specifier)
{
/* Start function scope
*/
do
{
cparseDeclaration(LVL.parameter);
- } while (token.value != TOK.leftCurly);
+ } while (token.value != TOK.leftCurly && token.value != TOK.endOfFile);
/* Since there were declarations, the parameter-list must have been
* an identifier-list.
}
}
- addFuncName = false; // gets set to true if somebody references __func__ in this function
+ /* gets set to true if somebody references __func__ in this function, //ditto for __FUNCTION__ */
+ addFuncName = false;
+ refFuncName = false;
+ pretFuncName = false;
const locFunc = token.loc;
auto body = cparseStatement(ParseStatementFlags.curly); // don't start a new scope; continue with parameter scope
STC stc = specifiersToSTC(LVL.global, specifier);
stc &= ~STC.gshared; // no gshared functions
- auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, stc, ft, specifier.noreturn);
+ auto fd = new AST.FuncDeclaration(id.loc, prevloc, id.name, stc, ft, specifier.noreturn);
specifiersToFuncDeclaration(fd, specifier);
+ auto stmts = new AST.Statements();
+
if (addFuncName)
- {
- auto s = createFuncName(locFunc, id);
- body = new AST.CompoundStatement(locFunc, s, body);
- }
+ stmts.push(createFuncName(locFunc, id.name, Id.__func__));
+
+ if (refFuncName)
+ stmts.push(createFuncName(locFunc, id.name, Id.FUNCTION));
+
+ if (pretFuncName)
+ stmts.push(createPrettyFunc(locFunc, fd));
+
+ stmts.push(body);
+
+ body = new AST.CompoundStatement(locFunc, stmts);
fd.fbody = body;
// TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
}
AST.Type t;
- Loc loc;
//printf("parseDeclarationSpecifiers()\n");
TKW tkw;
SCW scw = specifier.scw & SCW.xtypedef;
MOD mod;
- Identifier id;
- Identifier previd;
+ AST.ArgumentLabel id;
+ AST.ArgumentLabel previd;
Lwhile:
while (1)
case TOK.identifier:
tkwx = TKW.xident;
- id = token.ident;
+ id = AST.ArgumentLabel(token.ident, token.loc);
break;
case TOK.struct_:
{
// C11 6.7.2.4
// type-specifier if followed by `( type-name )`
- auto tk = peek(&token);
- if (tk.value == TOK.leftParenthesis)
+ if (peekNext() == TOK.leftParenthesis)
{
- tk = peek(tk);
- if (isTypeName(tk) && tk.value == TOK.rightParenthesis)
- {
- nextToken();
- nextToken();
- t = cparseTypeName();
- tkwx = TKW.x_Atomic;
- break;
- }
+ nextToken();
+ nextToken();
+ t = cparseTypeName();
+ tkwx = TKW.x_Atomic;
+ break;
}
// C11 6.7.3 type-qualifier if not
modx = MOD.x_Atomic;
case TOK._Alignas:
{
- /* C11 6.7.5
- * _Alignas ( type-name )
- * _Alignas ( constant-expression )
- */
-
- if (level & (LVL.parameter | LVL.prototype))
- error("no alignment-specifier for parameters"); // C11 6.7.5-2
-
- nextToken();
- check(TOK.leftParenthesis);
- AST.Expression exp;
- auto tk = &token;
- if (isTypeName(tk)) // _Alignas ( type-name )
- {
- auto talign = cparseTypeName();
- /* Convert type to expression: `talign.alignof`
- */
- auto e = new AST.TypeExp(loc, talign);
- exp = new AST.DotIdExp(loc, e, Id.__xalignof);
- }
- else // _Alignas ( constant-expression )
- {
- exp = cparseConstantExp();
- }
-
- if (!specifier.alignExps)
- specifier.alignExps = new AST.Expressions(0);
- specifier.alignExps.push(exp);
- specifier.alignasExp = exp;
-
- check(TOK.rightParenthesis);
+ auto exp = cparseAlignasSpecifier(level);
+ if (!specifier.alignasExps)
+ specifier.alignasExps = new AST.Expressions();
+ specifier.alignasExps.push(exp);
+ specifier.lastAlignas = exp;
break;
}
case TOK.typeof_:
{
- nextToken();
- check(TOK.leftParenthesis);
-
- auto tk = &token;
- AST.Expression e;
- if (isTypeName(tk))
- e = new AST.TypeExp(loc, cparseTypeName());
- else
- e = cparseExpression();
- t = new AST.TypeTypeof(loc, e);
-
- if(token.value == TOK.rightParenthesis)
- nextToken();
- else
- {
- t = AST.Type.terror;
- error("`typeof` operator expects an expression or type name in parentheses");
-
- // skipParens et. al expect to be on the opening parenthesis
- int parens;
- loop: while(1)
- {
- switch(token.value)
- {
- case TOK.leftParenthesis:
- parens++;
- break;
- case TOK.rightParenthesis:
- parens--;
- if(parens < 0)
- goto case;
- break;
- case TOK.endOfFile:
- break loop;
- default:
- }
- nextToken();
- }
- }
-
+ /* GNU extension (added in C23)
+ */
+ t = cparseTypeofSpecifier();
tkwx = TKW.xtag;
break;
}
case TKW.xident:
{
- const idx = previd.toString();
+ const idx = previd.name.toString();
if (idx.length > 2 && idx[0] == '_' && idx[1] == '_') // leading double underscore
importBuiltins = true; // probably one of those compiler extensions
t = null;
/* Punch through to what the typedef is, to support things like:
* typedef T* T;
*/
- auto pt = lookupTypedef(previd);
+ auto pt = lookupTypedef(previd.name);
if (pt && *pt) // if previd is a known typedef
t = *pt;
if (!t)
- t = new AST.TypeIdentifier(loc, previd);
+ t = new AST.TypeIdentifier(previd.loc, previd.name);
break;
}
return t;
}
+ /**************
+ * C11 6.7.5
+ * alignment-specifier:
+ * _Alignas ( type-name )
+ * _Alignas ( constant-expression )
+ */
+ private AST.Expression cparseAlignasSpecifier(LVL level)
+ {
+ if (level & (LVL.parameter | LVL.prototype))
+ error("no alignment-specifier for parameters"); // C11 6.7.5-2
+
+ AST.Expression exp;
+ nextToken();
+ check(TOK.leftParenthesis);
+ if (startsTypeName(&token))
+ {
+ // _Alignas ( type-name )
+ auto talign = cparseTypeName();
+ // Convert type to expression: `talign.alignof`
+ auto e = new AST.TypeExp(loc, talign);
+ exp = new AST.DotIdExp(loc, e, Id.__xalignof);
+ }
+ else
+ {
+ // _Alignas ( constant-expression )
+ exp = cparseConstantExp();
+ }
+ check(TOK.rightParenthesis);
+ return exp;
+ }
+
+ /**************
+ * C23 6.7.2.5
+ * typeof-specifier:
+ * typeof ( expression )
+ * typeof ( type-name )
+ * typeof_unqual ( expression ) // not implemented
+ * typeof_unqual ( type-name ) // not implemented
+ */
+ private AST.Type cparseTypeofSpecifier()
+ {
+ nextToken();
+ check(TOK.leftParenthesis);
+
+ AST.Expression e;
+ if (startsTypeName(&token))
+ e = new AST.TypeExp(loc, cparseTypeName());
+ else
+ e = cparseExpression();
+
+ if (token.value != TOK.rightParenthesis)
+ {
+ // skipParens et. al expect to be on the opening parenthesis
+ error("`typeof` operator expects an expression or type name in parentheses");
+ int parens;
+ loop: while(1)
+ {
+ switch(token.value)
+ {
+ case TOK.leftParenthesis:
+ parens++;
+ break;
+ case TOK.rightParenthesis:
+ parens--;
+ if (parens < 0)
+ goto case;
+ break;
+ case TOK.endOfFile:
+ break loop;
+ default:
+ }
+ nextToken();
+ }
+ return AST.Type.terror;
+ }
+ nextToken();
+ return new AST.TypeTypeof(loc, e);
+ }
+
/********************************
* C11 6.7.6
* Parse a declarator (including function definitions).
* declared struct, union or enum tags.
*/
private AST.Type cparseDeclarator(DTR declarator, AST.Type tbase,
- out Identifier pident, ref Specifier specifier)
+ out AST.ArgumentLabel pident, ref Specifier specifier)
{
//printf("cparseDeclarator(%d, %s)\n", declarator, tbase.toChars());
AST.Types constTypes; // all the Types that will need `const` applied to them
//printf("identifier %s\n", token.ident.toChars());
if (declarator == DTR.xabstract)
error("identifier not allowed in abstract-declarator");
- pident = token.ident;
+ pident = AST.ArgumentLabel(token.ident, token.loc);
ts = t;
nextToken();
break;
}
break;
}
- if (declarator == DTR.xdirect && !pident)
+ if (declarator == DTR.xdirect && !pident.name)
error("expected identifier for declarator");
return ts;
}
tspec = toConst(tspec);
specifier.mod = MOD.xnone; // 'used' it
}
- Identifier id;
+ AST.ArgumentLabel id;
return cparseDeclarator(DTR.xabstract, tspec, id, specifier);
}
Specifier specifier;
specifier.packalign.setDefault();
+ const typeLoc = token.loc;
auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier);
if (!tspec)
{
specifier.mod = MOD.xnone; // 'used' it
}
- Identifier id;
- const paramLoc = token.loc;
+ AST.ArgumentLabel id;
auto t = cparseDeclarator(DTR.xparameter, tspec, id, specifier);
if (token.value == TOK.__attribute__)
cparseGnuAttributes(specifier);
if (specifier.mod & MOD.xconst)
t = toConst(t);
- auto param = new AST.Parameter(paramLoc, specifiersToSTC(LVL.parameter, specifier),
- t, id, null, null);
+ auto param = new AST.Parameter(id.name ? id.loc : typeLoc,
+ specifiersToSTC(LVL.parameter, specifier),
+ t, id.name, null, null);
parameters.push(param);
if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
break;
if (n < 1 || n & (n - 1) || 8192 < n)
error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n);
specifier.packalign.set(cast(uint)n);
- specifier.packalign.setPack(true);
+ specifier.packalign.setPack();
nextToken();
}
else
{
nextToken();
AST.Expression exp = cparseConstantExp();
- if (!specifier.alignExps)
- specifier.alignExps = new AST.Expressions(0);
- specifier.alignExps.push(exp);
+ if (!specifier.alignAttrs)
+ specifier.alignAttrs = new AST.Expressions();
+ specifier.alignAttrs.push(exp);
check(TOK.rightParenthesis);
}
else
else if (token.ident == Id.packed)
{
specifier.packalign.set(1);
- specifier.packalign.setPack(true);
+ specifier.packalign.setPack();
nextToken();
}
else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
* redeclaration, or reference to existing declaration.
* Defer to the semantic() pass with a TypeTag.
*/
- return new AST.TypeTag(loc, structOrUnion, tag, tagSpecifier.packalign, tagSpecifier.alignExps, null, members);
+ return new AST.TypeTag(loc, structOrUnion, tag, tagSpecifier.packalign, tagSpecifier.alignAttrs, null, members);
}
/*************************************
while (1)
{
- Identifier id;
+ AST.ArgumentLabel id;
AST.Type dt;
if (token.value == TOK.colon)
{
}
// C11 6.7.2.1-12 unnamed bit-field
- id = Identifier.generateAnonymousId("BitField");
+ id.name = Identifier.generateAnonymousId("BitField");
dt = tspec;
}
else
{
// C11 6.7.2.1-10 bit-field
nextToken();
+ if (id.name.isAnonymous)
+ id.loc = token.loc;
width = cparseConstantExp();
}
error("specifier-qualifier-list required");
else if (width)
{
- if (specifier.alignasExp)
- error(specifier.alignasExp.loc, "no alignment-specifier for bit field declaration"); // C11 6.7.5-2
- auto s = new AST.BitFieldDeclaration(width.loc, dt, id, width);
+ if (specifier.lastAlignas)
+ error(specifier.lastAlignas.loc, "no alignment-specifier for bit field declaration"); // C11 6.7.5-2
+ auto s = new AST.BitFieldDeclaration(id.loc, dt, id.name, width);
members.push(s);
}
- else if (id)
+ else if (id.name)
{
if (dt.ty == AST.Tvoid)
error("`void` has no value");
// declare the symbol
// Give member variables an implicit void initializer
auto initializer = new AST.VoidInitializer(token.loc);
- AST.Dsymbol s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(LVL.member, specifier));
+ AST.Dsymbol s = new AST.VarDeclaration(id.loc, dt, id.name, initializer, specifiersToSTC(LVL.member, specifier));
s = applySpecifier(s, specifier);
members.push(s);
}
/************************************
* Determine if the scanner is sitting on the start of a declaration.
* Params:
- * t = current token of the scanner
- * needId = flag with additional requirements for a declaration
- * endtok = ending token
- * pt = will be set ending token (if not null)
+ * t = first token
* Returns:
- * true at start of a declaration
+ * true if at start of a declaration
*/
- private bool isCDeclaration(ref Token* pt)
+ private bool startsDeclaration(Token* t)
{
- auto t = pt;
- //printf("isCDeclaration() %s\n", t.toChars());
- if (!isDeclarationSpecifiers(t))
+ // Labels aren't declarations.
+ if (token.value == TOK.identifier && peek(t).value == TOK.colon)
return false;
- while (1)
- {
- if (t.value == TOK.semicolon)
- {
- t = peek(t);
- pt = t;
- return true;
- }
- if (!isCDeclarator(t, DTR.xdirect))
- return false;
- if (t.value == TOK.asm_)
- {
- t = peek(t);
- if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
- return false;
- }
- if (t.value == TOK.__attribute__)
- {
- t = peek(t);
- if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
- return false;
- }
- if (t.value == TOK.assign)
- {
- t = peek(t);
- if (!isInitializer(t))
- return false;
- }
- switch (t.value)
- {
- case TOK.comma:
- t = peek(t);
- break;
-
- case TOK.semicolon:
- t = peek(t);
- pt = t;
- return true;
-
- default:
- return false;
- }
- }
- }
-
- /********************************
- * See if match for initializer.
- * Params:
- * pt = starting token, updated to one past end of initializer if true
- * Returns:
- * true if initializer
- */
- private bool isInitializer(ref Token* pt)
- {
- //printf("isInitializer()\n");
- auto t = pt;
-
- if (t.value == TOK.leftCurly)
- {
- if (!skipBraces(t))
- return false;
- pt = t;
+ if (token.value == TOK._Static_assert)
return true;
- }
- // skip over assignment-expression, ending before comma or semiColon or EOF
- if (!isAssignmentExpression(t))
- return false;
- pt = t;
- return true;
- }
+ if (startsDeclarationSpecifiers(t))
+ return true;
- /********************************
- * See if match for:
- * postfix-expression ( argument-expression-list(opt) )
- * Params:
- * pt = starting token, updated to one past end of initializer if true
- * Returns:
- * true if function call
- */
- private bool isFunctionCall(ref Token* pt)
- {
- //printf("isFunctionCall()\n");
- auto t = pt;
+ if (startsTypeName(t))
+ return true;
- if (!isPrimaryExpression(t))
- return false;
- if (t.value != TOK.leftParenthesis)
- return false;
- t = peek(t);
- while (1)
- {
- if (!isAssignmentExpression(t))
- return false;
- if (t.value == TOK.comma)
- {
- t = peek(t);
- continue;
- }
- if (t.value == TOK.rightParenthesis)
- {
- t = peek(t);
- break;
- }
- return false;
- }
- if (t.value != TOK.semicolon)
- return false;
- pt = t;
- return true;
+ return false;
}
/********************************
- * See if match for assignment-expression.
+ * Is this the start of a declaration-specifiers?
* Params:
- * pt = starting token, updated to one past end of assignment-expression if true
+ * t = first token
* Returns:
- * true if assignment-expression
+ * true if declaration-specifiers
*/
- private bool isAssignmentExpression(ref Token* pt)
+ private bool startsDeclarationSpecifiers(Token* t)
{
- auto t = pt;
- //printf("isAssignmentExpression() %s\n", t.toChars());
-
- /* This doesn't actually check for grammar matching an
- * assignment-expression. It just matches ( ) [ ] looking for
- * an ending token that would terminate one.
- */
- bool any;
+ bool seenType;
while (1)
{
switch (t.value)
{
- case TOK.comma:
- case TOK.semicolon:
- case TOK.rightParenthesis:
- case TOK.rightBracket:
- case TOK.endOfFile:
- case TOK.endOfLine:
- if (!any)
- return false;
- break;
-
- case TOK.leftParenthesis:
- if (!skipParens(t, &t))
- return false;
- /*
- https://issues.dlang.org/show_bug.cgi?id=22267
- If the parser encounters the following
- `identifier variableName = (expression);`
- the initializer is not identified as such since the parentheses
- cause the parser to keep walking indefinitely
- (whereas `(1) + 1` would not be affected.).
- */
- any = true;
- continue;
-
- case TOK.leftBracket:
- if (!skipBrackets(t))
- return false;
- continue;
-
- case TOK.leftCurly:
- if (!skipBraces(t))
- return false;
- continue;
-
- default:
- any = true; // assume token was part of an a-e
- t = peek(t);
- continue;
- }
- pt = t;
- return true;
- }
- }
-
- /********************************
- * See if match for constant-expression.
- * Params:
- * pt = starting token, updated to one past end of constant-expression if true
- * Returns:
- * true if constant-expression
- */
- private bool isConstantExpression(ref Token* pt)
- {
- return isAssignmentExpression(pt);
- }
-
- /********************************
- * See if match for declaration-specifiers.
- * No errors are diagnosed.
- * Params:
- * pt = starting token, updated to one past end of declaration-specifiers if true
- * Returns:
- * true if declaration-specifiers
- */
- private bool isDeclarationSpecifiers(ref Token* pt)
- {
- //printf("isDeclarationSpecifiers()\n");
-
- auto t = pt;
+ // typedef-name
+ case TOK.identifier:
+ if (!seenType)
+ {
+ if (isTypedef(t.ident))
+ return true;
+ t = peek(t);
+ seenType = true;
+ continue;
+ }
+ return true;
- bool seenType;
- bool any;
- while (1)
- {
- switch (t.value)
- {
// type-specifiers
case TOK.void_:
case TOK.char_:
case TOK._Bool:
//case TOK._Imaginary:
case TOK._Complex:
- t = peek(t);
- seenType = true;
- any = true;
- continue;
-
- case TOK.identifier: // typedef-name
- if (!seenType)
- {
- t = peek(t);
- seenType = true;
- any = true;
- continue;
- }
- break;
+ // struct-or-union-specifier
+ // enum-specifier
case TOK.struct_:
case TOK.union_:
case TOK.enum_:
- t = peek(t);
- if (t.value == TOK.__attribute__ ||
- t.value == TOK.__declspec)
- {
- t = peek(t);
- if (!skipParens(t, &t))
- return false;
- }
- if (t.value == TOK.identifier)
- {
- t = peek(t);
- if (t.value == TOK.leftCurly)
- {
- if (!skipBraces(t))
- return false;
- }
- }
- else if (t.value == TOK.leftCurly)
- {
- if (!skipBraces(t))
- return false;
- }
- else
- return false;
- any = true;
- continue;
// storage-class-specifiers
case TOK.typedef_:
case TOK.volatile:
case TOK.restrict:
case TOK.__stdcall:
- t = peek(t);
- any = true;
- continue;
case TOK._Alignas: // alignment-specifier
case TOK.__declspec: // decl-specifier
case TOK.__attribute__: // attribute-specifier
- t = peek(t);
- if (!skipParens(t, &t))
- return false;
- any = true;
- continue;
- // either atomic-type-specifier or type_qualifier
- case TOK._Atomic: // TODO _Atomic ( type-name )
- t = peek(t);
- if (t.value == TOK.leftParenthesis) // maybe atomic-type-specifier
- {
- auto tsave = t;
- t = peek(t);
- if (!isTypeName(t) || t.value != TOK.rightParenthesis)
- { // it's a type-qualifier
- t = tsave; // back up parser
- any = true;
- continue;
- }
- t = peek(t); // move past right parenthesis of atomic-type-specifier
- }
- any = true;
- continue;
+ // atomic-type-specifier
+ case TOK._Atomic:
+ case TOK.typeof_:
+ return true;
default:
- break;
- }
- break;
- }
-
- if (any)
- {
- pt = t;
- return true;
- }
- return false;
- }
-
- /**************************************
- * See if declaration-list is present.
- * Returns:
- * true if declaration-list is present, even an empty one
- */
- bool isDeclarationList(ref Token* pt)
- {
- auto t = pt;
- while (1)
- {
- if (t.value == TOK.leftCurly)
- {
- pt = t;
- return true;
- }
- if (!isCDeclaration(t))
- return false;
- }
- }
-
- /*******************************************
- * Skip braces.
- * Params:
- * pt = enters on left brace, set to token past right bracket on true
- * Returns:
- * true if successful
- */
- private bool skipBraces(ref Token* pt)
- {
- auto t = pt;
- if (t.value != TOK.leftCurly)
- return false;
-
- int braces = 0;
-
- while (1)
- {
- switch (t.value)
- {
- case TOK.leftCurly:
- ++braces;
- t = peek(t);
- continue;
-
- case TOK.rightCurly:
- --braces;
- if (braces == 0)
- {
- pt = peek(t);
- return true;
- }
- if (braces < 0)
- return false;
-
- t = peek(t);
- continue;
-
- case TOK.endOfFile:
return false;
-
- default:
- t = peek(t);
- continue;
}
}
}
return true;
}
- /***************************
+ /*******************************************
* Is this the start of a type-name?
* Params:
- * pt = first token; updated with past end of type-name if true
+ * t = first token
* Returns:
* true if start of type-name
*/
- private bool isTypeName(ref Token* pt)
+ private bool startsTypeName(Token* t)
{
- auto t = pt;
- //printf("isTypeName() %s\n", t.toChars());
- if (!isSpecifierQualifierList(t))
- return false;
- if (!isCDeclarator(t, DTR.xabstract))
- return false;
- if (t.value != TOK.rightParenthesis)
- return false;
- pt = t;
- return true;
- }
-
- /***************************
- * Is this the start of a specifier-qualifier-list?
- * Params:
- * pt = first token; updated with past end of specifier-qualifier-list if true
- * Returns:
- * true if start of specifier-qualifier-list
- */
- private bool isSpecifierQualifierList(ref Token* pt)
- {
- auto t = pt;
- bool result;
- while (1)
- {
- switch (t.value)
- {
- // Type Qualifiers
- case TOK.const_:
- case TOK.restrict:
- case TOK.volatile:
- case TOK.__stdcall:
-
- // Type Specifiers
- case TOK.char_:
- case TOK.signed:
- case TOK.unsigned:
- case TOK.int16:
- case TOK.int32:
- case TOK.int64:
- case TOK.__int128:
- case TOK.float32:
- case TOK.float64:
- case TOK.void_:
- case TOK._Bool:
- //case TOK._Imaginary: // ? missing in Spec
- case TOK._Complex:
- t = peek(t);
- break;
-
- case TOK.identifier:
- // Use typedef table to disambiguate
- if (isTypedef(t.ident))
- {
- t = peek(t);
- break;
- }
- else
- {
- return false;
- }
-
- // struct-or-union-specifier
- // enum-specifier
- case TOK.struct_:
- case TOK.union_:
- case TOK.enum_:
- t = peek(t);
- if (t.value == TOK.identifier)
- {
- t = peek(t);
- if (t.value == TOK.leftCurly)
- {
- if (!skipBraces(t))
- return false;
- }
- }
- else if (t.value == TOK.leftCurly)
- {
- if (!skipBraces(t))
- return false;
- }
- else
- return false;
- break;
-
- // atomic-type-specifier
- case TOK._Atomic:
- case TOK.typeof_:
- case TOK.__attribute__:
- t = peek(t);
- if (t.value != TOK.leftParenthesis ||
- !skipParens(t, &t))
- return false;
- break;
-
- default:
- if (result)
- pt = t;
- return result;
- }
- result = true;
- }
- }
-
- /************************************
- * Looking at the leading left parenthesis, and determine if it is
- * either of the following:
- * ( type-name ) cast-expression
- * ( type-name ) { initializer-list }
- * as opposed to:
- * ( expression )
- * Params:
- * pt = starting token, updated to one past end of constant-expression if true
- * afterParenType = true if already seen `( type-name )`
- * Returns:
- * true if matches ( type-name ) ...
- */
- private bool isCastExpression(ref Token* pt, bool afterParenType = false)
- {
- enum log = false;
- if (log) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token.toChars(pt.value), afterParenType);
- auto t = pt;
- switch (t.value)
- {
- case TOK.leftParenthesis:
- auto tk = peek(t); // move past left parenthesis
- if (!isTypeName(tk) || tk.value != TOK.rightParenthesis)
- {
- if (afterParenType)
- goto default; // could be ( type-name ) ( unary-expression )
- return false;
- }
- tk = peek(tk); // move past right parenthesis
-
- if (tk.value == TOK.leftCurly)
- {
- // ( type-name ) { initializer-list }
- if (!isInitializer(tk))
- {
- return false;
- }
- t = tk;
- break;
- }
-
- if (tk.value == TOK.leftParenthesis && peek(tk).value == TOK.rightParenthesis)
- {
- return false; // (type-name)() is not a cast (it might be a function call)
- }
-
- if (!isCastExpression(tk, true))
- {
- if (afterParenType) // could be ( type-name ) ( unary-expression )
- goto default; // where unary-expression also matched type-name
- return true;
- }
- // ( type-name ) cast-expression
- t = tk;
- break;
-
- default:
- if (!afterParenType || !isUnaryExpression(t, afterParenType))
- {
- return false;
- }
- // if we've already seen ( type-name ), then this is a cast
- break;
- }
- pt = t;
- if (log) printf("isCastExpression true\n");
- return true;
- }
-
- /********************************
- * See if match for unary-expression.
- * Params:
- * pt = starting token, updated to one past end of constant-expression if true
- * afterParenType = true if already seen ( type-name ) of a cast-expression
- * Returns:
- * true if unary-expression
- */
- private bool isUnaryExpression(ref Token* pt, bool afterParenType = false)
- {
- auto t = pt;
switch (t.value)
{
- case TOK.plusPlus:
- case TOK.minusMinus:
- t = peek(t);
- if (!isUnaryExpression(t, afterParenType))
- return false;
- break;
-
- case TOK.and:
- case TOK.mul:
- case TOK.min:
- case TOK.add:
- case TOK.not:
- case TOK.tilde:
- t = peek(t);
- if (!isCastExpression(t, afterParenType))
- return false;
- break;
+ case TOK.identifier:
+ // Use typedef table to disambiguate
+ return isTypedef(t.ident);
- case TOK._Alignof:
- case TOK.sizeof_:
- t = peek(t);
- if (t.value == TOK.leftParenthesis)
- {
- auto tk = peek(t);
- if (isTypeName(tk))
- {
- if (tk.value != TOK.rightParenthesis)
- return false;
- t = peek(tk);
- break;
- }
- }
- if (!isUnaryExpression(t, afterParenType))
- return false;
- break;
+ // Type Qualifiers
+ case TOK.const_:
+ case TOK.restrict:
+ case TOK.volatile:
+ case TOK.__stdcall:
- default:
- // Compound literals are handled by cast and sizeof expressions,
- // so be content with just seeing a primary expression.
- if (!isPrimaryExpression(t))
- return false;
- break;
- }
- pt = t;
- return true;
- }
+ // Type Specifiers
+ case TOK.char_:
+ case TOK.signed:
+ case TOK.unsigned:
+ case TOK.int16:
+ case TOK.int32:
+ case TOK.int64:
+ case TOK.__int128:
+ case TOK.float32:
+ case TOK.float64:
+ case TOK.void_:
+ case TOK._Bool:
- /********************************
- * See if match for primary-expression.
- * Params:
- * pt = starting token, updated to one past end of constant-expression if true
- * Returns:
- * true if primary-expression
- */
- private bool isPrimaryExpression(ref Token* pt)
- {
- auto t = pt;
- switch (t.value)
- {
- case TOK.identifier:
- case TOK.charLiteral:
- case TOK.wcharLiteral:
- case TOK.dcharLiteral:
- case TOK.int32Literal:
- case TOK.uns32Literal:
- case TOK.int64Literal:
- case TOK.uns64Literal:
- case TOK.float32Literal:
- case TOK.float64Literal:
- case TOK.float80Literal:
- case TOK.imaginary32Literal:
- case TOK.imaginary64Literal:
- case TOK.imaginary80Literal:
- case TOK.string_:
- t = peek(t);
- break;
+ //case TOK._Imaginary: // ? missing in Spec
+ case TOK._Complex:
- case TOK.leftParenthesis:
- // ( expression )
- if (!skipParens(t, &t))
- return false;
- break;
+ // struct-or-union-specifier
+ // enum-specifier
+ case TOK.struct_:
+ case TOK.union_:
+ case TOK.enum_:
- case TOK._Generic:
- t = peek(t);
- if (!skipParens(t, &t))
- return false;
- break;
+ // atomic-type-specifier
+ case TOK._Atomic:
+ case TOK.typeof_:
+ case TOK.__attribute__:
+ return true;
default:
return false;
}
- pt = t;
- return true;
}
//}
SCW scw; /// storage-class specifiers
MOD mod; /// type qualifiers
- AST.Expressions* alignExps; /// alignment
- AST.Expression alignasExp; /// Last _Alignas() for errors
- structalign_t packalign; /// #pragma pack alignment value
+ AST.Expressions* alignAttrs; /// __attribute__((aligned))
+ AST.Expressions* alignasExps; /// _Alignas()
+ AST.Expression lastAlignas; /// Last _Alignas() for errors
+ structalign_t packalign; /// #pragma pack alignment value
}
/***********************
* loc = location for this declaration
* id = identifier of function
* Returns:
- * statement representing the declaration of __func__
+ * statement representing the declaration of __func__, __FUNCTION__, or __PRETTY-FUNCTION__
*/
- private AST.Statement createFuncName(Loc loc, Identifier id)
+ private AST.Statement createFuncName(Loc loc, Identifier id, Identifier cident)
{
const fn = id.toString(); // function-name
auto efn = new AST.StringExp(loc, fn, fn.length, 1, 'c');
auto ifn = new AST.ExpInitializer(loc, efn);
- auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0
+ auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0, ditto for __pretty_chars__
+ auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
+ efn.type = tfn.makeImmutable();
+ efn.committed = true;
+ auto sfn = new AST.VarDeclaration(loc, tfn, cident, ifn, STC.gshared | STC.immutable_);
+ auto e = new AST.DeclarationExp(loc, sfn);
+ return new AST.ExpStatement(loc, e);
+ }
+
+ /* function for C __PRETTY_FUNCTION__ for non-linux systems */
+ private AST.Statement createPrettyFunc(Loc loc, AST.FuncDeclaration fd)
+ {
+ auto tf = fd.type.isTypeFunction();
+ auto funcSig = tf.next.toString();
+ funcSig ~= " ";
+ funcSig ~= fd.ident.toString();
+ funcSig ~= "(";
+
+ foreach (i, fparam ; tf.parameterList)
+ {
+ if (i > 0)
+ funcSig ~= ", ";
+
+ AST.Type t = tf.parameterList[i].type;
+ funcSig ~= t.toString();
+ }
+ funcSig ~= ")";
+
+ auto efn = new AST.StringExp(loc, funcSig);
+ auto ifn = new AST.ExpInitializer(loc, efn);
+ auto lenfn = new AST.IntegerExp(loc, funcSig.length + 1, AST.Type.tuns32); // +1 for terminating 0
auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
efn.type = tfn.makeImmutable();
efn.committed = true;
- auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_);
+ auto sfn = new AST.VarDeclaration(loc, tfn, Id.PRETTY_FUNCTION, ifn, STC.gshared | STC.immutable_);
auto e = new AST.DeclarationExp(loc, sfn);
return new AST.ExpStatement(loc, e);
}
// `const` is always applied to the return type, not the
// type function itself.
if (auto tf = t.isTypeFunction())
- tf.next = tf.next.addSTC(STC.const_);
+ tf.next = AST.addSTC(tf.next, STC.const_);
else if (auto tt = t.isTypeTag())
tt.mod |= MODFlags.const_;
else
*/
auto tn = t.nextOf();
if (!tn || tn.isConst())
- t = t.addSTC(STC.const_);
+ t = AST.addSTC(t, STC.const_);
}
return t;
}
}
}
- if (specifier.alignExps)
+ if (specifier.alignAttrs)
+ {
+ //printf(" applying attribute((aligned)) %s\n", (*specifier.alignAttrs)[0].toChars());
+ // Wrap declaration in an AlignDeclaration
+ auto decls = new AST.Dsymbols(1);
+ (*decls)[0] = s;
+ s = new AST.AlignDeclaration(s.loc, specifier.alignAttrs, decls);
+ }
+ if (specifier.alignasExps)
{
- //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
+ //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignasExps)[0].toChars(), cast(int)specifier.packalign);
// Wrap declaration in an AlignDeclaration
auto decls = new AST.Dsymbols(1);
(*decls)[0] = s;
- s = new AST.AlignDeclaration(s.loc, specifier.alignExps, decls);
+ auto ad = new AST.AlignDeclaration(s.loc, specifier.alignasExps, decls);
+ ad.salign.setAlignas();
+ s = ad;
}
else if (!specifier.packalign.isDefault() && !specifier.packalign.isUnknown())
{
if (n < 1 || n & (n - 1) || ushort.max < n)
error(loc, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
packalign.set(cast(uint)n);
- packalign.setPack(true);
+ packalign.setPack();
}
scan(&n);
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
+import dmd.expressionsem;
import dmd.func;
import dmd.globals : dinteger_t, sinteger_t, uinteger_t;
import dmd.location;
import dmd.mtype;
import dmd.root.bitarray;
-import dmd.root.complex;
import dmd.root.ctfloat;
import dmd.root.port;
import dmd.root.rmem;
import dmd.tokens;
import dmd.typesem;
-import dmd.visitor;
/****************************************************************/
/* A type meant as a union of all the Expression types,
// and op is EXP.add or EXP.min
Expression pointerArithmetic(UnionExp* pue, Loc loc, EXP op, Type type, Expression eptr, Expression e2)
{
- if (eptr.type.nextOf().ty == Tvoid)
+ Expression cant()
{
- error(loc, "cannot perform arithmetic on `void*` pointers at compile time");
- Lcant:
emplaceExp!(CTFEExp)(pue, EXP.cantExpression);
return pue.exp();
}
+ if (eptr.type.nextOf().ty == Tvoid)
+ {
+ error(loc, "cannot perform arithmetic on `void*` pointers at compile time");
+ return cant();
+ }
if (eptr.op == EXP.address)
eptr = eptr.isAddrExp().e1;
dinteger_t ofs1;
if (agg1.isSymOffExp().var.type.ty != Tsarray)
{
error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time");
- goto Lcant;
+ return cant();
}
}
else if (agg1.op != EXP.string_ && agg1.op != EXP.arrayLiteral)
{
error(loc, "cannot perform pointer arithmetic on non-arrays at compile time");
- goto Lcant;
+ return cant();
}
dinteger_t ofs2 = e2.toInteger();
Type pointee = agg1.type.toBasetype().nextOf();
else
{
error(loc, "CTFE internal error: bad pointer operation");
- goto Lcant;
+ return cant();
}
if (indx < 0 || len < indx)
{
error(loc, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx, len);
- goto Lcant;
+ return cant();
}
if (agg1.op == EXP.symbolOffset)
{
if (agg1.op != EXP.arrayLiteral && agg1.op != EXP.string_)
{
error(loc, "CTFE internal error: pointer arithmetic `%s`", agg1.toChars());
- goto Lcant;
+ return cant();
}
if (auto tsa = eptr.type.toBasetype().isTypeSArray())
{
import dmd.astenums;
import dmd.attrib;
import dmd.common.outbuffer : OutBuffer;
-import dmd.dclass : ClassDeclaration;
-import dmd.declaration : TypeInfoDeclaration;
+import dmd.dclass : ClassDeclaration, BaseClass;
+import dmd.declaration : TypeInfoDeclaration, VarDeclaration, TupleDeclaration;
import dmd.denum : EnumDeclaration;
import dmd.dmodule /*: Module*/;
import dmd.dscope : Scope;
import dmd.location : Loc;
import dmd.mtype /*: Covariant, Type, Parameter, ParameterList*/;
import dmd.rootobject : RootObject;
+import dmd.root.optional;
+import dmd.root.longdouble : real_t = longdouble;
+import dmd.root.complex;
import dmd.semantic3;
import dmd.statement : Statement, AsmStatement, GccAsmStatement;
return dmd.mangle.mangleToBuffer(ti, buf);
}
-/***********************************************************
- * dmodule.d
- */
-FuncDeclaration findGetMembers(ScopeDsymbol dsym)
-{
- return dmd.dmodule.findGetMembers(dsym);
-}
-
/***********************************************************
* doc.d
*/
return dmd.dsymbolsem.isPOD(sd);
}
+bool fillVtbl(BaseClass* bc, ClassDeclaration cd, FuncDeclarations* vtbl, int newinstance)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.fillVtbl(bc, cd, vtbl, newinstance);
+}
+
+bool overloadInsert(Dsymbol ds, Dsymbol s)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.overloadInsert(ds, s);
+}
+
+bool equals(const Dsymbol ds, const Dsymbol s)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.equals(ds, s);
+}
+
+Type getType(Dsymbol ds)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.getType(ds);
+}
+
+uinteger_t size(Dsymbol ds, Loc loc)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.size(ds, loc);
+}
+
+void semantic3OnDependencies(Module m)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.semantic3OnDependencies(m);
+}
+
+void addDeferredSemantic(Dsymbol s)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.addDeferredSemantic(s);
+}
+
+void addDeferredSemantic2(Dsymbol s)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.addDeferredSemantic2(s);
+}
+
+void addDeferredSemantic3(Dsymbol s)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.addDeferredSemantic3(s);
+}
+
+void runDeferredSemantic()
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.runDeferredSemantic();
+}
+
+void runDeferredSemantic2()
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.runDeferredSemantic2();
+}
+
+void runDeferredSemantic3()
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.runDeferredSemantic3();
+}
+
+bool isOverlappedWith(VarDeclaration vd, VarDeclaration v)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.isOverlappedWith(vd, v);
+}
+
+Scope* newScope(AggregateDeclaration ad, Scope* sc)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.newScope(ad, sc);
+}
+
+Dsymbol search(Scope* sc, Loc loc, Identifier ident, out Dsymbol pscopesym,
+ SearchOptFlags flags = SearchOpt.all)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.search(sc, loc, ident, pscopesym, flags);
+}
+
+void addObjcSymbols(Dsymbol sym, ClassDeclarations* classes, ClassDeclarations* categories)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.addObjcSymbols(sym, classes, categories);
+}
+
+FuncDeclaration findGetMembers(ScopeDsymbol dsym)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.findGetMembers(dsym);
+}
+
/***********************************************************
* dtemplate.d
*/
/***********************************************************
* dtoh.d
*/
-void genCppHdrFiles(ref Modules ms)
+void genCppHdrFiles(ref Modules ms, ErrorSink eSink)
{
import dmd.dtoh;
- return dmd.dtoh.genCppHdrFiles(ms);
+ return dmd.dtoh.genCppHdrFiles(ms, eSink);
}
/***********************************************************
return dmd.enumsem.getDefaultValue(ed, loc);
}
+
/***********************************************************
- * expression.d
+ * expressionsem.d
*/
+
void expandTuples(Expressions* exps, ArgumentLabels* names = null)
{
- return dmd.expression.expandTuples(exps, names);
+ import dmd.expressionsem;
+ return dmd.expressionsem.expandTuples(exps, names);
}
-/***********************************************************
- * expressionsem.d
- */
Expression expressionSemantic(Expression e, Scope* sc)
{
import dmd.expressionsem;
return dmd.expressionsem.fill(sd, loc, elements, ctorinit);
}
+bool isIdentical(const Expression exp, const Expression e)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.isIdentical(exp, e);
+}
+
+bool equals(const Expression exp, const Expression e)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.equals(exp, e);
+}
+
+bool isLvalue(Expression exp)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.isLvalue(exp);
+}
+
+int getFieldIndex(ClassReferenceExp cre, Type fieldtype, uint fieldoffset)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.getFieldIndex(cre, fieldtype, fieldoffset);
+}
+
+void fillTupleExpExps(TupleExp te, TupleDeclaration tup)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.fillTupleExpExps(te, tup);
+}
+
+Optional!bool toBool(Expression exp)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.toBool(exp);
+}
+
+StringExp toStringExp(Expression exp)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.toStringExp(exp);
+}
+
+dinteger_t toInteger(Expression exp)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.toInteger(exp);
+}
+
+uinteger_t toUInteger(Expression exp)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.toUInteger(exp);
+}
+
+real_t toReal(Expression exp)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.toReal(exp);
+}
+
+complex_t toComplex(Expression exp)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.toComplex(exp);
+}
+
+real_t toImaginary(Expression exp)
+{
+ import dmd.expressionsem;
+ return dmd.expressionsem.toImaginary(exp);
+}
+
/***********************************************************
- * func.d
+ * funcsem.d
*/
FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, StorageClass stc = STC.none)
{
- return FuncDeclaration.genCfunc(fparams, treturn, name, cast(STC) stc);
+ import dmd.funcsem;
+ return dmd.funcsem.genCfunc(fparams, treturn, name, cast(STC) stc);
}
FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, StorageClass stc = STC.none)
{
- return FuncDeclaration.genCfunc(fparams, treturn, id, cast(STC) stc);
+ import dmd.funcsem;
+ return dmd.funcsem.genCfunc(fparams, treturn, id, cast(STC) stc);
}
-/***********************************************************
- * funcsem.d
- */
bool functionSemantic(FuncDeclaration fd)
{
import dmd.funcsem;
return dmd.funcsem.isPure(fd);
}
+bool needsClosure(FuncDeclaration fd)
+{
+ import dmd.funcsem;
+ return dmd.funcsem.needsClosure(fd);
+}
+
/***********************************************************
* hdrgen.d
*/
return dmd.json.tryParseJsonField(fieldName);
}
-/***********************************************************
- * mtype.d
- */
-AggregateDeclaration isAggregate(Type t)
-{
- return dmd.mtype.isAggregate(t);
-}
-
/***********************************************************
* optimize.d
*/
/***********************************************************
* typesem.d
*/
+bool hasDeprecatedAliasThis(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.hasDeprecatedAliasThis(type);
+}
+
+AggregateDeclaration isAggregate(Type t)
+{
+ import dmd.typesem;
+ return dmd.typesem.isAggregate(t);
+}
+
bool hasPointers(Type t)
{
import dmd.typesem;
return dmd.typesem.merge2(type);
}
+Type toBasetype(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.toBasetype(type);
+}
+
Expression defaultInit(Type mt, Loc loc, const bool isCfile = false)
{
import dmd.typesem;
return dmd.typesem.referenceTo(type);
}
+Type memType(TypeEnum type)
+{
+ import dmd.typesem;
+ return dmd.typesem.memType(type);
+}
+
uinteger_t size(Type type)
{
import dmd.typesem;
return dmd.typesem.size(type, loc);
}
+structalign_t alignment(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.alignment(type);
+}
+
+uint alignsize(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.alignsize(type);
+}
+
MATCH implicitConvTo(Type from, Type to)
{
import dmd.dcast;
return dmd.typesem.defaultInitLiteral(t, loc);
}
+bool hasUnsafeBitpatterns(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.hasUnsafeBitpatterns(type);
+}
+
+bool hasInvariant(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.hasInvariant(type);
+}
+
+bool hasVoidInitPointers(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.hasVoidInitPointers(type);
+}
+
+void Type_init()
+{
+ import dmd.typesem;
+ return dmd.typesem.Type_init();
+}
+
+void transitive(TypeNext type)
+{
+ import dmd.typesem;
+ return dmd.typesem.transitive(type);
+}
+
+Type makeConst(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.makeConst(type);
+}
+
+Type makeImmutable(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.makeImmutable(type);
+}
+
+Type makeMutable(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.makeMutable(type);
+}
+
+Type makeShared(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.makeShared(type);
+}
+
+Type makeSharedConst(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.makeSharedConst(type);
+}
+
+Type makeWild(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.makeWild(type);
+}
+
+Type makeWildConst(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.makeWildConst(type);
+}
+
+Type makeSharedWild(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.makeSharedWild(type);
+}
+
+Type makeSharedWildConst(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.makeSharedWildConst(type);
+}
+
+Type nextOf(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.nextOf(type);
+}
+
+Type baseElemOf(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.baseElemOf(type);
+}
+
+Type isLazyArray(Parameter param)
+{
+ import dmd.typesem;
+ return dmd.typesem.isLazyArray(param);
+}
+
+MOD deduceWild(Type type, Type t, bool isRef)
+{
+ import dmd.typesem;
+ return dmd.typesem.deduceWild(type, t, isRef);
+}
+
+bool isIntegral(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.isIntegral(type);
+}
+
+bool isFloating(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.isFloating(type);
+}
+
+bool isScalar(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.isScalar(type);
+}
+
+bool isReal(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.isReal(type);
+}
+
+bool isComplex(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.isComplex(type);
+}
+
+bool isImaginary(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.isImaginary(type);
+}
+
+bool isString(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.isString(type);
+}
+
+bool isBoolean(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.isBoolean(type);
+}
+
+bool isUnsigned(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.isUnsigned(type);
+}
+
+bool needsNested(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.needsNested(type);
+}
+
+bool needsDestruction(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.needsDestruction(type);
+}
+
+bool needsCopyOrPostblit(Type type)
+{
+ import dmd.typesem;
+ return dmd.typesem.needsCopyOrPostblit(type);
+}
+
/***********************************************************
* typinf.d
*/
/**
* templatesem.d
*/
+bool declareParameter(TemplateParameter tp, Scope* sc)
+{
+ import dmd.templatesem;
+ return dmd.templatesem.declareParameter(tp, sc);
+}
+
bool needsCodegen(TemplateInstance ti)
{
import dmd.templatesem;
import core.stdc.stdio;
import core.stdc.string;
import dmd.aggregate;
-import dmd.aliasthis;
import dmd.arrayop;
import dmd.arraytypes;
import dmd.astenums;
}
auto ad = isAggregate(e.type);
- if (ad && ad.aliasthis)
+ if (!ad || !ad.aliasthis)
+ return e.castTo(sc, t);
+
+ if (!ad.type || ad.type.isTypeError())
+ return e;
+ auto ts = ad.type.isTypeStruct();
+ const adMatch = ts
+ ? ts.implicitConvToWithoutAliasThis(t)
+ : ad.type.isTypeClass().implicitConvToWithoutAliasThis(t);
+
+ if (adMatch)
+ return e.castTo(sc, t);
+
+ Type tob = t.toBasetype();
+ Type t1b = e.type.toBasetype();
+ if (ad == isAggregate(tob))
+ return e.castTo(sc, t);
+
+ if (t1b.ty == Tclass && tob.ty == Tclass)
{
- if (!ad.type || ad.type.isTypeError())
- return e;
- auto ts = ad.type.isTypeStruct();
- const adMatch = ts
- ? ts.implicitConvToWithoutAliasThis(t)
- : ad.type.isTypeClass().implicitConvToWithoutAliasThis(t);
-
- if (!adMatch)
+ ClassDeclaration t1cd = t1b.isClassHandle();
+ ClassDeclaration tocd = tob.isClassHandle();
+ int offset;
+ if (tocd.isBaseOf(t1cd, &offset))
{
- Type tob = t.toBasetype();
- Type t1b = e.type.toBasetype();
- if (ad != isAggregate(tob))
- {
- if (t1b.ty == Tclass && tob.ty == Tclass)
- {
- ClassDeclaration t1cd = t1b.isClassHandle();
- ClassDeclaration tocd = tob.isClassHandle();
- int offset;
- if (tocd.isBaseOf(t1cd, &offset))
- {
- auto result = new CastExp(e.loc, e, t);
- result.type = t;
- return result;
- }
- }
-
- /* Forward the cast to our alias this member, rewrite to:
- * cast(to)e1.aliasthis
- */
- auto result = resolveAliasThis(sc, e);
- return result.castTo(sc, t);
- }
+ auto result = new CastExp(e.loc, e, t);
+ result.type = t;
+ return result;
}
}
- return e.castTo(sc, t);
+ /* Forward the cast to our alias this member, rewrite to:
+ * cast(to)e1.aliasthis
+ */
+ auto result = resolveAliasThis(sc, e);
+ return result.castTo(sc, t);
}
auto result = e.optimize(WANTvalue);
return implicitCastTo(result, sc, t);
}
- if (t.ty != Terror && e.type.ty != Terror)
+ if (t.ty == Terror || e.type.ty == Terror)
+ return ErrorExp.get();
+
+ if (!t.deco)
+ {
+ error(e.loc, "forward reference to type `%s`", t.toChars());
+ return ErrorExp.get();
+ }
+
+ //printf("type %p ty %d deco %p\n", type, type.ty, type.deco);
+ //type = type.typeSemantic(loc, sc);
+ //printf("type %s t %s\n", type.deco, t.deco);
+ auto ts = toAutoQualChars(e.type, t);
+
+ // Special case for improved diagnostic when const to mutable conversion
+ // fails due to struct/union having pointers
+ if (e.type.ty == Tstruct && t.ty == Tstruct &&
+ e.type.isTypeStruct().sym == t.isTypeStruct().sym &&
+ e.type.mod == MODFlags.const_ && t.mod == 0 && e.type.hasPointers)
{
- if (!t.deco)
+ auto sym = e.type.isTypeStruct().sym;
+ error(e.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s` because %s `%s` contains pointers or references",
+ e.toErrMsg(), ts[0], ts[1], sym.kind(), sym.toErrMsg());
+ return ErrorExp.get();
+ }
+
+ // Special case for pointer conversions
+ if (e.type.toBasetype().ty == Tpointer && t.toBasetype().ty == Tpointer)
+ {
+ Type fromPointee = e.type.nextOf();
+ Type toPointee = t.nextOf();
+ // Const -> mutable conversion (disallowed)
+ if (fromPointee.isConst() && !toPointee.isConst())
{
- error(e.loc, "forward reference to type `%s`", t.toChars());
+ error(e.loc, "cannot implicitly convert `%s` to `%s`", e.type.toChars(), t.toChars());
+ errorSupplemental(e.loc, "Note: Converting const to mutable requires an explicit cast (`cast(int*)`).");
+ return ErrorExp.get();
}
- else
+ // Incompatible pointee types (e.g., int* -> float* )
+ else if (fromPointee.toBasetype().ty != toPointee.toBasetype().ty)
{
- //printf("type %p ty %d deco %p\n", type, type.ty, type.deco);
- //type = type.typeSemantic(loc, sc);
- //printf("type %s t %s\n", type.deco, t.deco);
- auto ts = toAutoQualChars(e.type, t);
-
- // Special case for improved diagnostic when const to mutable conversion
- // fails due to struct/union having pointers
- if (e.type.ty == Tstruct && t.ty == Tstruct &&
- e.type.isTypeStruct().sym == t.isTypeStruct().sym &&
- e.type.mod == MODFlags.const_ && t.mod == 0 && e.type.hasPointers)
- {
- auto sym = e.type.isTypeStruct().sym;
- error(e.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s` because %s `%s` contains pointers or references",
- e.toErrMsg(), ts[0], ts[1], sym.kind(), sym.toErrMsg());
- return ErrorExp.get();
- }
-
- // Special case for pointer conversions
- if (e.type.toBasetype().ty == Tpointer && t.toBasetype().ty == Tpointer)
- {
- Type fromPointee = e.type.nextOf();
- Type toPointee = t.nextOf();
- // Const -> mutable conversion (disallowed)
- if (fromPointee.isConst() && !toPointee.isConst())
- {
- error(e.loc, "cannot implicitly convert `%s` to `%s`", e.type.toChars(), t.toChars());
- errorSupplemental(e.loc, "Note: Converting const to mutable requires an explicit cast (`cast(int*)`).");
- return ErrorExp.get();
- }
- // Incompatible pointee types (e.g., int* -> float* )
- else if (fromPointee.toBasetype().ty != toPointee.toBasetype().ty)
- {
- error(e.loc, "cannot implicitly convert `%s` to `%s`", e.type.toChars(), t.toChars());
- errorSupplemental(e.loc, "Note: Pointer types point to different base types (`%s` vs `%s`)", fromPointee.toChars(), toPointee.toChars());
- return ErrorExp.get();
- }
- }
- error(e.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", e.toErrMsg(), ts[0], ts[1]);
+ error(e.loc, "cannot implicitly convert `%s` to `%s`", e.type.toChars(), t.toChars());
+ errorSupplemental(e.loc, "Note: Pointer types point to different base types (`%s` vs `%s`)", fromPointee.toChars(), toPointee.toChars());
+ return ErrorExp.get();
}
}
+ error(e.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", e.toErrMsg(), ts[0], ts[1]);
+
return ErrorExp.get();
}
{
if (e.committed && tynto != tyn)
return MATCH.nomatch;
- size_t fromlen = e.numberOfCodeUnits(tynto);
+ string s;
+ size_t fromlen = e.numberOfCodeUnits(tynto, s);
+ if (s)
+ error(e.loc, "%.*s", cast(int)s.length, s.ptr);
size_t tolen = cast(size_t)t.isTypeSArray().dim.toInteger();
if (tolen < fromlen)
return MATCH.nomatch;
{
if (e.committed && tynto != tyn)
return MATCH.nomatch;
- size_t fromlen = e.numberOfCodeUnits(tynto);
+ string s;
+ size_t fromlen = e.numberOfCodeUnits(tynto, s);
+ if (s)
+ error(e.loc, "%.*s", cast(int)s.length, s.ptr);
size_t tolen = cast(size_t)t.isTypeSArray().dim.toInteger();
if (tolen < fromlen)
return MATCH.nomatch;
if (sd.isNested())
return MATCH.nomatch;
}
- if (ntb.isZeroInit(e.loc))
+ if (!ntb.isZeroInit(e.loc))
{
- /* Zeros are implicitly convertible, except for special cases.
- */
- if (auto tc = ntb.isTypeClass())
- {
- /* With new() must look at the class instance initializer.
- */
- ClassDeclaration cd = tc.sym;
+ Expression earg = e.newtype.defaultInitLiteral(e.loc);
+ Type targ = e.newtype.toBasetype();
- cd.size(e.loc); // resolve any forward references
+ if (implicitMod(earg, targ, mod) == MATCH.nomatch)
+ return MATCH.nomatch;
+ return MATCH.constant;
+ }
+ /* Zeros are implicitly convertible, except for special cases.
+ */
+ auto tc = ntb.isTypeClass();
+ if (!tc)
+ return MATCH.constant;
- if (cd.isNested())
- return MATCH.nomatch; // uplevel reference may not be convertible
+ /* With new() must look at the class instance initializer.
+ */
+ ClassDeclaration cd = tc.sym;
+
+ cd.size(e.loc); // resolve any forward references
- assert(!cd.isInterfaceDeclaration());
+ if (cd.isNested())
+ return MATCH.nomatch; // uplevel reference may not be convertible
- struct ClassCheck
+ assert(!cd.isInterfaceDeclaration());
+
+ static bool convertible(Expression e, ClassDeclaration cd, MOD mod)
+ {
+ for (size_t i = 0; i < cd.fields.length; i++)
+ {
+ VarDeclaration v = cd.fields[i];
+ Initializer _init = v._init;
+ if (_init)
{
- extern (C++) static bool convertible(Expression e, ClassDeclaration cd, MOD mod)
+ if (_init.isVoidInitializer())
+ {
+ }
+ else if (ExpInitializer ei = _init.isExpInitializer())
{
- for (size_t i = 0; i < cd.fields.length; i++)
- {
- VarDeclaration v = cd.fields[i];
- Initializer _init = v._init;
- if (_init)
- {
- if (_init.isVoidInitializer())
- {
- }
- else if (ExpInitializer ei = _init.isExpInitializer())
- {
- // https://issues.dlang.org/show_bug.cgi?id=21319
- // This is to prevent re-analyzing the same expression
- // over and over again.
- if (ei.exp == e)
- return false;
- Type tb = v.type.toBasetype();
- if (implicitMod(ei.exp, tb, mod) == MATCH.nomatch)
- return false;
- }
- else
- {
- /* Enhancement: handle StructInitializer and ArrayInitializer
- */
- return false;
- }
- }
- else if (!v.type.isZeroInit(e.loc))
- return false;
- }
- return cd.baseClass ? convertible(e, cd.baseClass, mod) : true;
+ // https://issues.dlang.org/show_bug.cgi?id=21319
+ // This is to prevent re-analyzing the same expression
+ // over and over again.
+ if (ei.exp == e)
+ return false;
+ Type tb = v.type.toBasetype();
+ if (implicitMod(ei.exp, tb, mod) == MATCH.nomatch)
+ return false;
+ }
+ else
+ {
+ /* Enhancement: handle StructInitializer and ArrayInitializer
+ */
+ return false;
}
}
-
- if (!ClassCheck.convertible(e, cd, mod))
- return MATCH.nomatch;
+ else if (!v.type.isZeroInit(e.loc))
+ return false;
}
+ return cd.baseClass ? convertible(e, cd.baseClass, mod) : true;
}
- else
- {
- Expression earg = e.newtype.defaultInitLiteral(e.loc);
- Type targ = e.newtype.toBasetype();
- if (implicitMod(earg, targ, mod) == MATCH.nomatch)
- return MATCH.nomatch;
- }
+ if (!convertible(e, cd, mod))
+ return MATCH.nomatch;
/* Success
*/
}
return MATCH.nomatch;
}
- if (auto tsa = to.isTypeSArray())
- {
- if (from == to)
- return MATCH.exact;
+ auto tsa = to.isTypeSArray();
+ if (!tsa)
+ return MATCH.nomatch;
- if (from.dim.equals(tsa.dim))
- {
- MATCH m = from.next.implicitConvTo(tsa.next);
+ if (from == to)
+ return MATCH.exact;
- /* Allow conversion to non-interface base class.
- */
- if (m == MATCH.convert &&
- from.next.ty == Tclass)
- {
- if (auto toc = tsa.next.isTypeClass)
- {
- if (!toc.sym.isInterfaceDeclaration)
- return MATCH.convert;
- }
- }
+ if (!from.dim.equals(tsa.dim))
+ return MATCH.nomatch;
- /* Since static arrays are value types, allow
- * conversions from const elements to non-const
- * ones, just like we allow conversion from const int
- * to int.
- */
- if (m >= MATCH.constant)
- {
- if (from.mod != to.mod)
- m = MATCH.constant;
- return m;
- }
+ MATCH m = from.next.implicitConvTo(tsa.next);
+
+ /* Allow conversion to non-interface base class.
+ */
+ if (m == MATCH.convert &&
+ from.next.ty == Tclass)
+ {
+ if (auto toc = tsa.next.isTypeClass)
+ {
+ if (!toc.sym.isInterfaceDeclaration)
+ return MATCH.convert;
}
}
+
+ /* Since static arrays are value types, allow
+ * conversions from const elements to non-const
+ * ones, just like we allow conversion from const int
+ * to int.
+ */
+ if (m >= MATCH.constant)
+ {
+ if (from.mod != to.mod)
+ m = MATCH.constant;
+ return m;
+ }
+
return MATCH.nomatch;
}
if (from.equals(to))
return MATCH.exact;
- if (auto ta = to.isTypeDArray())
- {
- if (!MODimplicitConv(from.next.mod, ta.next.mod))
- return MATCH.nomatch; // not const-compatible
+ auto ta = to.isTypeDArray();
+ if (!ta)
+ return visitType(from);
- /* Allow conversion to void[]
- */
- if (from.next.ty != Tvoid && ta.next.ty == Tvoid)
- {
- return MATCH.convert;
- }
+ if (!MODimplicitConv(from.next.mod, ta.next.mod))
+ return MATCH.nomatch; // not const-compatible
- MATCH m = from.next.constConv(ta.next);
- if (m > MATCH.nomatch)
- {
- if (m == MATCH.exact && from.mod != to.mod)
- m = MATCH.constant;
- return m;
- }
+ /* Allow conversion to void[]
+ */
+ if (from.next.ty != Tvoid && ta.next.ty == Tvoid)
+ {
+ return MATCH.convert;
+ }
+
+ MATCH m = from.next.constConv(ta.next);
+ if (m > MATCH.nomatch)
+ {
+ if (m == MATCH.exact && from.mod != to.mod)
+ m = MATCH.constant;
+ return m;
}
return visitType(from);
if (from.equals(to))
return MATCH.exact;
- if (auto ta = to.isTypeAArray())
- {
- if (!MODimplicitConv(from.next.mod, ta.next.mod))
- return MATCH.nomatch; // not const-compatible
+ auto ta = to.isTypeAArray();
+ if (!ta)
+ return visitType(from);
- if (!MODimplicitConv(from.index.mod, ta.index.mod))
- return MATCH.nomatch; // not const-compatible
+ if (!MODimplicitConv(from.next.mod, ta.next.mod))
+ return MATCH.nomatch; // not const-compatible
- MATCH m = from.next.constConv(ta.next);
- MATCH mi = from.index.constConv(ta.index);
- if (m > MATCH.nomatch && mi > MATCH.nomatch)
- {
- return MODimplicitConv(from.mod, to.mod) ? MATCH.constant : MATCH.nomatch;
- }
+ if (!MODimplicitConv(from.index.mod, ta.index.mod))
+ return MATCH.nomatch; // not const-compatible
+
+ MATCH m = from.next.constConv(ta.next);
+ MATCH mi = from.index.constConv(ta.index);
+ if (m > MATCH.nomatch && mi > MATCH.nomatch)
+ {
+ return MODimplicitConv(from.mod, to.mod) ? MATCH.constant : MATCH.nomatch;
}
+
return visitType(from);
}
if (tf.equals(to))
return MATCH.constant;
- if (tf.covariant(to) == Covariant.yes)
+ if (tf.covariant(to) != Covariant.yes)
+ return MATCH.nomatch;
+
+ Type tret = tf.nextOf();
+ Type toret = to.nextOf();
+ if (tret.ty == Tclass && toret.ty == Tclass)
{
- Type tret = tf.nextOf();
- Type toret = to.nextOf();
- if (tret.ty == Tclass && toret.ty == Tclass)
- {
- /* https://issues.dlang.org/show_bug.cgi?id=10219
- * Check covariant interface return with offset tweaking.
- * interface I {}
- * class C : Object, I {}
- * I function() dg = function C() {} // should be error
- */
- int offset = 0;
- if (toret.isBaseOf(tret, &offset) && offset != 0)
- return MATCH.nomatch;
- }
- return MATCH.convert;
+ /* https://issues.dlang.org/show_bug.cgi?id=10219
+ * Check covariant interface return with offset tweaking.
+ * interface I {}
+ * class C : Object, I {}
+ * I function() dg = function C() {} // should be error
+ */
+ int offset = 0;
+ if (toret.isBaseOf(tret, &offset) && offset != 0)
+ return MATCH.nomatch;
}
-
- return MATCH.nomatch;
+ return MATCH.convert;
}
MATCH visitPointer(TypePointer from)
if (from.equals(to))
return MATCH.exact;
- if (auto toDg = to.isTypeDelegate())
- {
- MATCH m = implicitPointerConv(from.next.isTypeFunction(), toDg.next);
-
- // Retain the old behaviour for this refactoring
- // Should probably be changed to constant to match function pointers
- if (m > MATCH.convert)
- m = MATCH.convert;
+ auto toDg = to.isTypeDelegate();
+ if (!toDg)
+ return MATCH.nomatch;
- return m;
- }
+ MATCH m = implicitPointerConv(from.next.isTypeFunction(), toDg.next);
- return MATCH.nomatch;
+ // Retain the old behaviour for this refactoring
+ // Should probably be changed to constant to match function pointers
+ if (m > MATCH.convert)
+ return MATCH.convert;
+ return m;
}
MATCH visitStruct(TypeStruct from)
{
if (from == to)
return MATCH.exact;
- if (auto tt = to.isTypeTuple())
+ auto tt = to.isTypeTuple();
+ if (!tt)
+ return MATCH.nomatch;
+
+ if (from.arguments.length != tt.arguments.length)
+ return MATCH.nomatch;
+
+ MATCH m = MATCH.exact;
+ for (size_t i = 0; i < tt.arguments.length; i++)
{
- if (from.arguments.length == tt.arguments.length)
- {
- MATCH m = MATCH.exact;
- for (size_t i = 0; i < tt.arguments.length; i++)
- {
- Parameter arg1 = (*from.arguments)[i];
- Parameter arg2 = (*tt.arguments)[i];
- MATCH mi = arg1.type.implicitConvTo(arg2.type);
- if (mi < m)
- m = mi;
- }
- return m;
- }
+ Parameter arg1 = (*from.arguments)[i];
+ Parameter arg2 = (*tt.arguments)[i];
+ MATCH mi = arg1.type.implicitConvTo(arg2.type);
+ if (mi < m)
+ m = mi;
}
- return MATCH.nomatch;
+ return m;
}
MATCH visitNull(TypeNull from)
const(bool) tob_isA = ((tob.isIntegral() || tob.isFloating()) && tob.ty != Tvector);
const(bool) t1b_isA = ((t1b.isIntegral() || t1b.isFloating()) && t1b.ty != Tvector);
+ Expression ok()
+ {
+ auto result = new CastExp(e.loc, e, t);
+ result.type = t; // Don't call semantic()
+ //printf("Returning: %s\n", result.toChars());
+ return result;
+ }
// Try casting the alias this member.
// Return the expression if it succeeds, null otherwise.
Expression tryAliasThisCast()
}
bool hasAliasThis;
+
+ Expression fail()
+ {
+ /* if the cast cannot be performed, maybe there is an alias
+ * this that can be used for casting.
+ */
+ if (hasAliasThis)
+ {
+ if (auto result = tryAliasThisCast())
+ return result;
+ }
+ error(e.loc, "cannot cast expression `%s` of type `%s` to `%s`", e.toChars(), e.type.toChars(), t.toChars());
+ return ErrorExp.get();
+ }
+
if (AggregateDeclaration t1ad = isAggregate(t1b))
{
AggregateDeclaration toad = isAggregate(tob);
ClassDeclaration tocd = tob.isClassHandle();
int offset;
if (tocd.isBaseOf(t1cd, &offset))
- goto Lok;
+ return ok();
}
hasAliasThis = true;
}
{
// Casting static array to vector with same size, e.g. `cast(int4) int[4]`
if (t1b.size(e.loc) != tob.size(e.loc))
- goto Lfail;
+ return fail();
return new VectorExp(e.loc, e, tob).expressionSemantic(sc);
}
//printf("test1 e = %s, e.type = %s, tob = %s\n", e.toChars(), e.type.toChars(), tob.toChars());
if (tob.ty == Tsarray)
{
if (t1b.size(e.loc) == tob.size(e.loc))
- goto Lok;
+ return ok();
}
- goto Lfail;
+ return fail();
}
else if (t1b.implicitConvTo(tob) == MATCH.constant && t.equals(e.type.constOf()))
{
// arithmetic values vs. T*
if (tob_isA && (t1b_isA || t1b.ty == Tpointer) || t1b_isA && (tob_isA || tob.ty == Tpointer))
{
- goto Lok;
+ return ok();
}
// arithmetic values vs. references or fat values
if (tob_isA && (t1b_isR || t1b_isFV) || t1b_isA && (tob_isR || tob_isFV))
{
- goto Lfail;
+ return fail();
}
// Bugzlla 3133: A cast between fat values is possible only when the sizes match.
}
if (t1b.size(e.loc) == tob.size(e.loc))
- goto Lok;
+ return ok();
auto ts = toAutoQualChars(e.type, t);
error(e.loc, "cannot cast expression `%s` of type `%s` to `%s` because of different sizes",
return ErrorExp.get();
}
}
- goto Lok;
+ return ok();
}
- goto Lfail;
+ return fail();
}
/* For references, any reinterpret casts are allowed to same 'ty' type.
* class/interface A to B (will be a dynamic cast if possible)
*/
if (tob.ty == t1b.ty && tob_isR && t1b_isR)
- goto Lok;
+ return ok();
// typeof(null) <-- non-null references or values
if (tob.ty == Tnull && t1b.ty != Tnull)
- goto Lfail; // https://issues.dlang.org/show_bug.cgi?id=14629
+ return fail(); // https://issues.dlang.org/show_bug.cgi?id=14629
// typeof(null) --> non-null references or arithmetic values
if (t1b.ty == Tnull && tob.ty != Tnull)
- goto Lok;
+ return ok();
// Check size mismatch of references.
// Tarray and Tdelegate are (void*).sizeof*2, but others have (void*).sizeof.
{
// T[] da;
// cast(U*)da; // ==> cast(U*)da.ptr;
- goto Lok;
+ return ok();
}
if (tob.ty == Tpointer && t1b.ty == Tdelegate)
{
// cast(U*)dg; // ==> cast(U*)dg.ptr;
// Note that it happens even when U is a Tfunction!
deprecation(e.loc, "casting from %s to %s is deprecated", e.type.toChars(), t.toChars());
- goto Lok;
+ return ok();
}
- goto Lfail;
+ return fail();
}
if (t1b.ty == Tvoid && tob.ty != Tvoid)
{
- Lfail:
- /* if the cast cannot be performed, maybe there is an alias
- * this that can be used for casting.
- */
- if (hasAliasThis)
- {
- if (auto result = tryAliasThisCast())
- return result;
- }
- error(e.loc, "cannot cast expression `%s` of type `%s` to `%s`", e.toChars(), e.type.toChars(), t.toChars());
- return ErrorExp.get();
+ return fail();
}
+ return ok();
- Lok:
- auto result = new CastExp(e.loc, e, t);
- result.type = t; // Don't call semantic()
- //printf("Returning: %s\n", result.toChars());
- return result;
}
Expression visitError(ErrorExp e)
Expression visitReal(RealExp e)
{
- if (!e.type.equals(t))
+ if (e.type.equals(t))
+ return e;
+
+ if ((e.type.isReal() && t.isReal()) || (e.type.isImaginary() && t.isImaginary()))
{
- if ((e.type.isReal() && t.isReal()) || (e.type.isImaginary() && t.isImaginary()))
- {
- auto result = e.copy();
- result.type = t;
- return result;
- }
- else
- return visit(e);
+ auto result = e.copy();
+ result.type = t;
+ return result;
}
- return e;
+ else
+ return visit(e);
}
Expression visitComplex(ComplexExp e)
{
- if (!e.type.equals(t))
+ if (e.type.equals(t))
+ return e;
+
+ if (e.type.isComplex() && t.isComplex())
{
- if (e.type.isComplex() && t.isComplex())
- {
- auto result = e.copy();
- result.type = t;
- return result;
- }
- else
- return visit(e);
+ auto result = e.copy();
+ result.type = t;
+ return result;
}
- return e;
+ else
+ return visit(e);
}
Expression visitStructLiteral(StructLiteralExp e)
}
if (e.committed)
- goto Lcast;
+ return lcast();
static auto X(T, U)(T tf, U tt)
{
default:
assert(typeb.nextOf().size() != tb.nextOf().size());
- goto Lcast;
+ return lcast();
}
}
L2:
}
se.type = t;
return se;
-
- Lcast:
- auto result = new CastExp(e.loc, se, t);
- result.type = t; // so semantic() won't be run on e
- return result;
}
Expression visitAddr(AddrExp e)
Expression visitAle(ArrayLiteralExp ale)
{
Type tb = t.toBasetype();
- if (tb.isStaticOrDynamicArray())
+ if (!tb.isStaticOrDynamicArray())
+ return ale;
+
+ Type tn = tb.nextOf();
+ if (ale.basis)
+ ale.basis = inferType(ale.basis, tn, flag);
+ for (size_t i = 0; i < ale.elements.length; i++)
{
- Type tn = tb.nextOf();
- if (ale.basis)
- ale.basis = inferType(ale.basis, tn, flag);
- for (size_t i = 0; i < ale.elements.length; i++)
+ if (Expression e = (*ale.elements)[i])
{
- if (Expression e = (*ale.elements)[i])
- {
- e = inferType(e, tn, flag);
- (*ale.elements)[i] = e;
- }
+ e = inferType(e, tn, flag);
+ (*ale.elements)[i] = e;
}
}
+
return ale;
}
Expression visitAar(AssocArrayLiteralExp aale)
{
Type tb = t.toBasetype();
- if (auto taa = tb.isTypeAArray())
+ auto taa = tb.isTypeAArray();
+ if (!taa)
+ return aale;
+
+ Type ti = taa.index;
+ Type tv = taa.nextOf();
+ for (size_t i = 0; i < aale.keys.length; i++)
{
- Type ti = taa.index;
- Type tv = taa.nextOf();
- for (size_t i = 0; i < aale.keys.length; i++)
+ if (Expression e = (*aale.keys)[i])
{
- if (Expression e = (*aale.keys)[i])
- {
- e = inferType(e, ti, flag);
- (*aale.keys)[i] = e;
- }
+ e = inferType(e, ti, flag);
+ (*aale.keys)[i] = e;
}
- for (size_t i = 0; i < aale.values.length; i++)
+ }
+ for (size_t i = 0; i < aale.values.length; i++)
+ {
+ if (Expression e = (*aale.values)[i])
{
- if (Expression e = (*aale.values)[i])
- {
- e = inferType(e, tv, flag);
- (*aale.values)[i] = e;
- }
+ e = inferType(e, tv, flag);
+ (*aale.values)[i] = e;
}
}
+
return aale;
}
t1b = t1.toBasetype();
t2b = t2.toBasetype();
+ static bool isComplexStruct(Type t)
+ {
+ if (t.ty != Tstruct)
+ return false;
+
+ TypeStruct ts = t.isTypeStruct();
+
+ return ts.sym.toString() == "_Complex";
+ }
+
+ static bool isComplexStructOfType(Type t, Type t2)
+ {
+ if (!isComplexStruct(t))
+ return false;
+
+ TypeStruct ts = t.toBasetype().isTypeStruct();
+
+ Type memberType = ts.sym.fields[0].type.toBasetype();
+
+ /* encure the complex member types fall under one of the complex types */
+ switch (memberType.toBasetype().ty)
+ {
+ case Tfloat32:
+ case Tfloat64:
+ case Tfloat80:
+ break;
+ default:
+ return false;
+ }
+
+ switch (t2.toBasetype().ty)
+ {
+ case Tfloat32:
+ case Tfloat64:
+ case Tfloat80:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /* check for complex structure and an associate complex type in a single condexp */
+ if (sc && sc.inCfile)
+ {
+ if (isComplexStructOfType(t1b, t2b))
+ {
+ return Lret(e1.type.toBasetype());
+ }
+ else if (isComplexStructOfType(t2b, t1b))
+ {
+ return Lret(e2.type.toBasetype());
+ }
+ }
+
const ty = implicitConvCommonTy(t1b.ty, t2b.ty);
if (ty != Terror)
{
void fix16997(Scope* sc, UnaExp ue)
{
if (global.params.fix16997 || sc.inCfile)
+ {
ue.e1 = integralPromotions(ue.e1, sc); // desired C-like behavor
- else
+ return;
+ }
+
+ switch (ue.e1.type.toBasetype.ty)
{
- switch (ue.e1.type.toBasetype.ty)
- {
- case Tint8:
- case Tuns8:
- case Tint16:
- case Tuns16:
- //case Tbool: // these operations aren't allowed on bool anyway
- case Tchar:
- case Twchar:
- case Tdchar:
- deprecation(ue.loc, "integral promotion not done for `%s`, remove '-revert=intpromote' switch or `%scast(int)(%s)`",
- ue.toChars(), EXPtoString(ue.op).ptr, ue.e1.toChars());
- break;
+ case Tint8:
+ case Tuns8:
+ case Tint16:
+ case Tuns16:
+ //case Tbool: // these operations aren't allowed on bool anyway
+ case Tchar:
+ case Twchar:
+ case Tdchar:
+ deprecation(ue.loc, "integral promotion not done for `%s`, remove '-revert=intpromote' switch or `%scast(int)(%s)`",
+ ue.toChars(), EXPtoString(ue.op).ptr, ue.e1.toChars());
+ return;
- default:
- break;
- }
+ default:
+ return;
}
}
/***********************************
- * See if both types are arrays that can be compared
+ * See if an AA key can be compared
* for equality without any casting. Return true if so.
* This is to enable comparing things like an immutable
* array with a mutable one.
*/
-extern (D) bool arrayTypeCompatibleWithoutCasting(Type t1, Type t2)
+extern (D) bool keyCompatibleWithoutCasting(Expression ekey, Type t2)
{
+ Type t1 = ekey.type;
t1 = t1.toBasetype();
t2 = t2.toBasetype();
{
if (t1.nextOf().implicitConvTo(t2.nextOf()) >= MATCH.constant || t2.nextOf().implicitConvTo(t1.nextOf()) >= MATCH.constant)
return true;
+ return false;
}
- return false;
+ if (implicitConvTo(ekey, t2) < MATCH.constant)
+ return false;
+ if (auto ts = t1.isTypeStruct())
+ return implicitConvToThroughAliasThis(ts, t2) == MATCH.nomatch;
+ if (auto tc = t1.isTypeClass())
+ return implicitConvToThroughAliasThis(tc, t2) == MATCH.nomatch;
+ return true;
}
/******************************************************************/
import dmd.mtype;
import dmd.objc;
import dmd.root.rmem;
-import dmd.target;
import dmd.visitor;
/***********************************************************
this.type = type;
}
- /****************************************
- * Fill in vtbl[] for base class based on member functions of class cd.
- * Input:
- * vtbl if !=NULL, fill it in
- * newinstance !=0 means all entries must be filled in by members
- * of cd, not members of any base classes of cd.
- * Returns:
- * true if any entries were filled in by members of cd (not exclusively
- * by base classes)
- */
- extern (C++) bool fillVtbl(ClassDeclaration cd, FuncDeclarations* vtbl, int newinstance)
- {
- bool result = false;
-
- //printf("BaseClass.fillVtbl(this='%s', cd='%s')\n", sym.toChars(), cd.toChars());
- if (vtbl)
- vtbl.setDim(sym.vtbl.length);
-
- // first entry is ClassInfo reference
- for (size_t j = sym.vtblOffset(); j < sym.vtbl.length; j++)
- {
- FuncDeclaration ifd = sym.vtbl[j].isFuncDeclaration();
-
- //printf(" vtbl[%d] is '%s'\n", j, ifd ? ifd.toChars() : "null");
- assert(ifd);
-
- // Find corresponding function in this class
- auto tf = ifd.type.toTypeFunction();
- auto fd = cd.findFunc(ifd.ident, tf);
- if (fd && !fd.isAbstract())
- {
- if (fd.toParent() == cd)
- result = true;
- }
- else
- fd = null;
- if (vtbl)
- (*vtbl)[j] = fd;
- }
- return result;
- }
-
extern (D) void copyBaseInterfaces(BaseClasses* vtblInterfaces)
{
//printf("+copyBaseInterfaces(), %s\n", sym.toChars());
if (id == Id.__sizeof || id == Id.__xalignof || id == Id._mangleof)
classError("%s `%s` illegal class name", null);
- // BUG: What if this is the wrong TypeInfo, i.e. it is nested?
- if (id.toChars()[0] == 'T')
+ final /* final to work around dscanner bug*/
+ void check(Identifier _id, ref ClassDeclaration cd)
{
- if (id == Id.TypeInfo)
+ if (id == _id)
{
if (!inObject)
classError("%s `%s` %s", msg.ptr);
- Type.dtypeinfo = this;
+ cd = this;
}
- if (id == Id.TypeInfo_Class)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfoclass = this;
- }
- if (id == Id.TypeInfo_Interface)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfointerface = this;
- }
- if (id == Id.TypeInfo_Struct)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfostruct = this;
- }
- if (id == Id.TypeInfo_Pointer)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfopointer = this;
- }
- if (id == Id.TypeInfo_Array)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfoarray = this;
- }
- if (id == Id.TypeInfo_StaticArray)
- {
- //if (!inObject)
- // Type.typeinfostaticarray.classError("%s `%s` %s", msg);
- Type.typeinfostaticarray = this;
- }
- if (id == Id.TypeInfo_AssociativeArray)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfoassociativearray = this;
- }
- if (id == Id.TypeInfo_Enum)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfoenum = this;
- }
- if (id == Id.TypeInfo_Function)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfofunction = this;
- }
- if (id == Id.TypeInfo_Delegate)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfodelegate = this;
- }
- if (id == Id.TypeInfo_Tuple)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfotypelist = this;
- }
- if (id == Id.TypeInfo_Const)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfoconst = this;
- }
- if (id == Id.TypeInfo_Invariant)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfoinvariant = this;
- }
- if (id == Id.TypeInfo_Shared)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfoshared = this;
- }
- if (id == Id.TypeInfo_Wild)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfowild = this;
- }
- if (id == Id.TypeInfo_Vector)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- Type.typeinfovector = this;
- }
- }
-
- if (id == Id.Object)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- object = this;
- }
-
- if (id == Id.Throwable)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- throwable = this;
}
- if (id == Id.Exception)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- exception = this;
- }
- if (id == Id.Error)
- {
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- errorException = this;
- }
- if (id == Id.cpp_type_info_ptr)
+ // BUG: What if this is the wrong TypeInfo, i.e. it is nested?
+ if (id.toChars()[0] == 'T')
{
- if (!inObject)
- classError("%s `%s` %s", msg.ptr);
- cpp_type_info_ptr = this;
+ check(Id.TypeInfo, Type.dtypeinfo);
+ check(Id.TypeInfo_Class, Type.typeinfoclass);
+ check(Id.TypeInfo_Interface, Type.typeinfointerface);
+ check(Id.TypeInfo_Struct, Type.typeinfostruct);
+ check(Id.TypeInfo_Pointer, Type.typeinfopointer);
+ check(Id.TypeInfo_Array, Type.typeinfoarray);
+ check(Id.TypeInfo_StaticArray, Type.typeinfostaticarray);
+ check(Id.TypeInfo_AssociativeArray, Type.typeinfoassociativearray);
+ check(Id.TypeInfo_Enum, Type.typeinfoenum);
+ check(Id.TypeInfo_Function, Type.typeinfofunction);
+ check(Id.TypeInfo_Delegate, Type.typeinfodelegate);
+ check(Id.TypeInfo_Tuple, Type.typeinfotypelist);
+ check(Id.TypeInfo_Const, Type.typeinfoconst);
+ check(Id.TypeInfo_Invariant, Type.typeinfoinvariant);
+ check(Id.TypeInfo_Shared, Type.typeinfoshared);
+ check(Id.TypeInfo_Wild, Type.typeinfowild);
+ check(Id.TypeInfo_Vector, Type.typeinfovector);
}
+ check(Id.Object, object);
+ check(Id.Throwable, throwable);
+ check(Id.Exception, exception);
+ check(Id.Error, errorException);
+ check(Id.cpp_type_info_ptr, cpp_type_info_ptr);
baseok = Baseok.none;
}
return cd;
}
- override Scope* newScope(Scope* sc)
- {
- auto sc2 = super.newScope(sc);
- if (isCOMclass())
- {
- /* This enables us to use COM objects under Linux and
- * work with things like XPCOM
- */
- sc2.linkage = target.systemLinkage();
- }
- return sc2;
- }
-
/*********************************************
* Determine if 'this' is a base class of cd.
* This is used to detect circular inheritance only.
return classKind == ClassKind.d;
}
- /****************
- * Find virtual function matching identifier and type.
- * Used to build virtual function tables for interface implementations.
- * Params:
- * ident = function's identifier
- * tf = function's type
- * Returns:
- * function symbol if found, null if not
- * Errors:
- * prints error message if more than one match
- */
- extern (D) final FuncDeclaration findFunc(Identifier ident, TypeFunction tf)
- {
- //printf("ClassDeclaration.findFunc(%s, %s) %s\n", ident.toChars(), tf.toChars(), toChars());
- FuncDeclaration fdmatch = null;
- FuncDeclaration fdambig = null;
-
- void updateBestMatch(FuncDeclaration fd)
- {
- fdmatch = fd;
- fdambig = null;
- //printf("Lfd fdmatch = %s %s [%s]\n", fdmatch.toChars(), fdmatch.type.toChars(), fdmatch.loc.toChars());
- }
-
- void searchVtbl(ref Dsymbols vtbl)
- {
- import dmd.typesem : covariant;
- bool seenInterfaceVirtual;
- foreach (s; vtbl)
- {
- auto fd = s.isFuncDeclaration();
- if (!fd)
- continue;
-
- // the first entry might be a ClassInfo
- //printf("\t[%d] = %s\n", i, fd.toChars());
- if (ident != fd.ident || fd.type.covariant(tf) != Covariant.yes)
- {
- //printf("\t\t%d\n", fd.type.covariant(tf));
- continue;
- }
-
- //printf("fd.parent.isClassDeclaration() = %p\n", fd.parent.isClassDeclaration());
- if (!fdmatch)
- {
- updateBestMatch(fd);
- continue;
- }
- if (fd == fdmatch)
- continue;
-
- /* Functions overriding interface functions for extern(C++) with VC++
- * are not in the normal vtbl, but in vtblFinal. If the implementation
- * is again overridden in a child class, both would be found here.
- * The function in the child class should override the function
- * in the base class, which is done here, because searchVtbl is first
- * called for the child class. Checking seenInterfaceVirtual makes
- * sure, that the compared functions are not in the same vtbl.
- */
- if (fd.interfaceVirtual &&
- fd.interfaceVirtual is fdmatch.interfaceVirtual &&
- !seenInterfaceVirtual &&
- fdmatch.type.covariant(fd.type) == Covariant.yes)
- {
- seenInterfaceVirtual = true;
- continue;
- }
-
- {
- // Function type matching: exact > covariant
- MATCH m1 = tf.equals(fd.type) ? MATCH.exact : MATCH.nomatch;
- MATCH m2 = tf.equals(fdmatch.type) ? MATCH.exact : MATCH.nomatch;
- if (m1 > m2)
- {
- updateBestMatch(fd);
- continue;
- }
- else if (m1 < m2)
- continue;
- }
- {
- MATCH m1 = (tf.mod == fd.type.mod) ? MATCH.exact : MATCH.nomatch;
- MATCH m2 = (tf.mod == fdmatch.type.mod) ? MATCH.exact : MATCH.nomatch;
- if (m1 > m2)
- {
- updateBestMatch(fd);
- continue;
- }
- else if (m1 < m2)
- continue;
- }
- {
- // The way of definition: non-mixin > mixin
- MATCH m1 = fd.parent.isClassDeclaration() ? MATCH.exact : MATCH.nomatch;
- MATCH m2 = fdmatch.parent.isClassDeclaration() ? MATCH.exact : MATCH.nomatch;
- if (m1 > m2)
- {
- updateBestMatch(fd);
- continue;
- }
- else if (m1 < m2)
- continue;
- }
-
- fdambig = fd;
- //printf("Lambig fdambig = %s %s [%s]\n", fdambig.toChars(), fdambig.type.toChars(), fdambig.loc.toChars());
- }
- }
-
- searchVtbl(vtbl);
- for (auto cd = this; cd; cd = cd.baseClass)
- {
- searchVtbl(cd.vtblFinal);
- }
-
- if (fdambig)
- classError("%s `%s` ambiguous virtual function `%s`", fdambig.toChars());
-
- return fdmatch;
- }
-
/****************************************
*/
final bool isCOMclass() const
return "class";
}
- /****************************************
- */
- override final void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories)
- {
- .objc.addSymbols(this, classes, categories);
- }
-
// Back end
Dsymbol vtblsym;
return id;
}
-
- override Scope* newScope(Scope* sc)
- {
- auto sc2 = super.newScope(sc);
- if (com)
- sc2.linkage = LINK.windows;
- else if (classKind == ClassKind.cpp)
- sc2.linkage = LINK.cpp;
- else if (classKind == ClassKind.objc)
- sc2.linkage = LINK.objc;
- return sc2;
- }
-
/*******************************************
* Determine if 'this' is a base class of cd.
* (Actually, if it is an interface supported by cd)
import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
-import dmd.ctorflow;
-import dmd.dclass;
-import dmd.delegatize;
-import dmd.dscope;
-import dmd.dstruct;
import dmd.dsymbol;
-import dmd.dsymbolsem : toAlias;
import dmd.dtemplate;
import dmd.errors;
+import dmd.errorsink;
import dmd.expression;
import dmd.func;
import dmd.globals;
-import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.init;
import dmd.location;
import dmd.mtype;
import dmd.common.outbuffer;
-import dmd.rootobject;
import dmd.root.filename;
import dmd.target;
-import dmd.tokens;
-import dmd.typesem : typeSemantic, size;
+import dmd.targetcompiler;
import dmd.visitor;
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
/******************************************
*/
void ObjectNotFound(Loc loc, Identifier id)
{
global.gag = 0; // never gag the fatal error
- error(loc, "`%s` not found. object.d may be incorrectly installed or corrupt.", id.toChars());
- version (IN_LLVM)
- {
- errorSupplemental(loc, "ldc2 might not be correctly installed.");
- errorSupplemental(loc, "Please check your ldc2.conf configuration file.");
- errorSupplemental(loc, "Installation instructions can be found at http://wiki.dlang.org/LDC.");
- }
- else version (MARS)
- {
- errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions.");
- const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found";
- errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr);
- }
+ const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found";
+
+ mixin HostObjectNotFound;
+ hostObjectNotFound(loc, id.toChars(), dmdConfFile, global.errorSink); // print host-specific diagnostic
fatal();
}
return "declaration";
}
- override final ulong size(Loc loc)
- {
- assert(type);
- const sz = type.size();
- if (sz == SIZE_INVALID)
- errors = true;
- return sz;
- }
-
final bool isStatic() const pure nothrow @nogc @safe
{
return (storage_class & STC.static_) != 0;
return "sequence";
}
- override Type getType()
- {
- /* If this tuple represents a type, return that type
- */
-
- //printf("TupleDeclaration::getType() %s\n", toChars());
- if (isexp || building)
- return null;
- if (!tupletype)
- {
- /* It's only a type tuple if all the Object's are types
- */
- for (size_t i = 0; i < objects.length; i++)
- {
- RootObject o = (*objects)[i];
- if (!o.isType())
- {
- //printf("\tnot[%d], %p, %d\n", i, o, o.dyncast());
- return null;
- }
- }
-
- /* We know it's a type tuple, so build the TypeTuple
- */
- Types* types = cast(Types*)objects;
- auto args = new Parameters(objects.length);
- OutBuffer buf;
- int hasdeco = 1;
- for (size_t i = 0; i < types.length; i++)
- {
- Type t = (*types)[i];
- //printf("type = %s\n", t.toChars());
- version (none)
- {
- buf.printf("_%s_%d", ident.toChars(), i);
- auto id = Identifier.idPool(buf.extractSlice());
- auto arg = new Parameter(Loc.initial, STC.in_, t, id, null);
- }
- else
- {
- auto arg = new Parameter(Loc.initial, STC.none, t, null, null, null);
- }
- (*args)[i] = arg;
- if (!t.deco)
- hasdeco = 0;
- }
-
- tupletype = new TypeTuple(args);
- if (hasdeco)
- return tupletype.typeSemantic(Loc.initial, null);
- }
- return tupletype;
- }
-
override bool needThis()
{
//printf("TupleDeclaration::needThis(%s)\n", toChars());
return sa;
}
- override bool overloadInsert(Dsymbol s)
- {
- //printf("[%s] AliasDeclaration::overloadInsert('%s') s = %s %s @ [%s]\n",
- // loc.toChars(), toChars(), s.kind(), s.toChars(), s.loc.toChars());
-
- /** Aliases aren't overloadable themselves, but if their Aliasee is
- * overloadable they are converted to an overloadable Alias (either
- * FuncAliasDeclaration or OverDeclaration).
- *
- * This is done by moving the Aliasee into such an overloadable alias
- * which is then used to replace the existing Aliasee. The original
- * Alias (_this_) remains a useless shell.
- *
- * This is a horrible mess. It was probably done to avoid replacing
- * existing AST nodes and references, but it needs a major
- * simplification b/c it's too complex to maintain.
- *
- * A simpler approach might be to merge any colliding symbols into a
- * simple Overload class (an array) and then later have that resolve
- * all collisions.
- */
- if (semanticRun >= PASS.semanticdone)
- {
- /* Semantic analysis is already finished, and the aliased entity
- * is not overloadable.
- */
- if (type)
- {
- /*
- If type has been resolved already we could
- still be inserting an alias from an import.
-
- If we are handling an alias then pretend
- it was inserting and return true, if not then
- false since we didn't even pretend to insert something.
- */
- return this._import && this.equals(s);
- }
-
- // https://issues.dlang.org/show_bug.cgi?id=23865
- // only insert if the symbol can be part of a set
- const s1 = s.toAlias();
- const isInsertCandidate = s1.isFuncDeclaration() || s1.isOverDeclaration() || s1.isTemplateDeclaration();
-
- /* When s is added in member scope by static if, mixin("code") or others,
- * aliassym is determined already. See the case in: test/compilable/test61.d
- */
- auto sa = aliassym.toAlias();
-
- if (auto td = s.toAlias().isTemplateDeclaration())
- s = td.funcroot ? td.funcroot : td;
-
- if (auto fd = sa.isFuncDeclaration())
- {
- auto fa = new FuncAliasDeclaration(ident, fd);
- fa.visibility = visibility;
- fa.parent = parent;
- aliassym = fa;
- if (isInsertCandidate)
- return aliassym.overloadInsert(s);
- }
- if (auto td = sa.isTemplateDeclaration())
- {
- auto od = new OverDeclaration(ident, td.funcroot ? td.funcroot : td);
- od.visibility = visibility;
- od.parent = parent;
- aliassym = od;
- if (isInsertCandidate)
- return aliassym.overloadInsert(s);
- }
- if (auto od = sa.isOverDeclaration())
- {
- if (sa.ident != ident || sa.parent != parent)
- {
- od = new OverDeclaration(ident, od);
- od.visibility = visibility;
- od.parent = parent;
- aliassym = od;
- }
- if (isInsertCandidate)
- return od.overloadInsert(s);
- }
- if (auto os = sa.isOverloadSet())
- {
- if (sa.ident != ident || sa.parent != parent)
- {
- os = new OverloadSet(ident, os);
- // TODO: visibility is lost here b/c OverloadSets have no visibility attribute
- // Might no be a practical issue, b/c the code below fails to resolve the overload anyhow.
- // ----
- // module os1;
- // import a, b;
- // private alias merged = foo; // private alias to overload set of a.foo and b.foo
- // ----
- // module os2;
- // import a, b;
- // public alias merged = bar; // public alias to overload set of a.bar and b.bar
- // ----
- // module bug;
- // import os1, os2;
- // void test() { merged(123); } // should only look at os2.merged
- //
- // os.visibility = visibility;
- os.parent = parent;
- aliassym = os;
- }
- if (isInsertCandidate)
- {
- os.push(s);
- return true;
- }
- }
- return false;
- }
-
- /* Don't know yet what the aliased symbol is, so assume it can
- * be overloaded and check later for correctness.
- */
- if (overnext)
- return overnext.overloadInsert(s);
- if (s is this)
- return true;
- overnext = s;
- return true;
- }
-
override const(char)* kind() const
{
return "alias";
}
- override Type getType()
- {
- if (type)
- return type;
- return toAlias(this).getType();
- }
-
override bool isOverloadable() const
{
// assume overloadable until alias is resolved
return "overload alias"; // todo
}
- override bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
-
- auto s = isDsymbol(o);
- if (!s)
- return false;
-
- if (auto od2 = s.isOverDeclaration())
- return this.aliassym.equals(od2.aliassym);
- return this.aliassym == s;
- }
-
- override bool overloadInsert(Dsymbol s)
- {
- //printf("OverDeclaration::overloadInsert('%s') aliassym = %p, overnext = %p\n", s.toChars(), aliassym, overnext);
- if (overnext)
- return overnext.overloadInsert(s);
- if (s == this)
- return true;
- overnext = s;
- return true;
- }
-
override bool isOverloadable() const
{
return true;
bool isCmacro; /// it is a C macro turned into a C declaration
bool dllImport; /// __declspec(dllimport)
bool dllExport; /// __declspec(dllexport)
- version (MARS)
- {
- bool inClosure; /// is inserted into a GC allocated closure
- bool inAlignSection; /// is inserted into an aligned section on stack
- }
+ mixin VarDeclarationExtra;
bool systemInferred; /// @system was inferred from initializer
}
override final inout(AggregateDeclaration) isThis() inout
{
- if (!(storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.gshared | STC.ctfe)))
+ if (storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.gshared | STC.ctfe))
+ return null;
+
+ /* The casting is necessary because `s = s.parent` is otherwise rejected
+ */
+ for (auto s = cast(Dsymbol)this; s; s = s.parent)
{
- /* The casting is necessary because `s = s.parent` is otherwise rejected
- */
- for (auto s = cast(Dsymbol)this; s; s = s.parent)
- {
- if (auto ad = (cast(inout)s).isMember())
- return ad;
- if (!s.parent || !s.parent.isTemplateMixin())
- break;
- }
+ if (auto ad = (cast(inout)s).isMember())
+ return ad;
+ if (!s.parent || !s.parent.isTemplateMixin())
+ break;
}
+
return null;
}
return (storage_class & STC.ctfe) != 0; // || !isDataseg();
}
- final bool isOverlappedWith(VarDeclaration v)
- {
- const vsz = v.type.size();
- const tsz = type.size();
- assert(vsz != SIZE_INVALID && tsz != SIZE_INVALID);
-
- // Overlap is checked by comparing bit offsets
- auto bitoffset = offset * 8;
- auto vbitoffset = v.offset * 8;
-
- // Bitsize of types are overridden by any bitfield widths.
- ulong tbitsize;
- if (auto bf = isBitFieldDeclaration())
- {
- bitoffset += bf.bitOffset;
- tbitsize = bf.fieldWidth;
- }
- else
- tbitsize = tsz * 8;
-
- ulong vbitsize;
- if (auto vbf = v.isBitFieldDeclaration())
- {
- vbitoffset += vbf.bitOffset;
- vbitsize = vbf.fieldWidth;
- }
- else
- vbitsize = vsz * 8;
-
- return bitoffset < vbitoffset + vbitsize &&
- vbitoffset < bitoffset + tbitsize;
- }
-
/*************************************
* Return true if we can take the address of this variable.
*/
{
v.visit(this);
}
-
- /***********************************
- * Retrieve the .min or .max values.
- * Only valid after semantic analysis.
- * Params:
- * id = Id.min or Id.max
- * Returns:
- * the min or max value
- */
- final ulong getMinMax(Identifier id)
- {
- const width = fieldWidth;
- const uns = type.isUnsigned();
- const min = id == Id.min;
- ulong v;
- assert(width != 0); // should have been rejected in semantic pass
- if (width == ulong.sizeof * 8)
- v = uns ? (min ? ulong.min : ulong.max)
- : (min ? long.min : long.max);
- else
- v = uns ? (min ? 0
- : (1L << width) - 1)
- : (min ? -(1L << (width - 1))
- : (1L << (width - 1)) - 1);
- return v;
- }
}
/***********************************************************
bool checkClosure(FuncDeclaration* fd);
MATCH leastAsSpecialized(FuncDeclaration *f, FuncDeclaration *g, ArgumentLabels *names);
PURE isPure(FuncDeclaration *f);
+ bool needsClosure(FuncDeclaration *fd);
FuncDeclaration *genCfunc(Parameters *args, Type *treturn, const char *name, StorageClass stc=0);
FuncDeclaration *genCfunc(Parameters *args, Type *treturn, Identifier *id, StorageClass stc=0);
bool isAbstract(ClassDeclaration *cd);
+ bool overloadInsert(Dsymbol *ds, Dsymbol *s);
+ bool equals(const Dsymbol * const ds, const Dsymbol * const s);
}
//enum STC : ulong from astenums.d:
bool noUnderscore() const;
const char *kind() const override;
- uinteger_t size(Loc loc) override final;
-
bool isStatic() const { return (storage_class & STCstatic) != 0; }
LINK resolvedLinkage() const; // returns the linkage, resolving the target-specific `System` one
TupleDeclaration *syntaxCopy(Dsymbol *) override;
const char *kind() const override;
- Type *getType() override;
bool needThis() override;
void accept(Visitor *v) override { v->visit(this); }
static AliasDeclaration *create(Loc loc, Identifier *id, Type *type);
AliasDeclaration *syntaxCopy(Dsymbol *) override;
- bool overloadInsert(Dsymbol *s) override;
const char *kind() const override;
- Type *getType() override;
bool isOverloadable() const override;
void accept(Visitor *v) override { v->visit(this); }
Dsymbol *aliassym;
const char *kind() const override;
- bool equals(const RootObject * const o) const override;
- bool overloadInsert(Dsymbol *s) override;
Dsymbol *isUnique();
bool isOverloadable() const override;
bool isDataseg() override final;
bool isThreadlocal() override final;
bool isCTFE();
- bool isOverlappedWith(VarDeclaration *v);
bool canTakeAddressOf();
bool needsScopeDtor();
// Eliminate need for dynamic_cast
AttributeViolation* pureViolation;
AttributeViolation* nothrowViolation;
+ void* parametersDFAInfo;
+
// Formerly FUNCFLAGS
uint32_t flags;
bool purityInprocess() const;
FuncDeclaration *fdensure(FuncDeclaration *fde);
Expressions *fdrequireParams(Expressions *fdrp);
Expressions *fdensureParams(Expressions *fdep);
- bool equals(const RootObject * const o) const override final;
- bool overloadInsert(Dsymbol *s) override;
bool inUnittest();
LabelDsymbol *searchLabel(Identifier *ident, Loc loc);
const char *toPrettyChars(bool QualifyTypes = false) override;
virtual bool addPreInvariant();
virtual bool addPostInvariant();
const char *kind() const override;
- bool needsClosure();
bool hasNestedFrameRefs();
ParameterList getParameterList();
bool isVirtual() const override;
bool addPreInvariant() override;
bool addPostInvariant() override;
- bool overloadInsert(Dsymbol *s) override;
void accept(Visitor *v) override { v->visit(this); }
};
bool isVirtual() const override;
bool addPreInvariant() override;
bool addPostInvariant() override;
- bool overloadInsert(Dsymbol *s) override;
void accept(Visitor *v) override { v->visit(this); }
};
import dmd.declaration;
import dmd.dscope;
import dmd.dsymbol;
+import dmd.dsymbolsem : toParentP;
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
return ed;
}
- override Type getType()
- {
- return type;
- }
-
override const(char)* kind() const
{
return "enum";
scopesym.addAccessiblePackage(mod, visibility); // d
}
- override bool overloadInsert(Dsymbol s)
- {
- /* Allow multiple imports with the same package base, but disallow
- * alias collisions
- * https://issues.dlang.org/show_bug.cgi?id=5412
- */
- assert(ident && ident == s.ident);
- if (aliasId)
- return false;
- const imp = s.isImport();
- return imp && !imp.aliasId;
- }
-
override void accept(Visitor v)
{
v.visit(this);
import dmd.rootobject;
import dmd.root.utf;
import dmd.statement;
+import dmd.semantic2 : findFunc;
import dmd.tokens;
-import dmd.typesem : mutableOf, equivalent, pointerTo, sarrayOf, arrayOf, size, merge, defaultInitLiteral;
+import dmd.typesem;
import dmd.utils : arrayCastBigEndian;
import dmd.visitor;
result = CTFEExp.voidexp;
return;
}
- else if (isArrayConstruction(fd.ident))
- {
- // In expressionsem.d, `T[x] ea = eb;` was lowered to:
- // `_d_array{,set}ctor(ea[], eb[]);`.
- // The following code will rewrite it back to `ea = eb` and
- // then interpret that expression.
-
- if (fd.ident == Id._d_arrayctor)
- assert(e.arguments.length == 3);
- else
- assert(e.arguments.length == 2);
-
- Expression ea = (*e.arguments)[0];
- if (ea.isCastExp)
- ea = ea.isCastExp.e1;
-
- Expression eb = (*e.arguments)[1];
- if (eb.isCastExp() && fd.ident == Id._d_arrayctor)
- eb = eb.isCastExp.e1;
-
- ConstructExp ce = new ConstructExp(e.loc, ea, eb);
- ce.type = ea.type;
-
- ce.type = ea.type;
- result = interpret(ce, istate);
-
- return;
- }
}
else if (auto soe = ecall.isSymOffExp())
{
import dmd.compiler;
import dmd.cparse;
import dmd.declaration;
-import dmd.dimport;
import dmd.dmacro;
import dmd.doc;
-import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem : dsymbolSemantic;
import dmd.errors;
-import dmd.errorsink;
import dmd.expression;
import dmd.file_manager;
import dmd.func;
import dmd.identifier;
import dmd.location;
import dmd.parse;
-import dmd.root.aav;
import dmd.root.array;
import dmd.root.file;
import dmd.root.filename;
import dmd.root.port;
-import dmd.root.rmem;
import dmd.root.string;
-import dmd.rootobject;
-import dmd.semantic2;
-import dmd.semantic3;
import dmd.target;
-import dmd.utils;
import dmd.visitor;
version (Windows)
else
static assert(0);
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
-
-// function used to call semantic3 on a module's dependencies
-void semantic3OnDependencies(Module m)
-{
- if (!m)
- return;
-
- if (m.semanticRun > PASS.semantic3)
- return;
-
- m.semantic3(null);
-
- foreach (i; 1 .. m.aimports.length)
- semantic3OnDependencies(m.aimports[i]);
-}
-
/**
* Remove generated .di files on error and exit
*/
return "package";
}
- override bool equals(const RootObject o) const
- {
- // custom 'equals' for bug 17441. "package a" and "module a" are not equal
- if (this == o)
- return true;
- auto p = cast(Package)o;
- return p && isModule() == p.isModule() && ident.equals(p.ident);
- }
-
/****************************************************
* Input:
* packages[] the pkg1.pkg2 of pkg1.pkg2.mod
if (doHdrGen)
hdrfile = setOutfilename(global.params.dihdr.name, global.params.dihdr.dir, arg, hdr_ext);
- this.edition = Edition.min;
+ this.edition = global.params.edition;
}
extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
p.nextToken();
checkCompiledImport();
members = p.parseModule();
- assert(!p.md); // C doesn't have module declarations
+ md = p.md;
+ if (md)
+ {
+ this.ident = md.id;
+ dst = Package.resolve(md.packages, &this.parent, &ppack);
+ }
+
numlines = p.linnum;
}
else
File.remove(docfile.toChars());
}
- /*******************************************
- * Can't run semantic on s now, try again later.
- */
- extern (D) static void addDeferredSemantic(Dsymbol s)
- {
- //printf("Module::addDeferredSemantic('%s')\n", s.toChars());
- if (!s.deferred)
- {
- s.deferred = true;
- deferred.push(s);
- }
- }
-
- extern (D) static void addDeferredSemantic2(Dsymbol s)
- {
- //printf("Module::addDeferredSemantic2('%s')\n", s.toChars());
- if (!s.deferred2)
- {
- s.deferred2 = true;
- deferred2.push(s);
- }
- }
-
- extern (D) static void addDeferredSemantic3(Dsymbol s)
- {
- //printf("Module::addDeferredSemantic3('%s')\n", s.toChars());
- if (!s.deferred3)
- {
- s.deferred3 = true;
- deferred3.push(s);
- }
- }
-
- /******************************************
- * Run semantic() on deferred symbols.
- */
- static void runDeferredSemantic()
- {
- __gshared int nested;
- if (nested)
- return;
- //if (deferred.length) printf("+Module::runDeferredSemantic(), len = %ld\n", deferred.length);
- nested++;
-
- size_t len;
- do
- {
- len = deferred.length;
- if (!len)
- break;
-
- Dsymbol* todo;
- Dsymbol* todoalloc = null;
- Dsymbol tmp;
- if (len == 1)
- {
- todo = &tmp;
- }
- else
- {
- todo = cast(Dsymbol*)Mem.check(malloc(len * Dsymbol.sizeof));
- todoalloc = todo;
- }
- memcpy(todo, deferred.tdata(), len * Dsymbol.sizeof);
- foreach (Dsymbol s; Module.deferred[])
- s.deferred = false;
- deferred.setDim(0);
-
- foreach (i; 0..len)
- {
- Dsymbol s = todo[i];
- s.dsymbolSemantic(null);
- //printf("deferred: %s, parent = %s\n", s.toChars(), s.parent.toChars());
- }
- //printf("\tdeferred.length = %ld, len = %ld\n", deferred.length, len);
- if (todoalloc)
- free(todoalloc);
- }
- while (deferred.length != len); // while making progress
- nested--;
- //printf("-Module::runDeferredSemantic(), len = %ld\n", deferred.length);
- }
-
- static void runDeferredSemantic2()
- {
- Module.runDeferredSemantic();
-
- Dsymbols* a = &Module.deferred2;
- for (size_t i = 0; i < a.length; i++)
- {
- Dsymbol s = (*a)[i];
- s.deferred2 = false;
- //printf("[%d] %s semantic2a\n", i, s.toPrettyChars());
- s.semantic2(null);
-
- if (global.errors)
- break;
- }
- a.setDim(0);
- }
-
- static void runDeferredSemantic3()
- {
- Module.runDeferredSemantic2();
-
- Dsymbols* a = &Module.deferred3;
- for (size_t i = 0; i < a.length; i++)
- {
- Dsymbol s = (*a)[i];
- s.deferred3 = false;
- //printf("[%d] %s semantic3a\n", i, s.toPrettyChars());
- s.semantic3(null);
-
- if (global.errors)
- break;
- }
- a.setDim(0);
- }
-
extern (D) static void clearCache() nothrow
{
foreach (Module m; amodules)
return buf;
}
-
-/*******************************************
- * Look for member of the form:
- * const(MemberInfo)[] getMembers(string);
- * Returns NULL if not found
- */
-FuncDeclaration findGetMembers(ScopeDsymbol dsym)
-{
- import dmd.opover : search_function;
- Dsymbol s = search_function(dsym, Id.getmembers);
- FuncDeclaration fdx = s ? s.isFuncDeclaration() : null;
- version (none)
- {
- // Finish
- __gshared TypeFunction tfgetmembers;
- if (!tfgetmembers)
- {
- Scope sc;
- sc.eSink = global.errorSink;
- Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null);
- auto parameters = new Parameters(p);
- Type tret = null;
- TypeFunction tf = new TypeFunction(parameters, tret, VarArg.none, LINK.d);
- tfgetmembers = tf.dsymbolSemantic(Loc.initial, &sc).isTypeFunction();
- }
- if (fdx)
- fdx = fdx.overloadExactMatch(tfgetmembers);
- }
- if (fdx && fdx.isVirtual())
- fdx = null;
- return fdx;
-}
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dsymbolsem;
+import dmd.templatesem : computeOneMember;
import dmd.dtemplate;
import dmd.errorsink;
import dmd.func;
import dmd.location;
import dmd.mtype;
import dmd.root.array;
-import dmd.root.file;
-import dmd.root.filename;
import dmd.common.outbuffer;
import dmd.root.port;
import dmd.root.rmem;
import dmd.tokens;
import dmd.visitor;
-private:
+/****************************************************
+ * Generate Ddoc text for Module `m` and append it to `outbuf`.
+ * Params:
+ * m = Module
+ * ddoctext = combined text of .ddoc files for macro definitions
+ * datetime = charz returned by ctime()
+ * eSink = send error messages to eSink
+ * outbuf = append the Ddoc text to this
+ */
+public
+void gendocfile(Module m, const char[] ddoctext, const char* datetime, ErrorSink eSink, ref OutBuffer outbuf)
+{
+ // Load internal default macros first
+ DocComment.parseMacros(m.escapetable, m.macrotable, ddoc_default[]);
+
+ // Ddoc files override default macros
+ DocComment.parseMacros(m.escapetable, m.macrotable, ddoctext);
+
+ Scope* sc = scopeCreateGlobal(m, eSink); // create root scope
+ DocComment* dc = DocComment.parse(m, m.comment);
+ dc.pmacrotable = &m.macrotable;
+ dc.escapetable = m.escapetable;
+ sc.lastdc = dc;
+ // Generate predefined macros
+ // Set the title to be the name of the module
+ {
+ const p = m.toPrettyChars().toDString;
+ m.macrotable.define("TITLE", p);
+ }
+ // Set time macros
+ m.macrotable.define("DATETIME", datetime[0 .. 26]);
+ m.macrotable.define("YEAR", datetime[20 .. 20 + 4]);
+
+ const srcfilename = m.srcfile.toString();
+ m.macrotable.define("SRCFILENAME", srcfilename);
+ const docfilename = m.docfile.toString();
+ m.macrotable.define("DOCFILENAME", docfilename);
+ if (dc.copyright)
+ {
+ dc.copyright.nooutput = 1;
+ m.macrotable.define("COPYRIGHT", dc.copyright.body_);
+ }
+
+ OutBuffer buf;
+ if (m.filetype == FileType.ddoc)
+ {
+ Loc loc = m.md ? m.md.loc : m.loc;
+
+ size_t commentlen = m.comment ? strlen(cast(char*)m.comment) : 0;
+ Dsymbols a;
+ // https://issues.dlang.org/show_bug.cgi?id=9764
+ // Don't push m in a, to prevent emphasize ddoc file name.
+ if (dc.macros)
+ {
+ commentlen = dc.macros.name.ptr - m.comment;
+ dc.macros.write(loc, dc, sc, &a, buf);
+ }
+ buf.write(m.comment[0 .. commentlen]);
+ highlightText(sc, &a, loc, buf, 0);
+ }
+ else
+ {
+ Dsymbols a;
+ a.push(m);
+ dc.writeSections(sc, &a, buf);
+ emitMemberComments(m, buf, sc);
+ }
+ //printf("BODY= '%.*s'\n", cast(int)buf.length, buf.data);
+ m.macrotable.define("BODY", buf[]);
+
+ OutBuffer buf2;
+ buf2.writestring("$(DDOC)");
+ size_t end = buf2.length;
+
+ // Expand buf in place with macro expansions
+ const success = m.macrotable.expand(buf2, 0, end, null, global.recursionLimit, &isIdStart, &isIdTail);
+ if (!success)
+ eSink.error(Loc.initial, "DDoc macro expansion limit exceeded; more than %d expansions.", global.recursionLimit);
+
+ /* Remove all the escape sequences from buf,
+ * and make CR-LF the newline.
+ */
+ const slice = buf2[];
+ outbuf.reserve(slice.length);
+ auto p = slice.ptr;
+ for (size_t j = 0; j < slice.length; j++)
+ {
+ const c = p[j];
+ if (c == 0xFF && j + 1 < slice.length)
+ {
+ j++;
+ continue;
+ }
+ if (c == '\n')
+ outbuf.writeByte('\r');
+ else if (c == '\r')
+ {
+ outbuf.writestring("\r\n");
+ if (j + 1 < slice.length && p[j + 1] == '\n')
+ {
+ j++;
+ }
+ continue;
+ }
+ outbuf.writeByte(c);
+ }
+}
+
+/****************************************************
+ * Having unmatched parentheses can hose the output of Ddoc,
+ * as the macros depend on properly nested parentheses.
+ * This function replaces all ( with $(LPAREN) and ) with $(RPAREN)
+ * to preserve text literally. This also means macros in the
+ * text won't be expanded.
+ */
+public
+void escapeDdocString(ref OutBuffer buf, size_t start)
+{
+ for (size_t u = start; u < buf.length; u++)
+ {
+ const c = buf[u];
+ switch (c)
+ {
+ case '$':
+ buf.remove(u, 1);
+ buf.insert(u, "$(DOLLAR)");
+ u += 8;
+ break;
+ case '(':
+ buf.remove(u, 1); //remove the (
+ buf.insert(u, "$(LPAREN)"); //insert this instead
+ u += 8; //skip over newly inserted macro
+ break;
+ case ')':
+ buf.remove(u, 1); //remove the )
+ buf.insert(u, "$(RPAREN)"); //insert this instead
+ u += 8; //skip over newly inserted macro
+ break;
+ default:
+ break;
+ }
+ }
+}
+/****************************************************
+ * Generate Ddoc file for Module m.
+ * Params:
+ * m = Module
+ * ddoctext_ptr = combined text of .ddoc files for macro definitions
+ * ddoctext_length = extant of ddoctext_ptr
+ * datetime = charz returned by ctime()
+ * eSink = send error messages to eSink
+ * outbuf = append the Ddoc text to this
+ */
+public
+void gendocfile(Module m, const char* ddoctext_ptr, size_t ddoctext_length, const char* datetime, ErrorSink eSink, ref OutBuffer outbuf)
+{
+ gendocfile(m, ddoctext_ptr[0 .. ddoctext_length], datetime, eSink, outbuf);
+}
public
struct Escape
/***********************************************************
*/
-class Section
+public
+struct DocComment
{
- const(char)[] name;
- const(char)[] body_;
- int nooutput;
-
- override string toString() const
- {
- assert(0);
- }
+ Sections sections; // Section*[]
+ Section summary;
+ Section copyright;
+ Section macros;
+ MacroTable* pmacrotable;
+ Escape* escapetable;
+ Dsymbols a;
- void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
+ static DocComment* parse(Dsymbol s, const(char)* comment)
{
- assert(a.length);
- if (name.length)
+ //printf("parse(%s): '%s'\n", s.toChars(), comment);
+ auto dc = new DocComment();
+ dc.a.push(s);
+ if (!comment)
+ return dc;
+ dc.parseSections(comment);
+ for (size_t i = 0; i < dc.sections.length; i++)
{
- static immutable table =
- [
- "AUTHORS",
- "BUGS",
- "COPYRIGHT",
- "DATE",
- "DEPRECATED",
- "EXAMPLES",
- "HISTORY",
- "LICENSE",
- "RETURNS",
- "SEE_ALSO",
- "STANDARDS",
- "THROWS",
- "VERSION",
- ];
- foreach (entry; table)
+ Section sec = dc.sections[i];
+ if (iequals("copyright", sec.name))
{
- if (iequals(entry, name))
- {
- buf.printf("$(DDOC_%s ", entry.ptr);
- goto L1;
- }
+ dc.copyright = sec;
+ }
+ if (iequals("macros", sec.name))
+ {
+ dc.macros = sec;
}
- buf.writestring("$(DDOC_SECTION ");
- // Replace _ characters with spaces
- buf.writestring("$(DDOC_SECTION_H ");
- size_t o = buf.length;
- foreach (char c; name)
- buf.writeByte((c == '_') ? ' ' : c);
- escapeStrayParenthesis(loc, buf, o, false, sc.eSink);
- buf.writestring(")");
- }
- else
- {
- buf.writestring("$(DDOC_DESCRIPTION ");
}
- L1:
- size_t o = buf.length;
- buf.write(body_);
- escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
- highlightText(sc, a, loc, buf, o);
- buf.writestring(")");
+ return dc;
}
-}
-/***********************************************************
- */
-final class ParamSection : Section
-{
- override void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
+ /************************************************
+ * Parse macros out of Macros: section.
+ * Macros are of the form:
+ * name1 = value1
+ *
+ * name2 = value2
+ */
+ extern(D) static void parseMacros(
+ Escape* escapetable, ref MacroTable pmacrotable, const(char)[] m)
{
- assert(a.length);
- Dsymbol s = (*a)[0]; // test
- const(char)* p = body_.ptr;
- size_t len = body_.length;
+ const(char)* p = m.ptr;
+ size_t len = m.length;
const(char)* pend = p + len;
const(char)* tempstart = null;
size_t templen = 0;
size_t namelen = 0; // !=0 if line continuation
const(char)* textstart = null;
size_t textlen = 0;
- size_t paramcount = 0;
- buf.writestring("$(DDOC_PARAMS ");
while (p < pend)
{
// Skip to start of macro
while (1)
{
+ if (p >= pend)
+ goto Ldone;
switch (*p)
{
case ' ':
case '\t':
p++;
continue;
+ case '\r':
case '\n':
p++;
goto Lcont;
default:
- if (isIdStart(p) || isCVariadicArg(p[0 .. cast(size_t)(pend - p)]))
+ if (isIdStart(p))
break;
if (namelen)
- goto Ltext;
- // continuation of prev macro
+ goto Ltext; // continuation of prev macro
goto Lskipline;
}
break;
}
tempstart = p;
- while (isIdTail(p))
+ while (1)
+ {
+ if (p >= pend)
+ goto Ldone;
+ if (!isIdTail(p))
+ break;
p += utfStride(p);
- if (isCVariadicArg(p[0 .. cast(size_t)(pend - p)]))
- p += 3;
+ }
templen = p - tempstart;
- while (*p == ' ' || *p == '\t')
+ while (1)
+ {
+ if (p >= pend)
+ goto Ldone;
+ if (!(*p == ' ' || *p == '\t'))
+ break;
p++;
+ }
if (*p != '=')
{
if (namelen)
- goto Ltext;
- // continuation of prev macro
+ goto Ltext; // continuation of prev macro
goto Lskipline;
}
p++;
+ if (p >= pend)
+ goto Ldone;
if (namelen)
{
- // Output existing param
+ // Output existing macro
L1:
- //printf("param '%.*s' = '%.*s'\n", cast(int)namelen, namestart, cast(int)textlen, textstart);
- ++paramcount;
- HdrGenState hgs;
- buf.writestring("$(DDOC_PARAM_ROW ");
- {
- buf.writestring("$(DDOC_PARAM_ID ");
- {
- size_t o = buf.length;
- Parameter fparam = isFunctionParameter(a, namestart[0 .. namelen]);
- if (!fparam)
- {
- // Comments on a template might refer to function parameters within.
- // Search the parameters of nested eponymous functions (with the same name.)
- fparam = isEponymousFunctionParameter(a, namestart[0 .. namelen]);
- }
- bool isCVariadic = isCVariadicParameter(a, namestart[0 .. namelen]);
- if (isCVariadic)
- {
- buf.writestring("...");
- }
- else if (fparam && fparam.type && fparam.ident)
- {
- toCBuffer(fparam.type, buf, fparam.ident, hgs);
- }
- else
- {
- if (isTemplateParameter(a, namestart, namelen))
- {
- // 10236: Don't count template parameters for params check
- --paramcount;
- }
- else if (!fparam)
- {
- sc.eSink.warning(s.loc, "Ddoc: function declaration has no parameter '%.*s'", cast(int)namelen, namestart);
- }
- buf.write(namestart[0 .. namelen]);
- }
- escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
- highlightCode(sc, a, buf, o);
- }
- buf.writestring(")");
- buf.writestring("$(DDOC_PARAM_DESC ");
- {
- size_t o = buf.length;
- buf.write(textstart[0 .. textlen]);
- escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
- highlightText(sc, a, loc, buf, o);
- }
- buf.writestring(")");
- }
- buf.writestring(")");
+ //printf("macro '%.*s' = '%.*s'\n", cast(int)namelen, namestart, cast(int)textlen, textstart);
+ if (iequals("ESCAPES", namestart[0 .. namelen]))
+ parseEscapes(escapetable, textstart[0 .. textlen]);
+ else
+ pmacrotable.define(namestart[0 .. namelen], textstart[0 .. textlen]);
namelen = 0;
if (p >= pend)
break;
}
namestart = tempstart;
namelen = templen;
- while (*p == ' ' || *p == '\t')
+ while (p < pend && (*p == ' ' || *p == '\t'))
p++;
textstart = p;
Ltext:
- while (*p != '\n')
+ while (p < pend && *p != '\r' && *p != '\n')
p++;
textlen = p - textstart;
p++;
+ //printf("p = %p, pend = %p\n", p, pend);
Lcont:
continue;
Lskipline:
// Ignore this line
- while (*p++ != '\n')
- {
- }
+ while (p < pend && *p != '\r' && *p != '\n')
+ p++;
}
+ Ldone:
if (namelen)
- goto L1;
- // write out last one
- buf.writestring(")");
- TypeFunction tf = a.length == 1 ? isTypeFunction(s) : null;
- if (tf)
+ goto L1; // write out last one
+ }
+
+ /**************************************
+ * Parse escapes of the form:
+ * /c/string/
+ * where c is a single character.
+ * Multiple escapes can be separated
+ * by whitespace and/or commas.
+ */
+ static void parseEscapes(Escape* escapetable, const(char)[] text)
+ {
+ if (!escapetable)
{
- size_t pcount = (tf.parameterList.parameters ? tf.parameterList.parameters.length : 0) +
- cast(int)(tf.parameterList.varargs == VarArg.variadic);
- if (pcount != paramcount)
+ escapetable = new Escape();
+ memset(escapetable, 0, Escape.sizeof);
+ }
+ //printf("parseEscapes('%.*s') pescapetable = %p\n", cast(int)text.length, text.ptr, escapetable);
+ const(char)* p = text.ptr;
+ const(char)* pend = p + text.length;
+ while (1)
+ {
+ while (1)
{
- sc.eSink.warning(s.loc, "Ddoc: parameter count mismatch, expected %llu, got %llu",
- cast(ulong) pcount, cast(ulong) paramcount);
- if (paramcount == 0)
- {
- // Chances are someone messed up the format
- sc.eSink.warningSupplemental(s.loc, "Note that the format is `param = description`");
- }
+ if (p + 4 >= pend)
+ return;
+ if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ','))
+ break;
+ p++;
}
+ if (p[0] != '/' || p[2] != '/')
+ return;
+ char c = p[1];
+ p += 3;
+ const(char)* start = p;
+ while (1)
+ {
+ if (p >= pend)
+ return;
+ if (*p == '/')
+ break;
+ p++;
+ }
+ size_t len = p - start;
+ char* s = cast(char*)memcpy(mem.xmalloc(len + 1), start, len);
+ s[len] = 0;
+ escapetable.strings[c] = s[0 .. len];
+ //printf("\t%c = '%s'\n", c, s);
+ p++;
}
}
-}
-/***********************************************************
- */
-final class MacroSection : Section
-{
- override void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
+ /*****************************************
+ * Parse next paragraph out of *pcomment.
+ * Update *pcomment to point past paragraph.
+ * Returns NULL if no more paragraphs.
+ * If paragraph ends in 'identifier:',
+ * then (*pcomment)[0 .. idlen] is the identifier.
+ */
+ void parseSections(const(char)* comment)
{
- //printf("MacroSection::write()\n");
- DocComment.parseMacros(dc.escapetable, *dc.pmacrotable, body_);
- }
-}
+ const(char)* p;
+ const(char)* pstart;
+ const(char)* pend;
+ const(char)* idstart = null; // dead-store to prevent spurious warning
+ size_t idlen;
+ const(char)* name = null;
+ size_t namelen = 0;
+ //printf("parseSections('%s')\n", comment);
+ p = comment;
+ while (*p)
+ {
+ const(char)* pstart0 = p;
+ p = skipwhitespace(p);
+ pstart = p;
+ pend = p;
-alias Sections = Array!(Section);
+ // Undo indent if starting with a list item
+ if ((*p == '-' || *p == '+' || *p == '*') && (*(p+1) == ' ' || *(p+1) == '\t'))
+ pstart = pstart0;
+ else
+ {
+ const(char)* pitem = p;
+ while (*pitem >= '0' && *pitem <= '9')
+ ++pitem;
+ if (pitem > p && *pitem == '.' && (*(pitem+1) == ' ' || *(pitem+1) == '\t'))
+ pstart = pstart0;
+ }
-// Workaround for missing Parameter instance for variadic params. (it's unnecessary to instantiate one).
-bool isCVariadicParameter(Dsymbols* a, const(char)[] p) @safe
-{
- foreach (member; *a)
- {
- TypeFunction tf = isTypeFunction(member);
- if (tf && tf.parameterList.varargs == VarArg.variadic && p == "...")
- return true;
- }
- return false;
-}
+ /* Find end of section, which is ended by one of:
+ * 'identifier:' (but not inside a code section)
+ * '\0'
+ */
+ idlen = 0;
+ int inCode = 0;
+ while (1)
+ {
+ // Check for start/end of a code section
+ if (*p == '-' || *p == '`' || *p == '~')
+ {
+ char c = *p;
+ int numdash = 0;
+ while (*p == c)
+ {
+ ++numdash;
+ p++;
+ }
+ // BUG: handle UTF PS and LS too
+ if ((!*p || *p == '\r' || *p == '\n' || (!inCode && c != '-')) && numdash >= 3)
+ {
+ inCode = inCode == c ? false : c;
+ if (inCode)
+ {
+ // restore leading indentation
+ while (pstart0 < pstart && isIndentWS(pstart - 1))
+ --pstart;
+ }
+ }
+ pend = p;
+ }
+ if (!inCode && isIdStart(p))
+ {
+ const(char)* q = p + utfStride(p);
+ while (isIdTail(q))
+ q += utfStride(q);
-Dsymbol getEponymousMember(TemplateDeclaration td) @safe
-{
- if (!td.onemember)
- return null;
- if (AggregateDeclaration ad = td.onemember.isAggregateDeclaration())
- return ad;
- if (FuncDeclaration fd = td.onemember.isFuncDeclaration())
- return fd;
- if (auto em = td.onemember.isEnumMember())
- return null; // Keep backward compatibility. See compilable/ddoc9.d
- if (VarDeclaration vd = td.onemember.isVarDeclaration())
- return td.constraint ? null : vd;
- return null;
-}
+ // Detected tag ends it
+ if (*q == ':' && isupper(*p)
+ && (isspace(q[1]) || q[1] == 0))
+ {
+ idlen = q - p;
+ idstart = p;
+ for (pend = p; pend > pstart; pend--)
+ {
+ if (pend[-1] == '\n')
+ break;
+ }
+ p = q + 1;
+ break;
+ }
+ }
+ while (1)
+ {
+ if (!*p)
+ goto L1;
+ if (*p == '\n')
+ {
+ p++;
+ if (*p == '\n' && !summary && !namelen && !inCode)
+ {
+ pend = p;
+ p++;
+ goto L1;
+ }
+ break;
+ }
+ p++;
+ pend = p;
+ }
+ p = skipwhitespace(p);
+ }
+ L1:
+ if (namelen || pstart < pend)
+ {
+ Section s;
+ if (iequals("Params", name[0 .. namelen]))
+ s = new ParamSection();
+ else if (iequals("Macros", name[0 .. namelen]))
+ s = new MacroSection();
+ else
+ s = new Section();
+ s.name = name[0 .. namelen];
+ s.body_ = pstart[0 .. pend - pstart];
+ s.nooutput = 0;
+ //printf("Section: '%.*s' = '%.*s'\n", cast(int)s.namelen, s.name, cast(int)s.bodylen, s.body);
+ sections.push(s);
+ if (!summary && !namelen)
+ summary = s;
+ }
+ if (idlen)
+ {
+ name = idstart;
+ namelen = idlen;
+ }
+ else
+ {
+ name = null;
+ namelen = 0;
+ if (!*p)
+ break;
+ }
+ }
+ }
-TemplateDeclaration getEponymousParent(Dsymbol s) @safe
-{
- if (!s.parent)
- return null;
- TemplateDeclaration td = s.parent.isTemplateDeclaration();
- return (td && getEponymousMember(td)) ? td : null;
+ void writeSections(Scope* sc, Dsymbols* a, ref OutBuffer buf)
+ {
+ assert(a.length);
+ //printf("DocComment::writeSections()\n");
+ Loc loc = (*a)[0].loc;
+ if (Module m = (*a)[0].isModule())
+ {
+ if (m.md)
+ loc = m.md.loc;
+ }
+ size_t offset1 = buf.length;
+ buf.writestring("$(DDOC_SECTIONS ");
+ size_t offset2 = buf.length;
+ for (size_t i = 0; i < sections.length; i++)
+ {
+ Section sec = sections[i];
+ if (sec.nooutput)
+ continue;
+ //printf("Section: '%.*s' = '%.*s'\n", cast(int)sec.namelen, sec.name, cast(int)sec.bodylen, sec.body);
+ if (!sec.name.length && i == 0)
+ {
+ buf.writestring("$(DDOC_SUMMARY ");
+ size_t o = buf.length;
+ buf.write(sec.body_);
+ escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
+ highlightText(sc, a, loc, buf, o);
+ buf.writestring(")");
+ }
+ else
+ sec.write(loc, &this, sc, a, buf);
+ }
+ for (size_t i = 0; i < a.length; i++)
+ {
+ Dsymbol s = (*a)[i];
+ if (Dsymbol td = getEponymousParent(s))
+ s = td;
+ for (UnitTestDeclaration utd = s.ddocUnittest; utd; utd = utd.ddocUnittest)
+ {
+ if (utd.visibility.kind == Visibility.Kind.private_ || !utd.comment || !utd.fbody)
+ continue;
+ // Strip whitespaces to avoid showing empty summary
+ const(char)* c = utd.comment;
+ while (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')
+ ++c;
+ buf.writestring("$(DDOC_EXAMPLES ");
+ size_t o = buf.length;
+ buf.writestring(cast(char*)c);
+ if (utd.codedoc)
+ {
+ auto codedoc = utd.codedoc.stripLeadingNewlines;
+ size_t n = getCodeIndent(codedoc);
+ while (n--)
+ buf.writeByte(' ');
+ buf.writestring("----\n");
+ buf.writestring(codedoc);
+ buf.writestring("----\n");
+ highlightText(sc, a, loc, buf, o);
+ }
+ buf.writestring(")");
+ }
+ }
+ if (buf.length == offset2)
+ {
+ /* Didn't write out any sections, so back out last write
+ */
+ buf.setsize(offset1);
+ buf.writestring("\n");
+ }
+ else
+ buf.writestring(")");
+ }
}
-immutable ddoc_default = import("default_ddoc_theme." ~ ddoc_ext);
-immutable ddoc_decl_s = "$(DDOC_DECL ";
-immutable ddoc_decl_e = ")\n";
-immutable ddoc_decl_dd_s = "$(DDOC_DECL_DD ";
-immutable ddoc_decl_dd_e = ")\n";
-
-/****************************************************
- * Generate Ddoc file for Module m.
- * Params:
- * m = Module
- * ddoctext_ptr = combined text of .ddoc files for macro definitions
- * ddoctext_length = extant of ddoctext_ptr
- * datetime = charz returned by ctime()
- * eSink = send error messages to eSink
- * outbuf = append the Ddoc text to this
- */
-public
-void gendocfile(Module m, const char* ddoctext_ptr, size_t ddoctext_length, const char* datetime, ErrorSink eSink, ref OutBuffer outbuf)
-{
- gendocfile(m, ddoctext_ptr[0 .. ddoctext_length], datetime, eSink, outbuf);
-}
+private:
-/****************************************************
- * Generate Ddoc text for Module `m` and append it to `outbuf`.
- * Params:
- * m = Module
- * ddoctext = combined text of .ddoc files for macro definitions
- * datetime = charz returned by ctime()
- * eSink = send error messages to eSink
- * outbuf = append the Ddoc text to this
+/***********************************************************
*/
-public
-void gendocfile(Module m, const char[] ddoctext, const char* datetime, ErrorSink eSink, ref OutBuffer outbuf)
+class Section
{
- // Load internal default macros first
- DocComment.parseMacros(m.escapetable, m.macrotable, ddoc_default[]);
-
- // Ddoc files override default macros
- DocComment.parseMacros(m.escapetable, m.macrotable, ddoctext);
-
- Scope* sc = Scope.createGlobal(m, eSink); // create root scope
- DocComment* dc = DocComment.parse(m, m.comment);
- dc.pmacrotable = &m.macrotable;
- dc.escapetable = m.escapetable;
- sc.lastdc = dc;
- // Generate predefined macros
- // Set the title to be the name of the module
- {
- const p = m.toPrettyChars().toDString;
- m.macrotable.define("TITLE", p);
- }
- // Set time macros
- m.macrotable.define("DATETIME", datetime[0 .. 26]);
- m.macrotable.define("YEAR", datetime[20 .. 20 + 4]);
+ const(char)[] name;
+ const(char)[] body_;
+ int nooutput;
- const srcfilename = m.srcfile.toString();
- m.macrotable.define("SRCFILENAME", srcfilename);
- const docfilename = m.docfile.toString();
- m.macrotable.define("DOCFILENAME", docfilename);
- if (dc.copyright)
+ override string toString() const
{
- dc.copyright.nooutput = 1;
- m.macrotable.define("COPYRIGHT", dc.copyright.body_);
+ assert(0);
}
- OutBuffer buf;
- if (m.filetype == FileType.ddoc)
+ void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
{
- Loc loc = m.md ? m.md.loc : m.loc;
-
- size_t commentlen = m.comment ? strlen(cast(char*)m.comment) : 0;
- Dsymbols a;
- // https://issues.dlang.org/show_bug.cgi?id=9764
- // Don't push m in a, to prevent emphasize ddoc file name.
- if (dc.macros)
+ assert(a.length);
+ if (name.length)
{
- commentlen = dc.macros.name.ptr - m.comment;
- dc.macros.write(loc, dc, sc, &a, buf);
+ static immutable table =
+ [
+ "AUTHORS",
+ "BUGS",
+ "COPYRIGHT",
+ "DATE",
+ "DEPRECATED",
+ "EXAMPLES",
+ "HISTORY",
+ "LICENSE",
+ "RETURNS",
+ "SEE_ALSO",
+ "STANDARDS",
+ "THROWS",
+ "VERSION",
+ ];
+ foreach (entry; table)
+ {
+ if (iequals(entry, name))
+ {
+ buf.printf("$(DDOC_%s ", entry.ptr);
+ goto L1;
+ }
+ }
+ buf.writestring("$(DDOC_SECTION ");
+ // Replace _ characters with spaces
+ buf.writestring("$(DDOC_SECTION_H ");
+ size_t o = buf.length;
+ foreach (char c; name)
+ buf.writeByte((c == '_') ? ' ' : c);
+ escapeStrayParenthesis(loc, buf, o, false, sc.eSink);
+ buf.writestring(")");
+ }
+ else
+ {
+ buf.writestring("$(DDOC_DESCRIPTION ");
}
- buf.write(m.comment[0 .. commentlen]);
- highlightText(sc, &a, loc, buf, 0);
- }
- else
- {
- Dsymbols a;
- a.push(m);
- dc.writeSections(sc, &a, buf);
- emitMemberComments(m, buf, sc);
+ L1:
+ size_t o = buf.length;
+ buf.write(body_);
+ escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
+ highlightText(sc, a, loc, buf, o);
+ buf.writestring(")");
}
- //printf("BODY= '%.*s'\n", cast(int)buf.length, buf.data);
- m.macrotable.define("BODY", buf[]);
-
- OutBuffer buf2;
- buf2.writestring("$(DDOC)");
- size_t end = buf2.length;
-
- // Expand buf in place with macro expansions
- const success = m.macrotable.expand(buf2, 0, end, null, global.recursionLimit, &isIdStart, &isIdTail);
- if (!success)
- eSink.error(Loc.initial, "DDoc macro expansion limit exceeded; more than %d expansions.", global.recursionLimit);
+}
- /* Remove all the escape sequences from buf,
- * and make CR-LF the newline.
- */
- const slice = buf2[];
- outbuf.reserve(slice.length);
- auto p = slice.ptr;
- for (size_t j = 0; j < slice.length; j++)
+/***********************************************************
+ */
+final class ParamSection : Section
+{
+ override void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
{
- const c = p[j];
- if (c == 0xFF && j + 1 < slice.length)
+ assert(a.length);
+ Dsymbol s = (*a)[0]; // test
+ const(char)* p = body_.ptr;
+ size_t len = body_.length;
+ const(char)* pend = p + len;
+ const(char)* tempstart = null;
+ size_t templen = 0;
+ const(char)* namestart = null;
+ size_t namelen = 0; // !=0 if line continuation
+ const(char)* textstart = null;
+ size_t textlen = 0;
+ size_t paramcount = 0;
+ buf.writestring("$(DDOC_PARAMS ");
+ while (p < pend)
{
- j++;
+ // Skip to start of macro
+ while (1)
+ {
+ switch (*p)
+ {
+ case ' ':
+ case '\t':
+ p++;
+ continue;
+ case '\n':
+ p++;
+ goto Lcont;
+ default:
+ if (isIdStart(p) || isCVariadicArg(p[0 .. cast(size_t)(pend - p)]))
+ break;
+ if (namelen)
+ goto Ltext;
+ // continuation of prev macro
+ goto Lskipline;
+ }
+ break;
+ }
+ tempstart = p;
+ while (isIdTail(p))
+ p += utfStride(p);
+ if (isCVariadicArg(p[0 .. cast(size_t)(pend - p)]))
+ p += 3;
+ templen = p - tempstart;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p != '=')
+ {
+ if (namelen)
+ goto Ltext;
+ // continuation of prev macro
+ goto Lskipline;
+ }
+ p++;
+ if (namelen)
+ {
+ // Output existing param
+ L1:
+ //printf("param '%.*s' = '%.*s'\n", cast(int)namelen, namestart, cast(int)textlen, textstart);
+ ++paramcount;
+ HdrGenState hgs;
+ buf.writestring("$(DDOC_PARAM_ROW ");
+ {
+ buf.writestring("$(DDOC_PARAM_ID ");
+ {
+ size_t o = buf.length;
+ Parameter fparam = isFunctionParameter(a, namestart[0 .. namelen]);
+ if (!fparam)
+ {
+ // Comments on a template might refer to function parameters within.
+ // Search the parameters of nested eponymous functions (with the same name.)
+ fparam = isEponymousFunctionParameter(a, namestart[0 .. namelen]);
+ }
+ bool isCVariadic = isCVariadicParameter(a, namestart[0 .. namelen]);
+ if (isCVariadic)
+ {
+ buf.writestring("...");
+ }
+ else if (fparam && fparam.type && fparam.ident)
+ {
+ toCBuffer(fparam.type, buf, fparam.ident, hgs);
+ }
+ else
+ {
+ if (isTemplateParameter(a, namestart, namelen))
+ {
+ // 10236: Don't count template parameters for params check
+ --paramcount;
+ }
+ else if (!fparam)
+ {
+ sc.eSink.warning(s.loc, "Ddoc: function declaration has no parameter '%.*s'", cast(int)namelen, namestart);
+ }
+ buf.write(namestart[0 .. namelen]);
+ }
+ escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
+ highlightCode(sc, a, buf, o);
+ }
+ buf.writestring(")");
+ buf.writestring("$(DDOC_PARAM_DESC ");
+ {
+ size_t o = buf.length;
+ buf.write(textstart[0 .. textlen]);
+ escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
+ highlightText(sc, a, loc, buf, o);
+ }
+ buf.writestring(")");
+ }
+ buf.writestring(")");
+ namelen = 0;
+ if (p >= pend)
+ break;
+ }
+ namestart = tempstart;
+ namelen = templen;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ textstart = p;
+ Ltext:
+ while (*p != '\n')
+ p++;
+ textlen = p - textstart;
+ p++;
+ Lcont:
continue;
+ Lskipline:
+ // Ignore this line
+ while (*p++ != '\n')
+ {
+ }
}
- if (c == '\n')
- outbuf.writeByte('\r');
- else if (c == '\r')
+ if (namelen)
+ goto L1;
+ // write out last one
+ buf.writestring(")");
+ TypeFunction tf = a.length == 1 ? isTypeFunction(s) : null;
+ if (tf)
{
- outbuf.writestring("\r\n");
- if (j + 1 < slice.length && p[j + 1] == '\n')
+ size_t pcount = (tf.parameterList.parameters ? tf.parameterList.parameters.length : 0) +
+ cast(int)(tf.parameterList.varargs == VarArg.variadic);
+ if (pcount != paramcount)
{
- j++;
+ sc.eSink.warning(s.loc, "Ddoc: parameter count mismatch, expected %llu, got %llu",
+ cast(ulong) pcount, cast(ulong) paramcount);
+ if (paramcount == 0)
+ {
+ // Chances are someone messed up the format
+ sc.eSink.warningSupplemental(s.loc, "Note that the format is `param = description`");
+ }
}
- continue;
}
- outbuf.writeByte(c);
}
}
-/****************************************************
- * Having unmatched parentheses can hose the output of Ddoc,
- * as the macros depend on properly nested parentheses.
- * This function replaces all ( with $(LPAREN) and ) with $(RPAREN)
- * to preserve text literally. This also means macros in the
- * text won't be expanded.
+/***********************************************************
*/
-public
-void escapeDdocString(ref OutBuffer buf, size_t start)
+final class MacroSection : Section
{
- for (size_t u = start; u < buf.length; u++)
+ override void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
{
- const c = buf[u];
- switch (c)
- {
- case '$':
- buf.remove(u, 1);
- buf.insert(u, "$(DOLLAR)");
- u += 8;
- break;
- case '(':
- buf.remove(u, 1); //remove the (
- buf.insert(u, "$(LPAREN)"); //insert this instead
- u += 8; //skip over newly inserted macro
- break;
- case ')':
- buf.remove(u, 1); //remove the )
- buf.insert(u, "$(RPAREN)"); //insert this instead
- u += 8; //skip over newly inserted macro
- break;
- default:
- break;
- }
+ //printf("MacroSection::write()\n");
+ DocComment.parseMacros(dc.escapetable, *dc.pmacrotable, body_);
+ }
+}
+
+alias Sections = Array!(Section);
+
+// Workaround for missing Parameter instance for variadic params. (it's unnecessary to instantiate one).
+bool isCVariadicParameter(Dsymbols* a, const(char)[] p) @safe
+{
+ foreach (member; *a)
+ {
+ TypeFunction tf = isTypeFunction(member);
+ if (tf && tf.parameterList.varargs == VarArg.variadic && p == "...")
+ return true;
}
+ return false;
+}
+
+Dsymbol getEponymousMember(TemplateDeclaration td)
+{
+ td.computeOneMember();
+ if (!td.onemember)
+ return null;
+ if (AggregateDeclaration ad = td.onemember.isAggregateDeclaration())
+ return ad;
+ if (FuncDeclaration fd = td.onemember.isFuncDeclaration())
+ return fd;
+ if (auto em = td.onemember.isEnumMember())
+ return null; // Keep backward compatibility. See compilable/ddoc9.d
+ if (VarDeclaration vd = td.onemember.isVarDeclaration())
+ return td.constraint ? null : vd;
+ return null;
+}
+
+TemplateDeclaration getEponymousParent(Dsymbol s)
+{
+ if (!s.parent)
+ return null;
+ TemplateDeclaration td = s.parent.isTemplateDeclaration();
+ return (td && getEponymousMember(td)) ? td : null;
}
+immutable ddoc_default = import("default_ddoc_theme." ~ ddoc_ext);
+immutable ddoc_decl_s = "$(DDOC_DECL ";
+immutable ddoc_decl_e = ")\n";
+immutable ddoc_decl_dd_s = "$(DDOC_DECL_DD ";
+immutable ddoc_decl_dd_e = ")\n";
+
/****************************************************
* Having unmatched parentheses can hose the output of Ddoc,
* as the macros depend on properly nested parentheses.
par_open--;
break;
default:
- break;
- }
- }
- }
-}
-
-// Basically, this is to skip over things like private{} blocks in a struct or
-// class definition that don't add any components to the qualified name.
-Scope* skipNonQualScopes(Scope* sc) @safe
-{
- while (sc && !sc.scopesym)
- sc = sc.enclosing;
- return sc;
-}
-
-bool emitAnchorName(ref OutBuffer buf, Dsymbol s, Scope* sc, bool includeParent)
-{
- if (!s || s.isPackage() || s.isModule())
- return false;
- // Add parent names first
- bool dot = false;
- auto eponymousParent = getEponymousParent(s);
- if (includeParent && s.parent || eponymousParent)
- dot = emitAnchorName(buf, s.parent, sc, includeParent);
- else if (includeParent && sc)
- dot = emitAnchorName(buf, sc.scopesym, skipNonQualScopes(sc.enclosing), includeParent);
- // Eponymous template members can share the parent anchor name
- if (eponymousParent)
- return dot;
- if (dot)
- buf.writeByte('.');
- // Use "this" not "__ctor"
- TemplateDeclaration td;
- if (s.isCtorDeclaration() || ((td = s.isTemplateDeclaration()) !is null && td.onemember && td.onemember.isCtorDeclaration()))
- {
- buf.writestring("this");
- }
- else
- {
- buf.writestring(s.ident ? s.ident.toString : "__anonymous");
- }
- return true;
-}
-
-void emitAnchor(ref OutBuffer buf, Dsymbol s, Scope* sc, bool forHeader = false)
-{
- Identifier ident;
- {
- OutBuffer anc;
- emitAnchorName(anc, s, skipNonQualScopes(sc), true);
- ident = Identifier.idPool(anc[]);
- }
-
- auto pcount = cast(void*)ident in sc.anchorCounts;
- typeof(*pcount) count;
- if (!forHeader)
- {
- if (pcount)
- {
- // Existing anchor,
- // don't write an anchor for matching consecutive ditto symbols
- TemplateDeclaration td = getEponymousParent(s);
- if (sc.prevAnchor == ident && sc.lastdc && (isDitto(s.comment) || (td && isDitto(td.comment))))
- return;
-
- count = ++*pcount;
- }
- else
- {
- sc.anchorCounts[cast(void*)ident] = 1;
- count = 1;
- }
- }
-
- // cache anchor name
- sc.prevAnchor = ident;
- auto macroName = forHeader ? "DDOC_HEADER_ANCHOR" : "DDOC_ANCHOR";
-
- if (auto imp = s.isImport())
- {
- // For example: `public import core.stdc.string : memcpy, memcmp;`
- if (imp.aliases.length > 0)
- {
- for(int i = 0; i < imp.aliases.length; i++)
- {
- // Need to distinguish between
- // `public import core.stdc.string : memcpy, memcmp;` and
- // `public import core.stdc.string : copy = memcpy, compare = memcmp;`
- auto a = imp.aliases[i];
- auto id = a ? a : imp.names[i];
- auto loc = Loc.init;
- Dsymbol pscopesym;
- if (auto symFromId = sc.search(loc, id, pscopesym))
- {
- emitAnchor(buf, symFromId, sc, forHeader);
- }
- }
- }
- else
- {
- // For example: `public import str = core.stdc.string;`
- if (imp.aliasId)
- {
- auto symbolName = imp.aliasId.toString();
-
- buf.printf("$(%.*s %.*s", cast(int) macroName.length, macroName.ptr,
- cast(int) symbolName.length, symbolName.ptr);
-
- if (forHeader)
- {
- buf.printf(", %.*s", cast(int) symbolName.length, symbolName.ptr);
- }
- }
- else
- {
- // The general case: `public import core.stdc.string;`
-
- // fully qualify imports so `core.stdc.string` doesn't appear as `core`
- void printFullyQualifiedImport()
- {
- foreach (const pid; imp.packages)
- {
- buf.printf("%s.", pid.toChars());
- }
- buf.writestring(imp.id.toString());
- }
-
- buf.printf("$(%.*s ", cast(int) macroName.length, macroName.ptr);
- printFullyQualifiedImport();
-
- if (forHeader)
- {
- buf.printf(", ");
- printFullyQualifiedImport();
- }
- }
-
- buf.writeByte(')');
- }
- }
- else
- {
- // buf.writestring("<<<");
- // buf.writestring(typeof(ident).stringof);
- // buf.writestring(">>>");
- // auto symbolName = ident.toString();
- auto symbolName = ident.toChars().toDString();
- buf.printf("$(%.*s %.*s", cast(int) macroName.length, macroName.ptr,
- cast(int) symbolName.length, symbolName.ptr);
-
- // only append count once there's a duplicate
- if (count > 1)
- buf.printf(".%u", count);
-
- if (forHeader)
- {
- Identifier shortIdent;
- {
- OutBuffer anc;
- emitAnchorName(anc, s, skipNonQualScopes(sc), false);
- shortIdent = Identifier.idPool(anc[]);
- }
-
- auto shortName = shortIdent.toString();
- buf.printf(", %.*s", cast(int) shortName.length, shortName.ptr);
- }
-
- buf.writeByte(')');
- }
-}
-
-/******************************* emitComment **********************************/
-
-/** Get leading indentation from 'src' which represents lines of code. */
-size_t getCodeIndent(const(char)* src)
-{
- while (src && (*src == '\r' || *src == '\n'))
- ++src; // skip until we find the first non-empty line
- size_t codeIndent = 0;
- while (src && (*src == ' ' || *src == '\t'))
- {
- codeIndent++;
- src++;
- }
- return codeIndent;
-}
-
-/** Recursively expand template mixin member docs into the scope. */
-void expandTemplateMixinComments(TemplateMixin tm, ref OutBuffer buf, Scope* sc)
-{
- if (!tm.semanticRun)
- tm.dsymbolSemantic(sc);
- TemplateDeclaration td = (tm && tm.tempdecl) ? tm.tempdecl.isTemplateDeclaration() : null;
- if (td && td.members)
- {
- for (size_t i = 0; i < td.members.length; i++)
- {
- Dsymbol sm = (*td.members)[i];
- TemplateMixin tmc = sm.isTemplateMixin();
- if (tmc && tmc.comment)
- expandTemplateMixinComments(tmc, buf, sc);
- else
- emitComment(sm, buf, sc);
- }
- }
-}
-
-void emitMemberComments(ScopeDsymbol sds, ref OutBuffer buf, Scope* sc)
-{
- if (!sds.members)
- return;
- //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars());
- const(char)[] m = "$(DDOC_MEMBERS ";
- if (sds.isTemplateDeclaration())
- m = "$(DDOC_TEMPLATE_MEMBERS ";
- else if (sds.isClassDeclaration())
- m = "$(DDOC_CLASS_MEMBERS ";
- else if (sds.isStructDeclaration())
- m = "$(DDOC_STRUCT_MEMBERS ";
- else if (sds.isEnumDeclaration())
- m = "$(DDOC_ENUM_MEMBERS ";
- else if (sds.isModule())
- m = "$(DDOC_MODULE_MEMBERS ";
- size_t offset1 = buf.length; // save starting offset
- buf.writestring(m);
- size_t offset2 = buf.length; // to see if we write anything
- sc = sc.push(sds);
- for (size_t i = 0; i < sds.members.length; i++)
- {
- Dsymbol s = (*sds.members)[i];
- //printf("\ts = '%s'\n", s.toChars());
- // only expand if parent is a non-template (semantic won't work)
- if (s.comment && s.isTemplateMixin() && s.parent && !s.parent.isTemplateDeclaration())
- expandTemplateMixinComments(cast(TemplateMixin)s, buf, sc);
- emitComment(s, buf, sc);
- }
- emitComment(null, buf, sc);
- sc.pop();
- if (buf.length == offset2)
- {
- /* Didn't write out any members, so back out last write
- */
- buf.setsize(offset1);
+ break;
+ }
+ }
}
- else
- buf.writestring(")");
}
-void emitVisibility(ref OutBuffer buf, Import i)
+// Basically, this is to skip over things like private{} blocks in a struct or
+// class definition that don't add any components to the qualified name.
+Scope* skipNonQualScopes(Scope* sc) @safe
{
- // imports are private by default, which is different from other declarations
- // so they should explicitly show their visibility
- emitVisibility(buf, i.visibility);
+ while (sc && !sc.scopesym)
+ sc = sc.enclosing;
+ return sc;
}
-void emitVisibility(ref OutBuffer buf, Declaration d)
+bool emitAnchorName(ref OutBuffer buf, Dsymbol s, Scope* sc, bool includeParent)
{
- auto vis = d.visibility;
- if (vis.kind != Visibility.Kind.undefined && vis.kind != Visibility.Kind.public_)
+ if (!s || s.isPackage() || s.isModule())
+ return false;
+ // Add parent names first
+ bool dot = false;
+ auto eponymousParent = getEponymousParent(s);
+ if (includeParent && s.parent || eponymousParent)
+ dot = emitAnchorName(buf, s.parent, sc, includeParent);
+ else if (includeParent && sc)
+ dot = emitAnchorName(buf, sc.scopesym, skipNonQualScopes(sc.enclosing), includeParent);
+ // Eponymous template members can share the parent anchor name
+ if (eponymousParent)
+ return dot;
+ if (dot)
+ buf.writeByte('.');
+ // Use "this" not "__ctor"
+ TemplateDeclaration td = s.isTemplateDeclaration();
+ td.computeOneMember();
+ if (s.isCtorDeclaration() || (td !is null && td.onemember && td.onemember.isCtorDeclaration()))
{
- emitVisibility(buf, vis);
+ buf.writestring("this");
}
+ else
+ {
+ buf.writestring(s.ident ? s.ident.toString : "__anonymous");
+ }
+ return true;
}
-void emitVisibility(ref OutBuffer buf, Visibility vis)
-{
- visibilityToBuffer(buf, vis);
- buf.writeByte(' ');
-}
-
-void emitComment(Dsymbol s, ref OutBuffer buf, Scope* sc)
+void emitAnchor(ref OutBuffer buf, Dsymbol s, Scope* sc, bool forHeader = false)
{
- extern (C++) final class EmitComment : Visitor
+ Identifier ident;
{
- alias visit = Visitor.visit;
- public:
- OutBuffer* buf;
- Scope* sc;
-
- extern (D) this(ref OutBuffer buf, Scope* sc) scope
- {
- this.buf = &buf;
- this.sc = sc;
- }
-
- override void visit(Dsymbol)
- {
- }
-
- override void visit(InvariantDeclaration)
- {
- }
-
- override void visit(UnitTestDeclaration)
- {
- }
-
- override void visit(PostBlitDeclaration)
- {
- }
-
- override void visit(DtorDeclaration)
- {
- }
-
- override void visit(StaticCtorDeclaration)
- {
- }
-
- override void visit(StaticDtorDeclaration)
- {
- }
-
- override void visit(TypeInfoDeclaration)
- {
- }
-
- void emit(Scope* sc, Dsymbol s, const(char)* com)
- {
- if (s && sc.lastdc && isDitto(com))
- {
- sc.lastdc.a.push(s);
- return;
- }
- // Put previous doc comment if exists
- if (DocComment* dc = sc.lastdc)
- {
- assert(dc.a.length > 0, "Expects at least one declaration for a" ~
- "documentation comment");
-
- auto symbol = dc.a[0];
-
- buf.writestring("$(DDOC_MEMBER");
- buf.writestring("$(DDOC_MEMBER_HEADER");
- emitAnchor(*buf, symbol, sc, true);
- buf.writeByte(')');
-
- // Put the declaration signatures as the document 'title'
- buf.writestring(ddoc_decl_s);
- for (size_t i = 0; i < dc.a.length; i++)
- {
- Dsymbol sx = dc.a[i];
- // the added linebreaks in here make looking at multiple
- // signatures more appealing
- if (i == 0)
- {
- size_t o = buf.length;
- toDocBuffer(sx, *buf, sc);
- highlightCode(sc, sx, *buf, o);
- buf.writestring("$(DDOC_OVERLOAD_SEPARATOR)");
- continue;
- }
- buf.writestring("$(DDOC_DITTO ");
- {
- size_t o = buf.length;
- toDocBuffer(sx, *buf, sc);
- highlightCode(sc, sx, *buf, o);
- }
- buf.writestring("$(DDOC_OVERLOAD_SEPARATOR)");
- buf.writeByte(')');
- }
- buf.writestring(ddoc_decl_e);
- // Put the ddoc comment as the document 'description'
- buf.writestring(ddoc_decl_dd_s);
- {
- dc.writeSections(sc, &dc.a, *buf);
- if (ScopeDsymbol sds = dc.a[0].isScopeDsymbol())
- emitMemberComments(sds, *buf, sc);
- }
- buf.writestring(ddoc_decl_dd_e);
- buf.writeByte(')');
- //printf("buf.2 = [[%.*s]]\n", cast(int)(buf.length - o0), buf.data + o0);
- }
- if (s)
- {
- DocComment* dc = DocComment.parse(s, com);
- dc.pmacrotable = &sc._module.macrotable;
- sc.lastdc = dc;
- }
- }
+ OutBuffer anc;
+ emitAnchorName(anc, s, skipNonQualScopes(sc), true);
+ ident = Identifier.idPool(anc[]);
+ }
- override void visit(Import imp)
+ auto pcount = cast(void*)ident in sc.anchorCounts;
+ typeof(*pcount) count;
+ if (!forHeader)
+ {
+ if (pcount)
{
- if (imp.visible().kind != Visibility.Kind.public_ && sc.visibility.kind != Visibility.Kind.export_)
+ // Existing anchor,
+ // don't write an anchor for matching consecutive ditto symbols
+ TemplateDeclaration td = getEponymousParent(s);
+ if (sc.prevAnchor == ident && sc.lastdc && (isDitto(s.comment) || (td && isDitto(td.comment))))
return;
- if (imp.comment)
- emit(sc, imp, imp.comment);
+ count = ++*pcount;
}
-
- override void visit(Declaration d)
+ else
{
- //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", d, d.toChars(), d.comment);
- //printf("type = %p\n", d.type);
- const(char)* com = d.comment;
- if (TemplateDeclaration td = getEponymousParent(d))
- {
- if (isDitto(td.comment))
- com = td.comment;
- else
- com = Lexer.combineComments(td.comment.toDString(), com.toDString(), true);
- }
- else
- {
- if (!d.ident)
- return;
- if (!d.type)
- {
- if (!d.isCtorDeclaration() &&
- !d.isAliasDeclaration() &&
- !d.isVarDeclaration())
- {
- return;
- }
- }
- if (d.visibility.kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
- return;
- }
- if (!com)
- return;
- emit(sc, d, com);
+ sc.anchorCounts[cast(void*)ident] = 1;
+ count = 1;
}
+ }
- override void visit(AggregateDeclaration ad)
- {
- //printf("AggregateDeclaration::emitComment() '%s'\n", ad.toChars());
- const(char)* com = ad.comment;
- if (TemplateDeclaration td = getEponymousParent(ad))
- {
- if (isDitto(td.comment))
- com = td.comment;
- else
- com = Lexer.combineComments(td.comment.toDString(), com.toDString(), true);
- }
- else
- {
- if (ad.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
- return;
- if (!ad.comment)
- return;
- }
- if (!com)
- return;
- emit(sc, ad, com);
- }
+ // cache anchor name
+ sc.prevAnchor = ident;
+ auto macroName = forHeader ? "DDOC_HEADER_ANCHOR" : "DDOC_ANCHOR";
- override void visit(TemplateDeclaration td)
+ if (auto imp = s.isImport())
+ {
+ // For example: `public import core.stdc.string : memcpy, memcmp;`
+ if (imp.aliases.length > 0)
{
- //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", td.toChars(), td.kind());
- if (td.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
- return;
- if (!td.comment)
- return;
- if (Dsymbol ss = getEponymousMember(td))
+ for(int i = 0; i < imp.aliases.length; i++)
{
- ss.accept(this);
- return;
+ // Need to distinguish between
+ // `public import core.stdc.string : memcpy, memcmp;` and
+ // `public import core.stdc.string : copy = memcpy, compare = memcmp;`
+ auto a = imp.aliases[i];
+ auto id = a ? a : imp.names[i];
+ auto loc = Loc.init;
+ Dsymbol pscopesym;
+ if (auto symFromId = sc.search(loc, id, pscopesym))
+ {
+ emitAnchor(buf, symFromId, sc, forHeader);
+ }
}
- emit(sc, td, td.comment);
}
-
- override void visit(EnumDeclaration ed)
+ else
{
- if (ed.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
- return;
- if (ed.isAnonymous() && ed.members)
+ // For example: `public import str = core.stdc.string;`
+ if (imp.aliasId)
{
- for (size_t i = 0; i < ed.members.length; i++)
+ auto symbolName = imp.aliasId.toString();
+
+ buf.printf("$(%.*s %.*s", cast(int) macroName.length, macroName.ptr,
+ cast(int) symbolName.length, symbolName.ptr);
+
+ if (forHeader)
{
- Dsymbol s = (*ed.members)[i];
- emitComment(s, *buf, sc);
+ buf.printf(", %.*s", cast(int) symbolName.length, symbolName.ptr);
}
- return;
}
- if (!ed.comment)
- return;
- if (ed.isAnonymous())
- return;
- emit(sc, ed, ed.comment);
- }
+ else
+ {
+ // The general case: `public import core.stdc.string;`
- override void visit(EnumMember em)
- {
- //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", em, em.toChars(), em.comment);
- if (em.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
- return;
- if (!em.comment)
- return;
- emit(sc, em, em.comment);
- }
+ // fully qualify imports so `core.stdc.string` doesn't appear as `core`
+ void printFullyQualifiedImport()
+ {
+ foreach (const pid; imp.packages)
+ {
+ buf.printf("%s.", pid.toChars());
+ }
+ buf.writestring(imp.id.toString());
+ }
- override void visit(AttribDeclaration ad)
- {
- //printf("AttribDeclaration::emitComment(sc = %p)\n", sc);
- /* A general problem with this,
- * illustrated by https://issues.dlang.org/show_bug.cgi?id=2516
- * is that attributes are not transmitted through to the underlying
- * member declarations for template bodies, because semantic analysis
- * is not done for template declaration bodies
- * (only template instantiations).
- * Hence, Ddoc omits attributes from template members.
- */
- Dsymbols* d = ad.include(null);
- if (d)
- {
- for (size_t i = 0; i < d.length; i++)
+ buf.printf("$(%.*s ", cast(int) macroName.length, macroName.ptr);
+ printFullyQualifiedImport();
+
+ if (forHeader)
{
- Dsymbol s = (*d)[i];
- //printf("AttribDeclaration::emitComment %s\n", s.toChars());
- emitComment(s, *buf, sc);
+ buf.printf(", ");
+ printFullyQualifiedImport();
}
}
- }
- override void visit(VisibilityDeclaration pd)
- {
- if (pd.decl)
- {
- Scope* scx = sc;
- sc = sc.copy();
- sc.visibility = pd.visibility;
- visit(cast(AttribDeclaration)pd);
- scx.lastdc = sc.lastdc;
- sc = sc.pop();
- }
+ buf.writeByte(')');
}
+ }
+ else
+ {
+ // buf.writestring("<<<");
+ // buf.writestring(typeof(ident).stringof);
+ // buf.writestring(">>>");
+ // auto symbolName = ident.toString();
+ auto symbolName = ident.toChars().toDString();
+ buf.printf("$(%.*s %.*s", cast(int) macroName.length, macroName.ptr,
+ cast(int) symbolName.length, symbolName.ptr);
- override void visit(ConditionalDeclaration cd)
+ // only append count once there's a duplicate
+ if (count > 1)
+ buf.printf(".%u", count);
+
+ if (forHeader)
{
- //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc);
- if (cd.condition.inc != Include.notComputed)
- {
- visit(cast(AttribDeclaration)cd);
- return;
- }
- /* If generating doc comment, be careful because if we're inside
- * a template, then include(null) will fail.
- */
- Dsymbols* d = cd.decl ? cd.decl : cd.elsedecl;
- for (size_t i = 0; i < d.length; i++)
+ Identifier shortIdent;
{
- Dsymbol s = (*d)[i];
- emitComment(s, *buf, sc);
+ OutBuffer anc;
+ emitAnchorName(anc, s, skipNonQualScopes(sc), false);
+ shortIdent = Identifier.idPool(anc[]);
}
+
+ auto shortName = shortIdent.toString();
+ buf.printf(", %.*s", cast(int) shortName.length, shortName.ptr);
}
- }
- scope EmitComment v = new EmitComment(buf, sc);
- if (!s)
- v.emit(sc, null, null);
- else
- s.accept(v);
+ buf.writeByte(')');
+ }
}
-void toDocBuffer(Dsymbol s, ref OutBuffer buf, Scope* sc)
+/******************************* emitComment **********************************/
+
+/** Get leading indentation from 'src' which represents lines of code. */
+size_t getCodeIndent(const(char)* src)
{
- extern (C++) final class ToDocBuffer : Visitor
+ while (src && (*src == '\r' || *src == '\n'))
+ ++src; // skip until we find the first non-empty line
+ size_t codeIndent = 0;
+ while (src && (*src == ' ' || *src == '\t'))
{
- alias visit = Visitor.visit;
- public:
- OutBuffer* buf;
- Scope* sc;
-
- extern (D) this(ref OutBuffer buf, Scope* sc) scope
- {
- this.buf = &buf;
- this.sc = sc;
- }
+ codeIndent++;
+ src++;
+ }
+ return codeIndent;
+}
- override void visit(Dsymbol s)
+/** Recursively expand template mixin member docs into the scope. */
+void expandTemplateMixinComments(TemplateMixin tm, ref OutBuffer buf, Scope* sc)
+{
+ if (!tm.semanticRun)
+ tm.dsymbolSemantic(sc);
+ TemplateDeclaration td = (tm && tm.tempdecl) ? tm.tempdecl.isTemplateDeclaration() : null;
+ if (td && td.members)
+ {
+ for (size_t i = 0; i < td.members.length; i++)
{
- //printf("Dsymbol::toDocbuffer() %s\n", s.toChars());
- HdrGenState hgs;
- hgs.ddoc = true;
- toCBuffer(s, *buf, hgs);
+ Dsymbol sm = (*td.members)[i];
+ TemplateMixin tmc = sm.isTemplateMixin();
+ if (tmc && tmc.comment)
+ expandTemplateMixinComments(tmc, buf, sc);
+ else
+ emitComment(sm, buf, sc);
}
+ }
+}
- void prefix(Dsymbol s)
- {
- if (s.isDeprecated())
- buf.writestring("deprecated ");
- if (Declaration d = s.isDeclaration())
- {
- emitVisibility(*buf, d);
- if (d.isStatic())
- buf.writestring("static ");
- else if (d.isFinal())
- buf.writestring("final ");
- else if (d.isAbstract())
- buf.writestring("abstract ");
+void emitMemberComments(ScopeDsymbol sds, ref OutBuffer buf, Scope* sc)
+{
+ if (!sds.members)
+ return;
+ //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars());
+ const(char)[] m = "$(DDOC_MEMBERS ";
+ if (sds.isTemplateDeclaration())
+ m = "$(DDOC_TEMPLATE_MEMBERS ";
+ else if (sds.isClassDeclaration())
+ m = "$(DDOC_CLASS_MEMBERS ";
+ else if (sds.isStructDeclaration())
+ m = "$(DDOC_STRUCT_MEMBERS ";
+ else if (sds.isEnumDeclaration())
+ m = "$(DDOC_ENUM_MEMBERS ";
+ else if (sds.isModule())
+ m = "$(DDOC_MODULE_MEMBERS ";
+ size_t offset1 = buf.length; // save starting offset
+ buf.writestring(m);
+ size_t offset2 = buf.length; // to see if we write anything
+ sc = sc.push(sds);
+ for (size_t i = 0; i < sds.members.length; i++)
+ {
+ Dsymbol s = (*sds.members)[i];
+ //printf("\ts = '%s'\n", s.toChars());
+ // only expand if parent is a non-template (semantic won't work)
+ if (s.comment && s.isTemplateMixin() && s.parent && !s.parent.isTemplateDeclaration())
+ expandTemplateMixinComments(cast(TemplateMixin)s, buf, sc);
+ emitComment(s, buf, sc);
+ }
+ emitComment(null, buf, sc);
+ sc.pop();
+ if (buf.length == offset2)
+ {
+ /* Didn't write out any members, so back out last write
+ */
+ buf.setsize(offset1);
+ }
+ else
+ buf.writestring(")");
+}
- if (d.isFuncDeclaration()) // functionToBufferFull handles this
- return;
+void emitVisibility(ref OutBuffer buf, Import i)
+{
+ // imports are private by default, which is different from other declarations
+ // so they should explicitly show their visibility
+ emitVisibility(buf, i.visibility);
+}
- if (d.isImmutable())
- buf.writestring("immutable ");
- if (d.storage_class & STC.shared_)
- buf.writestring("shared ");
- if (d.isWild())
- buf.writestring("inout ");
- if (d.isConst())
- buf.writestring("const ");
+void emitVisibility(ref OutBuffer buf, Declaration d)
+{
+ auto vis = d.visibility;
+ if (vis.kind != Visibility.Kind.undefined && vis.kind != Visibility.Kind.public_)
+ {
+ emitVisibility(buf, vis);
+ }
+}
- if (d.isSynchronized())
- buf.writestring("synchronized ");
+void emitVisibility(ref OutBuffer buf, Visibility vis)
+{
+ visibilityToBuffer(buf, vis);
+ buf.writeByte(' ');
+}
- if (d.storage_class & STC.manifest)
- buf.writestring("enum ");
+void emitComment(Dsymbol s, ref OutBuffer buf, Scope* sc)
+{
+ extern (C++) final class EmitComment : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ OutBuffer* buf;
+ Scope* sc;
- // Add "auto" for the untyped variable in template members
- if (!d.type && d.isVarDeclaration() &&
- !d.isImmutable() && !(d.storage_class & STC.shared_) && !d.isWild() && !d.isConst() &&
- !d.isSynchronized())
- {
- buf.writestring("auto ");
- }
- }
+ extern (D) this(ref OutBuffer buf, Scope* sc) scope
+ {
+ this.buf = &buf;
+ this.sc = sc;
}
- override void visit(Import i)
+ override void visit(Dsymbol)
{
- HdrGenState hgs;
- hgs.ddoc = true;
- emitVisibility(*buf, i);
- toCBuffer(i, *buf, hgs);
}
- override void visit(Declaration d)
+ override void visit(InvariantDeclaration)
{
- if (!d.ident)
- return;
- TemplateDeclaration td = getEponymousParent(d);
- //printf("Declaration::toDocbuffer() %s, originalType = %s, td = %s\n", d.toChars(), d.originalType ? d.originalType.toChars() : "--", td ? td.toChars() : "--");
- HdrGenState hgs;
- hgs.ddoc = true;
- if (d.isDeprecated())
- buf.writestring("$(DEPRECATED ");
- prefix(d);
- if (d.type)
- {
- Type origType = d.originalType ? d.originalType : d.type;
- if (origType.ty == Tfunction)
- {
- functionToBufferFull(cast(TypeFunction)origType, *buf, d.ident, hgs, td);
- }
- else
- toCBuffer(origType, *buf, d.ident, hgs);
- }
- else
- buf.writestring(d.ident.toString());
- if (d.isVarDeclaration() && td)
- {
- buf.writeByte('(');
- if (td.origParameters && td.origParameters.length)
- {
- for (size_t i = 0; i < td.origParameters.length; i++)
- {
- if (i)
- buf.writestring(", ");
- toCBuffer((*td.origParameters)[i], *buf, hgs);
- }
- }
- buf.writeByte(')');
- }
- // emit constraints if declaration is a templated declaration
- if (td && td.constraint)
- {
- bool noFuncDecl = td.isFuncDeclaration() is null;
- if (noFuncDecl)
- {
- buf.writestring("$(DDOC_CONSTRAINT ");
- }
+ }
- toCBuffer(td.constraint, *buf, hgs);
+ override void visit(UnitTestDeclaration)
+ {
+ }
- if (noFuncDecl)
- {
- buf.writestring(")");
- }
- }
- if (d.isDeprecated())
- buf.writestring(")");
- buf.writestring(";\n");
+ override void visit(PostBlitDeclaration)
+ {
}
- override void visit(AliasDeclaration ad)
+ override void visit(DtorDeclaration)
{
- //printf("AliasDeclaration::toDocbuffer() %s\n", ad.toChars());
- if (!ad.ident)
- return;
- if (ad.isDeprecated())
- buf.writestring("deprecated ");
- emitVisibility(*buf, ad);
- buf.printf("alias %s = ", ad.toChars());
- if (Dsymbol s = ad.aliassym) // ident alias
- {
- prettyPrintDsymbol(s, ad.parent);
- }
- else if (Type type = ad.getType()) // type alias
- {
- if (type.ty == Tclass || type.ty == Tstruct || type.ty == Tenum)
- {
- import dmd.typesem : toDsymbol;
- if (Dsymbol s = type.toDsymbol(null)) // elaborate type
- prettyPrintDsymbol(s, ad.parent);
- else
- buf.writestring(type.toChars());
- }
- else
- {
- // simple type
- buf.writestring(type.toChars());
- }
- }
- buf.writestring(";\n");
}
- void parentToBuffer(Dsymbol s)
+ override void visit(StaticCtorDeclaration)
{
- if (s && !s.isPackage() && !s.isModule())
- {
- parentToBuffer(s.parent);
- buf.writestring(s.toChars());
- buf.writestring(".");
- }
}
- static bool inSameModule(Dsymbol s, Dsymbol p) @safe
+ override void visit(StaticDtorDeclaration)
{
- for (; s; s = s.parent)
- {
- if (s.isModule())
- break;
- }
- for (; p; p = p.parent)
- {
- if (p.isModule())
- break;
- }
- return s == p;
}
- void prettyPrintDsymbol(Dsymbol s, Dsymbol parent)
+ override void visit(TypeInfoDeclaration)
{
- if (s.parent && (s.parent == parent)) // in current scope -> naked name
+ }
+
+ void emit(Scope* sc, Dsymbol s, const(char)* com)
+ {
+ if (s && sc.lastdc && isDitto(com))
{
- buf.writestring(s.toChars());
+ sc.lastdc.a.push(s);
+ return;
}
- else if (!inSameModule(s, parent)) // in another module -> full name
+ // Put previous doc comment if exists
+ if (DocComment* dc = sc.lastdc)
{
- buf.writestring(s.toPrettyChars());
+ assert(dc.a.length > 0, "Expects at least one declaration for a" ~
+ "documentation comment");
+
+ auto symbol = dc.a[0];
+
+ buf.writestring("$(DDOC_MEMBER");
+ buf.writestring("$(DDOC_MEMBER_HEADER");
+ emitAnchor(*buf, symbol, sc, true);
+ buf.writeByte(')');
+
+ // Put the declaration signatures as the document 'title'
+ buf.writestring(ddoc_decl_s);
+ for (size_t i = 0; i < dc.a.length; i++)
+ {
+ Dsymbol sx = dc.a[i];
+ // the added linebreaks in here make looking at multiple
+ // signatures more appealing
+ if (i == 0)
+ {
+ size_t o = buf.length;
+ toDocBuffer(sx, *buf, sc);
+ highlightCode(sc, sx, *buf, o);
+ buf.writestring("$(DDOC_OVERLOAD_SEPARATOR)");
+ continue;
+ }
+ buf.writestring("$(DDOC_DITTO ");
+ {
+ size_t o = buf.length;
+ toDocBuffer(sx, *buf, sc);
+ highlightCode(sc, sx, *buf, o);
+ }
+ buf.writestring("$(DDOC_OVERLOAD_SEPARATOR)");
+ buf.writeByte(')');
+ }
+ buf.writestring(ddoc_decl_e);
+ // Put the ddoc comment as the document 'description'
+ buf.writestring(ddoc_decl_dd_s);
+ {
+ dc.writeSections(sc, &dc.a, *buf);
+ if (ScopeDsymbol sds = dc.a[0].isScopeDsymbol())
+ emitMemberComments(sds, *buf, sc);
+ }
+ buf.writestring(ddoc_decl_dd_e);
+ buf.writeByte(')');
+ //printf("buf.2 = [[%.*s]]\n", cast(int)(buf.length - o0), buf.data + o0);
}
- else // nested in a type in this module -> full name w/o module name
+ if (s)
{
- // if alias is nested in a user-type use module-scope lookup
- if (!parent.isModule() && !parent.isPackage())
- buf.writestring(".");
- parentToBuffer(s.parent);
- buf.writestring(s.toChars());
+ DocComment* dc = DocComment.parse(s, com);
+ dc.pmacrotable = &sc._module.macrotable;
+ sc.lastdc = dc;
}
}
- override void visit(AggregateDeclaration ad)
+ override void visit(Import imp)
{
- if (!ad.ident)
+ if (imp.visible().kind != Visibility.Kind.public_ && sc.visibility.kind != Visibility.Kind.export_)
return;
- version (none)
- {
- emitVisibility(buf, ad);
- }
- buf.printf("%s %s", ad.kind(), ad.toChars());
- buf.writestring(";\n");
+
+ if (imp.comment)
+ emit(sc, imp, imp.comment);
}
- override void visit(StructDeclaration sd)
+ override void visit(Declaration d)
{
- //printf("StructDeclaration::toDocbuffer() %s\n", sd.toChars());
- if (!sd.ident)
- return;
- version (none)
- {
- emitVisibility(buf, sd);
- }
- if (TemplateDeclaration td = getEponymousParent(sd))
+ //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", d, d.toChars(), d.comment);
+ //printf("type = %p\n", d.type);
+ const(char)* com = d.comment;
+ if (TemplateDeclaration td = getEponymousParent(d))
{
- toDocBuffer(td, *buf, sc);
+ if (isDitto(td.comment))
+ com = td.comment;
+ else
+ com = Lexer.combineComments(td.comment.toDString(), com.toDString(), true);
}
else
{
- buf.printf("%s %s", sd.kind(), sd.toChars());
+ if (!d.ident)
+ return;
+ if (!d.type)
+ {
+ if (!d.isCtorDeclaration() &&
+ !d.isAliasDeclaration() &&
+ !d.isVarDeclaration())
+ {
+ return;
+ }
+ }
+ if (d.visibility.kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
+ return;
}
- buf.writestring(";\n");
+ if (!com)
+ return;
+ emit(sc, d, com);
}
- override void visit(ClassDeclaration cd)
+ override void visit(AggregateDeclaration ad)
{
- //printf("ClassDeclaration::toDocbuffer() %s\n", cd.toChars());
- if (!cd.ident)
- return;
- version (none)
- {
- emitVisibility(*buf, cd);
- }
- if (TemplateDeclaration td = getEponymousParent(cd))
+ //printf("AggregateDeclaration::emitComment() '%s'\n", ad.toChars());
+ const(char)* com = ad.comment;
+ if (TemplateDeclaration td = getEponymousParent(ad))
{
- toDocBuffer(td, *buf, sc);
+ if (isDitto(td.comment))
+ com = td.comment;
+ else
+ com = Lexer.combineComments(td.comment.toDString(), com.toDString(), true);
}
else
{
- if (!cd.isInterfaceDeclaration() && cd.isAbstract())
- buf.writestring("abstract ");
- buf.printf("%s %s", cd.kind(), cd.toChars());
+ if (ad.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
+ return;
+ if (!ad.comment)
+ return;
}
- int any = 0;
- for (size_t i = 0; i < cd.baseclasses.length; i++)
- {
- BaseClass* bc = (*cd.baseclasses)[i];
- if (bc.sym && bc.sym.ident == Id.Object)
- continue;
- if (any)
- buf.writestring(", ");
- else
- {
- buf.writestring(": ");
- any = 1;
- }
+ if (!com)
+ return;
+ emit(sc, ad, com);
+ }
- if (bc.sym)
- {
- buf.printf("$(DDOC_PSUPER_SYMBOL %s)", bc.sym.toPrettyChars());
- }
- else
- {
- HdrGenState hgs;
- toCBuffer(bc.type, *buf, null, hgs);
- }
+ override void visit(TemplateDeclaration td)
+ {
+ //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", td.toChars(), td.kind());
+ if (td.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
+ return;
+ if (!td.comment)
+ return;
+ if (Dsymbol ss = getEponymousMember(td))
+ {
+ ss.accept(this);
+ return;
}
- buf.writestring(";\n");
+ emit(sc, td, td.comment);
}
override void visit(EnumDeclaration ed)
{
- if (!ed.ident)
+ if (ed.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
return;
- buf.printf("%s %s", ed.kind(), ed.toChars());
- if (ed.memtype)
+ if (ed.isAnonymous() && ed.members)
{
- buf.writestring(": $(DDOC_ENUM_BASETYPE ");
- HdrGenState hgs;
- toCBuffer(ed.memtype, *buf, null, hgs);
- buf.writestring(")");
+ for (size_t i = 0; i < ed.members.length; i++)
+ {
+ Dsymbol s = (*ed.members)[i];
+ emitComment(s, *buf, sc);
+ }
+ return;
}
- buf.writestring(";\n");
+ if (!ed.comment)
+ return;
+ if (ed.isAnonymous())
+ return;
+ emit(sc, ed, ed.comment);
}
override void visit(EnumMember em)
{
- if (!em.ident)
+ //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", em, em.toChars(), em.comment);
+ if (em.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
return;
- buf.writestring(em.toChars());
- }
- }
-
- scope ToDocBuffer v = new ToDocBuffer(buf, sc);
- s.accept(v);
-}
-
-/***********************************************************
- */
-public
-struct DocComment
-{
- Sections sections; // Section*[]
- Section summary;
- Section copyright;
- Section macros;
- MacroTable* pmacrotable;
- Escape* escapetable;
- Dsymbols a;
-
- static DocComment* parse(Dsymbol s, const(char)* comment)
- {
- //printf("parse(%s): '%s'\n", s.toChars(), comment);
- auto dc = new DocComment();
- dc.a.push(s);
- if (!comment)
- return dc;
- dc.parseSections(comment);
- for (size_t i = 0; i < dc.sections.length; i++)
- {
- Section sec = dc.sections[i];
- if (iequals("copyright", sec.name))
- {
- dc.copyright = sec;
- }
- if (iequals("macros", sec.name))
- {
- dc.macros = sec;
- }
+ if (!em.comment)
+ return;
+ emit(sc, em, em.comment);
}
- return dc;
- }
- /************************************************
- * Parse macros out of Macros: section.
- * Macros are of the form:
- * name1 = value1
- *
- * name2 = value2
- */
- extern(D) static void parseMacros(
- Escape* escapetable, ref MacroTable pmacrotable, const(char)[] m)
- {
- const(char)* p = m.ptr;
- size_t len = m.length;
- const(char)* pend = p + len;
- const(char)* tempstart = null;
- size_t templen = 0;
- const(char)* namestart = null;
- size_t namelen = 0; // !=0 if line continuation
- const(char)* textstart = null;
- size_t textlen = 0;
- while (p < pend)
+ override void visit(AttribDeclaration ad)
{
- // Skip to start of macro
- while (1)
+ //printf("AttribDeclaration::emitComment(sc = %p)\n", sc);
+ /* A general problem with this,
+ * illustrated by https://issues.dlang.org/show_bug.cgi?id=2516
+ * is that attributes are not transmitted through to the underlying
+ * member declarations for template bodies, because semantic analysis
+ * is not done for template declaration bodies
+ * (only template instantiations).
+ * Hence, Ddoc omits attributes from template members.
+ */
+ Dsymbols* d = ad.include(null);
+ if (d)
{
- if (p >= pend)
- goto Ldone;
- switch (*p)
+ for (size_t i = 0; i < d.length; i++)
{
- case ' ':
- case '\t':
- p++;
- continue;
- case '\r':
- case '\n':
- p++;
- goto Lcont;
- default:
- if (isIdStart(p))
- break;
- if (namelen)
- goto Ltext; // continuation of prev macro
- goto Lskipline;
- }
- break;
- }
- tempstart = p;
- while (1)
- {
- if (p >= pend)
- goto Ldone;
- if (!isIdTail(p))
- break;
- p += utfStride(p);
- }
- templen = p - tempstart;
- while (1)
- {
- if (p >= pend)
- goto Ldone;
- if (!(*p == ' ' || *p == '\t'))
- break;
- p++;
- }
- if (*p != '=')
- {
- if (namelen)
- goto Ltext; // continuation of prev macro
- goto Lskipline;
- }
- p++;
- if (p >= pend)
- goto Ldone;
- if (namelen)
- {
- // Output existing macro
- L1:
- //printf("macro '%.*s' = '%.*s'\n", cast(int)namelen, namestart, cast(int)textlen, textstart);
- if (iequals("ESCAPES", namestart[0 .. namelen]))
- parseEscapes(escapetable, textstart[0 .. textlen]);
- else
- pmacrotable.define(namestart[0 .. namelen], textstart[0 .. textlen]);
- namelen = 0;
- if (p >= pend)
- break;
- }
- namestart = tempstart;
- namelen = templen;
- while (p < pend && (*p == ' ' || *p == '\t'))
- p++;
- textstart = p;
- Ltext:
- while (p < pend && *p != '\r' && *p != '\n')
- p++;
- textlen = p - textstart;
- p++;
- //printf("p = %p, pend = %p\n", p, pend);
- Lcont:
- continue;
- Lskipline:
- // Ignore this line
- while (p < pend && *p != '\r' && *p != '\n')
- p++;
+ Dsymbol s = (*d)[i];
+ //printf("AttribDeclaration::emitComment %s\n", s.toChars());
+ emitComment(s, *buf, sc);
+ }
+ }
}
- Ldone:
- if (namelen)
- goto L1; // write out last one
- }
- /**************************************
- * Parse escapes of the form:
- * /c/string/
- * where c is a single character.
- * Multiple escapes can be separated
- * by whitespace and/or commas.
- */
- static void parseEscapes(Escape* escapetable, const(char)[] text)
- {
- if (!escapetable)
+ override void visit(VisibilityDeclaration pd)
{
- escapetable = new Escape();
- memset(escapetable, 0, Escape.sizeof);
+ if (pd.decl)
+ {
+ Scope* scx = sc;
+ sc = sc.copy();
+ sc.visibility = pd.visibility;
+ visit(cast(AttribDeclaration)pd);
+ scx.lastdc = sc.lastdc;
+ sc = sc.pop();
+ }
}
- //printf("parseEscapes('%.*s') pescapetable = %p\n", cast(int)text.length, text.ptr, escapetable);
- const(char)* p = text.ptr;
- const(char)* pend = p + text.length;
- while (1)
+
+ override void visit(ConditionalDeclaration cd)
{
- while (1)
+ //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc);
+ if (cd.condition.inc != Include.notComputed)
{
- if (p + 4 >= pend)
- return;
- if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ','))
- break;
- p++;
- }
- if (p[0] != '/' || p[2] != '/')
+ visit(cast(AttribDeclaration)cd);
return;
- char c = p[1];
- p += 3;
- const(char)* start = p;
- while (1)
+ }
+ /* If generating doc comment, be careful because if we're inside
+ * a template, then include(null) will fail.
+ */
+ Dsymbols* d = cd.decl ? cd.decl : cd.elsedecl;
+ for (size_t i = 0; i < d.length; i++)
{
- if (p >= pend)
- return;
- if (*p == '/')
- break;
- p++;
+ Dsymbol s = (*d)[i];
+ emitComment(s, *buf, sc);
}
- size_t len = p - start;
- char* s = cast(char*)memcpy(mem.xmalloc(len + 1), start, len);
- s[len] = 0;
- escapetable.strings[c] = s[0 .. len];
- //printf("\t%c = '%s'\n", c, s);
- p++;
}
}
- /*****************************************
- * Parse next paragraph out of *pcomment.
- * Update *pcomment to point past paragraph.
- * Returns NULL if no more paragraphs.
- * If paragraph ends in 'identifier:',
- * then (*pcomment)[0 .. idlen] is the identifier.
- */
- void parseSections(const(char)* comment)
+ scope EmitComment v = new EmitComment(buf, sc);
+ if (!s)
+ v.emit(sc, null, null);
+ else
+ s.accept(v);
+}
+
+void toDocBuffer(Dsymbol s, ref OutBuffer buf, Scope* sc)
+{
+ extern (C++) final class ToDocBuffer : Visitor
{
- const(char)* p;
- const(char)* pstart;
- const(char)* pend;
- const(char)* idstart = null; // dead-store to prevent spurious warning
- size_t idlen;
- const(char)* name = null;
- size_t namelen = 0;
- //printf("parseSections('%s')\n", comment);
- p = comment;
- while (*p)
+ alias visit = Visitor.visit;
+ public:
+ OutBuffer* buf;
+ Scope* sc;
+
+ extern (D) this(ref OutBuffer buf, Scope* sc) scope
{
- const(char)* pstart0 = p;
- p = skipwhitespace(p);
- pstart = p;
- pend = p;
+ this.buf = &buf;
+ this.sc = sc;
+ }
- // Undo indent if starting with a list item
- if ((*p == '-' || *p == '+' || *p == '*') && (*(p+1) == ' ' || *(p+1) == '\t'))
- pstart = pstart0;
- else
+ override void visit(Dsymbol s)
+ {
+ //printf("Dsymbol::toDocbuffer() %s\n", s.toChars());
+ HdrGenState hgs;
+ hgs.ddoc = true;
+ toCBuffer(s, *buf, hgs);
+ }
+
+ void prefix(Dsymbol s)
+ {
+ if (s.isDeprecated())
+ buf.writestring("deprecated ");
+ if (Declaration d = s.isDeclaration())
{
- const(char)* pitem = p;
- while (*pitem >= '0' && *pitem <= '9')
- ++pitem;
- if (pitem > p && *pitem == '.' && (*(pitem+1) == ' ' || *(pitem+1) == '\t'))
- pstart = pstart0;
+ emitVisibility(*buf, d);
+ if (d.isStatic())
+ buf.writestring("static ");
+ else if (d.isFinal())
+ buf.writestring("final ");
+ else if (d.isAbstract())
+ buf.writestring("abstract ");
+
+ if (d.isFuncDeclaration()) // functionToBufferFull handles this
+ return;
+
+ if (d.isImmutable())
+ buf.writestring("immutable ");
+ if (d.storage_class & STC.shared_)
+ buf.writestring("shared ");
+ if (d.isWild())
+ buf.writestring("inout ");
+ if (d.isConst())
+ buf.writestring("const ");
+
+ if (d.isSynchronized())
+ buf.writestring("synchronized ");
+
+ if (d.storage_class & STC.manifest)
+ buf.writestring("enum ");
+
+ // Add "auto" for the untyped variable in template members
+ if (!d.type && d.isVarDeclaration() &&
+ !d.isImmutable() && !(d.storage_class & STC.shared_) && !d.isWild() && !d.isConst() &&
+ !d.isSynchronized())
+ {
+ buf.writestring("auto ");
+ }
}
+ }
- /* Find end of section, which is ended by one of:
- * 'identifier:' (but not inside a code section)
- * '\0'
- */
- idlen = 0;
- int inCode = 0;
- while (1)
+ override void visit(Import i)
+ {
+ HdrGenState hgs;
+ hgs.ddoc = true;
+ emitVisibility(*buf, i);
+ toCBuffer(i, *buf, hgs);
+ }
+
+ override void visit(Declaration d)
+ {
+ if (!d.ident)
+ return;
+ TemplateDeclaration td = getEponymousParent(d);
+ //printf("Declaration::toDocbuffer() %s, originalType = %s, td = %s\n", d.toChars(), d.originalType ? d.originalType.toChars() : "--", td ? td.toChars() : "--");
+ HdrGenState hgs;
+ hgs.ddoc = true;
+ if (d.isDeprecated())
+ buf.writestring("$(DEPRECATED ");
+ prefix(d);
+ if (d.type)
{
- // Check for start/end of a code section
- if (*p == '-' || *p == '`' || *p == '~')
+ Type origType = d.originalType ? d.originalType : d.type;
+ if (origType.ty == Tfunction)
{
- char c = *p;
- int numdash = 0;
- while (*p == c)
- {
- ++numdash;
- p++;
- }
- // BUG: handle UTF PS and LS too
- if ((!*p || *p == '\r' || *p == '\n' || (!inCode && c != '-')) && numdash >= 3)
- {
- inCode = inCode == c ? false : c;
- if (inCode)
- {
- // restore leading indentation
- while (pstart0 < pstart && isIndentWS(pstart - 1))
- --pstart;
- }
- }
- pend = p;
+ functionToBufferFull(cast(TypeFunction)origType, *buf, d.ident, hgs, td);
}
- if (!inCode && isIdStart(p))
+ else
+ toCBuffer(origType, *buf, d.ident, hgs);
+ }
+ else
+ buf.writestring(d.ident.toString());
+ if (d.isVarDeclaration() && td)
+ {
+ buf.writeByte('(');
+ if (td.origParameters && td.origParameters.length)
{
- const(char)* q = p + utfStride(p);
- while (isIdTail(q))
- q += utfStride(q);
-
- // Detected tag ends it
- if (*q == ':' && isupper(*p)
- && (isspace(q[1]) || q[1] == 0))
+ for (size_t i = 0; i < td.origParameters.length; i++)
{
- idlen = q - p;
- idstart = p;
- for (pend = p; pend > pstart; pend--)
- {
- if (pend[-1] == '\n')
- break;
- }
- p = q + 1;
- break;
+ if (i)
+ buf.writestring(", ");
+ toCBuffer((*td.origParameters)[i], *buf, hgs);
}
}
- while (1)
+ buf.writeByte(')');
+ }
+ // emit constraints if declaration is a templated declaration
+ if (td && td.constraint)
+ {
+ bool noFuncDecl = td.isFuncDeclaration() is null;
+ if (noFuncDecl)
+ {
+ buf.writestring("$(DDOC_CONSTRAINT ");
+ }
+
+ toCBuffer(td.constraint, *buf, hgs);
+
+ if (noFuncDecl)
{
- if (!*p)
- goto L1;
- if (*p == '\n')
- {
- p++;
- if (*p == '\n' && !summary && !namelen && !inCode)
- {
- pend = p;
- p++;
- goto L1;
- }
- break;
- }
- p++;
- pend = p;
+ buf.writestring(")");
}
- p = skipwhitespace(p);
}
- L1:
- if (namelen || pstart < pend)
+ if (d.isDeprecated())
+ buf.writestring(")");
+ buf.writestring(";\n");
+ }
+
+ override void visit(AliasDeclaration ad)
+ {
+ //printf("AliasDeclaration::toDocbuffer() %s\n", ad.toChars());
+ if (!ad.ident)
+ return;
+ if (ad.isDeprecated())
+ buf.writestring("deprecated ");
+ emitVisibility(*buf, ad);
+ buf.printf("alias %s = ", ad.toChars());
+ if (Dsymbol s = ad.aliassym) // ident alias
{
- Section s;
- if (iequals("Params", name[0 .. namelen]))
- s = new ParamSection();
- else if (iequals("Macros", name[0 .. namelen]))
- s = new MacroSection();
+ prettyPrintDsymbol(s, ad.parent);
+ }
+ else if (Type type = dmd.dsymbolsem.getType(ad)) // type alias
+ {
+ if (type.ty == Tclass || type.ty == Tstruct || type.ty == Tenum)
+ {
+ import dmd.typesem : toDsymbol;
+ if (Dsymbol s = type.toDsymbol(null)) // elaborate type
+ prettyPrintDsymbol(s, ad.parent);
+ else
+ buf.writestring(type.toChars());
+ }
else
- s = new Section();
- s.name = name[0 .. namelen];
- s.body_ = pstart[0 .. pend - pstart];
- s.nooutput = 0;
- //printf("Section: '%.*s' = '%.*s'\n", cast(int)s.namelen, s.name, cast(int)s.bodylen, s.body);
- sections.push(s);
- if (!summary && !namelen)
- summary = s;
+ {
+ // simple type
+ buf.writestring(type.toChars());
+ }
}
- if (idlen)
+ buf.writestring(";\n");
+ }
+
+ void parentToBuffer(Dsymbol s)
+ {
+ if (s && !s.isPackage() && !s.isModule())
{
- name = idstart;
- namelen = idlen;
+ parentToBuffer(s.parent);
+ buf.writestring(s.toChars());
+ buf.writestring(".");
}
- else
+ }
+
+ static bool inSameModule(Dsymbol s, Dsymbol p) @safe
+ {
+ for (; s; s = s.parent)
{
- name = null;
- namelen = 0;
- if (!*p)
+ if (s.isModule())
+ break;
+ }
+ for (; p; p = p.parent)
+ {
+ if (p.isModule())
break;
}
+ return s == p;
}
- }
- void writeSections(Scope* sc, Dsymbols* a, ref OutBuffer buf)
- {
- assert(a.length);
- //printf("DocComment::writeSections()\n");
- Loc loc = (*a)[0].loc;
- if (Module m = (*a)[0].isModule())
+ void prettyPrintDsymbol(Dsymbol s, Dsymbol parent)
{
- if (m.md)
- loc = m.md.loc;
+ if (s.parent && (s.parent == parent)) // in current scope -> naked name
+ {
+ buf.writestring(s.toChars());
+ }
+ else if (!inSameModule(s, parent)) // in another module -> full name
+ {
+ buf.writestring(s.toPrettyChars());
+ }
+ else // nested in a type in this module -> full name w/o module name
+ {
+ // if alias is nested in a user-type use module-scope lookup
+ if (!parent.isModule() && !parent.isPackage())
+ buf.writestring(".");
+ parentToBuffer(s.parent);
+ buf.writestring(s.toChars());
+ }
}
- size_t offset1 = buf.length;
- buf.writestring("$(DDOC_SECTIONS ");
- size_t offset2 = buf.length;
- for (size_t i = 0; i < sections.length; i++)
+
+ override void visit(AggregateDeclaration ad)
{
- Section sec = sections[i];
- if (sec.nooutput)
- continue;
- //printf("Section: '%.*s' = '%.*s'\n", cast(int)sec.namelen, sec.name, cast(int)sec.bodylen, sec.body);
- if (!sec.name.length && i == 0)
+ if (!ad.ident)
+ return;
+ version (none)
{
- buf.writestring("$(DDOC_SUMMARY ");
- size_t o = buf.length;
- buf.write(sec.body_);
- escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
- highlightText(sc, a, loc, buf, o);
- buf.writestring(")");
+ emitVisibility(buf, ad);
+ }
+ buf.printf("%s %s", ad.kind(), ad.toChars());
+ buf.writestring(";\n");
+ }
+
+ override void visit(StructDeclaration sd)
+ {
+ //printf("StructDeclaration::toDocbuffer() %s\n", sd.toChars());
+ if (!sd.ident)
+ return;
+ version (none)
+ {
+ emitVisibility(buf, sd);
+ }
+ if (TemplateDeclaration td = getEponymousParent(sd))
+ {
+ toDocBuffer(td, *buf, sc);
}
else
- sec.write(loc, &this, sc, a, buf);
+ {
+ buf.printf("%s %s", sd.kind(), sd.toChars());
+ }
+ buf.writestring(";\n");
}
- for (size_t i = 0; i < a.length; i++)
+
+ override void visit(ClassDeclaration cd)
{
- Dsymbol s = (*a)[i];
- if (Dsymbol td = getEponymousParent(s))
- s = td;
- for (UnitTestDeclaration utd = s.ddocUnittest; utd; utd = utd.ddocUnittest)
+ //printf("ClassDeclaration::toDocbuffer() %s\n", cd.toChars());
+ if (!cd.ident)
+ return;
+ version (none)
{
- if (utd.visibility.kind == Visibility.Kind.private_ || !utd.comment || !utd.fbody)
+ emitVisibility(*buf, cd);
+ }
+ if (TemplateDeclaration td = getEponymousParent(cd))
+ {
+ toDocBuffer(td, *buf, sc);
+ }
+ else
+ {
+ if (!cd.isInterfaceDeclaration() && cd.isAbstract())
+ buf.writestring("abstract ");
+ buf.printf("%s %s", cd.kind(), cd.toChars());
+ }
+ int any = 0;
+ for (size_t i = 0; i < cd.baseclasses.length; i++)
+ {
+ BaseClass* bc = (*cd.baseclasses)[i];
+ if (bc.sym && bc.sym.ident == Id.Object)
continue;
- // Strip whitespaces to avoid showing empty summary
- const(char)* c = utd.comment;
- while (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')
- ++c;
- buf.writestring("$(DDOC_EXAMPLES ");
- size_t o = buf.length;
- buf.writestring(cast(char*)c);
- if (utd.codedoc)
+ if (any)
+ buf.writestring(", ");
+ else
{
- auto codedoc = utd.codedoc.stripLeadingNewlines;
- size_t n = getCodeIndent(codedoc);
- while (n--)
- buf.writeByte(' ');
- buf.writestring("----\n");
- buf.writestring(codedoc);
- buf.writestring("----\n");
- highlightText(sc, a, loc, buf, o);
+ buf.writestring(": ");
+ any = 1;
+ }
+
+ if (bc.sym)
+ {
+ buf.printf("$(DDOC_PSUPER_SYMBOL %s)", bc.sym.toPrettyChars());
+ }
+ else
+ {
+ HdrGenState hgs;
+ toCBuffer(bc.type, *buf, null, hgs);
}
+ }
+ buf.writestring(";\n");
+ }
+
+ override void visit(EnumDeclaration ed)
+ {
+ if (!ed.ident)
+ return;
+ buf.printf("%s %s", ed.kind(), ed.toChars());
+ if (ed.memtype)
+ {
+ buf.writestring(": $(DDOC_ENUM_BASETYPE ");
+ HdrGenState hgs;
+ toCBuffer(ed.memtype, *buf, null, hgs);
buf.writestring(")");
}
+ buf.writestring(";\n");
}
- if (buf.length == offset2)
+
+ override void visit(EnumMember em)
{
- /* Didn't write out any sections, so back out last write
- */
- buf.setsize(offset1);
- buf.writestring("\n");
+ if (!em.ident)
+ return;
+ buf.writestring(em.toChars());
}
- else
- buf.writestring(")");
}
+
+ scope ToDocBuffer v = new ToDocBuffer(buf, sc);
+ s.accept(v);
}
/*****************************************
/****************************************************
*/
-Parameter isEponymousFunctionParameter(Dsymbols* a, const(char)[] p) @safe
+Parameter isEponymousFunctionParameter(Dsymbols* a, const(char)[] p)
{
foreach (Dsymbol dsym; *a)
{
TemplateDeclaration td = dsym.isTemplateDeclaration();
+ td.computeOneMember();
if (td && td.onemember)
{
/* Case 1: we refer to a template declaration inside the template
{
size_t iStart = i + 1;
size_t iEnd = iStart;
- if (iEnd >= buf.length || buf[iEnd] != '[' || (iEnd+1 < buf.length && buf[iEnd+1] == ']'))
+ if (iEnd >= buf.length)
+ return i;
+ if (buf[iEnd] != '[' || (iEnd+1 < buf.length && buf[iEnd+1] == ']'))
{
// collapsed reference [foo][] or shortcut reference [foo]
iStart = delimiter.iStart + delimiter.count - 1;
import core.stdc.stdio;
import core.stdc.string;
import dmd.aggregate;
-import dmd.arraytypes;
import dmd.astenums;
import dmd.attrib;
import dmd.ctorflow;
import dmd.doc;
import dmd.dstruct;
import dmd.dsymbol;
-import dmd.dsymbolsem;
import dmd.dtemplate;
-import dmd.expression;
-import dmd.errors;
import dmd.errorsink;
import dmd.func;
import dmd.globals;
-import dmd.id;
import dmd.identifier;
-import dmd.importc;
-import dmd.location;
-import dmd.common.outbuffer;
import dmd.root.rmem;
-import dmd.root.speller;
import dmd.statement;
-import dmd.target;
-import dmd.tokens;
//version=LOGSEARCH;
bool canFree; /// is on free list
bool fullinst; /// fully instantiate templates
bool ctfeBlock; /// inside a `if (__ctfe)` block
+
+ /**
+ Is any symbol this scope is applied to known to have a compile time only accessible context.
+ This is not the same as `skipCodegen` nor can it be used to contribute to this.
+ It is meant for analysis engines to be conservative in what functions they analyse,
+ to prevent perceived false positives for meta-programming heavy code.
+ */
+ bool knownACompileTimeOnlyContext;
}
private extern (D) struct NonFlagBitFields
return new Scope();
}
- extern (D) static Scope* createGlobal(Module _module, ErrorSink eSink)
- {
- Scope* sc = Scope.alloc();
- *sc = Scope.init;
- sc._module = _module;
- sc.minst = _module;
- sc.scopesym = new ScopeDsymbol();
- sc.scopesym.symtab = new DsymbolTable();
- sc.eSink = eSink;
- assert(eSink);
- // Add top level package as member of this global scope
- Dsymbol m = _module;
- while (m.parent)
- m = m.parent;
- m.addMember(null, sc.scopesym);
- m.parent = null; // got changed by addMember()
- sc.previews.setFromParams(global.params);
-
- if (_module.filetype == FileType.c)
- sc.inCfile = true;
- // Create the module scope underneath the global scope
- sc = sc.push(_module);
- sc.parent = _module;
- return sc;
- }
-
extern (D) Scope* copy()
{
Scope* sc = Scope.alloc();
s.ctfeBlock = this.ctfeBlock;
s.previews = this.previews;
s.lastdc = null;
+ s.knownACompileTimeOnlyContext = this.knownACompileTimeOnlyContext;
assert(&this != s);
return s;
}
return pop();
}
-
- /*******************************
- * Merge results of `ctorflow` into `this`.
- * Params:
- * loc = for error messages
- * ctorflow = flow results to merge in
- */
- extern (D) void merge(Loc loc, const ref CtorFlow ctorflow)
- {
- if (!mergeCallSuper(this.ctorflow.callSuper, ctorflow.callSuper))
- error(loc, "one path skips constructor");
-
- const fies = ctorflow.fieldinit;
- 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)
- {
- 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());
- }
- }
- }
-
- /************************************
- * Perform unqualified name lookup by following the chain of scopes up
- * until found.
- *
- * Params:
- * loc = location to use for error messages
- * ident = name to look up
- * pscopesym = if supplied and name is found, set to scope that ident was found in, otherwise set to null
- * flags = modify search based on flags
- *
- * Returns:
- * symbol if found, null if not
- */
- extern (C++) Dsymbol search(Loc loc, Identifier ident, out Dsymbol pscopesym, SearchOptFlags flags = SearchOpt.all)
- {
- version (LOGSEARCH)
- {
- printf("Scope.search(%p, '%s' flags=x%x)\n", &this, ident.toChars(), flags);
- // Print scope chain
- for (Scope* sc = &this; sc; sc = sc.enclosing)
- {
- if (!sc.scopesym)
- continue;
- printf("\tscope %s\n", sc.scopesym.toChars());
- }
-
- static void printMsg(string txt, Dsymbol s)
- {
- printf("%.*s %s.%s, kind = '%s'\n", cast(int)txt.length, txt.ptr,
- s.parent ? s.parent.toChars() : "", s.toChars(), s.kind());
- }
- }
-
- // This function is called only for unqualified lookup
- assert(!(flags & (SearchOpt.localsOnly | SearchOpt.importsOnly)));
-
- /* If ident is "start at module scope", only look at module scope
- */
- if (ident == Id.empty)
- {
- // Look for module scope
- for (Scope* sc = &this; sc; sc = sc.enclosing)
- {
- assert(sc != sc.enclosing);
- if (!sc.scopesym)
- continue;
- if (Dsymbol s = sc.scopesym.isModule())
- {
- //printMsg("\tfound", s);
- pscopesym = sc.scopesym;
- return s;
- }
- }
- return null;
- }
-
- Dsymbol checkAliasThis(AggregateDeclaration ad, Identifier ident, SearchOptFlags flags, Expression* exp)
- {
- import dmd.mtype;
- if (!ad || !ad.aliasthis)
- return null;
-
- Declaration decl = ad.aliasthis.sym.isDeclaration();
- if (!decl)
- return null;
-
- Type t = decl.type;
- ScopeDsymbol sds;
- TypeClass tc;
- TypeStruct ts;
- switch(t.ty)
- {
- case Tstruct:
- ts = cast(TypeStruct)t;
- sds = ts.sym;
- break;
- case Tclass:
- tc = cast(TypeClass)t;
- sds = tc.sym;
- break;
- case Tinstance:
- sds = (cast(TypeInstance)t).tempinst;
- break;
- case Tenum:
- sds = (cast(TypeEnum)t).sym;
- break;
- default: break;
- }
-
- if (!sds)
- return null;
-
- Dsymbol ret = sds.search(loc, ident, flags);
- if (ret)
- {
- *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
- *exp = new DotIdExp(loc, *exp, ident);
- return ret;
- }
-
- if (!ts && !tc)
- return null;
-
- Dsymbol s;
- *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
- if (ts && !(ts.att & AliasThisRec.tracing))
- {
- ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracing);
- s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp);
- ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracing);
- }
- else if(tc && !(tc.att & AliasThisRec.tracing))
- {
- tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracing);
- s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp);
- tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracing);
- }
- return s;
- }
-
- Dsymbol searchScopes(SearchOptFlags flags)
- {
- for (Scope* sc = &this; sc; sc = sc.enclosing)
- {
- assert(sc != sc.enclosing);
- if (!sc.scopesym)
- continue;
- //printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc.scopesym.toChars(), sc.scopesym.kind(), flags);
-
- if (sc.scopesym.isModule())
- flags |= SearchOpt.unqualifiedModule; // tell Module.search() that SearchOpt.localsOnly is to be obeyed
- else if (sc.inCfile && sc.scopesym.isStructDeclaration())
- continue; // C doesn't have struct scope
-
- if (Dsymbol s = sc.scopesym.search(loc, ident, flags))
- {
- if (flags & SearchOpt.tagNameSpace)
- {
- // ImportC: if symbol is not a tag, look for it in tag table
- if (!s.isScopeDsymbol())
- {
- auto ps = cast(void*)s in sc._module.tagSymTab;
- if (!ps)
- goto NotFound;
- s = *ps;
- }
- }
- //printMsg("\tfound local", s);
- pscopesym = sc.scopesym;
- return s;
- }
-
- NotFound:
- if (sc.previews.fixAliasThis)
- {
- Expression exp = new ThisExp(loc);
- if (Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp))
- {
- //printf("found aliassym: %s\n", aliasSym.toChars());
- pscopesym = new ExpressionDsymbol(exp);
- return aliasSym;
- }
- }
-
- // Stop when we hit a module, but keep going if that is not just under the global scope
- if (sc.scopesym.isModule() && !(sc.enclosing && !sc.enclosing.enclosing))
- break;
- }
- return null;
- }
-
- if (this.ignoresymbolvisibility)
- flags |= SearchOpt.ignoreVisibility;
-
- // First look in local scopes
- Dsymbol s = searchScopes(flags | SearchOpt.localsOnly);
- version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s);
- if (!s)
- {
- // Second look in imported modules
- s = searchScopes(flags | SearchOpt.importsOnly);
- version (LOGSEARCH) if (s) printMsg("-Scope.search() found import", s);
- }
- return s;
- }
-
- extern (D) Dsymbol search_correct(Identifier ident)
- {
- if (global.gag)
- return null; // don't do it for speculative compiles; too time consuming
-
- /************************************************
- * Given the failed search attempt, try to find
- * one with a close spelling.
- * Params:
- * seed = identifier to search for
- * cost = set to the cost, which rises with each outer scope
- * Returns:
- * Dsymbol if found, null if not
- */
- extern (D) Dsymbol scope_search_fp(const(char)[] seed, out int cost)
- {
- //printf("scope_search_fp('%s')\n", seed);
- /* If not in the lexer's string table, it certainly isn't in the symbol table.
- * Doing this first is a lot faster.
- */
- if (!seed.length)
- return null;
- Identifier id = Identifier.lookup(seed);
- if (!id)
- return null;
- Scope* sc = &this;
- Module.clearCache();
- Dsymbol scopesym;
- Dsymbol s = sc.search(Loc.initial, id, scopesym, SearchOpt.ignoreErrors);
- if (!s)
- return null;
-
- // Do not show `@disable`d declarations
- if (auto decl = s.isDeclaration())
- if (decl.storage_class & STC.disable)
- return null;
- // Or `deprecated` ones if we're not in a deprecated scope
- if (s.isDeprecated() && !sc.isDeprecated())
- return null;
-
- for (cost = 0; sc; sc = sc.enclosing, ++cost)
- if (sc.scopesym == scopesym)
- break;
- if (scopesym != s.parent)
- {
- ++cost; // got to the symbol through an import
- if (s.visible().kind == Visibility.Kind.private_)
- return null;
- }
- return s;
- }
-
- Dsymbol scopesym;
- // search for exact name first
- if (auto s = search(Loc.initial, ident, scopesym, SearchOpt.ignoreErrors))
- return s;
- return speller!scope_search_fp(ident.toString());
- }
-
- /************************************
- * Maybe `ident` was a C or C++ name. Check for that,
- * and suggest the D equivalent.
- * Params:
- * ident = unknown identifier
- * Returns:
- * D identifier string if found, null if not
- */
- extern (D) static const(char)* search_correct_C(Identifier ident)
- {
- import dmd.astenums : Twchar;
- TOK tok;
- if (ident == Id.NULL)
- tok = TOK.null_;
- else if (ident == Id.TRUE)
- tok = TOK.true_;
- else if (ident == Id.FALSE)
- tok = TOK.false_;
- else if (ident == Id.unsigned)
- tok = TOK.uns32;
- else if (ident == Id.wchar_t)
- tok = target.c.wchar_tsize == 2 ? TOK.wchar_ : TOK.dchar_;
- else
- return null;
- return Token.toChars(tok);
- }
-
/***************************
* Find the innermost scope with a symbol table.
* Returns:
return null;
}
- /******************************
- * Add symbol s to innermost symbol table.
- * Params:
- * s = symbol to insert
- * Returns:
- * null if already in table, `s` if not
- */
- extern (D) Dsymbol insert(Dsymbol s)
- {
- //printf("insert() %s\n", s.toChars());
- if (VarDeclaration vd = s.isVarDeclaration())
- {
- if (lastVar)
- vd.lastVar = lastVar;
- lastVar = vd;
- }
- else if (WithScopeSymbol ss = s.isWithScopeSymbol())
- {
- if (VarDeclaration vd = ss.withstate.wthis)
- {
- if (lastVar)
- vd.lastVar = lastVar;
- lastVar = vd;
- }
- return null;
- }
-
- auto scopesym = inner().scopesym;
- //printf("\t\tscopesym = %p\n", scopesym);
- if (!scopesym.symtab)
- scopesym.symtab = new DsymbolTable();
- if (!this.inCfile)
- return scopesym.symtabInsert(s);
-
- // ImportC insert
- if (!scopesym.symtabInsert(s)) // if already in table
- {
- Dsymbol s2 = scopesym.symtabLookup(s, s.ident); // s2 is existing entry
- return handleTagSymbols(this, s, s2, scopesym);
- }
- return s; // inserted
- }
-
/********************************************
* Search enclosing scopes for ScopeDsymbol.
*/
// assert(0);
}
}
- /******************************
- */
- extern (D) structalign_t alignment()
- {
- if (aligndecl)
- {
- auto ad = aligndecl.getAlignment(&this);
- return ad.salign;
- }
- else
- {
- structalign_t sa;
- sa.setDefault();
- return sa;
- }
- }
+
@safe @nogc pure nothrow const:
/**********************************
* Checks whether the current scope (or any of its parents) is deprecated.
{
return _module && _module.edition >= edition;
}
+
+ extern (D) bool isKnownToHaveACompileTimeContext()
+ {
+ return this.knownACompileTimeOnlyContext || this.intypeof != 0 || this.traitsCompiles || this.ctfe ||
+ this.condition || this.inTemplateConstraint || this.ctfeBlock;
+ }
}
import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
-import dmd.attrib;
-import dmd.declaration;
import dmd.dmodule;
-import dmd.dscope;
import dmd.dsymbol;
-import dmd.dtemplate;
-import dmd.expression;
import dmd.func;
import dmd.id;
import dmd.identifier;
import dmd.location;
import dmd.mtype;
-import dmd.opover;
-import dmd.target;
-import dmd.tokens;
-import dmd.typinf;
import dmd.visitor;
enum StructFlags : int
import core.stdc.stdlib;
import dmd.aggregate;
-import dmd.aliasthis;
import dmd.arraytypes;
import dmd.attrib;
import dmd.astenums;
import dmd.dversion;
import dmd.dscope;
import dmd.dstruct;
-import dmd.dsymbolsem : toAlias;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
-import dmd.expressionsem : getDsymbol;
import dmd.func;
-import dmd.id;
import dmd.identifier;
-import dmd.init;
-import dmd.lexer;
import dmd.location;
import dmd.mtype;
import dmd.nspace;
semantic2done, // semantic2() done
semantic3, // semantic3() started
semantic3done, // semantic3() done
- inline, // inline started
- inlinedone, // inline done
+ inlinePragma, // inline pragma(inline, true) functions started
+ inlineAll, // inline all functions started
obj, // toObjFile() run
}
return toChars();
}
- override bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
- const s = o.isDsymbol();
- if (!s)
- return false;
- // Overload sets don't have an ident
- // Function-local declarations may have identical names
- // if they are declared in different scopes
- if (s && ident && s.ident && ident.equals(s.ident) && localNum == s.localNum)
- return true;
- return false;
- }
-
final bool isAnonymous() const
{
return ident is null || ident.isAnonymous;
return parent.toParentDeclImpl(localOnly);
}
- /**
- * Returns the declaration scope scope of `this` unless any of the symbols
- * `p1` or `p2` resides in its enclosing instantiation scope then the
- * latter is returned.
- */
- final Dsymbol toParentP(Dsymbol p1, Dsymbol p2 = null)
- {
- return followInstantiationContext(p1, p2) ? toParent2() : toParentLocal();
- }
-
final inout(TemplateInstance) isInstantiated() inout
{
if (!parent)
return parent.isInstantiated();
}
- /***
- * Returns true if any of the symbols `p1` or `p2` resides in the enclosing
- * instantiation scope of `this`.
- */
- final bool followInstantiationContext(Dsymbol p1, Dsymbol p2 = null)
- {
- static bool has2This(Dsymbol s)
- {
- if (auto f = s.isFuncDeclaration())
- return f.hasDualContext;
- if (auto ad = s.isAggregateDeclaration())
- return ad.vthis2 !is null;
- return false;
- }
-
- if (has2This(this))
- {
- assert(p1);
- auto outer = toParent();
- while (outer)
- {
- auto ti = outer.isTemplateInstance();
- if (!ti)
- break;
- foreach (oarg; *ti.tiargs)
- {
- auto sa = getDsymbol(oarg);
- if (!sa)
- continue;
- sa = sa.toAlias().toParent2();
- if (!sa)
- continue;
- if (sa == p1)
- return true;
- if (p2 && sa == p2)
- return true;
- }
- outer = ti.tempdecl.toParent();
- }
- return false;
- }
- return false;
- }
-
// Check if this function is a member of a template which has only been
// instantiated speculatively, eg from inside is(typeof()).
// Return the speculative template instance it is part of,
return "symbol";
}
- bool overloadInsert(Dsymbol s)
- {
- //printf("Dsymbol::overloadInsert('%s')\n", s.toChars());
- return false;
- }
-
- /*********************************
- * Returns:
- * SIZE_INVALID when the size cannot be determined
- */
- ulong size(Loc loc)
- {
- .error(loc, "%s `%s` symbol `%s` has no size", kind, toPrettyChars, toChars());
- return SIZE_INVALID;
- }
-
bool isforwardRef()
{
return false;
return ad ? ad.isClassDeclaration() : null;
}
- // is this a type?
- Type getType()
- {
- return null;
- }
-
// need a 'this' pointer?
bool needThis()
{
assert(0);
}
- void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories)
- {
- }
-
/****************************************
* Add documentation comment to Dsymbol.
* Ignore NULL comments.
return sds;
}
- extern (D) final OverloadSet mergeOverloadSet(Identifier ident, OverloadSet os, Dsymbol s)
- {
- if (!os)
- {
- os = new OverloadSet(ident);
- os.parent = this;
- }
- if (OverloadSet os2 = s.isOverloadSet())
- {
- // Merge the cross-module overload set 'os2' into 'os'
- if (os.a.length == 0)
- {
- os.a.setDim(os2.a.length);
- memcpy(os.a.tdata(), os2.a.tdata(), (os.a[0]).sizeof * os2.a.length);
- }
- else
- {
- for (size_t i = 0; i < os2.a.length; i++)
- {
- os = mergeOverloadSet(ident, os, os2.a[i]);
- }
- }
- }
- else
- {
- assert(s.isOverloadable());
- /* Don't add to os[] if s is alias of previous sym
- */
- for (size_t j = 0; j < os.a.length; j++)
- {
- Dsymbol s2 = os.a[j];
- if (s.toAlias() == s2.toAlias())
- {
- if (s2.isDeprecated() || (s2.visible() < s.visible() && s.visible().kind != Visibility.Kind.none))
- {
- os.a[j] = s;
- }
- goto Lcontinue;
- }
- }
- os.push(s);
- Lcontinue:
- }
- return os;
- }
-
void importScope(Dsymbol s, Visibility visibility) nothrow
{
//printf("%s.ScopeDsymbol::importScope(%s, %d)\n", toChars(), s.toChars(), visibility);
semantic2done, // semantic2() done
semantic3, // semantic3() started
semantic3done, // semantic3() done
- inline_, // inline started
- inlinedone, // inline done
+ inlinePragma, // inline pragma(inline, true) functions started
+ inlineAll, // inline all functions started
obj // toObjFile() run
};
CPPNamespaceDeclaration* cppnamespace(CPPNamespaceDeclaration* ns);
UserAttributeDeclaration* userAttribDecl(UserAttributeDeclaration* uad);
virtual const char *toPrettyCharsHelper(); // helper to print fully qualified (template) arguments
- bool equals(const RootObject * const o) const override;
bool isAnonymous() const;
Module *getModule();
bool isCsymbol();
virtual Identifier *getIdent();
virtual const char *toPrettyChars(bool QualifyTypes = false);
virtual const char *kind() const;
- virtual bool overloadInsert(Dsymbol *s);
- virtual uinteger_t size(Loc loc);
virtual bool isforwardRef();
virtual AggregateDeclaration *isThis(); // is a 'this' required to access the member
virtual bool isExport() const; // is Dsymbol exported?
AggregateDeclaration *isMemberDecl(); // is toParentDecl() an AggregateDeclaration?
AggregateDeclaration *isMemberLocal(); // is toParentLocal() an AggregateDeclaration?
ClassDeclaration *isClassMember(); // isMember() is a ClassDeclaration?
- virtual Type *getType(); // is this a type?
virtual bool needThis(); // need a 'this' pointer?
virtual Visibility visible();
virtual Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees
- virtual void addObjcSymbols(ClassDeclarations *, ClassDeclarations *) { }
virtual void addComment(const utf8_t *comment);
const utf8_t *comment(); // current value of comment
void setScope(Dsymbol *d, Scope *sc);
void importAll(Dsymbol *d, Scope *sc);
bool hasPointers(Dsymbol *d);
+ Type *getType(Dsymbol *d);
+ uinteger_t size(Dsymbol *ds, Loc loc);
+ void semantic3OnDependencies(Module *m);
+ void addDeferredSemantic(Dsymbol *s);
+ void addDeferredSemantic2(Dsymbol *s);
+ void addDeferredSemantic3(Dsymbol *s);
+ void runDeferredSemantic();
+ void runDeferredSemantic2();
+ void runDeferredSemantic3();
+ bool isOverlappedWith(VarDeclaration *vd, VarDeclaration *v);
+ Dsymbol* search(Scope *sc, Loc loc, Identifier* ident, Dsymbol*& pscopesym, uint32_t flags = 0u);
+ Scope *newScope(AggregateDeclaration * ad, Scope *sc);
+ void addObjcSymbols(Dsymbol *s, ClassDeclarations *, ClassDeclarations *);
}
import core.stdc.stdio;
import core.stdc.string;
+import core.stdc.stdlib;
import dmd.aggregate;
import dmd.aliasthis;
import dmd.dversion;
import dmd.enumsem;
import dmd.errors;
-import dmd.escape;
+import dmd.errorsink;
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
import dmd.importc;
import dmd.init;
import dmd.initsem;
-import dmd.intrange;
import dmd.hdrgen;
import dmd.lexer;
import dmd.location;
import dmd.mustuse;
import dmd.nspace;
import dmd.objc;
-import dmd.opover;
import dmd.optimize;
import dmd.parse;
-debug import dmd.printast;
import dmd.root.array;
-import dmd.root.filename;
import dmd.root.string;
-import dmd.common.outbuffer;
import dmd.root.rmem;
+import dmd.root.speller;
+import dmd.common.outbuffer;
import dmd.rootobject;
import dmd.safe;
import dmd.semantic2;
import dmd.sideeffect;
import dmd.staticassert;
import dmd.tokens;
-import dmd.utils;
import dmd.statement;
+import dmd.statementsem : ready;
import dmd.target;
-import dmd.templateparamsem;
+import dmd.targetcompiler;
import dmd.templatesem;
import dmd.typesem;
import dmd.visitor;
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
-
enum LOG = false;
-/*************************************
- * Does semantic analysis on the public face of declarations.
- */
-void dsymbolSemantic(Dsymbol dsym, Scope* sc)
-{
- scope v = new DsymbolSemanticVisitor(sc);
- dsym.accept(v);
-}
-
-/***************************************************
- * Determine the numerical value of the AlignmentDeclaration
- * Params:
- * ad = AlignmentDeclaration
- * sc = context
- * Returns:
- * ad with alignment value determined
+/***************************************
+ * Create a new scope from sc.
+ * semantic, semantic2 and semantic3 will use this for aggregate members.
*/
-AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc)
+Scope* newScope(AggregateDeclaration _this, Scope* sc)
{
- if (!ad.salign.isUnknown()) // UNKNOWN is 0
- return ad;
-
- if (!ad.exps)
+ static Scope* defaultNewScope(AggregateDeclaration _this, Scope* sc)
{
- ad.salign.setDefault();
- return ad;
+ auto sc2 = sc.push(_this);
+ sc2.stc &= STC.flowThruAggregate;
+ sc2.parent = _this;
+ sc2.inunion = _this.isUnionDeclaration();
+ sc2.visibility = Visibility(Visibility.Kind.public_);
+ sc2.explicitVisibility = false;
+ sc2.aligndecl = null;
+ sc2.userAttribDecl = null;
+ sc2.namespace = null;
+ return sc2;
}
- dinteger_t strictest = 0; // strictest alignment
- bool errors;
- foreach (ref exp; (*ad.exps)[])
+ static Scope* classNewScope(ClassDeclaration _this, Scope* sc)
{
- sc = sc.startCTFE();
- auto e = exp.expressionSemantic(sc);
- e = resolveProperties(sc, e);
- sc = sc.endCTFE();
- e = e.ctfeInterpret();
- exp = e; // could be re-evaluated if exps are assigned to more than one AlignDeclaration by CParser.applySpecifier(),
- // e.g. `_Alignas(8) int a, b;`
- if (e.op == EXP.error)
- errors = true;
- else
+ auto sc2 = defaultNewScope(_this, sc);
+ if (_this.isCOMclass())
{
- auto n = e.toInteger();
- if (sc.inCfile && n == 0) // C11 6.7.5-6 allows 0 for alignment
- continue;
-
- if (n < 1 || n & (n - 1) || ushort.max < n || !e.type.isIntegral())
- {
- error(ad.loc, "alignment must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
- errors = true;
- }
- if (n > strictest) // C11 6.7.5-6
- strictest = n;
+ /* This enables us to use COM objects under Linux and
+ * work with things like XPCOM
+ */
+ sc2.linkage = target.systemLinkage();
}
+ return sc2;
}
- if (errors || strictest == 0) // C11 6.7.5-6 says alignment of 0 means no effect
- ad.salign.setDefault();
- else
- ad.salign.set(cast(uint) strictest);
+ static Scope* interfaceNewScope(InterfaceDeclaration _this, Scope* sc)
+ {
+ auto sc2 = classNewScope(_this, sc);
+ if (_this.com)
+ sc2.linkage = LINK.windows;
+ else if (_this.classKind == ClassKind.cpp)
+ sc2.linkage = LINK.cpp;
+ else if (_this.classKind == ClassKind.objc)
+ sc2.linkage = LINK.objc;
+ return sc2;
+ }
- return ad;
+ if (auto _id = _this.isInterfaceDeclaration())
+ return interfaceNewScope(_id, sc);
+ else if (auto cd = _this.isClassDeclaration())
+ return classNewScope(cd, sc);
+ return defaultNewScope(_this, sc);
}
+void addObjcSymbols(Dsymbol _this, ClassDeclarations* classes, ClassDeclarations* categories)
+{
+ if (auto ad = _this.isAttribDeclaration())
+ objc.addSymbols(ad, classes, categories);
+ else if (auto cd = _this.isClassDeclaration())
+ objc.addSymbols(cd, classes, categories);
+}
-/*********************************
- * Resolve recursive tuple expansion in eponymous template.
+/************************************
+ * Maybe `ident` was a C or C++ name. Check for that,
+ * and suggest the D equivalent.
+ * Params:
+ * ident = unknown identifier
+ * Returns:
+ * D identifier string if found, null if not
*/
-Dsymbol toAlias2(Dsymbol s)
+const(char)* search_correct_C(Identifier ident)
{
- if (auto ad = s.isAliasDeclaration())
+ import dmd.astenums : Twchar;
+ TOK tok;
+ if (ident == Id.NULL)
+ tok = TOK.null_;
+ else if (ident == Id.TRUE)
+ tok = TOK.true_;
+ else if (ident == Id.FALSE)
+ tok = TOK.false_;
+ else if (ident == Id.unsigned)
+ tok = TOK.uns32;
+ else if (ident == Id.wchar_t)
+ tok = target.c.wchar_tsize == 2 ? TOK.wchar_ : TOK.dchar_;
+ else
+ return null;
+ return Token.toChars(tok);
+}
+
+/******************************
+ * Add symbol s to innermost symbol table.
+ * Params:
+ * _this = scope object
+ * s = symbol to insert
+ * Returns:
+ * null if already in table, `s` if not
+ */
+Dsymbol insert(Scope* _this, Dsymbol s)
+{
+ //printf("insert() %s\n", s.toChars());
+ if (VarDeclaration vd = s.isVarDeclaration())
{
- if (ad.inuse)
- {
- .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
- return ad;
- }
- ad.inuse = 1;
- Dsymbol ds = ad.aliassym ? ad.aliassym.toAlias2() : ad;
- ad.inuse = 0;
- return ds;
+ if (_this.lastVar)
+ vd.lastVar = _this.lastVar;
+ _this.lastVar = vd;
}
- if (auto td = s.isTupleDeclaration())
+ else if (WithScopeSymbol ss = s.isWithScopeSymbol())
{
- //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars());
- for (size_t i = 0; i < td.objects.length; i++)
+ if (VarDeclaration vd = ss.withstate.wthis)
{
- RootObject o = (*td.objects)[i];
- if (Dsymbol ds = isDsymbol(o))
- {
- ds = ds.toAlias2();
- (*td.objects)[i] = ds;
- }
+ if (_this.lastVar)
+ vd.lastVar = _this.lastVar;
+ _this.lastVar = vd;
}
- return td;
+ return null;
}
- return toAlias(s);
-}
-private Dsymbol toAliasImpl(AliasDeclaration ad)
-{
- static if (0)
- printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n",
- ad.loc.toChars(), ad.toChars(), ad, ad.aliassym ? ad.aliassym.toChars() : "", ad.aliassym ? ad.aliassym.kind() : "", ad.inuse);
- assert(ad != ad.aliassym);
- //static int count; if (++count == 10) *(char*)0=0;
+ auto scopesym = _this.inner().scopesym;
+ //printf("\t\tscopesym = %p\n", scopesym);
+ if (!scopesym.symtab)
+ scopesym.symtab = new DsymbolTable();
+ if (!_this.inCfile)
+ return scopesym.symtabInsert(s);
- Dsymbol err()
+ // ImportC insert
+ if (!scopesym.symtabInsert(s)) // if already in table
{
- // Avoid breaking "recursive alias" state during errors gagged
- if (global.gag)
- return ad;
- ad.aliassym = new AliasDeclaration(ad.loc, ad.ident, Type.terror);
- ad.type = Type.terror;
- return ad.aliassym;
- }
- // Reading the AliasDeclaration
- if (!ad.ignoreRead)
- ad.wasRead = true; // can never assign to this AliasDeclaration again
+ Dsymbol s2 = scopesym.symtabLookup(s, s.ident); // s2 is existing entry
- if (ad.inuse == 1 && ad.type && ad._scope)
- {
- ad.inuse = 2;
- const olderrors = global.errors;
- Dsymbol s = ad.type.toDsymbol(ad._scope);
- //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this);
- if (global.errors != olderrors)
- return err();
- if (s)
+ auto svar = s.isVarDeclaration();
+ auto s2var = s2.isVarDeclaration();
+ if (((svar && svar.storage_class & STC.extern_) &&
+ (s2var && s2var.storage_class & STC.extern_) && _this.func) ||
+ s.isFuncDeclaration())
{
- s = s.toAlias();
- if (global.errors != olderrors)
- return err();
- ad.aliassym = s;
- ad.inuse = 0;
+ return handleSymbolRedeclarations(*_this, s, s2, scopesym);
}
- else
+ else // aside externs and func decls, we should be free to handle tags
{
- Type t = ad.type.typeSemantic(ad.loc, ad._scope);
- if (t.ty == Terror)
- return err();
- if (global.errors != olderrors)
- return err();
- //printf("t = %s\n", t.toChars());
- ad.inuse = 0;
+ return handleTagSymbols(*_this, s, s2, scopesym);
}
}
- if (ad.inuse)
- {
- .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
- return err();
- }
-
- if (ad.semanticRun >= PASS.semanticdone)
- {
- // semantic is already done.
-
- // Do not see aliassym !is null, because of lambda aliases.
+ return s; // inserted
+}
- // Do not see type.deco !is null, even so "alias T = const int;` needs
- // semantic analysis to take the storage class `const` as type qualifier.
- }
- else
+/*******************************************
+ * Look for member of the form:
+ * const(MemberInfo)[] getMembers(string);
+ * Returns NULL if not found
+ */
+FuncDeclaration findGetMembers(ScopeDsymbol dsym)
+{
+ import dmd.opover : search_function;
+ Dsymbol s = search_function(dsym, Id.getmembers);
+ FuncDeclaration fdx = s ? s.isFuncDeclaration() : null;
+ version (none)
{
- // stop AliasAssign tuple building
- if (ad.aliassym)
- {
- if (auto td = ad.aliassym.isTupleDeclaration())
- {
- if (td.building)
- {
- td.building = false;
- ad.semanticRun = PASS.semanticdone;
- return td;
- }
- }
- }
- if (ad._import && ad._import._scope)
- {
- /* If this is an internal alias for selective/renamed import,
- * load the module first.
- */
- ad._import.dsymbolSemantic(null);
- }
- if (ad._scope)
+ // Finish
+ __gshared TypeFunction tfgetmembers;
+ if (!tfgetmembers)
{
- aliasSemantic(ad, ad._scope);
+ Scope sc;
+ sc.eSink = global.errorSink;
+ Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null);
+ auto parameters = new Parameters(p);
+ Type tret = null;
+ TypeFunction tf = new TypeFunction(parameters, tret, VarArg.none, LINK.d);
+ tfgetmembers = tf.dsymbolSemantic(Loc.initial, &sc).isTypeFunction();
}
+ if (fdx)
+ fdx = fdx.overloadExactMatch(tfgetmembers);
}
+ if (fdx && fdx.isVirtual())
+ fdx = null;
+ return fdx;
+}
- ad.inuse = 1;
- Dsymbol s = ad.aliassym ? ad.aliassym.toAlias() : ad;
- ad.inuse = 0;
- return s;
+/***********************************
+ * Retrieve the .min or .max values.
+ * Only valid after semantic analysis.
+ * Params:
+ * _this = bit field instance
+ * id = Id.min or Id.max
+ * Returns:
+ * the min or max value
+ */
+ulong getMinMax(BitFieldDeclaration _this, Identifier id)
+{
+ const width = _this.fieldWidth;
+ const uns = _this.type.isUnsigned();
+ const min = id == Id.min;
+ ulong v;
+ assert(width != 0); // should have been rejected in semantic pass
+ if (width == ulong.sizeof * 8)
+ v = uns ? (min ? ulong.min : ulong.max)
+ : (min ? long.min : long.max);
+ else
+ v = uns ? (min ? 0
+ : (1L << width) - 1)
+ : (min ? -(1L << (width - 1))
+ : (1L << (width - 1)) - 1);
+ return v;
}
-/*********************************
- * If this symbol is really an alias for another,
- * return that other.
- * If needed, semantic() is invoked due to resolve forward reference.
+/* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested.
*/
-Dsymbol toAlias(Dsymbol s)
+void makeNested(AggregateDeclaration _this)
{
- if (auto ad = s.isAliasDeclaration())
- {
- return ad.toAliasImpl();
- }
- if (auto imp = s.isImport())
- {
- if (imp.aliasId)
- return imp.mod;
- return imp;
- }
- if (auto vd = s.isVarDeclaration())
+ if (_this.enclosing) // if already nested
+ return;
+ if (_this.sizeok == Sizeok.done)
+ return;
+ if (_this.isUnionDeclaration() || _this.isInterfaceDeclaration())
+ return;
+ if (_this.storage_class & STC.static_)
+ return;
+
+ // If nested struct, add in hidden 'this' pointer to outer scope
+ auto s = _this.toParentLocal();
+ if (!s)
+ s = _this.toParent2();
+ if (!s)
+ return;
+ Type t = null;
+ if (auto fd = s.isFuncDeclaration())
{
- //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym);
- if ((!vd.type || !vd.type.deco) && vd._scope)
- dsymbolSemantic(vd, vd._scope);
+ _this.enclosing = fd;
- assert(vd != vd.aliasTuple);
- return vd.aliasTuple ? vd.aliasTuple.toAlias() : vd;
+ /* https://issues.dlang.org/show_bug.cgi?id=14422
+ * If a nested class parent is a function, its
+ * context pointer (== `outer`) should be void* always.
+ */
+ t = Type.tvoidptr;
}
- // resolve real symbol
- if (auto ti = s.isTemplateInstance())
+ else if (auto ad = s.isAggregateDeclaration())
{
- static if (LOG)
+ if (_this.isClassDeclaration() && ad.isClassDeclaration())
{
- printf("TemplateInstance.toAlias()\n");
+ _this.enclosing = ad;
}
- if (!ti.inst)
+ else if (_this.isStructDeclaration())
{
- // Maybe we can resolve it
- if (ti._scope)
- {
- dsymbolSemantic(ti, ti._scope);
- }
- if (!ti.inst)
+ if (auto ti = ad.parent.isTemplateInstance())
{
- .error(ti.loc, "%s `%s` cannot resolve forward reference", ti.kind, ti.toPrettyChars);
- ti.errors = true;
- return ti;
+ _this.enclosing = ti.enclosing;
}
}
+ t = ad.handleType();
+ }
+ if (_this.enclosing)
+ {
+ import dmd.typesem : alignment;
+ //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars());
+ assert(t);
+ if (t.ty == Tstruct)
+ t = Type.tvoidptr; // t should not be a ref type
- if (ti.inst != ti)
- return ti.inst.toAlias();
+ assert(!_this.vthis);
+ _this.vthis = new ThisDeclaration(_this.loc, t);
+ //vthis.storage_class |= STC.ref_;
- if (ti.aliasdecl)
- {
- return ti.aliasdecl.toAlias();
- }
+ // Emulate vthis.addMember()
+ _this.members.push(_this.vthis);
- return ti.inst;
+ // Emulate vthis.dsymbolSemantic()
+ _this.vthis.storage_class |= STC.field;
+ _this.vthis.parent = _this;
+ _this.vthis.visibility = Visibility(Visibility.Kind.public_);
+ _this.vthis.alignment = t.alignment();
+ _this.vthis.semanticRun = PASS.semanticdone;
+
+ if (_this.sizeok == Sizeok.fwd)
+ _this.fields.push(_this.vthis);
+
+ _this.makeNested2();
}
- return s;
}
-const(char)* getMessage(DeprecatedDeclaration dd)
+/* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer.
+ */
+void makeNested2(AggregateDeclaration _this)
{
- if (auto sc = dd._scope)
- {
- dd._scope = null;
+ import dmd.typesem : alignment;
- sc = sc.startCTFE();
- dd.msg = dd.msg.expressionSemantic(sc);
- dd.msg = resolveProperties(sc, dd.msg);
- sc = sc.endCTFE();
- dd.msg = dd.msg.ctfeInterpret();
+ if (_this.vthis2)
+ return;
+ if (!_this.vthis)
+ _this.makeNested(); // can't add second before first
+ if (!_this.vthis)
+ return;
+ if (_this.sizeok == Sizeok.done)
+ return;
+ if (_this.isUnionDeclaration() || _this.isInterfaceDeclaration())
+ return;
+ if (_this.storage_class & STC.static_)
+ return;
- if (auto se = dd.msg.toStringExp())
- dd.msgstr = se.toStringz().ptr;
- else
- error(dd.msg.loc, "compile time constant expected, not `%s`", dd.msg.toChars());
- }
- return dd.msgstr;
-}
+ auto s0 = _this.toParentLocal();
+ auto s = _this.toParent2();
+ if (!s || !s0 || s == s0)
+ return;
+ auto cd = s.isClassDeclaration();
+ Type t = cd ? cd.type : Type.tvoidptr;
-bool checkDeprecated(Dsymbol d, Loc loc, Scope* sc)
-{
- if (global.params.useDeprecated == DiagnosticReporting.off)
- return false;
- if (!d.isDeprecated())
- return false;
- // Don't complain if we're inside a deprecated symbol's scope
- if (sc.isDeprecated())
- return false;
- // Don't complain if we're inside a template constraint
- // https://issues.dlang.org/show_bug.cgi?id=21831
- if (sc.inTemplateConstraint)
- return false;
+ _this.vthis2 = new ThisDeclaration(_this.loc, t);
+ //vthis2.storage_class |= STC.ref_;
- const(char)* message = null;
- for (Dsymbol p = d; p; p = p.parent)
- {
- message = p.depdecl ? p.depdecl.getMessage() : null;
- if (message)
- break;
- }
- if (message)
- deprecation(loc, "%s `%s` is deprecated - %s", d.kind, d.toPrettyChars, message);
- else
- deprecation(loc, "%s `%s` is deprecated", d.kind, d.toPrettyChars);
+ // Emulate vthis2.addMember()
+ _this.members.push(_this.vthis2);
- if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
- ti.printInstantiationTrace(Classification.deprecation);
- else if (auto ti = sc.parent ? sc.parent.isTemplateInstance() : null)
- ti.printInstantiationTrace(Classification.deprecation);
+ // Emulate vthis2.dsymbolSemantic()
+ _this.vthis2.storage_class |= STC.field;
+ _this.vthis2.parent = _this;
+ _this.vthis2.visibility = Visibility(Visibility.Kind.public_);
+ _this.vthis2.alignment = t.alignment();
+ _this.vthis2.semanticRun = PASS.semanticdone;
- return true;
+ if (_this.sizeok == Sizeok.fwd)
+ _this.fields.push(_this.vthis2);
}
-/*********************************
- * Check type to see if it is based on a deprecated symbol.
+/************************************
+ * Perform unqualified name lookup by following the chain of scopes up
+ * until found.
+ *
+ * Params:
+ * _this = Scope object
+ * loc = location to use for error messages
+ * ident = name to look up
+ * pscopesym = if supplied and name is found, set to scope that ident was found in, otherwise set to null
+ * flags = modify search based on flags
+ *
+ * Returns:
+ * symbol if found, null if not
*/
-private void checkDeprecated(Type type, Loc loc, Scope* sc)
+Dsymbol search(Scope* _this, Loc loc, Identifier ident, out Dsymbol pscopesym, SearchOptFlags flags = SearchOpt.all)
{
- if (Dsymbol s = type.toDsymbol(sc))
+ version (LOGSEARCH)
{
- s.checkDeprecated(loc, sc);
- }
+ printf("Scope.search(%p, '%s' flags=x%x)\n", _this, ident.toChars(), flags);
+ // Print scope chain
+ for (Scope* sc = _this; sc; sc = sc.enclosing)
+ {
+ if (!sc.scopesym)
+ continue;
+ printf("\tscope %s\n", sc.scopesym.toChars());
+ }
- if (auto tn = type.nextOf())
- tn.checkDeprecated(loc, sc);
-}
+ static void printMsg(string txt, Dsymbol s)
+ {
+ printf("%.*s %s.%s, kind = '%s'\n", cast(int)txt.length, txt.ptr,
+ s.parent ? s.parent.toChars() : "", s.toChars(), s.kind());
+ }
+ }
-// Returns true if a contract can appear without a function body.
-package bool allowsContractWithoutBody(FuncDeclaration funcdecl)
-{
- assert(!funcdecl.fbody);
+ // This function is called only for unqualified lookup
+ assert(!(flags & (SearchOpt.localsOnly | SearchOpt.importsOnly)));
- /* Contracts can only appear without a body when they are virtual
- * interface functions or abstract.
+ /* If ident is "start at module scope", only look at module scope
*/
- Dsymbol parent = funcdecl.toParent();
- InterfaceDeclaration id = parent.isInterfaceDeclaration();
-
- if (!funcdecl.isAbstract() &&
- (funcdecl.fensures || funcdecl.frequires) &&
- !(id && funcdecl.isVirtual()))
+ if (ident == Id.empty)
{
- auto cd = parent.isClassDeclaration();
- if (!(cd && cd.isAbstract()))
- return false;
+ // Look for module scope
+ for (Scope* sc = _this; sc; sc = sc.enclosing)
+ {
+ assert(sc != sc.enclosing);
+ if (!sc.scopesym)
+ continue;
+ if (Dsymbol s = sc.scopesym.isModule())
+ {
+ //printMsg("\tfound", s);
+ pscopesym = sc.scopesym;
+ return s;
+ }
+ }
+ return null;
}
- return true;
-}
-/***************************************
- * Determine if struct is POD (Plain Old Data).
- *
- * POD is defined as:
- * $(OL
- * $(LI not nested)
- * $(LI no postblits, destructors, or assignment operators)
- * $(LI no `ref` fields or fields that are themselves non-POD)
- * )
- * The idea being these are compatible with C structs.
- *
- * Returns:
- * true if struct is POD
- */
-bool isPOD(StructDeclaration sd)
-{
- // If we've already determined whether this struct is POD.
- if (sd.ispod != ThreeState.none)
- return (sd.ispod == ThreeState.yes);
+ Dsymbol checkAliasThis(AggregateDeclaration ad, Identifier ident, SearchOptFlags flags, Expression* exp)
+ {
+ if (!ad || !ad.aliasthis)
+ return null;
- import dmd.clone;
+ Declaration decl = ad.aliasthis.sym.isDeclaration();
+ if (!decl)
+ return null;
- bool hasCpCtorLocal;
- bool hasMoveCtorLocal;
- bool needCopyCtor;
- bool needMoveCtor;
- needCopyOrMoveCtor(sd, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor);
+ Type t = decl.type;
+ ScopeDsymbol sds;
+ TypeClass tc;
+ TypeStruct ts;
+ switch(t.ty)
+ {
+ case Tstruct:
+ ts = cast(TypeStruct)t;
+ sds = ts.sym;
+ break;
+ case Tclass:
+ tc = cast(TypeClass)t;
+ sds = tc.sym;
+ break;
+ case Tinstance:
+ sds = (cast(TypeInstance)t).tempinst;
+ break;
+ case Tenum:
+ sds = (cast(TypeEnum)t).sym;
+ break;
+ default: break;
+ }
- if (sd.enclosing || // is nested
- search(sd, sd.loc, Id.postblit) || // has postblit
- search(sd, sd.loc, Id.dtor) || // has destructor
- /* This is commented out because otherwise buildkite vibe.d:
- `canCAS!Task` fails to compile
- */
- //hasMoveCtorLocal || // has move constructor
- hasCpCtorLocal) // has copy constructor
- {
- sd.ispod = ThreeState.no;
- return false;
- }
+ if (!sds)
+ return null;
- // Recursively check all fields are POD.
- for (size_t i = 0; i < sd.fields.length; i++)
- {
- VarDeclaration v = sd.fields[i];
- if (v.storage_class & STC.ref_)
+ Dsymbol ret = sds.search(loc, ident, flags);
+ if (ret)
{
- sd.ispod = ThreeState.no;
- return false;
+ *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
+ *exp = new DotIdExp(loc, *exp, ident);
+ return ret;
}
- if (auto ts = v.type.baseElemOf().isTypeStruct())
+ if (!ts && !tc)
+ return null;
+
+ Dsymbol s;
+ *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
+ if (ts && !(ts.att & AliasThisRec.tracing))
{
- if (!ts.sym.isPOD())
+ ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracing);
+ s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp);
+ ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracing);
+ }
+ else if(tc && !(tc.att & AliasThisRec.tracing))
+ {
+ tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracing);
+ s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp);
+ tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracing);
+ }
+ return s;
+ }
+
+ Dsymbol searchScopes(SearchOptFlags flags)
+ {
+ for (Scope* sc = _this; sc; sc = sc.enclosing)
+ {
+ assert(sc != sc.enclosing);
+ if (!sc.scopesym)
+ continue;
+ //printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc.scopesym.toChars(), sc.scopesym.kind(), flags);
+
+ if (sc.scopesym.isModule())
+ flags |= SearchOpt.unqualifiedModule; // tell Module.search() that SearchOpt.localsOnly is to be obeyed
+ else if (sc.inCfile && sc.scopesym.isStructDeclaration())
+ continue; // C doesn't have struct scope
+
+ if (Dsymbol s = sc.scopesym.search(loc, ident, flags))
{
- sd.ispod = ThreeState.no;
- return false;
+ if (flags & SearchOpt.tagNameSpace)
+ {
+ // ImportC: if symbol is not a tag, look for it in tag table
+ if (!s.isScopeDsymbol())
+ {
+ auto ps = cast(void*)s in sc._module.tagSymTab;
+ if (!ps)
+ goto NotFound;
+ s = *ps;
+ }
+ }
+ //printMsg("\tfound local", s);
+ pscopesym = sc.scopesym;
+ return s;
+ }
+
+ NotFound:
+ if (sc.previews.fixAliasThis)
+ {
+ Expression exp = new ThisExp(loc);
+ if (Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp))
+ {
+ //printf("found aliassym: %s\n", aliasSym.toChars());
+ pscopesym = new ExpressionDsymbol(exp);
+ return aliasSym;
+ }
}
+
+ // Stop when we hit a module, but keep going if that is not just under the global scope
+ if (sc.scopesym.isModule() && !(sc.enclosing && !sc.enclosing.enclosing))
+ break;
}
+ return null;
}
- sd.ispod = ThreeState.yes;
- return true;
-}
-
-/*
-If sd has a copy constructor and ctor is an rvalue constructor,
-issue an error.
+ if (_this.ignoresymbolvisibility)
+ flags |= SearchOpt.ignoreVisibility;
-Params:
- sd = struct declaration that may contain both an rvalue and copy constructor
- ctor = constructor that will be checked if it is an rvalue constructor
- ti = template instance the ctor is part of
+ // First look in local scopes
+ Dsymbol s = searchScopes(flags | SearchOpt.localsOnly);
+ version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s);
+ if (!s)
+ {
+ // Second look in imported modules
+ s = searchScopes(flags | SearchOpt.importsOnly);
+ version (LOGSEARCH) if (s) printMsg("-Scope.search() found import", s);
+ }
+ return s;
+}
-Return:
- `true` if sd has a copy constructor and ctor is an rvalue constructor
-*/
-bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti)
+Dsymbol search_correct(Scope* _this, Identifier ident)
{
- //printf("checkHasBothRvalueAndCpCtor() sd: %s ctor: %s ti: %s\n", sd.toChars(), ctor.toChars(), ti.toChars());
- /* cannot use ctor.isMoveCtor because semantic pass may not have been run yet,
- * so use isRvalueConstructor()
+ if (global.gag)
+ return null; // don't do it for speculative compiles; too time consuming
+
+ /************************************************
+ * Given the failed search attempt, try to find
+ * one with a close spelling.
+ * Params:
+ * seed = identifier to search for
+ * cost = set to the cost, which rises with each outer scope
+ * Returns:
+ * Dsymbol if found, null if not
*/
- if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor))
+ Dsymbol scope_search_fp(const(char)[] seed, out int cost)
{
- .error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars());
- .errorSupplemental(ti.loc, "Template instance `%s` creates an rvalue constructor for `struct %s`",
- ti.toPrettyChars(), sd.toChars());
+ //printf("scope_search_fp('%s')\n", seed);
+ /* If not in the lexer's string table, it certainly isn't in the symbol table.
+ * Doing this first is a lot faster.
+ */
+ if (!seed.length)
+ return null;
+ Identifier id = Identifier.lookup(seed);
+ if (!id)
+ return null;
+ Scope* sc = _this;
+ Module.clearCache();
+ Dsymbol scopesym;
+ Dsymbol s = sc.search(Loc.initial, id, scopesym, SearchOpt.ignoreErrors);
+ if (!s)
+ return null;
- return true;
+ // Do not show `@disable`d declarations
+ if (auto decl = s.isDeclaration())
+ if (decl.storage_class & STC.disable)
+ return null;
+ // Or `deprecated` ones if we're not in a deprecated scope
+ if (s.isDeprecated() && !sc.isDeprecated())
+ return null;
+
+ for (cost = 0; sc; sc = sc.enclosing, ++cost)
+ if (sc.scopesym == scopesym)
+ break;
+ if (scopesym != s.parent)
+ {
+ ++cost; // got to the symbol through an import
+ if (s.visible().kind == Visibility.Kind.private_)
+ return null;
+ }
+ return s;
}
- return false;
+ Dsymbol scopesym;
+ // search for exact name first
+ if (auto s = _this.search(Loc.initial, ident, scopesym, SearchOpt.ignoreErrors))
+ return s;
+ return speller!scope_search_fp(ident.toString());
}
-/************************************************
- * Check if ctor is an rvalue constructor.
- * A constructor that receives a single parameter of the same type as
- * `Unqual!typeof(this)` is an rvalue constructor.
- * Params:
- * sd = struct that ctor is a member of
- * ctor = constructor to test
- * Returns:
- * true if it is an rvalue constructor
- */
-bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
+structalign_t alignment(Scope* _this)
{
- // note commonality with setting isMoveCtor in the semantic code for CtorDeclaration
- auto tf = ctor.type.isTypeFunction();
- const dim = tf.parameterList.length;
- if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
+ if (_this.aligndecl)
{
- auto param = tf.parameterList[0];
- if (!(param.storageClass & STC.ref_) && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
- {
- return true;
- }
+ auto ad = _this.aligndecl.getAlignment(_this);
+ return ad.salign;
}
- return false;
+ else
+ {
+ structalign_t sa;
+ sa.setDefault();
+ return sa;
+ }
+}
+
+Scope* scopeCreateGlobal(Module _module, ErrorSink eSink)
+{
+ Scope* sc = Scope.alloc();
+ *sc = Scope.init;
+ sc._module = _module;
+ sc.minst = _module;
+ sc.scopesym = new ScopeDsymbol();
+ sc.scopesym.symtab = new DsymbolTable();
+ sc.eSink = eSink;
+ assert(eSink);
+ // Add top level package as member of this global scope
+ Dsymbol m = _module;
+ while (m.parent)
+ m = m.parent;
+ m.addMember(null, sc.scopesym);
+ m.parent = null; // got changed by addMember()
+ sc.previews.setFromParams(global.params);
+
+ if (_module.filetype == FileType.c)
+ sc.inCfile = true;
+ // Create the module scope underneath the global scope
+ sc = sc.push(_module);
+ sc.parent = _module;
+ return sc;
}
/*************************************
- * Find the `alias this` symbol of e's type.
- * Params:
- * sc = context
- * e = expression forming the `this`
- * gag = do not print errors, return `null` instead
- * findOnly = don't do further processing like resolving properties,
- * i.e. just return plain dotExp() result.
- * Returns:
- * Expression that is `e.aliasthis`
+ * Does semantic analysis on the public face of declarations.
*/
-Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false)
+void dsymbolSemantic(Dsymbol dsym, Scope* sc)
{
- //printf("resolveAliasThis() %s\n", toChars(e));
- import dmd.typesem : dotExp;
- for (AggregateDeclaration ad = isAggregate(e.type); ad;)
- {
- if (ad.aliasthis)
- {
- Loc loc = e.loc;
- Type tthis = (e.op == EXP.type ? e.type : null);
- const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag));
- 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 (tthis && ad.aliasthis.sym.needThis())
- {
- if (auto ve = e.isVarExp())
- {
- if (auto fd = ve.var.isFuncDeclaration())
- {
- // https://issues.dlang.org/show_bug.cgi?id=13009
- // Support better match for the overloaded alias this.
- bool hasOverloads;
- if (auto f = fd.overloadModMatch(loc, tthis, hasOverloads))
- {
- if (!hasOverloads)
- fd = f; // use exact match
- e = new VarExp(loc, fd, hasOverloads);
- e.type = f.type;
- e = new CallExp(loc, e);
- goto L1;
- }
- }
- }
- /* non-@property function is not called inside typeof(),
- * so resolve it ahead.
- */
- {
- ubyte save = sc.intypeof;
- sc.intypeof = 1; // bypass "need this" error check
- e = resolveProperties(sc, e);
- sc.intypeof = save;
- }
- L1:
- e = new TypeExp(loc, new TypeTypeof(loc, e));
- e = e.expressionSemantic(sc);
- }
- e = resolveProperties(sc, e);
- if (!gag)
- ad.aliasthis.checkDeprecatedAliasThis(loc, sc);
- else if (global.endGagging(olderrors))
- e = null;
- }
-
- import dmd.dclass : ClassDeclaration;
- auto cd = ad.isClassDeclaration();
- if ((!e || !ad.aliasthis) && cd && cd.baseClass && cd.baseClass != ClassDeclaration.object)
- {
- ad = cd.baseClass;
- continue;
- }
- break;
- }
- return e;
+ scope v = new DsymbolSemanticVisitor(sc);
+ dsym.accept(v);
}
-/**
- * Check if an `alias this` is deprecated
- *
- * Usually one would use `expression.checkDeprecated(scope, aliasthis)` to
- * check if `expression` uses a deprecated `aliasthis`, but this calls
- * `toPrettyChars` which lead to the following message:
- * "Deprecation: alias this `fullyqualified.aggregate.__anonymous` is deprecated"
- *
- * Params:
- * at = The `AliasThis` object to check
- * loc = `Loc` of the expression triggering the access to `at`
- * sc = `Scope` of the expression
- * (deprecations do not trigger in deprecated scopes)
- *
- * Returns:
- * Whether the alias this was reported as deprecated.
- */
-private bool checkDeprecatedAliasThis(AliasThis at, Loc loc, Scope* sc)
+// function used to call semantic3 on a module's dependencies
+void semantic3OnDependencies(Module m)
{
- if (global.params.useDeprecated != DiagnosticReporting.off
- && at.isDeprecated() && !sc.isDeprecated())
- {
- const(char)* message = null;
- for (Dsymbol p = at; p; p = p.parent)
- {
- message = p.depdecl ? p.depdecl.getMessage() : null;
- if (message)
- break;
- }
- if (message)
- deprecation(loc, "`alias %s this` is deprecated - %s",
- at.sym.toChars(), message);
- else
- deprecation(loc, "`alias %s this` is deprecated",
- at.sym.toChars());
+ if (!m)
+ return;
- if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
- ti.printInstantiationTrace(Classification.deprecation);
+ if (m.semanticRun > PASS.semantic3)
+ return;
- return true;
- }
- return false;
-}
+ m.semantic3(null);
-// Save the scope and defer semantic analysis on the Dsymbol.
-void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope* scx)
-{
- s._scope = scx ? scx : sc.copy();
- s._scope.setNoFree();
- Module.addDeferredSemantic(s);
+ foreach (i; 1 .. m.aimports.length)
+ semantic3OnDependencies(m.aimports[i]);
}
-struct Ungag
+/*******************************************
+ * Can't run semantic on s now, try again later.
+ */
+void addDeferredSemantic(Dsymbol s)
{
- uint oldgag;
-
- extern (D) this(uint old) nothrow @safe
+ //printf("Module::addDeferredSemantic('%s')\n", s.toChars());
+ if (!s.deferred)
{
- this.oldgag = old;
+ s.deferred = true;
+ Module.deferred.push(s);
}
+}
- extern (C++) ~this() nothrow
+void addDeferredSemantic2(Dsymbol s)
+{
+ //printf("Module::addDeferredSemantic2('%s')\n", s.toChars());
+ if (!s.deferred2)
{
- global.gag = oldgag;
+ s.deferred2 = true;
+ Module.deferred2.push(s);
}
}
-Ungag ungagSpeculative(const Dsymbol s)
+void addDeferredSemantic3(Dsymbol s)
{
- const oldgag = global.gag;
- if (global.gag && !s.isSpeculative() && !s.toParent2().isFuncDeclaration())
- global.gag = 0;
- return Ungag(oldgag);
+ //printf("Module::addDeferredSemantic3('%s')\n", s.toChars());
+ if (!s.deferred3)
+ {
+ s.deferred3 = true;
+ Module.deferred3.push(s);
+ }
}
-/*******************************************
- * Print deprecation warning if we're deprecated, when
- * this module is imported from scope sc.
- *
- * Params:
- * m = the module
- * sc = the scope into which we are imported
- * loc = the location of the import statement
+/******************************************
+ * Run semantic() on deferred symbols.
*/
-private void checkImportDeprecation(Module m, Loc loc, Scope* sc)
+void runDeferredSemantic()
{
- if (!m.md || !m.md.isdeprecated || sc.isDeprecated)
+ __gshared int nested;
+ if (nested)
return;
+ //if (Module.deferred.length) printf("+Module::runDeferredSemantic(), len = %ld\n", deferred.length);
+ nested++;
- Expression msg = m.md.msg;
- if (StringExp se = msg ? msg.toStringExp() : null)
+ size_t len;
+ do
{
- const slice = se.peekString();
- if (slice.length)
+ len = Module.deferred.length;
+ if (!len)
+ break;
+
+ Dsymbol* todo;
+ Dsymbol* todoalloc = null;
+ Dsymbol tmp;
+ if (len == 1)
{
- deprecation(m.loc, "%s `%s` is deprecated - %.*s", m.kind, m.toPrettyChars, cast(int)slice.length, slice.ptr);
- return;
+ todo = &tmp;
+ }
+ else
+ {
+ todo = cast(Dsymbol*)Mem.check(malloc(len * Dsymbol.sizeof));
+ todoalloc = todo;
+ }
+ memcpy(todo, Module.deferred.tdata(), len * Dsymbol.sizeof);
+ foreach (Dsymbol s; Module.deferred[])
+ s.deferred = false;
+ Module.deferred.setDim(0);
+
+ foreach (i; 0..len)
+ {
+ Dsymbol s = todo[i];
+ s.dsymbolSemantic(null);
+ //printf("deferred: %s, parent = %s\n", s.toChars(), s.parent.toChars());
}
+ //printf("\tdeferred.length = %ld, len = %ld\n", deferred.length, len);
+ if (todoalloc)
+ free(todoalloc);
}
- deprecation(m.loc, "%s `%s` is deprecated", m.kind, m.toPrettyChars);
+ while (Module.deferred.length != len); // while making progress
+ nested--;
+ //printf("-Module::runDeferredSemantic(), len = %ld\n", deferred.length);
}
-private extern(C++) final class DsymbolSemanticVisitor : Visitor
+void runDeferredSemantic2()
{
- alias visit = Visitor.visit;
+ runDeferredSemantic();
- Scope* sc;
- this(Scope* sc) scope @safe
+ Dsymbols* a = &Module.deferred2;
+ for (size_t i = 0; i < a.length; i++)
{
- this.sc = sc;
+ Dsymbol s = (*a)[i];
+ s.deferred2 = false;
+ //printf("[%d] %s semantic2a\n", i, s.toPrettyChars());
+ s.semantic2(null);
+
+ if (global.errors)
+ break;
}
+ a.setDim(0);
+}
- override void visit(Dsymbol dsym)
+void runDeferredSemantic3()
+{
+ runDeferredSemantic2();
+
+ Dsymbols* a = &Module.deferred3;
+ for (size_t i = 0; i < a.length; i++)
{
- .error(dsym.loc, "%s `%s` %p has no semantic routine", dsym.kind, dsym.toPrettyChars, dsym);
+ Dsymbol s = (*a)[i];
+ s.deferred3 = false;
+ //printf("[%d] %s semantic3a\n", i, s.toPrettyChars());
+ s.semantic3(null);
+
+ if (global.errors)
+ break;
}
+ a.setDim(0);
+}
- override void visit(ScopeDsymbol) { }
- override void visit(Declaration) { }
+bool isOverlappedWith(VarDeclaration _this, VarDeclaration v)
+{
+ import dmd.typesem : size;
+ const vsz = v.type.size();
+ const tsz = _this.type.size();
+ assert(vsz != SIZE_INVALID && tsz != SIZE_INVALID);
- override void visit(AliasThis dsym)
+ // Overlap is checked by comparing bit offsets
+ auto bitoffset = _this.offset * 8;
+ auto vbitoffset = v.offset * 8;
+
+ // Bitsize of types are overridden by any bitfield widths.
+ ulong tbitsize;
+ if (auto bf = _this.isBitFieldDeclaration())
{
- if (dsym.semanticRun != PASS.initial)
- return;
+ bitoffset += bf.bitOffset;
+ tbitsize = bf.fieldWidth;
+ }
+ else
+ tbitsize = tsz * 8;
- if (dsym._scope)
- {
- sc = dsym._scope;
- dsym._scope = null;
- }
+ ulong vbitsize;
+ if (auto vbf = v.isBitFieldDeclaration())
+ {
+ vbitoffset += vbf.bitOffset;
+ vbitsize = vbf.fieldWidth;
+ }
+ else
+ vbitsize = vsz * 8;
- if (!sc)
- return;
+ return bitoffset < vbitoffset + vbitsize &&
+ vbitoffset < bitoffset + tbitsize;
+}
- dsym.semanticRun = PASS.semantic;
- dsym.isDeprecated_ = !!(sc.stc & STC.deprecated_);
+private Type tupleDeclGetType(TupleDeclaration _this)
+{
+ /* If this tuple represents a type, return that type
+ */
- Dsymbol p = sc.parent.pastMixin();
- AggregateDeclaration ad = p.isAggregateDeclaration();
- if (!ad)
+ //printf("TupleDeclaration::getType() %s\n", toChars());
+ if (_this.isexp || _this.building)
+ return null;
+ if (_this.tupletype)
+ return _this.tupletype;
+
+ /* It's only a type tuple if all the Object's are types
+ */
+ for (size_t i = 0; i < _this.objects.length; i++)
+ {
+ RootObject o = (*_this.objects)[i];
+ if (!o.isType())
{
- error(dsym.loc, "alias this can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
- return;
+ //printf("\tnot[%d], %p, %d\n", i, o, o.dyncast());
+ return null;
}
+ }
- assert(ad.members);
- Dsymbol s = ad.search(dsym.loc, dsym.ident);
- if (!s)
+ /* We know it's a type tuple, so build the TypeTuple
+ */
+ Types* types = cast(Types*)_this.objects;
+ auto args = new Parameters(_this.objects.length);
+ OutBuffer buf;
+ int hasdeco = 1;
+ for (size_t i = 0; i < types.length; i++)
+ {
+ Type t = (*types)[i];
+ //printf("type = %s\n", t.toChars());
+ version (none)
{
- Dsymbol pscopesym;
- s = sc.search(dsym.loc, dsym.ident, pscopesym);
- if (s)
- error(dsym.loc, "`%s` is not a member of `%s`", s.toChars(), ad.toChars());
- else
- error(dsym.loc, "undefined identifier `%s`", dsym.ident.toChars());
- return;
+ buf.printf("_%s_%d", _this.ident.toChars(), i);
+ auto id = Identifier.idPool(buf.extractSlice());
+ auto arg = new Parameter(Loc.initial, STC.in_, t, id, null);
}
- if (ad.aliasthis && s != ad.aliasthis)
+ else
{
- error(dsym.loc, "there can be only one alias this");
- return;
+ auto arg = new Parameter(Loc.initial, STC.none, t, null, null, null);
}
+ (*args)[i] = arg;
+ if (!t.deco)
+ hasdeco = 0;
+ }
- /* disable the alias this conversion so the implicit conversion check
- * doesn't use it.
- */
- ad.aliasthis = null;
-
- Dsymbol sx = s;
- if (sx.isAliasDeclaration())
- sx = sx.toAlias();
- Declaration d = sx.isDeclaration();
- if (d && !d.isTupleDeclaration())
- {
- /* https://issues.dlang.org/show_bug.cgi?id=18429
- *
- * If the identifier in the AliasThis declaration
- * is defined later and is a voldemort type, we must
- * perform semantic on the declaration to deduce the type.
- */
- if (!d.type)
- d.dsymbolSemantic(sc);
+ _this.tupletype = new TypeTuple(args);
+ if (hasdeco)
+ return _this.tupletype.typeSemantic(Loc.initial, null);
- Type t = d.type;
- assert(t);
- if (ad.type.implicitConvTo(t) > MATCH.nomatch)
- {
- error(dsym.loc, "alias this is not reachable as `%s` already converts to `%s`", ad.toChars(), t.toChars());
- }
- }
+ return _this.tupletype;
+}
- dsym.sym = s;
- // Restore alias this
- ad.aliasthis = dsym;
- dsym.semanticRun = PASS.semanticdone;
- }
+private Type aliasDeclGetType(AliasDeclaration _this)
+{
+ if (_this.type)
+ return _this.type;
+ return toAlias(_this).getType();
+}
- override void visit(AliasDeclaration dsym)
+private Type aggregateDeclGetType(AggregateDeclaration _this)
+{
+ /* Apply storage classes to forward references. (Issue 22254)
+ * Note: Avoid interfaces for now. Implementing qualifiers on interface
+ * definitions exposed some issues in their TypeInfo generation in DMD.
+ * Related PR: https://github.com/dlang/dmd/pull/13312
+ */
+ if (_this.semanticRun == PASS.initial && !_this.isInterfaceDeclaration())
{
- if (dsym.semanticRun >= PASS.semanticdone)
- return;
- assert(dsym.semanticRun <= PASS.semantic);
-
- if (!sc)
- return;
-
- dsym.semanticRun = PASS.semantic;
+ auto stc = _this.storage_class;
+ if (_this._scope)
+ stc |= _this._scope.stc;
+ _this.type = _this.type.addSTC(stc);
+ }
+ return _this.type;
+}
- dsym.storage_class |= sc.stc & STC.deprecated_;
- dsym.visibility = sc.visibility;
- dsym.userAttribDecl = sc.userAttribDecl;
+Type getType(Dsymbol _this)
+{
+ if (auto td = _this.isTupleDeclaration())
+ return tupleDeclGetType(td);
+ else if (auto ad = _this.isAliasDeclaration())
+ return aliasDeclGetType(ad);
+ else if (auto agd = _this.isAggregateDeclaration())
+ return aggregateDeclGetType(agd);
+ else if (auto ed = _this.isEnumDeclaration())
+ return ed.type;
+
+ // is this a type?
+ return null;
+}
- if (!sc.func && dsym.inNonRoot())
- return;
+private uinteger_t aggregateDeclSize(AggregateDeclaration _this, Loc loc)
+{
+ //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
+ bool ok = determineSize(_this, loc);
+ //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
+ return ok ? _this.structsize : SIZE_INVALID;
+}
- aliasSemantic(dsym, sc);
- }
+private uinteger_t declSize(Declaration _this, Loc loc)
+{
+ import dmd.typesem: size;
+ assert(_this.type);
+ const sz = _this.type.size();
+ if (sz == SIZE_INVALID)
+ _this.errors = true;
+ return sz;
+}
- override void visit(AliasAssign dsym)
- {
- //printf("visit(AliasAssign)\n");
- if (dsym.semanticRun >= PASS.semanticdone)
- return;
- assert(dsym.semanticRun <= PASS.semantic);
+/*********************************
+ * Returns:
+ * SIZE_INVALID when the size cannot be determined
+ */
+uinteger_t size(Dsymbol _this, Loc loc)
+{
+ if (auto ad = _this.isAggregateDeclaration())
+ return aggregateDeclSize(ad, loc);
+ else if (auto d = _this.isDeclaration())
+ return declSize(d, loc);
+ .error(loc, "%s `%s` symbol `%s` has no size", _this.kind, _this.toPrettyChars, _this.toChars());
+ return SIZE_INVALID;
+}
- if (!sc.func && dsym.inNonRoot())
- return;
+private bool funcDeclEquals(const FuncDeclaration _this, const Dsymbol s)
+{
+ auto fd1 = _this;
+ auto fd2 = s.isFuncDeclaration();
+ if (!fd2)
+ return false;
- aliasAssignSemantic(dsym, sc);
- }
+ auto fa1 = fd1.isFuncAliasDeclaration();
+ auto faf1 = fa1 ? fa1.toAliasFunc() : fd1;
- override void visit(VarDeclaration dsym)
- {
- version (none)
- {
- printf("VarDeclaration::semantic('%s', parent = '%s') sem = %d\n",
- dsym.toChars(), sc.parent ? sc.parent.toChars() : null, dsym.semanticRun);
- printf(" type = %s\n", dsym.type ? dsym.type.toChars() : "null");
- printf(" stc = x%llx\n", dsym.storage_class);
- printf(" storage_class = x%llx\n", dsym.storage_class);
- printf("linkage = %d\n", dsym._linkage);
- //if (strcmp(toChars(), "mul") == 0) assert(0);
- }
- //if (semanticRun > PASS.initial)
- // return;
- //semanticRun = PSSsemantic;
+ auto fa2 = fd2.isFuncAliasDeclaration();
+ auto faf2 = fa2 ? fa2.toAliasFunc() : fd2;
- if (dsym.semanticRun >= PASS.semanticdone)
- return;
+ if (fa1 && fa2)
+ return faf1.equals(faf2) && fa1.hasOverloads == fa2.hasOverloads;
- if (sc && sc.inunion && sc.inunion.isAnonDeclaration())
- dsym.overlapped = true;
+ bool b1 = fa1 !is null;
+ if (b1 && faf1.isUnique() && !fa1.hasOverloads)
+ b1 = false;
- dsym.sequenceNumber = global.varSequenceNumber++;
- if (!dsym.isScope())
- dsym.maybeScope = true;
+ bool b2 = fa2 !is null;
+ if (b2 && faf2.isUnique() && !fa2.hasOverloads)
+ b2 = false;
- Scope* scx = null;
- if (dsym._scope)
- {
- sc = dsym._scope;
- scx = sc;
- dsym._scope = null;
- }
+ if (b1 != b2)
+ return false;
- if (!sc)
- return;
+ return faf1.toParent().equals(faf2.toParent()) &&
+ faf1.ident.equals(faf2.ident) &&
+ faf1.type.equals(faf2.type);
+}
- dsym.semanticRun = PASS.semantic;
+private bool overDeclEquals(const OverDeclaration _this, const Dsymbol s)
+{
+ if (auto od2 = s.isOverDeclaration())
+ return _this.aliassym.equals(od2.aliassym);
+ return _this.aliassym == s;
+}
- // 'static foreach' variables should not inherit scope properties
- // https://issues.dlang.org/show_bug.cgi?id=19482
- if ((dsym.storage_class & (STC.foreach_ | STC.local)) == (STC.foreach_ | STC.local))
- {
- dsym._linkage = LINK.d;
- dsym.visibility = Visibility(Visibility.Kind.public_);
- dsym.overlapped = false; // unset because it is modified early on this function
- dsym.userAttribDecl = null; // unset because it is set by Dsymbol.setScope()
- }
- else
- {
- /* Pick up storage classes from context, but except synchronized,
- * override, abstract, and final.
- */
- dsym.storage_class |= (sc.stc & ~(STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_));
- dsym.userAttribDecl = sc.userAttribDecl;
- dsym.cppnamespace = sc.namespace;
- dsym._linkage = sc.linkage;
- dsym.visibility = sc.visibility;
- dsym.alignment = sc.alignment();
- }
+private bool packageEquals(const Package _this, const Dsymbol s)
+{
+ // custom 'equals' for bug 17441. "package a" and "module a" are not equal
+ auto p = cast(Package)s;
+ return p && _this.isModule() == p.isModule() && _this.ident.equals(p.ident);
+}
- if (dsym.storage_class & STC.extern_ && dsym._init)
- {
- if (sc.inCfile)
- {
- // https://issues.dlang.org/show_bug.cgi?id=24447
- // extern int x = 3; is allowed in C
- dsym.storage_class &= ~STC.extern_;
- }
- else
- .error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars);
+bool equals(const Dsymbol _this, const Dsymbol s)
+{
+ if (_this == s)
+ return true;
- }
+ if(auto fd = _this.isFuncDeclaration())
+ return funcDeclEquals(fd, s);
+ else if (auto od = _this.isOverDeclaration())
+ return overDeclEquals(od, s);
+ else if (auto pkg = _this.isPackage())
+ return packageEquals(pkg, s);
+
+ // Overload sets don't have an ident
+ // Function-local declarations may have identical names
+ // if they are declared in different scopes
+ if (s && _this.ident && s.ident && _this.ident.equals(s.ident) && _this.localNum == s.localNum)
+ return true;
- AggregateDeclaration ad = dsym.isThis();
- if (ad)
- dsym.storage_class |= ad.storage_class & STC.TYPECTOR;
+ return false;
+}
- if ((dsym.storage_class & STC.auto_) && (dsym.storage_class & STC.ref_))
- {
- if (!(dsym.storage_class & STC.autoref))
- {
- .error(dsym.loc, "%s `%s` - `auto ref` variable must have `auto` and `ref` adjacent", dsym.kind, dsym.toChars());
- dsym.storage_class |= STC.autoref;
- }
- }
+private bool aliasOverloadInsert(AliasDeclaration ad, Dsymbol s)
+{
+ //printf("[%s] AliasDeclaration::overloadInsert('%s') s = %s %s @ [%s]\n",
+ // loc.toChars(), toChars(), s.kind(), s.toChars(), s.loc.toChars());
- /* If auto type inference, do the inference
+ /** Aliases aren't overloadable themselves, but if their Aliasee is
+ * overloadable they are converted to an overloadable Alias (either
+ * FuncAliasDeclaration or OverDeclaration).
+ *
+ * This is done by moving the Aliasee into such an overloadable alias
+ * which is then used to replace the existing Aliasee. The original
+ * Alias (_this_) remains a useless shell.
+ *
+ * This is a horrible mess. It was probably done to avoid replacing
+ * existing AST nodes and references, but it needs a major
+ * simplification b/c it's too complex to maintain.
+ *
+ * A simpler approach might be to merge any colliding symbols into a
+ * simple Overload class (an array) and then later have that resolve
+ * all collisions.
+ */
+ if (ad.semanticRun < PASS.semanticdone)
+ {
+ /* Don't know yet what the aliased symbol is, so assume it can
+ * be overloaded and check later for correctness.
*/
- int inferred = 0;
- if (!dsym.type)
- {
- dsym.inuse++;
+ if (ad.overnext)
+ return ad.overnext.overloadInsert(s);
+ if (s is ad)
+ return true;
+ ad.overnext = s;
+ return true;
+ }
+ /* Semantic analysis is already finished, and the aliased entity
+ * is not overloadable.
+ */
+ if (ad.type)
+ {
+ /*
+ If type has been resolved already we could
+ still be inserting an alias from an import.
- // Infering the type requires running semantic,
- // so mark the scope as ctfe if required
- bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func;
- if (needctfe)
- {
- sc.condition = true;
- sc = sc.startCTFE();
- }
- //printf("inferring type for %s with init %s\n", dsym.toChars(), dsym._init.toChars());
- dsym._init = dsym._init.inferType(sc);
- dsym.type = dsym._init.initializerToExpression(null, sc.inCfile).type;
- if (needctfe)
- sc = sc.endCTFE();
+ If we are handling an alias then pretend
+ it was inserting and return true, if not then
+ false since we didn't even pretend to insert something.
+ */
+ return ad._import && ad.equals(s);
+ }
- dsym.inuse--;
- inferred = 1;
+ // https://issues.dlang.org/show_bug.cgi?id=23865
+ // only insert if the symbol can be part of a set
+ const s1 = s.toAlias();
+ const isInsertCandidate = s1.isFuncDeclaration() || s1.isOverDeclaration() || s1.isTemplateDeclaration();
- /* This is a kludge to support the existing syntax for RAII
- * declarations.
- */
- dsym.storage_class &= ~STC.auto_;
- dsym.originalType = dsym.type.syntaxCopy();
+ /* When s is added in member scope by static if, mixin("code") or others,
+ * aliassym is determined already. See the case in: test/compilable/test61.d
+ */
+ auto sa = ad.aliassym.toAlias();
+
+ if (auto fd = sa.isFuncDeclaration())
+ {
+ auto fa = new FuncAliasDeclaration(ad.ident, fd);
+ fa.visibility = ad.visibility;
+ fa.parent = ad.parent;
+ ad.aliassym = fa;
+ if (isInsertCandidate)
+ return ad.aliassym.overloadInsert(s);
+ }
+ if (auto td = sa.isTemplateDeclaration())
+ {
+ auto od = new OverDeclaration(ad.ident, td.funcroot ? td.funcroot : td);
+ od.visibility = ad.visibility;
+ od.parent = ad.parent;
+ ad.aliassym = od;
+ if (isInsertCandidate)
+ return ad.aliassym.overloadInsert(s);
+ }
+ if (auto od = sa.isOverDeclaration())
+ {
+ if (sa.ident != ad.ident || sa.parent != ad.parent)
+ {
+ od = new OverDeclaration(ad.ident, od);
+ od.visibility = ad.visibility;
+ od.parent = ad.parent;
+ ad.aliassym = od;
+ }
+ if (isInsertCandidate)
+ return od.overloadInsert(s);
+ }
+ if (auto os = sa.isOverloadSet())
+ {
+ if (sa.ident != ad.ident || sa.parent != ad.parent)
+ {
+ os = new OverloadSet(ad.ident, os);
+ // TODO: visibility is lost here b/c OverloadSets have no visibility attribute
+ // Might no be a practical issue, b/c the code below fails to resolve the overload anyhow.
+ // ----
+ // module os1;
+ // import a, b;
+ // private alias merged = foo; // private alias to overload set of a.foo and b.foo
+ // ----
+ // module os2;
+ // import a, b;
+ // public alias merged = bar; // public alias to overload set of a.bar and b.bar
+ // ----
+ // module bug;
+ // import os1, os2;
+ // void test() { merged(123); } // should only look at os2.merged
+ //
+ // os.visibility = visibility;
+ os.parent = ad.parent;
+ ad.aliassym = os;
}
- else
+ if (isInsertCandidate)
{
- if (!dsym.originalType)
- dsym.originalType = dsym.type.syntaxCopy();
-
- /* Prefix function attributes of variable declaration can affect
- * its type:
- * pure nothrow void function() fp;
- * static assert(is(typeof(fp) == void function() pure nothrow));
- */
- Scope* sc2 = sc.push();
- sc2.stc |= (dsym.storage_class & STC.FUNCATTR);
- dsym.inuse++;
- dsym.type = dsym.type.typeSemantic(dsym.loc, sc2);
- dsym.inuse--;
- sc2.pop();
+ os.push(s);
+ return true;
}
- //printf(" semantic type = %s\n", dsym.type ? dsym.type.toChars() : "null");
- if (dsym.type.ty == Terror)
- dsym.errors = true;
+ }
+ return false;
+}
- dsym.type.checkDeprecated(dsym.loc, sc);
- dsym.parent = sc.parent;
- //printf("this = %p, parent = %p, '%s'\n", dsym, dsym.parent, dsym.parent.toChars());
+/**********************************
+ * Overload existing TemplateDeclaration '_this' with the new one 's'.
+ * Params:
+ * s = symbol to be inserted
+ * Return: true if successful; i.e. no conflict.
+ */
+private bool templateOverloadInsert(TemplateDeclaration _this, Dsymbol s){
+ static if (LOG)
+ {
+ printf("TemplateDeclaration.overloadInsert('%s')\n", s.toChars());
+ }
- /* If scope's alignment is the default, use the type's alignment,
- * otherwise the scope overrrides.
- */
- if (dsym.alignment.isDefault())
- dsym.alignment = dsym.type.alignment(); // use type's alignment
+ if (FuncDeclaration fd = s.isFuncDeclaration())
+ {
+ if (_this.funcroot)
+ return _this.funcroot.overloadInsert(fd);
+ _this.funcroot = fd;
+ return _this.funcroot.overloadInsert(_this);
+ }
- //printf("sc.stc = %x\n", sc.stc);
- //printf("storage_class = x%x\n", storage_class);
+ // https://issues.dlang.org/show_bug.cgi?id=15795
+ // if candidate is an alias and its sema is not run then
+ // insertion can fail because the thing it alias is not known
+ if (AliasDeclaration ad = s.isAliasDeclaration())
+ {
+ if (s._scope)
+ aliasSemantic(ad, s._scope);
+ if (ad.aliassym && ad.aliassym is _this)
+ return false;
+ }
- dsym.type.checkComplexTransition(dsym.loc, sc);
+ TemplateDeclaration td = s.toAlias().isTemplateDeclaration();
+ if (!td)
+ return false;
- // Calculate type size + safety checks
- if (dsym.storage_class & STC.gshared && !dsym.isMember())
- {
- sc.setUnsafe(false, dsym.loc, "using `__gshared` instead of `shared`");
- }
+ TemplateDeclaration pthis = _this;
+ TemplateDeclaration* ptd;
+ for (ptd = &pthis; *ptd; ptd = &(*ptd).overnext)
+ {
+ }
- Dsymbol parent = dsym.toParent();
+ td.overroot = _this;
+ *ptd = td;
+ static if (LOG)
+ {
+ printf("\ttrue: no conflict\n");
+ }
+ return true;
+}
- Type tb = dsym.type.toBasetype();
- Type tbn = tb.baseElemOf();
- if (tb.ty == Tvoid && !(dsym.storage_class & STC.lazy_))
+private bool importOverloadInsert(Import _this, Dsymbol s){
+ /* Allow multiple imports with the same package base, but disallow
+ * alias collisions
+ * https://issues.dlang.org/show_bug.cgi?id=5412
+ */
+ assert(_this.ident && _this.ident == s.ident);
+ if (_this.aliasId)
+ return false;
+ const imp = s.isImport();
+ return imp && !imp.aliasId;
+}
+
+/**
+ * Overload _this FuncDeclaration with the new one f.
+ * Return true if successful; i.e. no conflict.
+*/
+private bool funcOverloadInsert(FuncDeclaration _this, Dsymbol s){
+ //printf("FuncDeclaration::overloadInsert(s = %s) _this = %s\n", s.toChars(), toChars());
+ assert(s != _this);
+ if (AliasDeclaration ad = s.isAliasDeclaration())
+ {
+ if (_this.overnext)
+ return _this.overnext.overloadInsert(ad);
+ if (!ad.aliassym && ad.type.ty != Tident && ad.type.ty != Tinstance && ad.type.ty != Ttypeof)
{
- if (inferred)
- {
- .error(dsym.loc, "%s `%s` - type `%s` is inferred from initializer `%s`, and variables cannot be of type `void`",
- dsym.kind, dsym.toPrettyChars, dsym.type.toChars(), toChars(dsym._init));
- }
- else
- .error(dsym.loc, "%s `%s` - variables cannot be of type `void`", dsym.kind, dsym.toPrettyChars);
- dsym.type = Type.terror;
- tb = dsym.type;
- }
- if (tb.ty == Tfunction)
- {
- .error(dsym.loc, "%s `%s` cannot be declared to be a function", dsym.kind, dsym.toPrettyChars);
- dsym.type = Type.terror;
- tb = dsym.type;
- }
- if (auto ts = tb.isTypeStruct())
- {
- // Require declarations, except when it's just a reference (as done for pointers)
- // or when the variable is defined externally
- if (!ts.sym.members && !(dsym.storage_class & (STC.ref_ | STC.extern_)))
- {
- .error(dsym.loc, "%s `%s` - no definition of struct `%s`", dsym.kind, dsym.toPrettyChars, ts.toChars());
-
- // Explain why the definition is required when it's part of another type
- if (!dsym.type.isTypeStruct())
- {
- // Prefer Loc of the dependant type
- const s = dsym.type.toDsymbol(sc);
- const loc = (s ? s : dsym).loc;
- loc.errorSupplemental("required by type `%s`", dsym.type.toChars());
- }
- errorSupplemental(dsym.loc, "see https://dlang.org/spec/struct.html#opaque_struct_unions");
- errorSupplemental(dsym.loc, "perhaps declare a variable with pointer type `%s*` instead", dsym.type.toChars());
-
- // Flag variable as error to avoid invalid error messages due to unknown size
- dsym.type = Type.terror;
- }
+ //printf("\tad = '%s'\n", ad.type.toChars());
+ return false;
}
- if ((dsym.storage_class & STC.auto_) && !inferred && !(dsym.storage_class & STC.autoref))
- .error(dsym.loc, "%s `%s` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?", dsym.kind, dsym.toPrettyChars);
+ _this.overnext = ad;
+ //printf("\ttrue: no conflict\n");
+ return true;
+ }
+ TemplateDeclaration td = s.isTemplateDeclaration();
+ if (td)
+ {
+ if (!td.funcroot)
+ td.funcroot = _this;
+ if (_this.overnext)
+ return _this.overnext.overloadInsert(td);
+ _this.overnext = td;
+ return true;
+ }
+ FuncDeclaration fd = s.isFuncDeclaration();
+ if (!fd)
+ return false;
- if (auto tt = tb.isTypeTuple())
- {
- /* Instead, declare variables for each of the tuple elements
- * and add those.
- */
- size_t nelems = Parameter.dim(tt.arguments);
- Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression(null, sc.inCfile) : null;
- if (ie)
- ie = ie.expressionSemantic(sc);
- if (nelems > 0 && ie)
- {
- auto iexps = new Expressions(ie);
- auto exps = new Expressions();
- for (size_t pos = 0; pos < iexps.length; pos++)
- {
- Lexpand1:
- Expression e = (*iexps)[pos];
- Parameter arg = Parameter.getNth(tt.arguments, pos);
- arg.type = arg.type.typeSemantic(dsym.loc, sc);
- //printf("[%d] iexps.length = %d, ", pos, iexps.length);
- //printf("e = (%s %s, %s), ", Token.tochars[e.op], e.toChars(), e.type.toChars());
- //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
+ if (_this.overnext)
+ {
+ td = _this.overnext.isTemplateDeclaration();
+ if (td)
+ fd.overloadInsert(td);
+ else
+ return _this.overnext.overloadInsert(fd);
+ }
+ _this.overnext = fd;
+ //printf("\ttrue: no conflict\n");
+ return true;
+}
- if (e != ie)
- {
- if (iexps.length > nelems)
- goto Lnomatch;
- if (e.type.implicitConvTo(arg.type))
- continue;
- }
+private bool overdeclOverloadInsert(OverDeclaration _this, Dsymbol s){
+ //printf("OverDeclaration::overloadInsert('%s') _this.aliassym = %p, _this.overnext = %p\n", s.toChars(), _this.aliassym, _this.overnext);
+ if (_this.overnext)
+ return _this.overnext.overloadInsert(s);
+ if (s == _this)
+ return true;
+ _this.overnext = s;
+ return true;
+}
- if (auto te = e.isTupleExp())
- {
- if (iexps.length - 1 + te.exps.length > nelems)
- goto Lnomatch;
+bool overloadInsert(Dsymbol _this, Dsymbol s)
+{
+ // Cannot overload postblits or destructors
+ if(_this.isPostBlitDeclaration() || _this.isDtorDeclaration())
+ return false;
+ else if(OverDeclaration od = _this.isOverDeclaration())
+ return overdeclOverloadInsert(od, s);
+ else if(FuncDeclaration fd = _this.isFuncDeclaration())
+ return funcOverloadInsert(fd, s);
+ if(Import imp = _this.isImport())
+ return importOverloadInsert(imp, s);
+ else if(TemplateDeclaration td = _this.isTemplateDeclaration())
+ return templateOverloadInsert(td, s);
+ else if (AliasDeclaration ad = _this.isAliasDeclaration())
+ return aliasOverloadInsert(ad, s);
- iexps.remove(pos);
- iexps.insert(pos, te.exps);
- (*iexps)[pos] = Expression.combine(te.e0, (*iexps)[pos]);
- goto Lexpand1;
- }
- else if (isAliasThisTuple(e))
- {
- auto v = copyToTemp(STC.none, "__tup", e);
- v.dsymbolSemantic(sc);
- auto ve = new VarExp(dsym.loc, v);
- ve.type = e.type;
+ return false;
+}
- exps.setDim(1);
- (*exps)[0] = ve;
- expandAliasThisTuples(exps, 0);
+/***************************************************
+ * Determine the numerical value of the AlignmentDeclaration
+ * Params:
+ * ad = AlignmentDeclaration
+ * sc = context
+ * Returns:
+ * ad with alignment value determined
+ */
+AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc)
+{
+ if (!ad.salign.isUnknown()) // UNKNOWN is 0
+ return ad;
- for (size_t u = 0; u < exps.length; u++)
- {
- Lexpand2:
- Expression ee = (*exps)[u];
- arg = Parameter.getNth(tt.arguments, pos + u);
- arg.type = arg.type.typeSemantic(dsym.loc, sc);
- //printf("[%d+%d] exps.length = %d, ", pos, u, exps.length);
- //printf("ee = (%s %s, %s), ", Token.tochars[ee.op], ee.toChars(), ee.type.toChars());
- //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
+ if (!ad.exps)
+ {
+ ad.salign.setDefault();
+ return ad;
+ }
- size_t iexps_dim = iexps.length - 1 + exps.length;
- if (iexps_dim > nelems)
- goto Lnomatch;
- if (ee.type.implicitConvTo(arg.type))
- continue;
+ dinteger_t strictest = 0; // strictest alignment
+ bool errors;
+ foreach (ref exp; (*ad.exps)[])
+ {
+ sc = sc.startCTFE();
+ auto e = exp.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+ e = e.ctfeInterpret();
+ exp = e; // could be re-evaluated if exps are assigned to more than one AlignDeclaration by CParser.applySpecifier(),
+ // e.g. `_Alignas(8) int a, b;`
+ if (e.op == EXP.error)
+ errors = true;
+ else
+ {
+ auto n = e.toInteger();
+ if (sc.inCfile && n == 0) // C11 6.7.5-6 allows 0 for alignment
+ continue;
- if (expandAliasThisTuples(exps, u) != -1)
- goto Lexpand2;
- }
+ if (n < 1 || n & (n - 1) || ushort.max < n || !e.type.isIntegral())
+ {
+ error(ad.loc, "alignment must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
+ errors = true;
+ }
+ if (n > strictest) // C11 6.7.5-6
+ strictest = n;
+ }
+ }
- if ((*exps)[0] != ve)
- {
- Expression e0 = (*exps)[0];
- (*exps)[0] = new CommaExp(dsym.loc, new DeclarationExp(dsym.loc, v), e0);
- (*exps)[0].type = e0.type;
+ if (errors || strictest == 0) // C11 6.7.5-6 says alignment of 0 means no effect
+ ad.salign.setDefault();
+ else
+ ad.salign.set(cast(uint) strictest);
- iexps.remove(pos);
- iexps.insert(pos, exps);
- goto Lexpand1;
- }
- }
- }
- if (iexps.length < nelems)
- goto Lnomatch;
+ return ad;
+}
- ie = new TupleExp(dsym._init.loc, iexps);
- }
- Lnomatch:
- if (ie && ie.op == EXP.tuple)
+/*********************************
+ * Resolve recursive tuple expansion in eponymous template.
+ */
+Dsymbol toAlias2(Dsymbol s)
+{
+ if (auto ad = s.isAliasDeclaration())
+ {
+ if (ad.inuse)
+ {
+ .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
+ return ad;
+ }
+ ad.inuse = 1;
+ Dsymbol ds = ad.aliassym ? ad.aliassym.toAlias2() : ad;
+ ad.inuse = 0;
+ return ds;
+ }
+ if (auto td = s.isTupleDeclaration())
+ {
+ //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars());
+ for (size_t i = 0; i < td.objects.length; i++)
+ {
+ RootObject o = (*td.objects)[i];
+ if (Dsymbol ds = isDsymbol(o))
{
- auto te = ie.isTupleExp();
- size_t tedim = te.exps.length;
- if (tedim != nelems)
- {
- error(dsym.loc, "sequence of %d elements cannot be assigned to sequence of %d elements", cast(int)tedim, cast(int)nelems);
- for (size_t u = tedim; u < nelems; u++) // fill dummy expression
- te.exps.push(ErrorExp.get());
- }
+ ds = ds.toAlias2();
+ (*td.objects)[i] = ds;
}
+ }
+ return td;
+ }
+ return toAlias(s);
+}
- auto exps = new Objects(nelems);
- for (size_t i = 0; i < nelems; i++)
- {
- Parameter arg = Parameter.getNth(tt.arguments, i);
+private Dsymbol toAliasImpl(AliasDeclaration ad)
+{
+ static if (0)
+ printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n",
+ ad.loc.toChars(), ad.toChars(), ad, ad.aliassym ? ad.aliassym.toChars() : "", ad.aliassym ? ad.aliassym.kind() : "", ad.inuse);
+ assert(ad != ad.aliassym);
+ //static int count; if (++count == 10) *(char*)0=0;
- OutBuffer buf;
- buf.printf("__%s_field_%llu", dsym.ident.toChars(), cast(ulong)i);
- auto id = Identifier.idPool(buf[]);
+ Dsymbol err()
+ {
+ // Avoid breaking "recursive alias" state during errors gagged
+ if (global.gag)
+ return ad;
+ ad.aliassym = new AliasDeclaration(ad.loc, ad.ident, Type.terror);
+ ad.type = Type.terror;
+ return ad.aliassym;
+ }
+ // Reading the AliasDeclaration
+ if (!ad.ignoreRead)
+ ad.wasRead = true; // can never assign to this AliasDeclaration again
- Initializer ti;
- if (ie)
- {
- Expression einit = ie;
- if (auto te = ie.isTupleExp())
- {
- einit = (*te.exps)[i];
- if (i == 0)
- einit = Expression.combine(te.e0, einit);
- }
- ti = new ExpInitializer(einit.loc, einit);
- }
- else
- ti = dsym._init ? dsym._init.syntaxCopy() : null;
-
- STC storage_class = STC.temp | dsym.storage_class;
- if ((dsym.storage_class & STC.parameter) && (arg.storageClass & STC.parameter))
- storage_class |= arg.storageClass;
- auto v = new VarDeclaration(dsym.loc, arg.type, id, ti, storage_class);
- //printf("declaring field %s of type %s\n", v.toChars(), v.type.toChars());
- v.overlapped = dsym.overlapped;
-
- v.dsymbolSemantic(sc);
-
- Expression e = new VarExp(dsym.loc, v);
- (*exps)[i] = e;
- }
- auto v2 = new TupleDeclaration(dsym.loc, dsym.ident, exps);
- v2.parent = dsym.parent;
- v2.isexp = true;
- dsym.aliasTuple = v2;
- dsym.semanticRun = PASS.semanticdone;
- return;
- }
-
- /* Storage class can modify the type
- */
- dsym.type = dsym.type.addStorageClass(dsym.storage_class);
-
- /* Adjust storage class to reflect type
- */
- if (dsym.type.isConst())
+ if (ad.inuse == 1 && ad.type && ad._scope)
+ {
+ ad.inuse = 2;
+ const olderrors = global.errors;
+ Dsymbol s = ad.type.toDsymbol(ad._scope);
+ //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this);
+ if (global.errors != olderrors)
+ return err();
+ if (s)
{
- dsym.storage_class |= STC.const_;
- if (dsym.type.isShared())
- dsym.storage_class |= STC.shared_;
+ s = s.toAlias();
+ if (global.errors != olderrors)
+ return err();
+ ad.aliassym = s;
+ ad.inuse = 0;
}
- else if (dsym.type.isImmutable())
- dsym.storage_class |= STC.immutable_;
- else if (dsym.type.isShared())
- dsym.storage_class |= STC.shared_;
- else if (dsym.type.isWild())
- dsym.storage_class |= STC.wild;
-
- if (STC stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_))
+ else
{
- if (stc == STC.final_)
- .error(dsym.loc, "%s `%s` cannot be `final`, perhaps you meant `const`?", dsym.kind, dsym.toPrettyChars);
- else
- {
- OutBuffer buf;
- stcToBuffer(buf, stc);
- .error(dsym.loc, "%s `%s` cannot be `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars());
- }
- dsym.storage_class &= ~stc; // strip off
+ Type t = ad.type.typeSemantic(ad.loc, ad._scope);
+ if (t.ty == Terror)
+ return err();
+ if (global.errors != olderrors)
+ return err();
+ //printf("t = %s\n", t.toChars());
+ ad.inuse = 0;
}
+ }
+ if (ad.inuse)
+ {
+ .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
+ return err();
+ }
- // At this point we can add `scope` to the STC instead of `in`,
- // because we are never going to use this variable's STC for user messages
- if (dsym.storage_class & STC.constscoperef)
- dsym.storage_class |= STC.scope_;
+ if (ad.semanticRun >= PASS.semanticdone)
+ {
+ // semantic is already done.
- import dmd.typesem : hasPointers;
+ // Do not see aliassym !is null, because of lambda aliases.
- if (dsym.storage_class & STC.scope_)
+ // Do not see type.deco !is null, even so "alias T = const int;` needs
+ // semantic analysis to take the storage class `const` as type qualifier.
+ }
+ else
+ {
+ // stop AliasAssign tuple building
+ if (ad.aliassym)
{
- STC stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.gshared);
- if (stc)
- {
- OutBuffer buf;
- stcToBuffer(buf, stc);
- .error(dsym.loc, "%s `%s` cannot be `scope` and `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars());
- }
- else if (dsym.isMember())
- {
- error(dsym.loc, "field `%s` cannot be `scope`", dsym.toChars());
- }
- else if (!dsym.type.hasPointers())
+ if (auto td = ad.aliassym.isTupleDeclaration())
{
- dsym.storage_class &= ~STC.scope_; // silently ignore; may occur in generic code
- // https://issues.dlang.org/show_bug.cgi?id=23168
- if (dsym.storage_class & STC.returnScope)
+ if (td.building)
{
- dsym.storage_class &= ~(STC.return_ | STC.returnScope);
+ td.building = false;
+ ad.semanticRun = PASS.semanticdone;
+ return td;
}
}
}
-
- if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.gshared | STC.ctfe))
- {
- }
- else
+ if (ad._import && ad._import._scope)
{
- AggregateDeclaration aad = parent.isAggregateDeclaration();
- if (aad)
- {
- if (global.params.v.field && dsym.storage_class & (STC.const_ | STC.immutable_) && dsym._init && !dsym._init.isVoidInitializer())
- {
- const(char)* s = (dsym.storage_class & STC.immutable_) ? "immutable" : "const";
- message(dsym.loc, "`%s.%s` is `%s` field", ad.toPrettyChars(), dsym.toChars(), s);
- }
- dsym.storage_class |= STC.field;
- if (auto ts = tbn.isTypeStruct())
- if (ts.sym.noDefaultCtor)
- {
- if (!dsym.isThisDeclaration() && !dsym._init)
- aad.noDefaultCtor = true;
- }
- }
-
- InterfaceDeclaration id = parent.isInterfaceDeclaration();
- if (id)
- {
- error(dsym.loc, "field `%s` not allowed in interface", dsym.toChars());
- }
- else if (aad && aad.sizeok == Sizeok.done)
- {
- error(dsym.loc, "cannot declare field `%s` because it will change the determined size of `%s`", dsym.toChars(), aad.toChars());
- }
-
- /* Templates cannot add fields to aggregates
+ /* If this is an internal alias for selective/renamed import,
+ * load the module first.
*/
- TemplateInstance ti = parent.isTemplateInstance();
- if (ti)
- {
- // Take care of nested templates
- while (1)
- {
- TemplateInstance ti2 = ti.tempdecl.parent.isTemplateInstance();
- if (!ti2)
- break;
- ti = ti2;
- }
- // If it's a member template
- AggregateDeclaration ad2 = ti.tempdecl.isMember();
- if (ad2 && dsym.storage_class != STC.none)
- {
- .error(dsym.loc, "%s `%s` - cannot use template to add field to aggregate `%s`", dsym.kind, dsym.toPrettyChars, ad2.toChars());
- }
- }
+ ad._import.dsymbolSemantic(null);
}
-
- /* If the alignment of a stack local is greater than the stack alignment,
- * note it in the enclosing function's alignSectionVars
- */
- version (MARS)
+ if (ad._scope)
{
- if (!dsym.alignment.isDefault() && sc.func &&
- dsym.alignment.get() > target.stackAlign() &&
- sc.func && !dsym.isDataseg() && !dsym.isParameter() && !dsym.isField())
- {
- auto fd = sc.func;
- if (!fd.alignSectionVars)
- fd.alignSectionVars = new VarDeclarations();
- fd.alignSectionVars.push(dsym);
- }
+ aliasSemantic(ad, ad._scope);
}
+ }
- if ((dsym.storage_class & (STC.ref_ | STC.field)) == (STC.ref_ | STC.field) && dsym.ident != Id.This)
+ ad.inuse = 1;
+ Dsymbol s = ad.aliassym ? ad.aliassym.toAlias() : ad;
+ ad.inuse = 0;
+ return s;
+}
+
+/*********************************
+ * If this symbol is really an alias for another,
+ * return that other.
+ * If needed, semantic() is invoked due to resolve forward reference.
+ */
+Dsymbol toAlias(Dsymbol s)
+{
+ if (auto ad = s.isAliasDeclaration())
+ {
+ return ad.toAliasImpl();
+ }
+ if (auto imp = s.isImport())
+ {
+ if (imp.aliasId)
+ return imp.mod;
+ return imp;
+ }
+ if (auto vd = s.isVarDeclaration())
+ {
+ //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym);
+ if ((!vd.type || !vd.type.deco) && vd._scope)
+ dsymbolSemantic(vd, vd._scope);
+
+ assert(vd != vd.aliasTuple);
+ return vd.aliasTuple ? vd.aliasTuple.toAlias() : vd;
+ }
+ // resolve real symbol
+ if (auto ti = s.isTemplateInstance())
+ {
+ static if (LOG)
{
- .error(dsym.loc, "%s `%s` - field declarations cannot be `ref`", dsym.kind, dsym.toPrettyChars);
+ printf("TemplateInstance.toAlias()\n");
}
-
- if (dsym.type.hasWild())
+ if (!ti.inst)
{
- if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field) || dsym.isDataseg())
+ // Maybe we can resolve it
+ if (ti._scope)
{
- .error(dsym.loc, "%s `%s` - only parameters or stack-based variables can be `inout`", dsym.kind, dsym.toPrettyChars);
+ dsymbolSemantic(ti, ti._scope);
}
- FuncDeclaration func = sc.func;
- if (func)
+ if (!ti.inst)
{
- if (func.fes)
- func = func.fes.func;
- bool isWild = false;
- for (FuncDeclaration fd = func; fd; fd = fd.toParentDecl().isFuncDeclaration())
- {
- if (fd.type.isTypeFunction().iswild)
- {
- isWild = true;
- break;
- }
- }
- if (!isWild)
- {
- .error(dsym.loc, "%s `%s` - `inout` variables can only be declared inside `inout` functions", dsym.kind, dsym.toPrettyChars);
- }
+ .error(ti.loc, "%s `%s` cannot resolve forward reference", ti.kind, ti.toPrettyChars);
+ ti.errors = true;
+ return ti;
}
}
- if (!(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.ref_ | STC.result)) &&
- tbn.ty == Tstruct && tbn.isTypeStruct().sym.noDefaultCtor)
+ if (ti.inst != ti)
+ return ti.inst.toAlias();
+
+ if (ti.aliasdecl)
{
- if (!dsym._init)
- {
- if (dsym.isField())
- {
- /* For fields, we'll check the constructor later to make sure it is initialized
- */
- dsym.storage_class |= STC.nodefaultctor;
- }
- else if (dsym.storage_class & STC.parameter)
- {
- }
- else
- .error(dsym.loc, "%s `%s` - default construction is disabled for type `%s`", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
- }
+ return ti.aliasdecl.toAlias();
}
- bool dsymIsRef = (dsym.storage_class & (STC.ref_ | STC.field | STC.parameter | STC.temp | STC.foreach_)) == STC.ref_;
- if (dsymIsRef)
+ return ti.inst;
+ }
+ return s;
+}
+
+const(char)* getMessage(DeprecatedDeclaration dd)
+{
+ if (auto sc = dd._scope)
+ {
+ dd._scope = null;
+
+ sc = sc.startCTFE();
+ dd.msg = dd.msg.expressionSemantic(sc);
+ dd.msg = resolveProperties(sc, dd.msg);
+ sc = sc.endCTFE();
+ dd.msg = dd.msg.ctfeInterpret();
+
+ if (auto se = dd.msg.toStringExp())
+ dd.msgstr = se.toStringz().ptr;
+ else
+ error(dd.msg.loc, "compile time constant expected, not `%s`", dd.msg.toChars());
+ }
+ return dd.msgstr;
+}
+
+bool checkDeprecated(Dsymbol d, Loc loc, Scope* sc)
+{
+ if (global.params.useDeprecated == DiagnosticReporting.off)
+ return false;
+ if (!d.isDeprecated())
+ return false;
+ // Don't complain if we're inside a deprecated symbol's scope
+ if (sc.isDeprecated())
+ return false;
+ // Don't complain if we're inside a template constraint
+ // https://issues.dlang.org/show_bug.cgi?id=21831
+ if (sc.inTemplateConstraint)
+ return false;
+
+ const(char)* message = null;
+ for (Dsymbol p = d; p; p = p.parent)
+ {
+ message = p.depdecl ? p.depdecl.getMessage() : null;
+ if (message)
+ break;
+ }
+ if (message)
+ deprecation(loc, "%s `%s` is deprecated - %s", d.kind, d.toPrettyChars, message);
+ else
+ deprecation(loc, "%s `%s` is deprecated", d.kind, d.toPrettyChars);
+
+ if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
+ ti.printInstantiationTrace(Classification.deprecation);
+ else if (auto ti = sc.parent ? sc.parent.isTemplateInstance() : null)
+ ti.printInstantiationTrace(Classification.deprecation);
+
+ return true;
+}
+
+/*********************************
+ * Check type to see if it is based on a deprecated symbol.
+ */
+private void checkDeprecated(Type type, Loc loc, Scope* sc)
+{
+ if (Dsymbol s = type.toDsymbol(sc))
+ {
+ s.checkDeprecated(loc, sc);
+ }
+
+ if (auto tn = type.nextOf())
+ tn.checkDeprecated(loc, sc);
+}
+
+// Returns true if a contract can appear without a function body.
+package bool allowsContractWithoutBody(FuncDeclaration funcdecl)
+{
+ assert(!funcdecl.fbody);
+
+ /* Contracts can only appear without a body when they are virtual
+ * interface functions or abstract.
+ */
+ Dsymbol parent = funcdecl.toParent();
+ InterfaceDeclaration id = parent.isInterfaceDeclaration();
+
+ if (!funcdecl.isAbstract() &&
+ (funcdecl.fensures || funcdecl.frequires) &&
+ !(id && funcdecl.isVirtual()))
+ {
+ auto cd = parent.isClassDeclaration();
+ if (!(cd && cd.isAbstract()))
+ return false;
+ }
+ return true;
+}
+
+/***************************************
+ * Determine if struct is POD (Plain Old Data).
+ *
+ * POD is defined as:
+ * $(OL
+ * $(LI not nested)
+ * $(LI no postblits, destructors, or assignment operators)
+ * $(LI no `ref` fields or fields that are themselves non-POD)
+ * )
+ * The idea being these are compatible with C structs.
+ *
+ * Returns:
+ * true if struct is POD
+ */
+bool isPOD(StructDeclaration sd)
+{
+ // If we've already determined whether this struct is POD.
+ if (sd.ispod != ThreeState.none)
+ return (sd.ispod == ThreeState.yes);
+
+
+ bool hasCpCtorLocal;
+ bool hasMoveCtorLocal;
+ bool needCopyCtor;
+ bool needMoveCtor;
+ needCopyOrMoveCtor(sd, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor);
+
+ if (sd.enclosing || // is nested
+ search(sd, sd.loc, Id.postblit) || // has postblit
+ search(sd, sd.loc, Id.dtor) || // has destructor
+ /* This is commented out because otherwise buildkite vibe.d:
+ `canCAS!Task` fails to compile
+ */
+ //hasMoveCtorLocal || // has move constructor
+ hasCpCtorLocal) // has copy constructor
+ {
+ sd.ispod = ThreeState.no;
+ return false;
+ }
+
+ // Recursively check all fields are POD.
+ for (size_t i = 0; i < sd.fields.length; i++)
+ {
+ VarDeclaration v = sd.fields[i];
+ if (v.storage_class & STC.ref_)
{
- if (!dsym._init && dsym.ident != Id.This)
- {
- if (dsym.storage_class & STC.autoref)
- {
- dsymIsRef = false;
- dsym.storage_class &= ~STC.ref_;
- }
- else
- .error(dsym.loc, "%s `%s` - initializer is required for `ref` variable", dsym.kind, dsym.toPrettyChars);
- }
- else if (dsym._init.isVoidInitializer())
- {
- .error(dsym.loc, "%s `%s` - void initializer not allowed for `ref` variable", dsym.kind, dsym.toPrettyChars);
- }
+ sd.ispod = ThreeState.no;
+ return false;
}
- FuncDeclaration fd = parent.isFuncDeclaration();
- if (dsym.type.isScopeClass() && !(dsym.storage_class & STC.nodtor))
+ if (auto ts = v.type.baseElemOf().isTypeStruct())
{
- if (dsym.storage_class & (STC.field | STC.out_ | STC.ref_ | STC.static_ | STC.manifest | STC.gshared) || !fd)
- {
- .error(dsym.loc, "%s `%s` globals, statics, fields, manifest constants, ref and out parameters cannot be `scope`", dsym.kind, dsym.toPrettyChars);
- }
-
- // @@@DEPRECATED_2.097@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
- // Deprecated in 2.087
- // Remove this when the feature is removed from the language
- if (!(dsym.storage_class & STC.scope_))
+ if (!ts.sym.isPOD())
{
- if (!(dsym.storage_class & STC.parameter) && dsym.ident != Id.withSym)
- .error(dsym.loc, "%s `%s` reference to `scope class` must be `scope`", dsym.kind, dsym.toPrettyChars);
+ sd.ispod = ThreeState.no;
+ return false;
}
}
+ }
- // Calculate type size + safety checks
- if (sc && sc.func)
- {
- if (dsym._init && dsym._init.isVoidInitializer() && !(dsym.storage_class & STC.temp))
- {
- // Don't do these checks for STC.temp vars because the generated `opAssign`
- // for a struct with postblit and destructor void initializes a temporary
- // __swap variable, which can be trusted
+ sd.ispod = ThreeState.yes;
+ return true;
+}
- if (dsym.type.hasPointers()) // also computes type size
- sc.setUnsafe(false, dsym.loc,
- "`void` initializing a pointer");
- else if (dsym.type.hasInvariant())
- sc.setUnsafe(false, dsym.loc,
- "`void` initializing a struct with an invariant");
- else if (dsym.type.toBasetype().ty == Tbool)
- sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
- "void intializing a bool (which must always be 0 or 1)");
- else if (dsym.type.hasUnsafeBitpatterns())
- sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
- "`void` initializing a type with unsafe bit patterns");
- }
- else if (!dsym._init &&
- !(dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field | STC.parameter)) &&
- dsym.type.hasVoidInitPointers())
- {
- sc.setUnsafe(false, dsym.loc, "`void` initializers for pointers");
- }
- }
+/****************************************
+ * Fill in vtbl[] for base class based on member functions of class cd.
+ * Input:
+ * bc BaseClass
+ * vtbl if !=NULL, fill it in
+ * newinstance !=0 means all entries must be filled in by members
+ * of cd, not members of any base classes of cd.
+ * Returns:
+ * true if any entries were filled in by members of cd (not exclusively
+ * by base classes)
+ */
+bool fillVtbl(BaseClass* bc, ClassDeclaration cd, FuncDeclarations* vtbl, int newinstance)
+{
+ bool result = false;
- if ((!dsym._init || dsym._init.isVoidInitializer) && !fd)
- {
- // If not mutable, initializable by constructor only
- dsym.setInCtorOnly = true;
- }
+ //printf("BaseClass.fillVtbl(this='%s', cd='%s')\n", sym.toChars(), cd.toChars());
+ if (vtbl)
+ vtbl.setDim(bc.sym.vtbl.length);
- if (dsym._init)
- { } // remember we had an explicit initializer
- else if (dsym.storage_class & STC.manifest)
- .error(dsym.loc, "%s `%s` - manifest constants must have initializers", dsym.kind, dsym.toPrettyChars);
+ // first entry is ClassInfo reference
+ for (size_t j = bc.sym.vtblOffset(); j < bc.sym.vtbl.length; j++)
+ {
+ FuncDeclaration ifd = bc.sym.vtbl[j].isFuncDeclaration();
- // Don't allow non-extern, non-__gshared variables to be interfaced with C++
- if (dsym._linkage == LINK.cpp && !(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.gshared)) && dsym.isDataseg())
- {
- const char* p = (dsym.storage_class & STC.shared_) ? "shared" : "static";
- .error(dsym.loc, "%s `%s` cannot have `extern(C++)` linkage because it is `%s`", dsym.kind, dsym.toPrettyChars, p);
- errorSupplemental(dsym.loc, "perhaps declare it as `__gshared` instead");
- dsym.errors = true;
- }
+ //printf(" vtbl[%d] is '%s'\n", j, ifd ? ifd.toChars() : "null");
+ assert(ifd);
- bool isBlit = false;
- uinteger_t sz;
- if (sc.inCfile && !dsym._init)
+ // Find corresponding function in this class
+ auto tf = ifd.type.toTypeFunction();
+ auto fd = cd.findFunc(ifd.ident, tf);
+ if (fd && !fd.isAbstract())
{
- addDefaultCInitializer(dsym);
+ if (fd.toParent() == cd)
+ result = true;
}
- if (!dsym._init &&
- !(dsym.storage_class & (STC.static_ | STC.gshared | STC.extern_)) &&
- fd &&
- (!(dsym.storage_class & (STC.field | STC.in_ | STC.foreach_ | STC.parameter | STC.result)) ||
- (dsym.storage_class & STC.out_)) &&
- (sz = dsym.type.size()) != 0)
- {
- // Provide a default initializer
-
- //printf("Providing default initializer for '%s'\n", dsym.toChars());
- if (sz == SIZE_INVALID && dsym.type.ty != Terror)
- .error(dsym.loc, "%s `%s` - size of type `%s` is invalid", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
+ else
+ fd = null;
+ if (vtbl)
+ (*vtbl)[j] = fd;
+ }
+ return result;
+}
- Type tv = dsym.type;
- while (tv.ty == Tsarray) // Don't skip Tenum
- tv = tv.nextOf();
- if (tv.needsNested())
- {
- /* Nested struct requires valid enclosing frame pointer.
- * In StructLiteralExp::toElem(), it's calculated.
- */
- assert(tbn.ty == Tstruct);
- checkFrameAccess(dsym.loc, sc, tbn.isTypeStruct().sym);
+/*
+If sd has a copy constructor and ctor is an rvalue constructor,
+issue an error.
- Expression e = tv.defaultInitLiteral(dsym.loc);
- e = new BlitExp(dsym.loc, new VarExp(dsym.loc, dsym), e);
- e = e.expressionSemantic(sc);
- dsym._init = new ExpInitializer(dsym.loc, e);
- goto Ldtor;
- }
- if (tv.ty == Tstruct && tv.isTypeStruct().sym.zeroInit)
- {
- /* If a struct is all zeros, as a special case
- * set its initializer to the integer 0.
- * In AssignExp::toElem(), we check for this and issue
- * a memset() to initialize the struct.
- * Must do same check in interpreter.
- */
- Expression e = IntegerExp.literal!0;
- e = new BlitExp(dsym.loc, new VarExp(dsym.loc, dsym), e);
- e.type = dsym.type; // don't type check this, it would fail
- dsym._init = new ExpInitializer(dsym.loc, e);
- goto Ldtor;
- }
- if (dsym.type.baseElemOf().ty == Tvoid)
- {
- .error(dsym.loc, "%s `%s` of type `%s` does not have a default initializer", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
- }
- else if (auto e = dsym.type.defaultInit(dsym.loc))
- {
- dsym._init = new ExpInitializer(dsym.loc, e);
- }
+Params:
+ sd = struct declaration that may contain both an rvalue and copy constructor
+ ctor = constructor that will be checked if it is an rvalue constructor
+ ti = template instance the ctor is part of
- // Default initializer is always a blit
- isBlit = true;
- }
- if (dsym._init)
- {
- sc = sc.push();
- sc.stc &= ~(STC.TYPECTOR | STC.pure_ | STC.nothrow_ | STC.nogc | STC.ref_ | STC.disable);
+Return:
+ `true` if sd has a copy constructor and ctor is an rvalue constructor
+*/
+bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti)
+{
+ //printf("checkHasBothRvalueAndCpCtor() sd: %s ctor: %s ti: %s\n", sd.toChars(), ctor.toChars(), ti.toChars());
+ /* cannot use ctor.isMoveCtor because semantic pass may not have been run yet,
+ * so use isRvalueConstructor()
+ */
+ if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor))
+ {
+ .error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars());
+ .errorSupplemental(ti.loc, "Template instance `%s` creates an rvalue constructor for `struct %s`",
+ ti.toPrettyChars(), sd.toChars());
- if (sc.inCfile &&
- dsym.type.isTypeSArray() &&
- dsym.type.isTypeSArray().isIncomplete() &&
- dsym._init.isVoidInitializer() &&
- !(dsym.storage_class & STC.field))
- {
- .error(dsym.loc, "%s `%s` - incomplete array type must have initializer", dsym.kind, dsym.toPrettyChars);
- }
+ return true;
+ }
- ExpInitializer ei = dsym._init.isExpInitializer();
+ return false;
+}
- if (ei) // https://issues.dlang.org/show_bug.cgi?id=13424
- // Preset the required type to fail in FuncLiteralDeclaration::semantic3
- ei.exp = inferType(ei.exp, dsym.type);
+/************************************************
+ * Check if ctor is an rvalue constructor.
+ * A constructor that receives a single parameter of the same type as
+ * `Unqual!typeof(this)` is an rvalue constructor.
+ * Params:
+ * sd = struct that ctor is a member of
+ * ctor = constructor to test
+ * Returns:
+ * true if it is an rvalue constructor
+ */
+bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
+{
+ // note commonality with setting isMoveCtor in the semantic code for CtorDeclaration
+ auto tf = ctor.type.isTypeFunction();
+ const dim = tf.parameterList.length;
+ if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
+ {
+ auto param = tf.parameterList[0];
+ if (!(param.storageClass & STC.ref_) && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
+ {
+ return true;
+ }
+ }
+ return false;
+}
- // If inside function, there is no semantic3() call
- if (sc.func || sc.intypeof == 1)
+/*************************************
+ * Find the `alias this` symbol of e's type.
+ * Params:
+ * sc = context
+ * e = expression forming the `this`
+ * gag = do not print errors, return `null` instead
+ * findOnly = don't do further processing like resolving properties,
+ * i.e. just return plain dotExp() result.
+ * Returns:
+ * Expression that is `e.aliasthis`
+ */
+Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false)
+{
+ //printf("resolveAliasThis() %s\n", toChars(e));
+ import dmd.typesem : dotExp;
+ for (AggregateDeclaration ad = isAggregate(e.type); ad;)
+ {
+ if (ad.aliasthis)
+ {
+ Loc loc = e.loc;
+ Type tthis = (e.op == EXP.type ? e.type : null);
+ const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag));
+ 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 (tthis && ad.aliasthis.sym.needThis())
{
- // If local variable, use AssignExp to handle all the various
- // possibilities.
- if (fd && !(dsym.storage_class & (STC.manifest | STC.static_ | STC.gshared | STC.extern_)) && !dsym._init.isVoidInitializer())
+ if (auto ve = e.isVarExp())
{
- //printf("fd = '%s', var = '%s'\n", fd.toChars(), dsym.toChars());
- if (!ei)
- {
- ArrayInitializer ai = dsym._init.isArrayInitializer();
- Expression e;
- if (ai && tb.ty == Taarray)
- e = ai.toAssocArrayLiteral();
- else
- e = dsym._init.initializerToExpression(null, sc.inCfile);
- if (!e)
- {
- // Run semantic, but don't need to interpret
- dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITnointerpret);
- e = dsym._init.initializerToExpression(null, sc.inCfile);
- if (!e)
- {
- .error(dsym.loc, "%s `%s` is not a static and cannot have static initializer", dsym.kind, dsym.toPrettyChars);
- e = ErrorExp.get();
- }
- }
- ei = new ExpInitializer(dsym._init.loc, e);
- dsym._init = ei;
- }
- else if (sc.inCfile && dsym.type.isTypeSArray() &&
- dsym.type.isTypeSArray().isIncomplete())
- {
- // C11 6.7.9-22 determine the size of the incomplete array,
- // or issue an error that the initializer is invalid.
- dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
- }
-
- if (ei && dsym.isScope())
+ if (auto fd = ve.var.isFuncDeclaration())
{
- Expression ex = ei.exp.lastComma();
- if (ex.op == EXP.blit || ex.op == EXP.construct)
- ex = (cast(AssignExp)ex).e2;
- if (auto ne = ex.isNewExp())
- {
- if (ne.placement)
- {
- }
- /* See if initializer is a NewExp that can be allocated on the stack.
- */
- else if (dsym.type.toBasetype().ty == Tclass)
- {
- /* Unsafe to allocate on stack if constructor is not `scope` because the `this` can leak.
- * https://issues.dlang.org/show_bug.cgi?id=23145
- */
- if (ne.member && !(ne.member.storage_class & STC.scope_))
- {
- import dmd.escape : setUnsafeDIP1000;
- const inSafeFunc = sc.func && sc.func.isSafeBypassingInference(); // isSafeBypassingInference may call setUnsafe().
- if (setUnsafeDIP1000(*sc, false, dsym.loc, "`scope` allocation of `%s` with a non-`scope` constructor", dsym))
- errorSupplemental(ne.member.loc, "is the location of the constructor");
- }
- ne.onstack = 1;
- dsym.onstack = true;
- }
- }
- else if (auto fe = ex.isFuncExp())
- {
- // or a delegate that doesn't escape a reference to the function
- FuncDeclaration f = fe.fd;
- if (f.tookAddressOf)
- f.tookAddressOf--;
- }
- else if (auto ale = ex.isArrayLiteralExp())
+ // https://issues.dlang.org/show_bug.cgi?id=13009
+ // Support better match for the overloaded alias this.
+ bool hasOverloads;
+ if (auto f = fd.overloadModMatch(loc, tthis, hasOverloads))
{
- // or an array literal assigned to a `scope` variable
- if (sc.useDIP1000 == FeatureState.enabled
- && !dsym.type.nextOf().needsDestruction())
- ale.onstack = true;
+ if (!hasOverloads)
+ fd = f; // use exact match
+ e = new VarExp(loc, fd, hasOverloads);
+ e.type = f.type;
+ e = new CallExp(loc, e);
+ goto L1;
}
}
+ }
+ /* non-@property function is not called inside typeof(),
+ * so resolve it ahead.
+ */
+ {
+ ubyte save = sc.intypeof;
+ sc.intypeof = 1; // bypass "need this" error check
+ e = resolveProperties(sc, e);
+ sc.intypeof = save;
+ }
+ L1:
+ e = new TypeExp(loc, new TypeTypeof(loc, e));
+ e = e.expressionSemantic(sc);
+ }
+ e = resolveProperties(sc, e);
+ if (!gag)
+ ad.aliasthis.checkDeprecatedAliasThis(loc, sc);
+ else if (global.endGagging(olderrors))
+ e = null;
+ }
- Expression exp = ei.exp;
- Expression e1 = new VarExp(dsym.loc, dsym);
-
- void constructInit(bool isBlit)
- {
- if (isBlit)
- exp = new BlitExp(dsym.loc, e1, exp);
- else
- exp = new ConstructExp(dsym.loc, e1, exp);
- dsym.canassign++;
- exp = exp.expressionSemantic(sc);
- dsym.canassign--;
- }
+ import dmd.dclass : ClassDeclaration;
+ auto cd = ad.isClassDeclaration();
+ if ((!e || !ad.aliasthis) && cd && cd.baseClass && cd.baseClass != ClassDeclaration.object)
+ {
+ ad = cd.baseClass;
+ continue;
+ }
+ break;
+ }
+ return e;
+}
- if (dsymIsRef) // follow logic similar to typesem.argumentMatchParameter() and statementsem.visitForeach()
- {
- dsym.storage_class |= STC.nodtor;
- exp = exp.expressionSemantic(sc);
- Type tp = dsym.type;
- Type ta = exp.type;
- if (!exp.isLvalue())
- {
- if (dsym.storage_class & STC.autoref)
- {
- dsym.storage_class &= ~STC.ref_;
- constructInit(isBlit);
- }
- else
- {
- .error(dsym.loc, "rvalue `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
- exp = ErrorExp.get();
- }
- }
- else if (!ta.constConv(tp))
- {
- if (dsym.storage_class & STC.autoref)
- {
- dsym.storage_class &= ~STC.ref_;
- constructInit(false);
- }
- else
- {
- .error(dsym.loc, "type `%s` cannot be assigned to `ref %s %s`", ta.toChars(), tp.toChars(), dsym.toChars());
- exp = ErrorExp.get();
- }
- }
- else if (exp.isBitField())
- {
- if (dsym.storage_class & STC.autoref)
- {
- dsym.storage_class &= ~STC.ref_;
- constructInit(false);
- }
- else
- {
- .error(dsym.loc, "bitfield `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
- exp = ErrorExp.get();
- }
- }
- else
- {
- constructInit(false);
- }
- }
- else
- {
- constructInit(isBlit);
- }
+/**
+ * Check if an `alias this` is deprecated
+ *
+ * Usually one would use `expression.checkDeprecated(scope, aliasthis)` to
+ * check if `expression` uses a deprecated `aliasthis`, but this calls
+ * `toPrettyChars` which lead to the following message:
+ * "Deprecation: alias this `fullyqualified.aggregate.__anonymous` is deprecated"
+ *
+ * Params:
+ * at = The `AliasThis` object to check
+ * loc = `Loc` of the expression triggering the access to `at`
+ * sc = `Scope` of the expression
+ * (deprecations do not trigger in deprecated scopes)
+ *
+ * Returns:
+ * Whether the alias this was reported as deprecated.
+ */
+private bool checkDeprecatedAliasThis(AliasThis at, Loc loc, Scope* sc)
+{
+ if (global.params.useDeprecated != DiagnosticReporting.off
+ && at.isDeprecated() && !sc.isDeprecated())
+ {
+ const(char)* message = null;
+ for (Dsymbol p = at; p; p = p.parent)
+ {
+ message = p.depdecl ? p.depdecl.getMessage() : null;
+ if (message)
+ break;
+ }
+ if (message)
+ deprecation(loc, "`alias %s this` is deprecated - %s",
+ at.sym.toChars(), message);
+ else
+ deprecation(loc, "`alias %s this` is deprecated",
+ at.sym.toChars());
- if (exp.op == EXP.error)
- {
- dsym._init = new ErrorInitializer();
- ei = null;
- }
- else
- ei.exp = exp.optimize(WANTvalue);
- }
- else
- {
- // https://issues.dlang.org/show_bug.cgi?id=14166
- // Don't run CTFE for the temporary variables inside typeof
- dsym._init = dsym._init.initializerSemantic(sc, dsym.type, sc.intypeof == 1 ? INITnointerpret : INITinterpret);
- import dmd.semantic2 : lowerStaticAAs;
- lowerStaticAAs(dsym, sc);
- auto init_err = dsym._init.isExpInitializer();
- if (init_err && init_err.exp.op == EXP.showCtfeContext)
- {
- init_err.exp = ErrorExp.get();
- errorSupplemental(dsym.loc, "compile time context created here");
- }
- }
- }
- else if (parent.isAggregateDeclaration())
- {
- dsym._scope = scx ? scx : sc.copy();
- dsym._scope.setNoFree();
- }
- else if (dsym.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) ||
- dsym.type.isConst() || dsym.type.isImmutable() ||
- sc.inCfile)
- {
- /* Because we may need the results of a const declaration in a
- * subsequent type, such as an array dimension, before semantic2()
- * gets ordinarily run, try to run semantic2() now.
- * If a C array is of unknown size, the initializer can provide the size. Do this
- * eagerly because C does it eagerly.
- * Ignore failure.
- */
- if (!inferred)
- {
- 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
- if (ei && (ei.exp.op != EXP.scope_ ? true : !ei.exp.isScopeExp().sds.isPackage()))
- {
- if (ei.exp.type)
- {
- // If exp is already resolved we are done, our original init exp
- // could have a type painting that we need to respect
- // e.g. ['a'] typed as string, or [['z'], ""] as string[]
- // See https://issues.dlang.org/show_bug.cgi?id=15711
- }
- else
- {
- Expression exp = ei.exp.syntaxCopy();
+ if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
+ ti.printInstantiationTrace(Classification.deprecation);
- bool needctfe = dsym.isDataseg() || (dsym.storage_class & STC.manifest);
- if (needctfe)
- sc = sc.startCTFE();
- sc = sc.push();
- sc.varDecl = dsym; // https://issues.dlang.org/show_bug.cgi?id=24051
- exp = exp.expressionSemantic(sc);
- exp = resolveProperties(sc, exp);
- sc = sc.pop();
- if (needctfe)
- sc = sc.endCTFE();
- ei.exp = exp;
- }
+ return true;
+ }
+ return false;
+}
- Type tb2 = dsym.type.toBasetype();
- Type ti = ei.exp.type.toBasetype();
+// Save the scope and defer semantic analysis on the Dsymbol.
+void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope* scx)
+{
+ s._scope = scx ? scx : sc.copy();
+ s._scope.setNoFree();
+ addDeferredSemantic(s);
+}
- /* The problem is the following code:
- * struct CopyTest {
- * double x;
- * this(double a) { x = a * 10.0;}
- * this(this) { x += 2.0; }
- * }
- * const CopyTest z = CopyTest(5.3); // ok
- * const CopyTest w = z; // not ok, postblit not run
- * static assert(w.x == 55.0);
- * because the postblit doesn't get run on the initialization of w.
- */
- if (auto ts = ti.isTypeStruct())
- {
- StructDeclaration sd = ts.sym;
- /* Look to see if initializer involves a copy constructor
- * (which implies a postblit)
- */
- // there is a copy constructor
- // and exp is the same struct
- if (sd.postblit && tb2.toDsymbol(null) == sd)
- {
- // The only allowable initializer is a (non-copy) constructor
- if (ei.exp.isLvalue())
- .error(dsym.loc, "%s `%s` of type struct `%s` uses `this(this)`, which is not allowed in static initialization", dsym.kind, dsym.toPrettyChars, tb2.toChars());
- }
- }
- }
+struct Ungag
+{
+ uint oldgag;
- dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
- dsym.inuse--;
- if (global.errors > errors)
- {
- dsym._init = new ErrorInitializer();
- dsym.type = Type.terror;
- }
- }
- else
- {
- dsym._scope = scx ? scx : sc.copy();
- dsym._scope.setNoFree();
- }
- }
- sc = sc.pop();
+ extern (D) this(uint old) nothrow @safe
+ {
+ this.oldgag = old;
+ }
+
+ extern (C++) ~this() nothrow
+ {
+ global.gag = oldgag;
+ }
+}
+
+Ungag ungagSpeculative(const Dsymbol s)
+{
+ const oldgag = global.gag;
+ if (global.gag && !s.isSpeculative() && !s.toParent2().isFuncDeclaration())
+ global.gag = 0;
+ return Ungag(oldgag);
+}
+
+/*******************************************
+ * Print deprecation warning if we're deprecated, when
+ * this module is imported from scope sc.
+ *
+ * Params:
+ * m = the module
+ * sc = the scope into which we are imported
+ * loc = the location of the import statement
+ */
+private void checkImportDeprecation(Module m, Loc loc, Scope* sc)
+{
+ if (!m.md || !m.md.isdeprecated || sc.isDeprecated)
+ return;
+
+ Expression msg = m.md.msg;
+ if (StringExp se = msg ? msg.toStringExp() : null)
+ {
+ const slice = se.peekString();
+ if (slice.length)
+ {
+ deprecation(m.loc, "%s `%s` is deprecated - %.*s", m.kind, m.toPrettyChars, cast(int)slice.length, slice.ptr);
+ return;
}
+ }
+ deprecation(m.loc, "%s `%s` is deprecated", m.kind, m.toPrettyChars);
+}
- Ldtor:
- /* Build code to execute destruction, if necessary
- */
- dsym.edtor = dsym.callScopeDtor(sc);
- if (dsym.edtor)
+private extern(C++) final class DsymbolSemanticVisitor : Visitor
+{
+ import dmd.typesem: size;
+
+ alias visit = Visitor.visit;
+
+ Scope* sc;
+ this(Scope* sc) scope @safe
+ {
+ this.sc = sc;
+ }
+
+ override void visit(Dsymbol dsym)
+ {
+ .error(dsym.loc, "%s `%s` %p has no semantic routine", dsym.kind, dsym.toPrettyChars, dsym);
+ }
+
+ override void visit(ScopeDsymbol) { }
+ override void visit(Declaration) { }
+
+ override void visit(AliasThis dsym)
+ {
+ if (dsym.semanticRun != PASS.initial)
+ return;
+
+ if (dsym._scope)
{
- if (sc.func && dsym.storage_class & (STC.static_ | STC.gshared))
- dsym.edtor = dsym.edtor.expressionSemantic(sc._module._scope);
+ sc = dsym._scope;
+ dsym._scope = null;
+ }
+
+ if (!sc)
+ return;
+
+ dsym.semanticRun = PASS.semantic;
+ dsym.isDeprecated_ = !!(sc.stc & STC.deprecated_);
+
+ Dsymbol p = sc.parent.pastMixin();
+ AggregateDeclaration ad = p.isAggregateDeclaration();
+ if (!ad)
+ {
+ error(dsym.loc, "alias this can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
+ return;
+ }
+
+ assert(ad.members);
+ Dsymbol s = ad.search(dsym.loc, dsym.ident);
+ if (!s)
+ {
+ Dsymbol pscopesym;
+ s = sc.search(dsym.loc, dsym.ident, pscopesym);
+ if (s)
+ error(dsym.loc, "`%s` is not a member of `%s`", s.toChars(), ad.toChars());
else
- dsym.edtor = dsym.edtor.expressionSemantic(sc);
+ error(dsym.loc, "undefined identifier `%s`", dsym.ident.toChars());
+ return;
+ }
+ if (ad.aliasthis && s != ad.aliasthis)
+ {
+ error(dsym.loc, "there can be only one alias this");
+ return;
+ }
+
+ /* disable the alias this conversion so the implicit conversion check
+ * doesn't use it.
+ */
+ ad.aliasthis = null;
+
+ Dsymbol sx = s;
+ if (sx.isAliasDeclaration())
+ sx = sx.toAlias();
+ Declaration d = sx.isDeclaration();
+ if (d && !d.isTupleDeclaration())
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=18429
+ *
+ * If the identifier in the AliasThis declaration
+ * is defined later and is a voldemort type, we must
+ * perform semantic on the declaration to deduce the type.
+ */
+ if (!d.type)
+ d.dsymbolSemantic(sc);
- version (none)
+ Type t = d.type;
+ assert(t);
+ if (ad.type.implicitConvTo(t) > MATCH.nomatch)
{
- // currently disabled because of std.stdio.stdin, stdout and stderr
- if (dsym.isDataseg() && !(dsym.storage_class & STC.extern_))
- .error(dsym.loc, "%s `%s` static storage variables cannot have destructors", dsym.kind, dsym.toPrettyChars);
+ error(dsym.loc, "alias this is not reachable as `%s` already converts to `%s`", ad.toChars(), t.toChars());
}
}
+ dsym.sym = s;
+ // Restore alias this
+ ad.aliasthis = dsym;
dsym.semanticRun = PASS.semanticdone;
-
- if (dsym.type.toBasetype().ty == Terror)
- dsym.errors = true;
-
- if(sc.scopesym && !sc.scopesym.isAggregateDeclaration())
- {
- for (ScopeDsymbol sym = sc.scopesym; sym && dsym.endlinnum == 0;
- sym = sym.parent ? sym.parent.isScopeDsymbol() : null)
- dsym.endlinnum = sym.endlinnum;
- }
- }
-
- override void visit(TypeInfoDeclaration dsym)
- {
- assert(dsym._linkage == LINK.c);
}
- override void visit(CAsmDeclaration dsym)
+ override void visit(AliasDeclaration dsym)
{
if (dsym.semanticRun >= PASS.semanticdone)
return;
- import dmd.iasm : asmSemantic;
- asmSemantic(dsym, sc);
- dsym.semanticRun = PASS.semanticdone;
+ assert(dsym.semanticRun <= PASS.semantic);
+
+ if (!sc)
+ return;
+
+ dsym.semanticRun = PASS.semantic;
+
+ dsym.storage_class |= sc.stc & STC.deprecated_;
+ dsym.visibility = sc.visibility;
+ dsym.userAttribDecl = sc.userAttribDecl;
+
+ if (!sc.func && dsym.inNonRoot())
+ return;
+
+ aliasSemantic(dsym, sc);
}
- override void visit(BitFieldDeclaration dsym)
+ override void visit(AliasAssign dsym)
{
- //printf("BitField::semantic('%s')\n", dsym.toChars());
+ //printf("visit(AliasAssign)\n");
if (dsym.semanticRun >= PASS.semanticdone)
return;
+ assert(dsym.semanticRun <= PASS.semantic);
- visit(cast(VarDeclaration)dsym);
- if (dsym.errors)
+ if (!sc.func && dsym.inNonRoot())
return;
- if (!(sc.previews.bitfields || sc.inCfile))
- {
- version (IN_GCC)
- .error(dsym.loc, "%s `%s` use `-fpreview=bitfields` for bitfield support", dsym.kind, dsym.toPrettyChars);
- else
- .error(dsym.loc, "%s `%s` use -preview=bitfields for bitfield support", dsym.kind, dsym.toPrettyChars);
- }
-
- if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration())
- {
- .error(dsym.loc, "%s `%s` - bitfield must be member of struct, union, or class", dsym.kind, dsym.toPrettyChars);
- }
-
- sc = sc.startCTFE();
- auto width = dsym.width.expressionSemantic(sc);
- sc = sc.endCTFE();
- width = width.ctfeInterpret();
- if (!dsym.type.isIntegral())
- {
- // C11 6.7.2.1-5
- error(width.loc, "bitfield type `%s` is not an integer type", dsym.type.toChars());
- dsym.errors = true;
- }
- if (!width.isIntegerExp())
- {
- error(width.loc, "bitfield width `%s` is not an integer constant", dsym.width.toChars());
- dsym.errors = true;
- }
- const uwidth = width.toInteger(); // uwidth is unsigned
- if (uwidth == 0 && !dsym.isAnonymous())
- {
- error(width.loc, "bitfield `%s` has zero width", dsym.toChars());
- dsym.errors = true;
- }
- const sz = dsym.type.size();
- if (sz == SIZE_INVALID)
- dsym.errors = true;
- const max_width = sz * 8;
- if (uwidth > max_width)
- {
- error(width.loc, "width `%lld` of bitfield `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars());
- dsym.errors = true;
- }
- dsym.fieldWidth = cast(uint)uwidth;
+ aliasAssignSemantic(dsym, sc);
}
- override void visit(Import imp)
+ override void visit(VarDeclaration dsym)
{
- timeTraceBeginEvent(TimeTraceEventType.sema1Import);
- scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Import, imp);
- static if (LOG)
+ version (none)
{
- printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars());
- scope(exit)
- printf("-Import::semantic('%s'), pkg = %p\n", imp.toChars(), imp.pkg);
+ printf("VarDeclaration::semantic('%s', parent = '%s') sem = %d\n",
+ dsym.toChars(), sc.parent ? sc.parent.toChars() : null, dsym.semanticRun);
+ printf(" type = %s\n", dsym.type ? dsym.type.toChars() : "null");
+ printf(" stc = x%llx\n", dsym.storage_class);
+ printf(" storage_class = x%llx\n", dsym.storage_class);
+ printf("linkage = %d\n", dsym._linkage);
+ //if (strcmp(toChars(), "mul") == 0) assert(0);
}
- if (imp.semanticRun > PASS.initial)
+ //if (semanticRun > PASS.initial)
+ // return;
+ //semanticRun = PSSsemantic;
+
+ if (dsym.semanticRun >= PASS.semanticdone)
return;
- if (imp._scope)
+ if (sc && sc.inunion && sc.inunion.isAnonDeclaration())
+ dsym.overlapped = true;
+
+ dsym.sequenceNumber = global.varSequenceNumber++;
+ if (!dsym.isScope())
+ dsym.maybeScope = true;
+
+ Scope* scx = null;
+ if (dsym._scope)
{
- sc = imp._scope;
- imp._scope = null;
+ sc = dsym._scope;
+ scx = sc;
+ dsym._scope = null;
}
+
if (!sc)
return;
- imp.parent = sc.parent;
+ dsym.semanticRun = PASS.semantic;
- imp.semanticRun = PASS.semantic;
+ // 'static foreach' variables should not inherit scope properties
+ // https://issues.dlang.org/show_bug.cgi?id=19482
+ if ((dsym.storage_class & (STC.foreach_ | STC.local)) == (STC.foreach_ | STC.local))
+ {
+ dsym._linkage = LINK.d;
+ dsym.visibility = Visibility(Visibility.Kind.public_);
+ dsym.overlapped = false; // unset because it is modified early on this function
+ dsym.userAttribDecl = null; // unset because it is set by Dsymbol.setScope()
+ }
+ else
+ {
+ /* Pick up storage classes from context, but except synchronized,
+ * override, abstract, and final.
+ */
+ dsym.storage_class |= (sc.stc & ~(STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_));
+ dsym.userAttribDecl = sc.userAttribDecl;
+ dsym.cppnamespace = sc.namespace;
+ dsym._linkage = sc.linkage;
+ dsym.visibility = sc.visibility;
+ dsym.alignment = sc.alignment();
+ }
- // Load if not already done so
- if (!imp.mod)
+ if (dsym.storage_class & STC.extern_ && dsym._init)
{
- // https://issues.dlang.org/show_bug.cgi?id=22857
- // if parser errors occur when loading a module
- // we should just stop compilation
- if (imp.load(sc))
+ if (sc.inCfile)
{
- for (size_t i = 0; i < imp.aliasdecls.length; i++)
- imp.aliasdecls[i].type = Type.terror;
- return;
+ // https://issues.dlang.org/show_bug.cgi?id=24447
+ // extern int x = 3; is allowed in C
+ dsym.storage_class &= ~STC.extern_;
}
+ else
+ .error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars);
- if (imp.mod)
+ }
+
+ AggregateDeclaration ad = dsym.isThis();
+ if (ad)
+ dsym.storage_class |= ad.storage_class & STC.TYPECTOR;
+
+ if ((dsym.storage_class & STC.auto_) && (dsym.storage_class & STC.ref_))
+ {
+ if (!(dsym.storage_class & STC.autoref))
{
- imp.mod.importAll(null);
- imp.mod.checkImportDeprecation(imp.loc, sc);
+ .error(dsym.loc, "%s `%s` - `auto ref` variable must have `auto` and `ref` adjacent", dsym.kind, dsym.toChars());
+ dsym.storage_class |= STC.autoref;
}
}
- if (!imp.mod)
+
+ /* If auto type inference, do the inference
+ */
+ int inferred = 0;
+ if (!dsym.type)
{
- imp.semanticRun = PASS.semanticdone;
- addImportDep(global.params.moduleDeps, imp, sc._module);
- }
+ dsym.inuse++;
- // Modules need a list of each imported module
+ // Infering the type requires running semantic,
+ // so mark the scope as ctfe if required
+ bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func;
+ if (needctfe)
+ {
+ sc.condition = true;
+ sc = sc.startCTFE();
+ }
+ //printf("inferring type for %s with init %s\n", dsym.toChars(), dsym._init.toChars());
+ dsym._init = dsym._init.inferType(sc);
+ dsym.type = dsym._init.initializerToExpression(null, sc.inCfile).type;
+ if (needctfe)
+ sc = sc.endCTFE();
- // if inside a template instantiation, the instantianting
- // module gets the import.
- // https://issues.dlang.org/show_bug.cgi?id=17181
- Module importer = sc._module;
- if (sc.minst && sc.tinst)
+ dsym.inuse--;
+ inferred = 1;
+
+ /* This is a kludge to support the existing syntax for RAII
+ * declarations.
+ */
+ dsym.storage_class &= ~STC.auto_;
+ dsym.originalType = dsym.type.syntaxCopy();
+ }
+ else
{
- importer = sc.minst;
- if (!sc.tinst.importedModules.contains(imp.mod))
- sc.tinst.importedModules.push(imp.mod);
+ if (!dsym.originalType)
+ dsym.originalType = dsym.type.syntaxCopy();
+
+ /* Prefix function attributes of variable declaration can affect
+ * its type:
+ * pure nothrow void function() fp;
+ * static assert(is(typeof(fp) == void function() pure nothrow));
+ */
+ Scope* sc2 = sc.push();
+ sc2.stc |= (dsym.storage_class & STC.FUNCATTR);
+ dsym.inuse++;
+ dsym.type = dsym.type.typeSemantic(dsym.loc, sc2);
+ dsym.inuse--;
+ sc2.pop();
}
- //printf("%s imports %s\n", importer.toChars(), imp.mod.toChars());
- if (!importer.aimports.contains(imp.mod))
- importer.aimports.push(imp.mod);
+ //printf(" semantic type = %s\n", dsym.type ? dsym.type.toChars() : "null");
+ if (dsym.type.ty == Terror)
+ dsym.errors = true;
+
+ dsym.type.checkDeprecated(dsym.loc, sc);
+ dsym.parent = sc.parent;
+ //printf("this = %p, parent = %p, '%s'\n", dsym, dsym.parent, dsym.parent.toChars());
- if (sc.explicitVisibility)
- imp.visibility = sc.visibility;
+ /* If scope's alignment is the default, use the type's alignment,
+ * otherwise the scope overrrides.
+ */
+ import dmd.typesem : alignment;
+ if (dsym.alignment.isDefault())
+ dsym.alignment = dsym.type.alignment(); // use type's alignment
- if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import
+ if (sc.inCfile && !dsym.alignment.isDefault())
{
- ScopeDsymbol scopesym = sc.getScopesym();
-
- if (!imp.isstatic)
+ /* C11 6.7.5-4 alignment declaration cannot be less strict than the
+ * type alignment of the object or member being declared.
+ */
+ if (dsym.alignment.get() < dsym.type.alignsize())
{
- scopesym.importScope(imp.mod, imp.visibility);
+ if (dsym.alignment.fromAlignas())
+ {
+ error(dsym.loc, "`_Alignas` specifier cannot be less strict than alignment of `%s`",
+ dsym.toChars());
+ }
+ if (!dsym.alignment.isPack())
+ dsym.alignment.setDefault();
}
-
-
- imp.addPackageAccess(scopesym);
}
- // if a module has errors it means that parsing has failed.
- if (!imp.mod.errors)
- imp.mod.dsymbolSemantic(null);
+ //printf("sc.stc = %x\n", sc.stc);
+ //printf("storage_class = x%x\n", storage_class);
- if (imp.mod.needmoduleinfo)
+ dsym.type.checkComplexTransition(dsym.loc, sc);
+
+ // Calculate type size + safety checks
+ if (dsym.storage_class & STC.gshared && !dsym.isMember())
{
- //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars());
- importer.needmoduleinfo = 1;
+ sc.setUnsafe(false, dsym.loc, "using `__gshared` instead of `shared`");
}
- sc = sc.push(imp.mod);
- sc.visibility = imp.visibility;
- for (size_t i = 0; i < imp.aliasdecls.length; i++)
+ Dsymbol parent = dsym.toParent();
+
+ Type tb = dsym.type.toBasetype();
+ Type tbn = tb.baseElemOf();
+ if (tb.ty == Tvoid && !(dsym.storage_class & STC.lazy_))
{
- AliasDeclaration ad = imp.aliasdecls[i];
- //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope);
- Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], SearchOpt.ignorePrivateImports);
- if (sym)
+ if (inferred)
{
- import dmd.access : symbolIsVisible;
- if (!symbolIsVisible(sc, sym) && !sym.errors)
- {
- .error(imp.loc, "%s `%s` member `%s` is not visible from module `%s`", imp.mod.kind, imp.mod.toPrettyChars,
- imp.names[i].toChars(), sc._module.toChars());
- sym.errors = true;
- }
- ad.dsymbolSemantic(sc);
- // If the import declaration is in non-root module,
- // analysis of the aliased symbol is deferred.
- // Therefore, don't see the ad.aliassym or ad.type here.
+ .error(dsym.loc, "%s `%s` - type `%s` is inferred from initializer `%s`, and variables cannot be of type `void`",
+ dsym.kind, dsym.toPrettyChars, dsym.type.toChars(), toChars(dsym._init));
}
else
- {
- Dsymbol s = imp.mod.search_correct(imp.names[i]);
- // https://issues.dlang.org/show_bug.cgi?id=23908
- // Don't suggest symbols from the importer's module
- if (s && s.parent != importer)
- .error(imp.loc, "%s `%s` import `%s` not found, did you mean %s `%s`?", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars(), s.kind(), s.toPrettyChars());
- else
- .error(imp.loc, "%s `%s` import `%s` not found", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars());
- ad.type = Type.terror;
- }
+ .error(dsym.loc, "%s `%s` - variables cannot be of type `void`", dsym.kind, dsym.toPrettyChars);
+ dsym.type = Type.terror;
+ tb = dsym.type;
}
- sc = sc.pop();
-
- imp.semanticRun = PASS.semanticdone;
- addImportDep(global.params.moduleDeps, imp, sc._module);
- }
-
- void attribSemantic(AttribDeclaration ad)
- {
- if (ad.semanticRun != PASS.initial)
- return;
- ad.semanticRun = PASS.semantic;
- Dsymbols* d = ad.include(sc);
- //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d);
- if (!d)
+ if (tb.ty == Tfunction)
{
- ad.semanticRun = PASS.semanticdone;
- return;
+ .error(dsym.loc, "%s `%s` cannot be declared to be a function", dsym.kind, dsym.toPrettyChars);
+ dsym.type = Type.terror;
+ tb = dsym.type;
}
-
- Scope* sc2 = ad.newScope(sc);
- bool errors;
- for (size_t i = 0; i < d.length; i++)
+ if (auto ts = tb.isTypeStruct())
{
- Dsymbol s = (*d)[i];
- s.dsymbolSemantic(sc2);
- errors |= s.errors;
- }
- if (errors)
- ad.errors = true;
- if (sc2 != sc)
- sc2.pop();
-
- ad.semanticRun = PASS.semanticdone;
- }
+ // Require declarations, except when it's just a reference (as done for pointers)
+ // or when the variable is defined externally
+ if (!ts.sym.members && !(dsym.storage_class & (STC.ref_ | STC.extern_)))
+ {
+ .error(dsym.loc, "%s `%s` - no definition of struct `%s`", dsym.kind, dsym.toPrettyChars, ts.toChars());
- override void visit(AttribDeclaration atd)
- {
- attribSemantic(atd);
- }
+ // Explain why the definition is required when it's part of another type
+ if (!dsym.type.isTypeStruct())
+ {
+ // Prefer Loc of the dependant type
+ const s = dsym.type.toDsymbol(sc);
+ const loc = (s ? s : dsym).loc;
+ loc.errorSupplemental("required by type `%s`", dsym.type.toChars());
+ }
+ errorSupplemental(dsym.loc, "see https://dlang.org/spec/struct.html#opaque_struct_unions");
+ errorSupplemental(dsym.loc, "perhaps declare a variable with pointer type `%s*` instead", dsym.type.toChars());
- override void visit(AnonDeclaration scd)
- {
- //printf("\tAnonDeclaration::semantic isunion:%d ptr:%p\n", scd.isunion, scd);
- assert(sc.parent);
- auto p = sc.parent.pastMixin();
- auto ad = p.isAggregateDeclaration();
- if (!ad)
- {
- error(scd.loc, "%s can only be a part of an aggregate, not %s `%s`", scd.kind(), p.kind(), p.toChars());
- scd.errors = true;
- return;
+ // Flag variable as error to avoid invalid error messages due to unknown size
+ dsym.type = Type.terror;
+ }
}
+ if ((dsym.storage_class & STC.auto_) && !inferred && !(dsym.storage_class & STC.autoref))
+ .error(dsym.loc, "%s `%s` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?", dsym.kind, dsym.toPrettyChars);
- if (!scd.decl)
- return;
-
- sc = sc.push();
- sc.stc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.gshared);
- sc.inunion = scd.isunion ? scd : null;
- sc.resetAllFlags();
- for (size_t i = 0; i < scd.decl.length; i++)
+ if (auto tt = tb.isTypeTuple())
{
- Dsymbol s = (*scd.decl)[i];
- if (auto var = s.isVarDeclaration)
+ /* Instead, declare variables for each of the tuple elements
+ * and add those.
+ */
+ size_t nelems = Parameter.dim(tt.arguments);
+ Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression(null, sc.inCfile) : null;
+ if (ie)
+ ie = ie.expressionSemantic(sc);
+ if (nelems > 0 && ie)
{
- if (scd.isunion)
- var.overlapped = true;
- }
- s.dsymbolSemantic(sc);
- }
- sc = sc.pop();
- }
+ auto iexps = new Expressions(ie);
+ auto exps = new Expressions();
+ for (size_t pos = 0; pos < iexps.length; pos++)
+ {
+ Lexpand1:
+ Expression e = (*iexps)[pos];
+ Parameter arg = Parameter.getNth(tt.arguments, pos);
+ arg.type = arg.type.typeSemantic(dsym.loc, sc);
+ //printf("[%d] iexps.length = %d, ", pos, iexps.length);
+ //printf("e = (%s %s, %s), ", Token.tochars[e.op], e.toChars(), e.type.toChars());
+ //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
- override void visit(PragmaDeclaration pd)
- {
- import dmd.pragmasem : pragmaDeclSemantic;
- pragmaDeclSemantic(pd, sc);
- }
+ if (e != ie)
+ {
+ if (iexps.length > nelems)
+ goto Lnomatch;
+ if (e.type.implicitConvTo(arg.type))
+ continue;
+ }
- override void visit(StaticIfDeclaration sid)
- {
- attribSemantic(sid);
- }
+ if (auto te = e.isTupleExp())
+ {
+ if (iexps.length - 1 + te.exps.length > nelems)
+ goto Lnomatch;
- override void visit(StaticForeachDeclaration sfd)
- {
- attribSemantic(sfd);
- }
+ iexps.remove(pos);
+ iexps.insert(pos, te.exps);
+ (*iexps)[pos] = Expression.combine(te.e0, (*iexps)[pos]);
+ goto Lexpand1;
+ }
+ else if (isAliasThisTuple(e))
+ {
+ auto v = copyToTemp(STC.none, "__tup", e);
+ v.dsymbolSemantic(sc);
+ auto ve = new VarExp(dsym.loc, v);
+ ve.type = e.type;
- private Dsymbols* compileIt(MixinDeclaration cd)
- {
- //printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars());
- OutBuffer buf;
- if (expressionsToString(buf, sc, cd.exps, cd.loc, null, true))
- return null;
+ exps.setDim(1);
+ (*exps)[0] = ve;
+ expandAliasThisTuples(exps, 0);
- const errors = global.errors;
- const len = buf.length;
- buf.writeByte(0);
- const str = buf.extractSlice()[0 .. len];
- const bool doUnittests = global.params.parsingUnittestsRequired();
- scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
- adjustLocForMixin(str, cd.loc, *p.baseLoc, global.params.mixinOut);
- p.linnum = p.baseLoc.startLine;
- p.nextToken();
+ for (size_t u = 0; u < exps.length; u++)
+ {
+ Lexpand2:
+ Expression ee = (*exps)[u];
+ arg = Parameter.getNth(tt.arguments, pos + u);
+ arg.type = arg.type.typeSemantic(dsym.loc, sc);
+ //printf("[%d+%d] exps.length = %d, ", pos, u, exps.length);
+ //printf("ee = (%s %s, %s), ", Token.tochars[ee.op], ee.toChars(), ee.type.toChars());
+ //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
- auto d = p.parseDeclDefs(0);
- if (global.errors != errors)
- return null;
+ size_t iexps_dim = iexps.length - 1 + exps.length;
+ if (iexps_dim > nelems)
+ goto Lnomatch;
+ if (ee.type.implicitConvTo(arg.type))
+ continue;
+
+ if (expandAliasThisTuples(exps, u) != -1)
+ goto Lexpand2;
+ }
+
+ if ((*exps)[0] != ve)
+ {
+ Expression e0 = (*exps)[0];
+ (*exps)[0] = new CommaExp(dsym.loc, new DeclarationExp(dsym.loc, v), e0);
+ (*exps)[0].type = e0.type;
- if (p.token.value != TOK.endOfFile)
- {
- .error(cd.loc, "%s `%s` incomplete mixin declaration `%s`", cd.kind, cd.toPrettyChars, str.ptr);
- return null;
- }
- return d;
- }
+ iexps.remove(pos);
+ iexps.insert(pos, exps);
+ goto Lexpand1;
+ }
+ }
+ }
+ if (iexps.length < nelems)
+ goto Lnomatch;
- /***********************************************************
- * https://dlang.org/spec/module.html#mixin-declaration
- */
- override void visit(MixinDeclaration cd)
- {
- //printf("MixinDeclaration::semantic()\n");
- if (!cd.compiled)
- {
- cd.decl = compileIt(cd);
- attribAddMember(cd, sc, cd.scopesym);
- cd.compiled = true;
+ ie = new TupleExp(dsym._init.loc, iexps);
+ }
+ Lnomatch:
- if (cd._scope && cd.decl)
+ if (ie && ie.op == EXP.tuple)
{
- for (size_t i = 0; i < cd.decl.length; i++)
+ auto te = ie.isTupleExp();
+ size_t tedim = te.exps.length;
+ if (tedim != nelems)
{
- Dsymbol s = (*cd.decl)[i];
- s.setScope(cd._scope);
+ error(dsym.loc, "sequence of %d elements cannot be assigned to sequence of %d elements", cast(int)tedim, cast(int)nelems);
+ for (size_t u = tedim; u < nelems; u++) // fill dummy expression
+ te.exps.push(ErrorExp.get());
}
}
- }
- attribSemantic(cd);
- }
- override void visit(CPPNamespaceDeclaration ns)
- {
- Identifier identFromSE (StringExp se)
- {
- const sident = se.toStringz();
- if (!sident.length || !Identifier.isValidIdentifier(sident))
+ auto exps = new Objects(nelems);
+ for (size_t i = 0; i < nelems; i++)
{
- error(ns.exp.loc, "expected valid identifier for C++ namespace but got `%s`", se.toErrMsg());
- return null;
- }
- else
- return Identifier.idPool(sident);
- }
+ Parameter arg = Parameter.getNth(tt.arguments, i);
- if (ns.ident !is null)
- return attribSemantic(ns);
+ OutBuffer buf;
+ buf.printf("__%s_field_%llu", dsym.ident.toChars(), cast(ulong)i);
+ auto id = Identifier.idPool(buf[]);
- ns.cppnamespace = sc.namespace;
- sc = sc.startCTFE();
- ns.exp = ns.exp.expressionSemantic(sc);
- ns.exp = resolveProperties(sc, ns.exp);
- sc = sc.endCTFE();
- ns.exp = ns.exp.ctfeInterpret();
- // Can be either a tuple of strings or a string itself
- if (auto te = ns.exp.isTupleExp())
- {
- expandTuples(te.exps);
- CPPNamespaceDeclaration current = ns.cppnamespace;
- for (size_t d = 0; d < te.exps.length; ++d)
- {
- auto exp = (*te.exps)[d];
- auto prev = d ? current : ns.cppnamespace;
- current = (d + 1) != te.exps.length
- ? new CPPNamespaceDeclaration(ns.loc, exp, null)
- : ns;
- current.exp = exp;
- current.cppnamespace = prev;
- if (auto se = exp.toStringExp())
+ Initializer ti;
+ if (ie)
{
- current.ident = identFromSE(se);
- if (current.ident is null)
- return; // An error happened in `identFromSE`
+ Expression einit = ie;
+ if (auto te = ie.isTupleExp())
+ {
+ einit = (*te.exps)[i];
+ if (i == 0)
+ einit = Expression.combine(te.e0, einit);
+ }
+ ti = new ExpInitializer(einit.loc, einit);
}
else
- error(ns.exp.loc, "`%s`: index %llu is not a string constant, it is a `%s`",
- ns.exp.toChars(), cast(ulong) d, ns.exp.type.toChars());
- }
- }
- else if (auto se = ns.exp.toStringExp())
- ns.ident = identFromSE(se);
- // Empty Tuple
- else if (ns.exp.isTypeExp() && ns.exp.isTypeExp().type.toBasetype().isTypeTuple())
- {
- }
- else if (!ns.exp.type.isTypeError())
- error(ns.exp.loc, "compile time string constant (or sequence) expected, not `%s`",
- ns.exp.toChars());
- attribSemantic(ns);
- }
-
- override void visit(UserAttributeDeclaration uad)
- {
- //printf("UserAttributeDeclaration::semantic() %p\n", this);
- if (uad.decl && !uad._scope)
- uad.Dsymbol.setScope(sc); // for function local symbols
- arrayExpressionSemantic(uad.atts.peekSlice(), sc, true);
- return attribSemantic(uad);
- }
-
- override void visit(StaticAssert sa)
- {
- if (sa.semanticRun < PASS.semanticdone)
- sa.semanticRun = PASS.semanticdone;
- else
- return;
-
- // https://issues.dlang.org/show_bug.cgi?id=24645
- // This is a short-circuit. Usually, static assert conditions are evaluated
- // in semantic2, but it's not uncommon to use this pattern:
- // ---
- // version(X)
- // {}
- // else
- // static assert(false, "unsupported platform");
- // ---
- // However, without this short-circuit, the static assert error may get drowned
- // out by subsequent semantic1 (import) errors. Only short-circuit at module scope though,
- // inside mixin templates you want an instantiation trace (which you don't get here).
- if (sc.parent && sc.parent.isModule())
- if (auto i = sa.exp.isIntegerExp())
- if (i.toInteger() == 0)
- staticAssertFail(sa, sc);
- }
-
- override void visit(DebugSymbol ds)
- {
- //printf("DebugSymbol::semantic() %s\n", toChars());
- if (ds.semanticRun < PASS.semanticdone)
- ds.semanticRun = PASS.semanticdone;
- }
+ ti = dsym._init ? dsym._init.syntaxCopy() : null;
- override void visit(VersionSymbol vs)
- {
- if (vs.semanticRun < PASS.semanticdone)
- vs.semanticRun = PASS.semanticdone;
- }
+ STC storage_class = STC.temp | dsym.storage_class;
+ if ((dsym.storage_class & STC.parameter) && (arg.storageClass & STC.parameter))
+ storage_class |= arg.storageClass;
+ auto v = new VarDeclaration(dsym.loc, arg.type, id, ti, storage_class);
+ //printf("declaring field %s of type %s\n", v.toChars(), v.type.toChars());
+ v.overlapped = dsym.overlapped;
- override void visit(Package pkg)
- {
- if (pkg.semanticRun < PASS.semanticdone)
- pkg.semanticRun = PASS.semanticdone;
- }
+ v.dsymbolSemantic(sc);
- override void visit(Module m)
- {
- if (m.semanticRun != PASS.initial)
+ Expression e = new VarExp(dsym.loc, v);
+ (*exps)[i] = e;
+ }
+ auto v2 = new TupleDeclaration(dsym.loc, dsym.ident, exps);
+ v2.parent = dsym.parent;
+ v2.isexp = true;
+ dsym.aliasTuple = v2;
+ dsym.semanticRun = PASS.semanticdone;
return;
-
- timeTraceBeginEvent(TimeTraceEventType.sema1Module);
- scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Module, m);
-
- //printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
- m.semanticRun = PASS.semantic;
- // Note that modules get their own scope, from scratch.
- // This is so regardless of where in the syntax a module
- // gets imported, it is unaffected by context.
- Scope* sc = m._scope; // see if already got one from importAll()
- if (!sc)
- {
- sc = Scope.createGlobal(m, global.errorSink); // create root scope
}
- //printf("Module = %p, linkage = %d\n", sc.scopesym, sc.linkage);
- // Pass 1 semantic routines: do public side of the definition
- m.members.foreachDsymbol( (s)
- {
- //printf("\tModule('%s'): '%s'.dsymbolSemantic()\n", toChars(), s.toChars());
- s.dsymbolSemantic(sc);
- m.runDeferredSemantic();
- });
+ /* Storage class can modify the type
+ */
+ dsym.type = dsym.type.addStorageClass(dsym.storage_class);
- if (m.userAttribDecl)
+ /* Adjust storage class to reflect type
+ */
+ if (dsym.type.isConst())
{
- m.userAttribDecl.dsymbolSemantic(sc);
+ dsym.storage_class |= STC.const_;
+ if (dsym.type.isShared())
+ dsym.storage_class |= STC.shared_;
}
- if (!m._scope)
+ else if (dsym.type.isImmutable())
+ dsym.storage_class |= STC.immutable_;
+ else if (dsym.type.isShared())
+ dsym.storage_class |= STC.shared_;
+ else if (dsym.type.isWild())
+ dsym.storage_class |= STC.wild;
+
+ if (STC stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_))
{
- sc = sc.pop();
- sc.pop(); // 2 pops because Scope.createGlobal() created 2
+ if (stc == STC.final_)
+ .error(dsym.loc, "%s `%s` cannot be `final`, perhaps you meant `const`?", dsym.kind, dsym.toPrettyChars);
+ else
+ {
+ OutBuffer buf;
+ stcToBuffer(buf, stc);
+ .error(dsym.loc, "%s `%s` cannot be `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars());
+ }
+ dsym.storage_class &= ~stc; // strip off
}
- m.semanticRun = PASS.semanticdone;
- //printf("-Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
- }
-
- override void visit(EnumDeclaration ed)
- {
- enumSemantic(sc, ed);
- }
-
- override void visit(EnumMember em)
- {
- enumMemberSemantic(sc, em);
- }
- override void visit(TemplateDeclaration tempdecl)
- {
- templateDeclarationSemantic(sc, tempdecl);
- }
+ // At this point we can add `scope` to the STC instead of `in`,
+ // because we are never going to use this variable's STC for user messages
+ if (dsym.storage_class & STC.constscoperef)
+ dsym.storage_class |= STC.scope_;
- override void visit(TemplateInstance ti)
- {
- templateInstanceSemantic(ti, sc, ArgumentList());
- }
+ import dmd.typesem : hasPointers;
- override void visit(TemplateMixin tm)
- {
- static if (LOG)
- {
- printf("+TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
- fflush(stdout);
- }
- if (tm.semanticRun != PASS.initial)
+ if (dsym.storage_class & STC.scope_)
{
- // When a class/struct contains mixin members, and is done over
- // because of forward references, never reach here so semanticRun
- // has been reset to PASS.initial.
- static if (LOG)
+ STC stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.gshared);
+ if (stc)
{
- printf("\tsemantic done\n");
+ OutBuffer buf;
+ stcToBuffer(buf, stc);
+ .error(dsym.loc, "%s `%s` cannot be `scope` and `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars());
+ }
+ else if (dsym.isMember())
+ {
+ error(dsym.loc, "field `%s` cannot be `scope`", dsym.toChars());
+ }
+ else if (!dsym.type.hasPointers())
+ {
+ dsym.storage_class &= ~STC.scope_; // silently ignore; may occur in generic code
+ // https://issues.dlang.org/show_bug.cgi?id=23168
+ if (dsym.storage_class & STC.returnScope)
+ {
+ dsym.storage_class &= ~(STC.return_ | STC.returnScope);
+ }
}
- return;
- }
- tm.semanticRun = PASS.semantic;
- static if (LOG)
- {
- printf("\tdo semantic\n");
}
- Scope* scx = null;
- if (tm._scope)
+ if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.gshared | STC.ctfe))
{
- sc = tm._scope;
- scx = tm._scope; // save so we don't make redundant copies
- tm._scope = null;
}
-
- /* Run semantic on each argument, place results in tiargs[],
- * then find best match template with tiargs
- */
- if (!tm.findMixinTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, ArgumentList()))
+ else
{
- if (tm.semanticRun == PASS.initial) // forward reference had occurred
+ AggregateDeclaration aad = parent.isAggregateDeclaration();
+ if (aad)
{
- //printf("forward reference - deferring\n");
- return deferDsymbolSemantic(sc, tm, scx);
+ if (global.params.v.field && dsym.storage_class & (STC.const_ | STC.immutable_) && dsym._init && !dsym._init.isVoidInitializer())
+ {
+ const(char)* s = (dsym.storage_class & STC.immutable_) ? "immutable" : "const";
+ message(dsym.loc, "`%s.%s` is `%s` field", ad.toPrettyChars(), dsym.toChars(), s);
+ }
+ dsym.storage_class |= STC.field;
+ if (auto ts = tbn.isTypeStruct())
+ if (ts.sym.noDefaultCtor)
+ {
+ if (!dsym.isThisDeclaration() && !dsym._init)
+ aad.noDefaultCtor = true;
+ }
}
- tm.inst = tm;
- tm.errors = true;
- return; // error recovery
- }
-
- auto tempdecl = tm.tempdecl.isTemplateDeclaration();
- assert(tempdecl);
+ InterfaceDeclaration id = parent.isInterfaceDeclaration();
+ if (id)
+ {
+ error(dsym.loc, "field `%s` not allowed in interface", dsym.toChars());
+ }
+ else if (aad && aad.sizeok == Sizeok.done)
+ {
+ error(dsym.loc, "cannot declare field `%s` because it will change the determined size of `%s`", dsym.toChars(), aad.toChars());
+ }
- if (!tm.ident)
- {
- /* Assign scope local unique identifier, as same as lambdas.
+ /* Templates cannot add fields to aggregates
*/
- const(char)[] s = "__mixin";
-
- if (FuncDeclaration func = sc.parent.isFuncDeclaration())
+ TemplateInstance ti = parent.isTemplateInstance();
+ if (ti)
{
- tm.symtab = func.localsymtab;
- if (tm.symtab)
+ // Take care of nested templates
+ while (1)
{
- // Inside template constraint, symtab is not set yet.
- goto L1;
+ TemplateInstance ti2 = ti.tempdecl.parent.isTemplateInstance();
+ if (!ti2)
+ break;
+ ti = ti2;
+ }
+ // If it's a member template
+ AggregateDeclaration ad2 = ti.tempdecl.isMember();
+ if (ad2 && dsym.storage_class != STC.none)
+ {
+ .error(dsym.loc, "%s `%s` - cannot use template to add field to aggregate `%s`", dsym.kind, dsym.toPrettyChars, ad2.toChars());
}
- }
- else
- {
- tm.symtab = sc.parent.isScopeDsymbol().symtab;
- L1:
- assert(tm.symtab);
- tm.ident = Identifier.generateId(s, tm.symtab.length + 1);
- tm.symtab.insert(tm);
}
}
- tm.inst = tm;
- tm.parent = sc.parent;
+ mixin alignSectionVarsExtra; doAlign(); // align section variables
- /* Detect recursive mixin instantiations.
- */
- for (Dsymbol s = tm.parent; s; s = s.parent)
+ if ((dsym.storage_class & (STC.ref_ | STC.field)) == (STC.ref_ | STC.field) && dsym.ident != Id.This)
{
- //printf("\ts = '%s'\n", s.toChars());
- TemplateMixin tmix = s.isTemplateMixin();
- if (!tmix || tempdecl != tmix.tempdecl)
- continue;
+ .error(dsym.loc, "%s `%s` - field declarations cannot be `ref`", dsym.kind, dsym.toPrettyChars);
+ }
- /* Different argument list lengths happen with variadic args
- */
- if (tm.tiargs.length != tmix.tiargs.length)
- continue;
+ if (dsym.type.hasWild())
+ {
+ if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field) || dsym.isDataseg())
+ {
+ .error(dsym.loc, "%s `%s` - only parameters or stack-based variables can be `inout`", dsym.kind, dsym.toPrettyChars);
+ }
+ FuncDeclaration func = sc.func;
+ if (func)
+ {
+ if (func.fes)
+ func = func.fes.func;
+ bool isWild = false;
+ for (FuncDeclaration fd = func; fd; fd = fd.toParentDecl().isFuncDeclaration())
+ {
+ if (fd.type.isTypeFunction().iswild)
+ {
+ isWild = true;
+ break;
+ }
+ }
+ if (!isWild)
+ {
+ .error(dsym.loc, "%s `%s` - `inout` variables can only be declared inside `inout` functions", dsym.kind, dsym.toPrettyChars);
+ }
+ }
+ }
- for (size_t i = 0; i < tm.tiargs.length; i++)
+ if (!(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.ref_ | STC.result)) &&
+ tbn.ty == Tstruct && tbn.isTypeStruct().sym.noDefaultCtor)
+ {
+ if (!dsym._init)
{
- RootObject o = (*tm.tiargs)[i];
- Type ta = isType(o);
- Expression ea = isExpression(o);
- Dsymbol sa = isDsymbol(o);
- RootObject tmo = (*tmix.tiargs)[i];
- if (ta)
+ if (dsym.isField())
{
- Type tmta = isType(tmo);
- if (!tmta)
- goto Lcontinue;
- if (!ta.equals(tmta))
- goto Lcontinue;
+ /* For fields, we'll check the constructor later to make sure it is initialized
+ */
+ dsym.storage_class |= STC.nodefaultctor;
}
- else if (ea)
+ else if (dsym.storage_class & STC.parameter)
{
- Expression tme = isExpression(tmo);
- if (!tme || !ea.equals(tme))
- goto Lcontinue;
}
- else if (sa)
+ else
+ .error(dsym.loc, "%s `%s` - default construction is disabled for type `%s`", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
+ }
+ }
+
+ bool dsymIsRef = (dsym.storage_class & (STC.ref_ | STC.field | STC.parameter | STC.temp | STC.foreach_)) == STC.ref_;
+ if (dsymIsRef)
+ {
+ if (!dsym._init && dsym.ident != Id.This)
+ {
+ if (dsym.storage_class & STC.autoref)
{
- Dsymbol tmsa = isDsymbol(tmo);
- if (sa != tmsa)
- goto Lcontinue;
+ dsymIsRef = false;
+ dsym.storage_class &= ~STC.ref_;
}
else
- assert(0);
+ .error(dsym.loc, "%s `%s` - initializer is required for `ref` variable", dsym.kind, dsym.toPrettyChars);
}
- .error(tm.loc, "%s `%s` recursive mixin instantiation", tm.kind, tm.toPrettyChars);
- return;
+ else if (dsym._init.isVoidInitializer())
+ {
+ .error(dsym.loc, "%s `%s` - void initializer not allowed for `ref` variable", dsym.kind, dsym.toPrettyChars);
+ }
+ }
- Lcontinue:
- continue;
+ FuncDeclaration fd = parent.isFuncDeclaration();
+ if (dsym.type.isScopeClass() && !(dsym.storage_class & STC.nodtor))
+ {
+ if (dsym.storage_class & (STC.field | STC.out_ | STC.ref_ | STC.static_ | STC.manifest | STC.gshared) || !fd)
+ {
+ .error(dsym.loc, "%s `%s` globals, statics, fields, manifest constants, ref and out parameters cannot be `scope`", dsym.kind, dsym.toPrettyChars);
+ }
+
+ // @@@DEPRECATED_2.097@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
+ // Deprecated in 2.087
+ // Remove this when the feature is removed from the language
+ if (!(dsym.storage_class & STC.scope_))
+ {
+ if (!(dsym.storage_class & STC.parameter) && dsym.ident != Id.withSym)
+ .error(dsym.loc, "%s `%s` reference to `scope class` must be `scope`", dsym.kind, dsym.toPrettyChars);
+ }
+ }
+
+ // Calculate type size + safety checks
+ if (sc && sc.func)
+ {
+ if (dsym._init && dsym._init.isVoidInitializer() && !(dsym.storage_class & STC.temp))
+ {
+ // Don't do these checks for STC.temp vars because the generated `opAssign`
+ // for a struct with postblit and destructor void initializes a temporary
+ // __swap variable, which can be trusted
+
+ if (dsym.type.hasPointers()) // also computes type size
+ sc.setUnsafe(false, dsym.loc,
+ "`void` initializing a pointer");
+ else if (dsym.type.hasInvariant())
+ sc.setUnsafe(false, dsym.loc,
+ "`void` initializing a struct with an invariant");
+ else if (dsym.type.toBasetype().ty == Tbool)
+ sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
+ "`void` initializing a `bool` (which must always be 0 or 1)");
+ else if (dsym.type.hasUnsafeBitpatterns())
+ sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
+ "`void` initializing a type with unsafe bit patterns");
+ }
+ else if (!dsym._init &&
+ !(dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field | STC.parameter)) &&
+ dsym.type.hasVoidInitPointers())
+ {
+ sc.setUnsafe(false, dsym.loc, "`void` initializers for pointers");
+ }
}
- // Copy the syntax trees from the TemplateDeclaration
- tm.members = Dsymbol.arraySyntaxCopy(tempdecl.members);
- if (!tm.members)
- return;
-
- tm.symtab = new DsymbolTable();
-
- sc.getScopesym().importScope(tm, Visibility(Visibility.Kind.public_));
-
- static if (LOG)
+ if ((!dsym._init || dsym._init.isVoidInitializer) && !fd)
{
- printf("\tcreate scope for template parameters '%s'\n", tm.toChars());
+ // If not mutable, initializable by constructor only
+ dsym.setInCtorOnly = true;
}
- Scope* scy = sc.push(tm);
- scy.parent = tm;
-
- /* https://issues.dlang.org/show_bug.cgi?id=930
- *
- * If the template that is to be mixed in is in the scope of a template
- * instance, we have to also declare the type aliases in the new mixin scope.
- */
- auto parentInstance = tempdecl.parent ? tempdecl.parent.isTemplateInstance() : null;
- if (parentInstance)
- parentInstance.declareParameters(scy);
-
- tm.argsym = new ScopeDsymbol();
- tm.argsym.parent = scy.parent;
- Scope* argscope = scy.push(tm.argsym);
-
- const errorsave = global.errors;
-
- // Declare each template parameter as an alias for the argument type
- tm.declareParameters(argscope);
- // Add members to enclosing scope, as well as this scope
- tm.members.foreachDsymbol(s => s.addMember(argscope, tm));
+ if (dsym._init)
+ { } // remember we had an explicit initializer
+ else if (dsym.storage_class & STC.manifest)
+ .error(dsym.loc, "%s `%s` - manifest constants must have initializers", dsym.kind, dsym.toPrettyChars);
- // Do semantic() analysis on template instance members
- static if (LOG)
+ // Don't allow non-extern, non-__gshared variables to be interfaced with C++
+ if (dsym._linkage == LINK.cpp && !(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.gshared)) && dsym.isDataseg())
{
- printf("\tdo semantic() on template instance members '%s'\n", tm.toChars());
+ const char* p = (dsym.storage_class & STC.shared_) ? "shared" : "static";
+ .error(dsym.loc, "%s `%s` cannot have `extern(C++)` linkage because it is `%s`", dsym.kind, dsym.toPrettyChars, p);
+ errorSupplemental(dsym.loc, "perhaps declare it as `__gshared` instead");
+ dsym.errors = true;
}
- Scope* sc2 = argscope.push(tm);
- //size_t deferred_dim = Module.deferred.length;
- __gshared int nest;
- //printf("%d\n", nest);
- if (++nest > global.recursionLimit)
+ bool isBlit = false;
+ uinteger_t sz;
+ if (sc.inCfile && !dsym._init)
{
- global.gag = 0; // ensure error message gets printed
- .error(tm.loc, "%s `%s` recursive expansion", tm.kind, tm.toPrettyChars);
- fatal();
+ addDefaultCInitializer(dsym);
}
+ if (!dsym._init &&
+ !(dsym.storage_class & (STC.static_ | STC.gshared | STC.extern_)) &&
+ fd &&
+ (!(dsym.storage_class & (STC.field | STC.in_ | STC.foreach_ | STC.parameter | STC.result)) ||
+ (dsym.storage_class & STC.out_)) &&
+ (sz = dsym.type.size()) != 0)
+ {
+ // Provide a default initializer
- tm.members.foreachDsymbol( s => s.setScope(sc2) );
+ //printf("Providing default initializer for '%s'\n", dsym.toChars());
+ if (sz == SIZE_INVALID && dsym.type.ty != Terror)
+ .error(dsym.loc, "%s `%s` - size of type `%s` is invalid", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
- tm.members.foreachDsymbol( s => s.importAll(sc2) );
+ Type tv = dsym.type;
+ while (tv.ty == Tsarray) // Don't skip Tenum
+ tv = tv.nextOf();
+ if (tv.needsNested())
+ {
+ /* Nested struct requires valid enclosing frame pointer.
+ * In StructLiteralExp::toElem(), it's calculated.
+ */
+ assert(tbn.ty == Tstruct);
+ checkFrameAccess(dsym.loc, sc, tbn.isTypeStruct().sym);
- tm.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
+ Expression e = tv.defaultInitLiteral(dsym.loc);
+ e = new BlitExp(dsym.loc, new VarExp(dsym.loc, dsym), e);
+ e = e.expressionSemantic(sc);
+ dsym._init = new ExpInitializer(dsym.loc, e);
+ goto Ldtor;
+ }
+ if (tv.ty == Tstruct && tv.isTypeStruct().sym.zeroInit)
+ {
+ /* If a struct is all zeros, as a special case
+ * set its initializer to the integer 0.
+ * In AssignExp::toElem(), we check for this and issue
+ * a memset() to initialize the struct.
+ * Must do same check in interpreter.
+ */
+ Expression e = IntegerExp.literal!0;
+ e = new BlitExp(dsym.loc, new VarExp(dsym.loc, dsym), e);
+ e.type = dsym.type; // don't type check this, it would fail
+ dsym._init = new ExpInitializer(dsym.loc, e);
+ goto Ldtor;
+ }
+ if (dsym.type.baseElemOf().ty == Tvoid)
+ {
+ .error(dsym.loc, "%s `%s` of type `%s` does not have a default initializer", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
+ }
+ else if (auto e = dsym.type.defaultInit(dsym.loc))
+ {
+ dsym._init = new ExpInitializer(dsym.loc, e);
+ }
- nest--;
+ // Default initializer is always a blit
+ isBlit = true;
+ }
+ if (dsym._init)
+ {
+ sc = sc.push();
+ sc.stc &= ~(STC.TYPECTOR | STC.pure_ | STC.nothrow_ | STC.nogc | STC.ref_ | STC.disable);
- /* In DeclDefs scope, TemplateMixin does not have to handle deferred symbols.
- * Because the members would already call Module.addDeferredSemantic() for themselves.
- * See Struct, Class, Interface, and EnumDeclaration.dsymbolSemantic().
- */
- //if (!sc.func && Module.deferred.length > deferred_dim) {}
+ if (sc.inCfile &&
+ dsym.type.isTypeSArray() &&
+ dsym.type.isTypeSArray().isIncomplete() &&
+ dsym._init.isVoidInitializer() &&
+ !(dsym.storage_class & STC.field))
+ {
+ .error(dsym.loc, "%s `%s` - incomplete array type must have initializer", dsym.kind, dsym.toPrettyChars);
+ }
- AggregateDeclaration ad = tm.isMember();
- if (sc.func && !ad)
- {
- tm.semantic2(sc2);
- tm.semantic3(sc2);
- }
+ ExpInitializer ei = dsym._init.isExpInitializer();
- // Give additional context info if error occurred during instantiation
- if (global.errors != errorsave)
- {
- .error(tm.loc, "%s `%s` error instantiating", tm.kind, tm.toPrettyChars);
- tm.errors = true;
- }
+ if (ei) // https://issues.dlang.org/show_bug.cgi?id=13424
+ // Preset the required type to fail in FuncLiteralDeclaration::semantic3
+ ei.exp = inferType(ei.exp, dsym.type);
- sc2.pop();
- argscope.pop();
- scy.pop();
+ /*
+ * https://issues.dlang.org/show_bug.cgi?id=24474
+ * at function scope, the compiler thinks the type of the variable is not known yet
+ * (semantically complete) so looks like typeof gets locked in a cyclic situation
+ * semantics is actually done. just set it for importc
+ */
+ if (sc.func && dsym.type && dsym.type.deco && sc.inCfile)
+ dsym.semanticRun = PASS.semanticdone;
- static if (LOG)
- {
- printf("-TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
- }
- }
+ // If inside function, there is no semantic3() call
+ if (sc.func || sc.intypeof == 1)
+ {
+ // If local variable, use AssignExp to handle all the various
+ // possibilities.
+ if (fd && !(dsym.storage_class & (STC.manifest | STC.static_ | STC.gshared | STC.extern_)) && !dsym._init.isVoidInitializer())
+ {
+ //printf("fd = '%s', var = '%s'\n", fd.toChars(), dsym.toChars());
+ if (!ei)
+ {
+ ArrayInitializer ai = dsym._init.isArrayInitializer();
+ Expression e;
+ if (ai && tb.ty == Taarray)
+ e = ai.toAssocArrayLiteral();
+ else
+ e = dsym._init.initializerToExpression(dsym.type, sc.inCfile);
+ if (!e)
+ {
+ // Run semantic, but don't need to interpret
+ dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITnointerpret);
+ e = dsym._init.initializerToExpression(null, sc.inCfile);
+ if (!e)
+ {
+ .error(dsym.loc, "%s `%s` is not a static and cannot have static initializer", dsym.kind, dsym.toPrettyChars);
+ e = ErrorExp.get();
+ }
+ }
+ ei = new ExpInitializer(dsym._init.loc, e);
+ dsym._init = ei;
+ }
+ else if (sc.inCfile && dsym.type.isTypeSArray() &&
+ dsym.type.isTypeSArray().isIncomplete())
+ {
+ // C11 6.7.9-22 determine the size of the incomplete array,
+ // or issue an error that the initializer is invalid.
+ dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
+ }
- override void visit(Nspace ns)
- {
- if (ns.semanticRun != PASS.initial)
- return;
- static if (LOG)
- {
- printf("+Nspace::semantic('%s')\n", ns.toChars());
- scope(exit) printf("-Nspace::semantic('%s')\n", ns.toChars());
- }
- if (ns._scope)
- {
- sc = ns._scope;
- ns._scope = null;
- }
- if (!sc)
- return;
+ if (ei && dsym.isScope())
+ {
+ Expression ex = ei.exp.lastComma();
+ if (ex.op == EXP.blit || ex.op == EXP.construct)
+ ex = (cast(AssignExp)ex).e2;
+ if (auto ne = ex.isNewExp())
+ {
+ if (ne.placement)
+ {
+ }
+ /* See if initializer is a NewExp that can be allocated on the stack.
+ */
+ else if (dsym.type.toBasetype().ty == Tclass)
+ {
+ /* Unsafe to allocate on stack if constructor is not `scope` because the `this` can leak.
+ * https://issues.dlang.org/show_bug.cgi?id=23145
+ */
+ if (ne.member && !(ne.member.storage_class & STC.scope_))
+ {
+ import dmd.escape : setUnsafeDIP1000;
+ const inSafeFunc = sc.func && sc.func.isSafeBypassingInference(); // isSafeBypassingInference may call setUnsafe().
+ if (setUnsafeDIP1000(*sc, false, dsym.loc, "`scope` allocation of `%s` with a non-`scope` constructor", dsym))
+ errorSupplemental(ne.member.loc, "is the location of the constructor");
+ }
+ ne.onstack = 1;
+ dsym.onstack = true;
+ }
+ }
+ else if (auto fe = ex.isFuncExp())
+ {
+ // or a delegate that doesn't escape a reference to the function
+ FuncDeclaration f = fe.fd;
+ if (f.tookAddressOf)
+ f.tookAddressOf--;
+ }
+ else if (auto ale = ex.isArrayLiteralExp())
+ {
+ // or an array literal assigned to a `scope` variable
+ if (sc.useDIP1000 == FeatureState.enabled
+ && !dsym.type.nextOf().needsDestruction())
+ ale.onstack = true;
+ }
+ }
- bool repopulateMembers = false;
- if (ns.identExp)
- {
- // resolve the namespace identifier
- sc = sc.startCTFE();
- Expression resolved = ns.identExp.expressionSemantic(sc);
- resolved = resolveProperties(sc, resolved);
- sc = sc.endCTFE();
- resolved = resolved.ctfeInterpret();
- StringExp name = resolved.toStringExp();
- TupleExp tup = name ? null : resolved.isTupleExp();
- if (!tup && !name)
- {
- error(ns.loc, "expected string expression for namespace name, got `%s`", ns.identExp.toChars());
- return;
- }
- ns.identExp = resolved; // we don't need to keep the old AST around
- if (name)
- {
- const(char)[] ident = name.toStringz();
- if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
- {
- error(ns.loc, "expected valid identifier for C++ namespace but got `%.*s`", cast(int)ident.length, ident.ptr);
- return;
- }
- ns.ident = Identifier.idPool(ident);
- }
- else
- {
- // create namespace stack from the tuple
- Nspace parentns = ns;
- foreach (i, exp; *tup.exps)
- {
- name = exp.toStringExp();
- if (!name)
+ Expression exp = ei.exp;
+ Expression e1 = new VarExp(dsym.loc, dsym);
+
+ void constructInit(bool isBlit)
{
- error(ns.loc, "expected string expression for namespace name, got `%s`", exp.toChars());
- return;
+ if (isBlit)
+ exp = new BlitExp(dsym.loc, e1, exp);
+ else
+ exp = new ConstructExp(dsym.loc, e1, exp);
+ dsym.canassign++;
+ exp = exp.expressionSemantic(sc);
+ dsym.canassign--;
}
- const(char)[] ident = name.toStringz();
- if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
+
+ if (dsymIsRef) // follow logic similar to typesem.argumentMatchParameter() and statementsem.visitForeach()
{
- error(ns.loc, "expected valid identifier for C++ namespace but got `%.*s`", cast(int)ident.length, ident.ptr);
- return;
+ dsym.storage_class |= STC.nodtor;
+ exp = exp.expressionSemantic(sc);
+ Type tp = dsym.type;
+ Type ta = exp.type;
+ if (!exp.isLvalue())
+ {
+ if (dsym.storage_class & STC.autoref)
+ {
+ dsym.storage_class &= ~STC.ref_;
+ constructInit(isBlit);
+ }
+ else
+ {
+ .error(dsym.loc, "rvalue `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
+ exp = ErrorExp.get();
+ }
+ }
+ else if (!ta.constConv(tp))
+ {
+ if (dsym.storage_class & STC.autoref)
+ {
+ dsym.storage_class &= ~STC.ref_;
+ constructInit(false);
+ }
+ else
+ {
+ .error(dsym.loc, "type `%s` cannot be assigned to `ref %s %s`", ta.toChars(), tp.toChars(), dsym.toChars());
+ exp = ErrorExp.get();
+ }
+ }
+ else if (exp.isBitField())
+ {
+ if (dsym.storage_class & STC.autoref)
+ {
+ dsym.storage_class &= ~STC.ref_;
+ constructInit(false);
+ }
+ else
+ {
+ .error(dsym.loc, "bitfield `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
+ exp = ErrorExp.get();
+ }
+ }
+ else
+ {
+ constructInit(false);
+ }
}
- if (i == 0)
+ else
{
- ns.ident = Identifier.idPool(ident);
+ constructInit(isBlit);
+ }
+
+ if (exp.op == EXP.error)
+ {
+ dsym._init = new ErrorInitializer();
+ ei = null;
}
else
+ ei.exp = exp.optimize(WANTvalue);
+ }
+ else
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=14166
+ // Don't run CTFE for the temporary variables inside typeof
+ dsym._init = dsym._init.initializerSemantic(sc, dsym.type, sc.intypeof == 1 ? INITnointerpret : INITinterpret);
+ import dmd.semantic2 : lowerStaticAAs;
+ lowerStaticAAs(dsym, sc);
+ auto init_err = dsym._init.isExpInitializer();
+ if (init_err && init_err.exp.op == EXP.showCtfeContext)
{
- // insert the new namespace
- Nspace childns = new Nspace(ns.loc, Identifier.idPool(ident), null, parentns.members);
- parentns.members = new Dsymbols;
- parentns.members.push(childns);
- parentns = childns;
- repopulateMembers = true;
+ init_err.exp = ErrorExp.get();
+ errorSupplemental(dsym.loc, "compile time context created here");
}
}
}
- }
-
- ns.semanticRun = PASS.semantic;
- ns.parent = sc.parent;
- // Link does not matter here, if the UDA is present it will error
- checkGNUABITag(ns, LINK.cpp);
-
- if (!ns.members)
- {
- ns.semanticRun = PASS.semanticdone;
- return;
- }
- assert(sc);
- sc = sc.push(ns);
- sc.linkage = LINK.cpp; // note that namespaces imply C++ linkage
- sc.parent = ns;
- foreach (s; *ns.members)
- {
- if (repopulateMembers)
+ else if (parent.isAggregateDeclaration())
{
- s.addMember(sc, sc.scopesym);
- s.setScope(sc);
+ dsym._scope = scx ? scx : sc.copy();
+ dsym._scope.setNoFree();
}
- s.importAll(sc);
- }
- foreach (s; *ns.members)
- {
- static if (LOG)
+ else if (dsym.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) ||
+ dsym.type.isConst() || dsym.type.isImmutable() ||
+ sc.inCfile)
{
- printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind());
- }
- s.dsymbolSemantic(sc);
- }
- sc.pop();
- ns.semanticRun = PASS.semanticdone;
- }
-
- /// Do the semantic analysis on the external interface to the function.
- override void visit(FuncDeclaration funcdecl)
- {
- funcDeclarationSemantic(sc, funcdecl);
- }
-
- override void visit(CtorDeclaration ctd)
- {
- //printf("CtorDeclaration::semantic() %p %s\n", ctd, ctd.toChars());
- if (ctd.semanticRun >= PASS.semanticdone)
- return;
- if (ctd._scope)
- {
- sc = ctd._scope;
- ctd._scope = null;
- }
-
- ctd.parent = sc.parent;
- Dsymbol p = ctd.toParentDecl();
- AggregateDeclaration ad = p.isAggregateDeclaration();
- if (!ad)
- {
- error(ctd.loc, "constructor can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
- ctd.type = Type.terror;
- ctd.errors = true;
- return;
- }
+ /* Because we may need the results of a const declaration in a
+ * subsequent type, such as an array dimension, before semantic2()
+ * gets ordinarily run, try to run semantic2() now.
+ * If a C array is of unknown size, the initializer can provide the size. Do this
+ * eagerly because C does it eagerly.
+ * Ignore failure.
+ */
+ if (!inferred)
+ {
+ 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
+ if (ei && (ei.exp.op != EXP.scope_ ? true : !ei.exp.isScopeExp().sds.isPackage()))
+ {
+ if (ei.exp.type)
+ {
+ // If exp is already resolved we are done, our original init exp
+ // could have a type painting that we need to respect
+ // e.g. ['a'] typed as string, or [['z'], ""] as string[]
+ // See https://issues.dlang.org/show_bug.cgi?id=15711
+ }
+ else
+ {
+ Expression exp = ei.exp.syntaxCopy();
- sc = sc.push();
+ bool needctfe = dsym.isDataseg() || (dsym.storage_class & STC.manifest);
+ if (needctfe)
+ sc = sc.startCTFE();
+ sc = sc.push();
+ sc.varDecl = dsym; // https://issues.dlang.org/show_bug.cgi?id=24051
+ exp = exp.expressionSemantic(sc);
+ exp = resolveProperties(sc, exp);
+ sc = sc.pop();
+ if (needctfe)
+ sc = sc.endCTFE();
+ ei.exp = exp;
+ }
- if (sc.stc & STC.static_)
- {
- if (sc.stc & STC.shared_)
- error(ctd.loc, "`shared static` has no effect on a constructor inside a `shared static` block. Use `shared static this()`");
- else
- error(ctd.loc, "`static` has no effect on a constructor inside a `static` block. Use `static this()`");
- }
+ Type tb2 = dsym.type.toBasetype();
+ Type ti = ei.exp.type.toBasetype();
- sc.stc &= ~STC.static_; // not a static constructor
+ /* The problem is the following code:
+ * struct CopyTest {
+ * double x;
+ * this(double a) { x = a * 10.0;}
+ * this(this) { x += 2.0; }
+ * }
+ * const CopyTest z = CopyTest(5.3); // ok
+ * const CopyTest w = z; // not ok, postblit not run
+ * static assert(w.x == 55.0);
+ * because the postblit doesn't get run on the initialization of w.
+ */
+ if (auto ts = ti.isTypeStruct())
+ {
+ StructDeclaration sd = ts.sym;
+ /* Look to see if initializer involves a copy constructor
+ * (which implies a postblit)
+ */
+ // there is a copy constructor
+ // and exp is the same struct
+ if (sd.postblit && tb2.toDsymbol(null) == sd)
+ {
+ // The only allowable initializer is a (non-copy) constructor
+ if (ei.exp.isLvalue())
+ .error(dsym.loc, "%s `%s` of type struct `%s` uses `this(this)`, which is not allowed in static initialization", dsym.kind, dsym.toPrettyChars, tb2.toChars());
+ }
+ }
+ }
- funcDeclarationSemantic(sc, ctd);
- // Check short constructor: this() => expr;
- if (ctd.fbody)
- {
- if (auto s = ctd.fbody.isExpStatement())
- {
- if (s.exp)
- {
- auto ce = s.exp.isCallExp();
- // check this/super before semantic
- if (!ce || (!ce.e1.isThisExp() && !ce.e1.isSuperExp()))
+ dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
+ dsym.inuse--;
+ if (global.errors > errors)
{
- s.exp = s.exp.expressionSemantic(sc);
- if (s.exp.type.ty != Tvoid)
- error(s.loc, "can only return void expression, `this` call or `super` call from constructor");
+ dsym._init = new ErrorInitializer();
+ dsym.type = Type.terror;
}
}
+ else
+ {
+ dsym._scope = scx ? scx : sc.copy();
+ dsym._scope.setNoFree();
+ }
}
+ sc = sc.pop();
}
- sc.pop();
-
- if (ctd.errors)
- return;
-
- TypeFunction tf = ctd.type.toTypeFunction();
- immutable dim = tf.parameterList.length;
- auto sd = ad.isStructDeclaration();
-
- /* See if it's the default constructor
- * But, template constructor should not become a default constructor.
+ Ldtor:
+ /* Build code to execute destruction, if necessary
*/
- if (ad && (!ctd.parent.isTemplateInstance() || ctd.parent.isTemplateMixin()))
+ dsym.edtor = dsym.callScopeDtor(sc);
+ if (dsym.edtor)
{
- if (!sd)
- {
- if (dim == 0 && tf.parameterList.varargs == VarArg.none)
- ad.defaultCtor = ctd;
- return;
- }
+ if (sc.func && dsym.storage_class & (STC.static_ | STC.gshared))
+ dsym.edtor = dsym.edtor.expressionSemantic(sc._module._scope);
+ else
+ dsym.edtor = dsym.edtor.expressionSemantic(sc);
- if (dim == 0 && tf.parameterList.varargs == VarArg.none) // empty default ctor w/o any varargs
- {
- if (ctd.fbody || !(ctd.storage_class & STC.disable))
- {
- .error(ctd.loc, "%s `%s` default constructor for structs only allowed " ~
- "with `@disable`, no body, and no parameters", ctd.kind, ctd.toPrettyChars);
- ctd.storage_class |= STC.disable;
- ctd.fbody = null;
- }
- sd.noDefaultCtor = true;
- }
- else if (dim == 0 && tf.parameterList.varargs != VarArg.none) // allow varargs only ctor
- {
- }
- else if (dim && !tf.parameterList.hasArgsWithoutDefault)
- {
- if (ctd.storage_class & STC.disable)
- {
- .error(ctd.loc, "%s `%s` is marked `@disable`, so it cannot have default "~
- "arguments for all parameters.", ctd.kind, ctd.toPrettyChars);
- errorSupplemental(ctd.loc, "Use `@disable this();` if you want to disable default initialization.");
- }
- else
- .error(ctd.loc, "%s `%s` all parameters have default arguments, "~
- "but structs cannot have default constructors.", ctd.kind, ctd.toPrettyChars);
- }
- else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
+ version (none)
{
- //printf("tf: %s\n", toChars(tf));
- auto param = tf.parameterList[0];
- if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
- {
- //printf("copy constructor %p\n", ctd);
- assert(!ctd.isCpCtor && !ctd.isMoveCtor);
- if (param.storageClass & STC.ref_)
- ctd.isCpCtor = true; // copy constructor
- else
- ctd.isMoveCtor = true; // move constructor
- assert(!(ctd.isCpCtor && ctd.isMoveCtor));
- }
+ // currently disabled because of std.stdio.stdin, stdout and stderr
+ if (dsym.isDataseg() && !(dsym.storage_class & STC.extern_))
+ .error(dsym.loc, "%s `%s` static storage variables cannot have destructors", dsym.kind, dsym.toPrettyChars);
}
}
- // https://issues.dlang.org/show_bug.cgi?id=22593
- else if (auto ti = ctd.parent.isTemplateInstance())
+
+ dsym.semanticRun = PASS.semanticdone;
+
+ if (dsym.type.toBasetype().ty == Terror)
+ dsym.errors = true;
+
+ if(sc.scopesym && !sc.scopesym.isAggregateDeclaration())
{
- checkHasBothRvalueAndCpCtor(sd, ctd, ti);
+ for (ScopeDsymbol sym = sc.scopesym; sym && dsym.endlinnum == 0;
+ sym = sym.parent ? sym.parent.isScopeDsymbol() : null)
+ dsym.endlinnum = sym.endlinnum;
}
}
- override void visit(PostBlitDeclaration pbd)
+ override void visit(TypeInfoDeclaration dsym)
{
- //printf("PostBlitDeclaration::semantic() %s\n", toChars());
- //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id.dtor.toChars(), ident, Id.dtor);
- //printf("stc = x%llx\n", sc.stc);
- if (pbd.semanticRun >= PASS.semanticdone)
- return;
- if (pbd._scope)
- {
- sc = pbd._scope;
- pbd._scope = null;
- }
+ assert(dsym._linkage == LINK.c);
+ }
- pbd.parent = sc.parent;
- Dsymbol p = pbd.toParent2();
- StructDeclaration ad = p.isStructDeclaration();
- if (!ad)
- {
- error(pbd.loc, "postblit can only be a member of struct, not %s `%s`", p.kind(), p.toChars());
- pbd.type = Type.terror;
- pbd.errors = true;
+ override void visit(CAsmDeclaration dsym)
+ {
+ if (dsym.semanticRun >= PASS.semanticdone)
return;
- }
- if (pbd.ident == Id.postblit && pbd.semanticRun < PASS.semantic)
- ad.postblits.push(pbd);
- if (!pbd.type)
- pbd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, pbd.storage_class);
-
- sc = sc.push();
- sc.stc &= ~STC.static_; // not static
- sc.linkage = LINK.d;
-
- funcDeclarationSemantic(sc, pbd);
-
- sc.pop();
+ import dmd.iasm : asmSemantic;
+ asmSemantic(dsym, sc);
+ dsym.semanticRun = PASS.semanticdone;
}
- override void visit(DtorDeclaration dd)
+ override void visit(BitFieldDeclaration dsym)
{
- //printf("DtorDeclaration::semantic() %s\n", dd.toChars());
- //printf("ident: %s, %s, %p, %p\n", dd.ident.toChars(), Id.dtor.toChars(), dd.ident, Id.dtor);
- if (dd.semanticRun >= PASS.semanticdone)
+ //printf("BitField::semantic('%s')\n", dsym.toChars());
+ if (dsym.semanticRun >= PASS.semanticdone)
return;
- if (dd._scope)
+
+ visit(cast(VarDeclaration)dsym);
+ if (dsym.errors)
+ return;
+
+ if (!(sc.previews.bitfields || sc.inCfile))
{
- sc = dd._scope;
- dd._scope = null;
+ .error(dsym.loc, "%s `%s` use `-%spreview=bitfields` for bitfield support", dsym.kind, dsym.toPrettyChars, SwitchPrefix.ptr);
}
- dd.parent = sc.parent;
- Dsymbol p = dd.toParent2();
- AggregateDeclaration ad = p.isAggregateDeclaration();
- if (!ad)
+ if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration())
{
- error(dd.loc, "destructor can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
- dd.type = Type.terror;
- dd.errors = true;
- return;
+ .error(dsym.loc, "%s `%s` - bitfield must be member of struct, union, or class", dsym.kind, dsym.toPrettyChars);
}
- if (ad.isClassDeclaration() && ad.classKind == ClassKind.d)
+ sc = sc.startCTFE();
+ auto width = dsym.width.expressionSemantic(sc);
+ sc = sc.endCTFE();
+ width = width.ctfeInterpret();
+ if (!dsym.type.isIntegral())
{
- // Class destructors are implicitly `scope`
- dd.storage_class |= STC.scope_;
+ // C11 6.7.2.1-5
+ error(width.loc, "bitfield type `%s` is not an integer type", dsym.type.toChars());
+ dsym.errors = true;
}
+ if (!width.isIntegerExp())
+ {
+ error(width.loc, "bitfield width `%s` is not an integer constant", dsym.width.toChars());
+ dsym.errors = true;
+ }
+ const uwidth = width.toInteger(); // uwidth is unsigned
+ if (uwidth == 0 && !dsym.isAnonymous())
+ {
+ error(width.loc, "bitfield `%s` has zero width", dsym.toChars());
+ dsym.errors = true;
+ }
+ const sz = dsym.type.size();
+ if (sz == SIZE_INVALID)
+ dsym.errors = true;
+ const max_width = sz * 8;
+ if (uwidth > max_width)
+ {
+ error(width.loc, "width `%lld` of bitfield `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars());
+ dsym.errors = true;
+ }
+ dsym.fieldWidth = cast(uint)uwidth;
+ }
- if (dd.ident == Id.dtor && dd.semanticRun < PASS.semantic)
- ad.userDtors.push(dd);
- if (!dd.type)
+ override void visit(Import imp)
+ {
+ timeTraceBeginEvent(TimeTraceEventType.sema1Import);
+ scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Import, imp);
+ static if (LOG)
{
- dd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dd.storage_class);
- if (ad.classKind == ClassKind.cpp && dd.ident == Id.dtor)
- {
- if (auto cldec = ad.isClassDeclaration())
- {
- assert (cldec.cppDtorVtblIndex == -1); // double-call check already by dd.type
- if (cldec.baseClass && cldec.baseClass.cppDtorVtblIndex != -1)
- {
- // override the base virtual
- cldec.cppDtorVtblIndex = cldec.baseClass.cppDtorVtblIndex;
- }
- else if (!dd.isFinal())
- {
- // reserve the dtor slot for the destructor (which we'll create later)
- cldec.cppDtorVtblIndex = cast(int)cldec.vtbl.length;
- cldec.vtbl.push(dd);
- if (target.cpp.twoDtorInVtable)
- cldec.vtbl.push(dd); // deleting destructor uses a second slot
- }
- }
- }
+ printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars());
+ scope(exit)
+ printf("-Import::semantic('%s'), pkg = %p\n", imp.toChars(), imp.pkg);
}
+ if (imp.semanticRun > PASS.initial)
+ return;
- sc = sc.push();
- sc.stc &= ~STC.static_; // not a static destructor
- if (sc.linkage != LINK.cpp)
- sc.linkage = LINK.d;
+ if (imp._scope)
+ {
+ sc = imp._scope;
+ imp._scope = null;
+ }
+ if (!sc)
+ return;
- funcDeclarationSemantic(sc, dd);
+ imp.parent = sc.parent;
- sc.pop();
- }
+ imp.semanticRun = PASS.semantic;
- void visitStaticCDtorDeclaration(FuncDeclaration sd, bool isDestructor)
- {
- if (sd.semanticRun >= PASS.semanticdone)
- return;
- if (sd._scope)
+ // Load if not already done so
+ if (!imp.mod)
{
- sc = sd._scope;
- sd._scope = null;
+ // https://issues.dlang.org/show_bug.cgi?id=22857
+ // if parser errors occur when loading a module
+ // we should just stop compilation
+ if (imp.load(sc))
+ {
+ for (size_t i = 0; i < imp.aliasdecls.length; i++)
+ imp.aliasdecls[i].type = Type.terror;
+ return;
+ }
+
+ if (imp.mod)
+ {
+ imp.mod.importAll(null);
+ imp.mod.checkImportDeprecation(imp.loc, sc);
+ }
}
- sd.parent = sc.parent;
- Dsymbol p = sd.parent.pastMixin();
- const bool isShared = !!(sd.isSharedStaticDtorDeclaration() || sd.isSharedStaticCtorDeclaration());
- const(char)* what = isDestructor ? "destructor" : "constructor";
- if (!p.isScopeDsymbol())
+ if (!imp.mod)
{
- const(char)* s = isShared ? "shared " : "";
- error(sd.loc, "`%sstatic` %s can only be member of module/aggregate/template, not %s `%s`", s, what, p.kind(), p.toChars());
- sd.type = Type.terror;
- sd.errors = true;
- return;
+ imp.semanticRun = PASS.semanticdone;
+ addImportDep(global.params.moduleDeps, imp, sc._module);
}
- if (!sd.type)
- sd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sd.storage_class);
+ // Modules need a list of each imported module
- /* If the static [dc]tor appears within a template instantiation,
- * it could get called multiple times by the module constructors
- * for different modules. Thus, protect it with a gate.
- */
- if (sd.isInstantiated() && sd.semanticRun < PASS.semantic)
+ // if inside a template instantiation, the instantianting
+ // module gets the import.
+ // https://issues.dlang.org/show_bug.cgi?id=17181
+ Module importer = sc._module;
+ if (sc.minst && sc.tinst)
{
- /* Add this prefix to the constructor:
- * ```
- * static int gate;
- * if ([--|++]gate != [0|1]) return; // dependant on ctor/dtor
- * ```
- * or, for shared constructor:
- * ```
- * shared int gate;
- * enum op = isDestructor ? "-=" : "+=";
- * enum cmp = isDestructor ? 0 : 1;
- * if (core.atomic.atomicOp!op(gate, 1) != cmp) return;
- * ```
- */
+ importer = sc.minst;
+ if (!sc.tinst.importedModules.contains(imp.mod))
+ sc.tinst.importedModules.push(imp.mod);
+ }
+ //printf("%s imports %s\n", importer.toChars(), imp.mod.toChars());
+ if (!importer.aimports.contains(imp.mod))
+ importer.aimports.push(imp.mod);
- auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
- v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : STC.none);
+ if (sc.explicitVisibility)
+ imp.visibility = sc.visibility;
- Statement s = new ExpStatement(Loc.initial, v);
- auto sa = new Statements(s);
+ if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import
+ {
+ ScopeDsymbol scopesym = sc.getScopesym();
- Expression e;
- if (isShared)
- {
- e = doAtomicOp(isDestructor ? "-=" : "+=", v.ident, IntegerExp.literal!(1));
- if (e is null)
- {
- .error(sd.loc, "%s `%s` shared static %s within a template require `core.atomic : atomicOp` to be present", sd.kind, sd.toPrettyChars, what);
- return;
- }
- }
- else
+ if (!imp.isstatic)
{
- IntegerExp one = isDestructor ? IntegerExp.literal!(-1) : IntegerExp.literal!(1);
- e = new AddAssignExp(
- Loc.initial, new IdentifierExp(Loc.initial, v.ident), one);
+ scopesym.importScope(imp.mod, imp.visibility);
}
- IntegerExp cmp = isDestructor ? IntegerExp.literal!0 : IntegerExp.literal!1;
- e = new EqualExp(EXP.notEqual, Loc.initial, e, cmp);
- s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
- sa.push(s);
- if (sd.fbody)
- sa.push(sd.fbody);
- sd.fbody = new CompoundStatement(Loc.initial, sa);
- if (isDestructor)
- (cast(StaticDtorDeclaration)sd).vgate = v;
- }
- const LINK save = sc.linkage;
- if (save != LINK.d)
- {
- const(char)* s = isShared ? "shared " : "";
- deprecation(sd.loc, "`%sstatic` %s can only be of D linkage", s, what);
- // Just correct it
- sc.linkage = LINK.d;
+ imp.addPackageAccess(scopesym);
}
- funcDeclarationSemantic(sc, sd);
- sc.linkage = save;
- // We're going to need ModuleInfo
- Module m = sd.getModule();
- if (!m)
- m = sc._module;
- if (m)
+ // if a module has errors it means that parsing has failed.
+ if (!imp.mod.errors)
+ imp.mod.dsymbolSemantic(null);
+
+ if (imp.mod.needmoduleinfo)
{
- m.needmoduleinfo = 1;
- //printf("module2 %s needs moduleinfo\n", m.toChars());
+ //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars());
+ importer.needmoduleinfo = 1;
}
- }
- override void visit(StaticCtorDeclaration scd)
- {
- //printf("StaticCtorDeclaration::semantic()\n");
- visitStaticCDtorDeclaration(scd, false);
-
- foreachUda(scd, sc, (Expression e) {
- import dmd.attrib : isEnumAttribute;
- if (!isEnumAttribute(e, Id.udaStandalone))
- return 0;
- if (auto sharedCtor = scd.isSharedStaticCtorDeclaration())
+ sc = sc.push(imp.mod);
+ sc.visibility = imp.visibility;
+ for (size_t i = 0; i < imp.aliasdecls.length; i++)
+ {
+ AliasDeclaration ad = imp.aliasdecls[i];
+ //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope);
+ Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], SearchOpt.ignorePrivateImports);
+ if (sym)
{
- auto trust = sharedCtor.type.isTypeFunction().trust;
- if (trust != TRUST.system && trust != TRUST.trusted)
- error(e.loc, "a module constructor using `@%s` must be `@system` or `@trusted`", Id.udaStandalone.toChars());
- sharedCtor.standalone = true;
+ import dmd.access : symbolIsVisible;
+ if (!symbolIsVisible(sc, sym) && !sym.errors)
+ {
+ .error(imp.loc, "%s `%s` member `%s` is not visible from module `%s`", imp.mod.kind, imp.mod.toPrettyChars,
+ imp.names[i].toChars(), sc._module.toChars());
+ sym.errors = true;
+ }
+ ad.dsymbolSemantic(sc);
+ // If the import declaration is in non-root module,
+ // analysis of the aliased symbol is deferred.
+ // Therefore, don't see the ad.aliassym or ad.type here.
}
else
- .error(e.loc, "`@%s` can only be used on shared static constructors", Id.udaStandalone.toChars());
-
- return 1;
- });
- }
+ {
+ Dsymbol s = imp.mod.search_correct(imp.names[i]);
+ // https://issues.dlang.org/show_bug.cgi?id=23908
+ // Don't suggest symbols from the importer's module
+ if (s && s.parent != importer)
+ .error(imp.loc, "%s `%s` import `%s` not found, did you mean %s `%s`?", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars(), s.kind(), s.toPrettyChars());
+ else
+ .error(imp.loc, "%s `%s` import `%s` not found", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars());
+ ad.type = Type.terror;
+ }
+ }
+ sc = sc.pop();
- override void visit(StaticDtorDeclaration sdd)
- {
- visitStaticCDtorDeclaration(sdd, true);
+ imp.semanticRun = PASS.semanticdone;
+ addImportDep(global.params.moduleDeps, imp, sc._module);
}
- override void visit(InvariantDeclaration invd)
+ void attribSemantic(AttribDeclaration ad)
{
- if (invd.semanticRun >= PASS.semanticdone)
+ if (ad.semanticRun != PASS.initial)
return;
- if (invd._scope)
- {
- sc = invd._scope;
- invd._scope = null;
- }
-
- invd.parent = sc.parent;
- Dsymbol p = invd.parent.pastMixin();
- AggregateDeclaration ad = p.isAggregateDeclaration();
- if (!ad)
+ ad.semanticRun = PASS.semantic;
+ Dsymbols* d = ad.include(sc);
+ //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d);
+ if (!d)
{
- error(invd.loc, "`invariant` can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
- invd.type = Type.terror;
- invd.errors = true;
+ ad.semanticRun = PASS.semanticdone;
return;
}
- if (invd.ident != Id.classInvariant &&
- invd.semanticRun < PASS.semantic &&
- !ad.isUnionDeclaration() // users are on their own with union fields
- )
+
+ Scope* sc2 = ad.newScope(sc);
+ bool errors;
+ for (size_t i = 0; i < d.length; i++)
{
- invd.fixupInvariantIdent(ad.invs.length);
- ad.invs.push(invd);
+ Dsymbol s = (*d)[i];
+ s.dsymbolSemantic(sc2);
+ errors |= s.errors;
}
- if (!invd.type)
- invd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, invd.storage_class);
-
- sc = sc.push();
- sc.stc &= ~STC.static_; // not a static invariant
- sc.stc |= STC.const_; // invariant() is always const
- sc.contract = Contract.invariant_;
- sc.linkage = LINK.d;
-
- funcDeclarationSemantic(sc, invd);
+ if (errors)
+ ad.errors = true;
+ if (sc2 != sc)
+ sc2.pop();
- sc.pop();
+ ad.semanticRun = PASS.semanticdone;
}
- override void visit(UnitTestDeclaration utd)
- {
- if (utd.semanticRun >= PASS.semanticdone)
- return;
- if (utd._scope)
- {
- sc = utd._scope;
- utd._scope = null;
- }
-
- utd.visibility = sc.visibility;
+ override void visit(AttribDeclaration atd)
+ {
+ attribSemantic(atd);
+ }
- utd.parent = sc.parent;
- Dsymbol p = utd.parent.pastMixin();
- if (!p.isScopeDsymbol())
+ override void visit(AnonDeclaration scd)
+ {
+ //printf("\tAnonDeclaration::semantic isunion:%d ptr:%p\n", scd.isunion, scd);
+ assert(sc.parent);
+ auto p = sc.parent.pastMixin();
+ auto ad = p.isAggregateDeclaration();
+ if (!ad)
{
- error(utd.loc, "`unittest` can only be a member of module/aggregate/template, not %s `%s`", p.kind(), p.toChars());
- utd.type = Type.terror;
- utd.errors = true;
+ error(scd.loc, "%s can only be a part of an aggregate, not %s `%s`", scd.kind(), p.kind(), p.toChars());
+ scd.errors = true;
return;
}
- if (global.params.useUnitTests)
- {
- if (!utd.type)
- utd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, utd.storage_class);
- Scope* sc2 = sc.push();
- sc2.linkage = LINK.d;
- funcDeclarationSemantic(sc, utd);
- sc2.pop();
- }
+ if (!scd.decl)
+ return;
- version (none)
+ sc = sc.push();
+ sc.stc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.gshared);
+ sc.inunion = scd.isunion ? scd : null;
+ sc.resetAllFlags();
+ for (size_t i = 0; i < scd.decl.length; i++)
{
- // We're going to need ModuleInfo even if the unit tests are not
- // compiled in, because other modules may import this module and refer
- // to this ModuleInfo.
- // (This doesn't make sense to me?)
- Module m = utd.getModule();
- if (!m)
- m = sc._module;
- if (m)
+ Dsymbol s = (*scd.decl)[i];
+ if (auto var = s.isVarDeclaration)
{
- //printf("module3 %s needs moduleinfo\n", m.toChars());
- m.needmoduleinfo = 1;
+ if (scd.isunion)
+ var.overlapped = true;
}
+ s.dsymbolSemantic(sc);
}
+ sc = sc.pop();
}
- override void visit(NewDeclaration nd)
+ override void visit(PragmaDeclaration pd)
{
- //printf("NewDeclaration::semantic()\n");
- if (nd.semanticRun >= PASS.semanticdone)
- return;
- if (!nd.type)
- nd.type = new TypeFunction(ParameterList(), Type.tvoid.pointerTo(), LINK.d, nd.storage_class);
+ import dmd.pragmasem : pragmaDeclSemantic;
+ pragmaDeclSemantic(pd, sc);
+ }
- funcDeclarationSemantic(sc, nd);
+ override void visit(StaticIfDeclaration sid)
+ {
+ attribSemantic(sid);
}
- override void visit(StructDeclaration sd)
+ override void visit(StaticForeachDeclaration sfd)
{
- enum log = false;
- if (log) printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
+ attribSemantic(sfd);
+ }
- //static int count; if (++count == 20) assert(0);
+ private Dsymbols* compileIt(MixinDeclaration cd)
+ {
+ //printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars());
+ OutBuffer buf;
+ if (expressionsToString(buf, sc, cd.exps, cd.loc, null, true))
+ return null;
- if (sd.semanticRun >= PASS.semanticdone)
- return;
const errors = global.errors;
+ const len = buf.length;
+ buf.writeByte(0);
+ const str = buf.extractSlice()[0 .. len];
+ const bool doUnittests = global.params.parsingUnittestsRequired();
+ scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
+ adjustLocForMixin(str, cd.loc, *p.baseLoc, global.params.mixinOut);
+ p.linnum = p.baseLoc.startLine;
+ p.nextToken();
- //printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
- Scope* scx = null;
- if (sd._scope)
+ auto d = p.parseDeclDefs(0);
+ if (global.errors != errors)
+ return null;
+
+ if (p.token.value != TOK.endOfFile)
{
- sc = sd._scope;
- scx = sd._scope; // save so we don't make redundant copies
- sd._scope = null;
+ .error(cd.loc, "%s `%s` incomplete mixin declaration `%s`", cd.kind, cd.toPrettyChars, str.ptr);
+ return null;
}
+ return d;
+ }
- if (!sd.parent)
+ /***********************************************************
+ * https://dlang.org/spec/module.html#mixin-declaration
+ */
+ override void visit(MixinDeclaration cd)
+ {
+ //printf("MixinDeclaration::semantic()\n");
+ if (!cd.compiled)
{
- assert(sc.parent && sc.func);
- sd.parent = sc.parent;
+ cd.decl = compileIt(cd);
+ attribAddMember(cd, sc, cd.scopesym);
+ cd.compiled = true;
+
+ if (cd._scope && cd.decl)
+ {
+ for (size_t i = 0; i < cd.decl.length; i++)
+ {
+ Dsymbol s = (*cd.decl)[i];
+ s.setScope(cd._scope);
+ }
+ }
}
- assert(sd.parent && !sd.isAnonymous());
+ attribSemantic(cd);
+ }
- if (sd.errors)
- sd.type = Type.terror;
- if (sd.semanticRun == PASS.initial)
- sd.type = sd.type.addSTC(sc.stc | sd.storage_class);
- sd.type = sd.type.typeSemantic(sd.loc, sc);
- auto ts = sd.type.isTypeStruct();
- if (ts)
+ override void visit(CPPNamespaceDeclaration ns)
+ {
+ Identifier identFromSE (StringExp se)
{
- if (ts.sym != sd)
+ const sident = se.toStringz();
+ if (!sident.length || !Identifier.isValidIdentifier(sident))
{
- TemplateInstance ti = ts.sym.isInstantiated();
- if (ti && isError(ti))
- ts.sym = sd;
- /* For C modules, if module A contains `struct S;` and
- * module B contains `struct S { members...}` then replace
- * the former with the latter
- */
- else if (!ts.sym.members && sd.members)
- ts.sym = sd;
+ error(ns.exp.loc, "expected valid identifier for C++ namespace but got `%s`", se.toErrMsg());
+ return null;
}
+ else
+ return Identifier.idPool(sident);
}
- // Ungag errors when not speculative
- Ungag ungag = sd.ungagSpeculative();
+ if (ns.ident !is null)
+ return attribSemantic(ns);
- if (sd.semanticRun == PASS.initial)
+ ns.cppnamespace = sc.namespace;
+ sc = sc.startCTFE();
+ ns.exp = ns.exp.expressionSemantic(sc);
+ ns.exp = resolveProperties(sc, ns.exp);
+ sc = sc.endCTFE();
+ ns.exp = ns.exp.ctfeInterpret();
+ // Can be either a tuple of strings or a string itself
+ if (auto te = ns.exp.isTupleExp())
{
- sd.visibility = sc.visibility;
+ expandTuples(te.exps);
+ CPPNamespaceDeclaration current = ns.cppnamespace;
+ for (size_t d = 0; d < te.exps.length; ++d)
+ {
+ auto exp = (*te.exps)[d];
+ auto prev = d ? current : ns.cppnamespace;
+ current = (d + 1) != te.exps.length
+ ? new CPPNamespaceDeclaration(ns.loc, exp, null)
+ : ns;
+ current.exp = exp;
+ current.cppnamespace = prev;
+ if (auto se = exp.toStringExp())
+ {
+ current.ident = identFromSE(se);
+ if (current.ident is null)
+ return; // An error happened in `identFromSE`
+ }
+ else
+ error(ns.exp.loc, "`%s`: index %llu is not a string constant, it is a `%s`",
+ ns.exp.toChars(), cast(ulong) d, ns.exp.type.toChars());
+ }
+ }
+ else if (auto se = ns.exp.toStringExp())
+ ns.ident = identFromSE(se);
+ // Empty Tuple
+ else if (ns.exp.isTypeExp() && ns.exp.isTypeExp().type.toBasetype().isTypeTuple())
+ {
+ }
+ else if (!ns.exp.type.isTypeError())
+ error(ns.exp.loc, "compile time string constant (or sequence) expected, not `%s`",
+ ns.exp.toChars());
+ attribSemantic(ns);
+ }
- if (sd.alignment.isUnknown()) // can be set already by `struct __declspec(align(N)) Tag { ... }`
- sd.alignment = sc.alignment();
+ override void visit(UserAttributeDeclaration uad)
+ {
+ //printf("UserAttributeDeclaration::semantic() %p\n", this);
+ if (uad.decl && !uad._scope)
+ uad.Dsymbol.setScope(sc); // for function local symbols
+ arrayExpressionSemantic(uad.atts.peekSlice(), sc, true);
+ return attribSemantic(uad);
+ }
- sd.storage_class |= sc.stc;
- if (sd.storage_class & STC.abstract_)
- .error(sd.loc, "%s `%s` structs, unions cannot be `abstract`", sd.kind, sd.toPrettyChars);
+ override void visit(StaticAssert sa)
+ {
+ if (sa.semanticRun < PASS.semanticdone)
+ sa.semanticRun = PASS.semanticdone;
+ else
+ return;
- sd.userAttribDecl = sc.userAttribDecl;
+ // https://issues.dlang.org/show_bug.cgi?id=24645
+ // This is a short-circuit. Usually, static assert conditions are evaluated
+ // in semantic2, but it's not uncommon to use this pattern:
+ // ---
+ // version(X)
+ // {}
+ // else
+ // static assert(false, "unsupported platform");
+ // ---
+ // However, without this short-circuit, the static assert error may get drowned
+ // out by subsequent semantic1 (import) errors. Only short-circuit at module scope though,
+ // inside mixin templates you want an instantiation trace (which you don't get here).
+ if (sc.parent && sc.parent.isModule())
+ if (auto i = sa.exp.isIntegerExp())
+ if (i.toInteger() == 0)
+ staticAssertFail(sa, sc);
+ }
+
+ override void visit(DebugSymbol ds)
+ {
+ //printf("DebugSymbol::semantic() %s\n", toChars());
+ if (ds.semanticRun < PASS.semanticdone)
+ ds.semanticRun = PASS.semanticdone;
+ }
+
+ override void visit(VersionSymbol vs)
+ {
+ if (vs.semanticRun < PASS.semanticdone)
+ vs.semanticRun = PASS.semanticdone;
+ }
+
+ override void visit(Package pkg)
+ {
+ if (pkg.semanticRun < PASS.semanticdone)
+ pkg.semanticRun = PASS.semanticdone;
+ }
- if (sc.linkage == LINK.cpp)
- sd.classKind = ClassKind.cpp;
- else if (sc.linkage == LINK.c)
- sd.classKind = ClassKind.c;
- sd.cppnamespace = sc.namespace;
- sd.cppmangle = sc.cppmangle;
- }
- else if (sd.symtab && !scx)
+ override void visit(Module m)
+ {
+ if (m.semanticRun != PASS.initial)
return;
- sd.semanticRun = PASS.semantic;
- checkGNUABITag(sd, sc.linkage);
+ timeTraceBeginEvent(TimeTraceEventType.sema1Module);
+ scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Module, m);
- if (!sd.members) // if opaque declaration
- {
- if (log) printf("\topaque declaration %s\n", sd.toChars());
- sd.semanticRun = PASS.semanticdone;
- return;
- }
- if (!sd.symtab)
+ //printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
+ m.semanticRun = PASS.semantic;
+ // Note that modules get their own scope, from scratch.
+ // This is so regardless of where in the syntax a module
+ // gets imported, it is unaffected by context.
+ Scope* sc = m._scope; // see if already got one from importAll()
+ if (!sc)
{
- sd.symtab = new DsymbolTable();
-
- sd.members.foreachDsymbol( s => s.addMember(sc, sd) );
+ sc = scopeCreateGlobal(m, global.errorSink); // create root scope
}
- auto sc2 = sd.newScope(sc);
-
- /* Set scope so if there are forward references, we still might be able to
- * resolve individual members like enums.
- */
- sd.members.foreachDsymbol( s => s.setScope(sc2) );
- sd.members.foreachDsymbol( s => s.importAll(sc2) );
- sd.members.foreachDsymbol( (s) { s.dsymbolSemantic(sc2); if (sd.errors) s.errors = true; } );
-
- if (sd.errors)
- sd.type = Type.terror;
-
- if (!sd.determineFields())
+ //printf("Module = %p, linkage = %d\n", sc.scopesym, sc.linkage);
+ // Pass 1 semantic routines: do public side of the definition
+ m.members.foreachDsymbol( (s)
{
- if (sd.type.ty != Terror)
- {
- .error(sd.loc, "%s `%s` circular or forward reference", sd.kind, sd.toPrettyChars);
- sd.errors = true;
- sd.type = Type.terror;
- }
+ //printf("\tModule('%s'): '%s'.dsymbolSemantic()\n", toChars(), s.toChars());
+ s.dsymbolSemantic(sc);
+ runDeferredSemantic();
+ });
- sc2.pop();
- sd.semanticRun = PASS.semanticdone;
- return;
+ if (m.userAttribDecl)
+ {
+ m.userAttribDecl.dsymbolSemantic(sc);
}
- /* Following special member functions creation needs semantic analysis
- * completion of sub-structs in each field types. For example, buildDtor
- * needs to check existence of elaborate dtor in type of each fields.
- * See the case in compilable/test14838.d
- */
- foreach (v; sd.fields)
+ if (!m._scope)
{
- Type tb = v.type.baseElemOf();
- if (tb.ty != Tstruct)
- continue;
- auto sdec = (cast(TypeStruct)tb).sym;
- if (sdec.semanticRun >= PASS.semanticdone)
- continue;
-
- sc2.pop();
-
- if (log) printf("\tdeferring %s\n", sd.toChars());
- return deferDsymbolSemantic(sc, sd, scx);
+ sc = sc.pop();
+ sc.pop(); // 2 pops because scopeCreateGlobal() created 2
}
+ m.semanticRun = PASS.semanticdone;
+ //printf("-Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
+ }
- /* Look for special member functions.
- */
- sd.disableNew = sd.search(Loc.initial, Id.classNew) !is null;
+ override void visit(EnumDeclaration ed)
+ {
+ enumSemantic(sc, ed);
+ }
- // Look for the constructor
- sd.ctor = sd.searchCtor();
+ override void visit(EnumMember em)
+ {
+ enumMemberSemantic(sc, em);
+ }
- buildDtors(sd, sc2);
+ override void visit(TemplateDeclaration tempdecl)
+ {
+ templateDeclarationSemantic(sc, tempdecl);
+ }
- bool hasCopyCtor;
- bool hasMoveCtor;
- bool needCopyCtor;
- bool needMoveCtor;
- needCopyOrMoveCtor(sd, hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor);
- //printf("%s hasCopy %d hasMove %d needCopy %d needMove %d\n", sd.toChars(), hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor);
+ override void visit(TemplateInstance ti)
+ {
+ templateInstanceSemantic(ti, sc, ArgumentList());
+ }
- /* When generating a move ctor, generate a copy ctor too, otherwise
- * https://github.com/s-ludwig/taggedalgebraic/issues/75
- */
- if (0 && needMoveCtor && !hasCopyCtor)
+ override void visit(TemplateMixin tm)
+ {
+ static if (LOG)
{
- needCopyCtor = true;
+ printf("+TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
+ fflush(stdout);
}
-
- if (needCopyCtor)
+ if (tm.semanticRun != PASS.initial)
{
- assert(hasCopyCtor == false);
- buildCopyOrMoveCtor(sd, sc2, false); // build copy constructor
- hasCopyCtor = true;
+ // When a class/struct contains mixin members, and is done over
+ // because of forward references, never reach here so semanticRun
+ // has been reset to PASS.initial.
+ static if (LOG)
+ {
+ printf("\tsemantic done\n");
+ }
+ return;
}
- if (needMoveCtor)
+ tm.semanticRun = PASS.semantic;
+ static if (LOG)
{
- assert(hasMoveCtor == false);
- buildCopyOrMoveCtor(sd, sc2, true); // build move constructor
- hasMoveCtor = true;
+ printf("\tdo semantic\n");
}
- sd.hasCopyCtor = hasCopyCtor;
- sd.hasMoveCtor = hasMoveCtor;
-
- sd.postblit = buildPostBlit(sd, sc2);
-
- buildOpAssign(sd, sc2);
- buildOpEquals(sd, sc2);
- if (!sc2.inCfile &&
- global.params.useTypeInfo && Type.dtypeinfo) // these functions are used for TypeInfo
+ Scope* scx = null;
+ if (tm._scope)
{
- sd.xeq = buildXopEquals(sd, sc2);
- sd.xcmp = buildXopCmp(sd, sc2);
- sd.xhash = buildXtoHash(sd, sc2);
+ sc = tm._scope;
+ scx = tm._scope; // save so we don't make redundant copies
+ tm._scope = null;
}
- sd.inv = buildInv(sd, sc2);
+ /* Run semantic on each argument, place results in tiargs[],
+ * then find best match template with tiargs
+ */
+ if (!tm.findMixinTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, ArgumentList()))
+ {
+ if (tm.semanticRun == PASS.initial) // forward reference had occurred
+ {
+ //printf("forward reference - deferring\n");
+ return deferDsymbolSemantic(sc, tm, scx);
+ }
- sd.semanticRun = PASS.semanticdone;
- if (log) printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
+ tm.inst = tm;
+ tm.errors = true;
+ return; // error recovery
+ }
- sc2.pop();
+ auto tempdecl = tm.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
- if (sd.ctor)
+ if (!tm.ident)
{
- Dsymbol scall = sd.search(Loc.initial, Id.opCall);
- if (scall)
- {
- const xerrors = global.startGagging();
- sc = sc.push();
- sc.tinst = null;
- sc.minst = null;
- auto fcall = resolveFuncCall(sd.loc, sc, scall, null, null, ArgumentList(), FuncResolveFlag.quiet);
- sc = sc.pop();
- global.endGagging(xerrors);
+ /* Assign scope local unique identifier, as same as lambdas.
+ */
+ const(char)[] s = "__mixin";
- if (fcall && fcall.isStatic())
+ if (FuncDeclaration func = sc.parent.isFuncDeclaration())
+ {
+ tm.symtab = func.localsymtab;
+ if (tm.symtab)
{
- .error(fcall.loc, "%s `%s` `static opCall` is hidden by constructors and can never be called", sd.kind, sd.toPrettyChars);
- errorSupplemental(fcall.loc, "Please use a factory method instead, or replace all constructors with `static opCall`.");
+ // Inside template constraint, symtab is not set yet.
+ goto L1;
}
}
+ else
+ {
+ tm.symtab = sc.parent.isScopeDsymbol().symtab;
+ L1:
+ assert(tm.symtab);
+ tm.ident = Identifier.generateId(s, tm.symtab.length + 1);
+ tm.symtab.insert(tm);
+ }
}
- if (ts && ts.sym != sd)
+ tm.inst = tm;
+ tm.parent = sc.parent;
+
+ /* Detect recursive mixin instantiations.
+ */
+ for (Dsymbol s = tm.parent; s; s = s.parent)
{
- StructDeclaration sym = ts.sym;
- if (sd.isCsymbol() && sym.isCsymbol())
- {
+ //printf("\ts = '%s'\n", s.toChars());
+ TemplateMixin tmix = s.isTemplateMixin();
+ if (!tmix || tempdecl != tmix.tempdecl)
+ continue;
- if (!isCCompatible(sd, sym))
+ /* Different argument list lengths happen with variadic args
+ */
+ if (tm.tiargs.length != tmix.tiargs.length)
+ continue;
+
+ for (size_t i = 0; i < tm.tiargs.length; i++)
+ {
+ RootObject o = (*tm.tiargs)[i];
+ Type ta = isType(o);
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+ RootObject tmo = (*tmix.tiargs)[i];
+ if (ta)
{
- // Already issued an error.
- errorSupplemental(sd.loc, "C %ss with the same name from different imports are merged", sd.kind);
+ Type tmta = isType(tmo);
+ if (!tmta)
+ goto Lcontinue;
+ if (!ta.equals(tmta))
+ goto Lcontinue;
}
- else {
- /* This is two structs imported from different C files.
- * Just ignore sd, the second one. The first one will always
- * be found when going through the type.
- */
+ else if (ea)
+ {
+ Expression tme = isExpression(tmo);
+ if (!tme || !dmd.expressionsem.equals(ea, tme))
+ goto Lcontinue;
}
- }
- else
- {
- version (none)
+ else if (sa)
{
- printf("this = %p %s\n", sd, sd.toChars());
- printf("type = %d sym = %p, %s\n", sd.type.ty, sym, sym.toPrettyChars());
+ Dsymbol tmsa = isDsymbol(tmo);
+ if (sa != tmsa)
+ goto Lcontinue;
}
- // https://issues.dlang.org/show_bug.cgi?id=19024
- .error(sd.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", sd.kind, sd.toPrettyChars, sym.loc.toChars());
+ else
+ assert(0);
}
- }
+ .error(tm.loc, "%s `%s` recursive mixin instantiation", tm.kind, tm.toPrettyChars);
+ return;
- if (global.errors != errors)
- {
- // The type is no good.
- sd.type = Type.terror;
- sd.errors = true;
- if (sd.deferred)
- sd.deferred.errors = true;
+ Lcontinue:
+ continue;
}
- if (sd.deferred && !global.gag)
- {
- sd.deferred.semantic2(sc);
- sd.deferred.semantic3(sc);
- }
+ // Copy the syntax trees from the TemplateDeclaration
+ tm.members = Dsymbol.arraySyntaxCopy(tempdecl.members);
+ if (!tm.members)
+ return;
- version (none)
+ tm.symtab = new DsymbolTable();
+
+ sc.getScopesym().importScope(tm, Visibility(Visibility.Kind.public_));
+
+ static if (LOG)
{
- // @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
- // Deprecated in 2.100
- // Make an error in 2.110
- if (sd.storage_class & STC.scope_)
- deprecation(sd.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site.");
+ printf("\tcreate scope for template parameters '%s'\n", tm.toChars());
}
- //printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
- }
+ Scope* scy = sc.push(tm);
+ scy.parent = tm;
- //
- // Checks if two structs are compatible
- // Implements the rules according to C23 6.2.7
- //
- static bool isCCompatible(StructDeclaration a, StructDeclaration b)
- {
- // Get the name of a type, while avoiding exposing "__tagXXX" anonymous structs
- static const(char)* typeName(Type t)
+ /* https://issues.dlang.org/show_bug.cgi?id=930
+ *
+ * If the template that is to be mixed in is in the scope of a template
+ * instance, we have to also declare the type aliases in the new mixin scope.
+ */
+ auto parentInstance = tempdecl.parent ? tempdecl.parent.isTemplateInstance() : null;
+ if (parentInstance)
+ parentInstance.declareParameters(scy);
+
+ tm.argsym = new ScopeDsymbol();
+ tm.argsym.parent = scy.parent;
+ Scope* argscope = scy.push(tm.argsym);
+
+ const errorsave = global.errors;
+
+ // Declare each template parameter as an alias for the argument type
+ tm.declareParameters(argscope);
+
+ // Add members to enclosing scope, as well as this scope
+ tm.members.foreachDsymbol(s => s.addMember(argscope, tm));
+
+ // Do semantic() analysis on template instance members
+ static if (LOG)
{
- if (TypeStruct ts = t.isTypeStruct())
- {
- if (ts.sym.ident.toString().startsWith("__tag"))
- return ts.sym.isUnionDeclaration() ? "(anonymous union)".ptr: "(anonymous struct)".ptr;
- }
- return t.toChars();
+ printf("\tdo semantic() on template instance members '%s'\n", tm.toChars());
}
+ Scope* sc2 = argscope.push(tm);
+ //size_t deferred_dim = Module.deferred.length;
- void incompatError()
+ __gshared int nest;
+ //printf("%d\n", nest);
+ if (++nest > global.recursionLimit)
{
- .error(a.loc, "%s `%s` already exists with an incompatible definition.",
- a.kind, typeName(a.type));
- errorSupplemental(b.loc, "previously declared here");
+ global.gag = 0; // ensure error message gets printed
+ .error(tm.loc, "%s `%s` recursive expansion", tm.kind, tm.toPrettyChars);
+ fatal();
}
+ tm.members.foreachDsymbol( s => s.setScope(sc2) );
- // For recursive calls into unnamed structs (so Type.equals() doesn't work).
- static bool isCCompatibleUnnamedStruct(Type a, Type b)
- {
- TypeStruct ats = a.isTypeStruct();
- if (!ats) return false;
- TypeStruct bts = b.isTypeStruct();
- if (!bts) return false;
- // Hack, anonymous structs within a struct are given
- // an anonymous id starting with __tag.
- if (!ats.sym.ident.toString().startsWith("__tag"))
- return false;
- if (!bts.sym.ident.toString().startsWith("__tag"))
- return false;
- return isCCompatible(ats.sym, bts.sym);
- }
+ tm.members.foreachDsymbol( s => s.importAll(sc2) );
- if (a.fields.length != b.fields.length)
+ tm.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
+
+ nest--;
+
+ /* In DeclDefs scope, TemplateMixin does not have to handle deferred symbols.
+ * Because the members would already call addDeferredSemantic() for themselves.
+ * See Struct, Class, Interface, and EnumDeclaration.dsymbolSemantic().
+ */
+ //if (!sc.func && Module.deferred.length > deferred_dim) {}
+
+ AggregateDeclaration ad = tm.isMember();
+ if (sc.func && !ad)
{
- incompatError();
- errorSupplemental(a.loc, "`%s` has %zu field(s) while `%s` has %zu field(s)",
- a.toPrettyChars(), a.fields.length, b.toPrettyChars(), b.fields.length);
- return false;
+ tm.semantic2(sc2);
+ tm.semantic3(sc2);
}
- // both are structs or both are unions
- if ((a.isUnionDeclaration() is null) != (b.isUnionDeclaration() is null))
+
+ // Give additional context info if error occurred during instantiation
+ if (global.errors != errorsave)
{
- incompatError();
- errorSupplemental(a.loc, "`%s` is a %s while `%s` is a %s",
- a.toPrettyChars(), a.kind, b.toPrettyChars(), b.kind);
- return false;
+ .error(tm.loc, "%s `%s` error instantiating", tm.kind, tm.toPrettyChars);
+ tm.errors = true;
}
- if (a.alignment != b.alignment)
+
+ sc2.pop();
+ argscope.pop();
+ scy.pop();
+
+ static if (LOG)
{
- incompatError();
- errorSupplemental(a.loc, "`%s` has different alignment or packing", a.toPrettyChars());
- if (a.alignment.isDefault() && ! b.alignment.isDefault())
- {
- errorSupplemental(a.loc, "`%s` alignment: default", a.toPrettyChars());
- errorSupplemental(b.loc, "`%s` alignment: %u",
- b.toPrettyChars(), cast(uint)b.alignment.get());
- }
- else if (!a.alignment.isDefault() && b.alignment.isDefault())
- {
- errorSupplemental(a.loc, "`%s` alignment: %u",
- a.toPrettyChars(), cast(uint)a.alignment.get());
- errorSupplemental(b.loc, "`%s` alignment: default",
- b.toPrettyChars());
- }
- else if (a.alignment.get() != b.alignment.get())
- {
- errorSupplemental(a.loc, "`%s` alignment: %u",
- a.toPrettyChars(), cast(uint)a.alignment.get());
- errorSupplemental(b.loc, "`%s` alignment: %u",
- b.toPrettyChars(), cast(uint)b.alignment.get());
- }
- if (a.alignment.isPack() != b.alignment.isPack())
- {
- errorSupplemental(a.loc, "`%s` packed: %s",
- a.toPrettyChars(), a.alignment.isPack()?"true".ptr:"false".ptr);
- errorSupplemental(b.loc, "`%s` packed: %s",
- b.toPrettyChars(), b.alignment.isPack()?"true".ptr:"false".ptr);
- }
- return false;
+ printf("-TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
}
- foreach (size_t i, VarDeclaration a_field; a.fields[])
- {
- VarDeclaration b_field = b.fields[i];
- //
- // — there shall be a one-to-one correspondence between
- // their members such that each pair of corresponding
- // members are declared with compatible types;
- //
- if (!a_field.type.equals(b_field.type) && !isCCompatibleUnnamedStruct(a_field.type, b_field.type))
- {
- // Already errored, just bail
- incompatError();
- if (a_field.type.isTypeError()) return false;
- if (b_field.type.isTypeError()) return false;
+ }
- errorSupplemental(a_field.loc, "Field %zu differs in type", i);
- errorSupplemental(a_field.loc, "typeof(%s): %s",
- a_field.toChars(), typeName(a_field.type));
- errorSupplemental(b_field.loc, "typeof(%s): %s",
- b_field.toChars(), typeName(b_field.type));
- return false;
- }
- //
- // — if one member of the pair is declared with an
- // alignment specifier, the second is declared with an
- // equivalent alignment specifier;
- //
- if (a_field.alignment != b_field.alignment)
- {
- incompatError();
- errorSupplemental(a_field.loc, "Field %zu differs in alignment or packing", i);
- if (a_field.alignment.isDefault() && ! b_field.alignment.isDefault())
- {
- errorSupplemental(a_field.loc, "`%s.%s` alignment: default",
- a.toPrettyChars(),a_field.toChars());
- errorSupplemental(b_field.loc, "`%s.%s` alignment: %u",
- b.toPrettyChars(), b_field.toChars(), cast(uint)b_field.alignment.get());
- }
- else if (!a_field.alignment.isDefault() && b_field.alignment.isDefault())
- {
- errorSupplemental(a_field.loc, "`%s.%s` alignment: %u",
- a.toPrettyChars(), a_field.toChars(), cast(uint)a_field.alignment.get());
- errorSupplemental(b_field.loc, "`%s.%s` alignment: default",
- b.toPrettyChars(), b_field.toChars());
- }
- else if (a_field.alignment.get() != b_field.alignment.get())
- {
- errorSupplemental(a_field.loc, "`%s.%s` alignment: %u",
- a.toPrettyChars(), a_field.toChars(),
- cast(uint)a_field.alignment.get());
- errorSupplemental(b_field.loc, "`%s.%s` alignment: %u",
- b.toPrettyChars(), b_field.toChars(),
- cast(uint)b_field.alignment.get());
- }
- if (a_field.alignment.isPack() != b_field.alignment.isPack())
- {
- errorSupplemental(a_field.loc, "`%s.%s` packed: %s",
- a.toPrettyChars(), a_field.toChars(),
- a_field.alignment.isPack()?"true".ptr:"false".ptr);
- errorSupplemental(b_field.loc, "`%s.%s` packed: %s",
- b.toPrettyChars(), b_field.toChars(),
- b_field.alignment.isPack()?"true".ptr:"false".ptr);
- }
- return false;
+ override void visit(Nspace ns)
+ {
+ if (ns.semanticRun != PASS.initial)
+ return;
+ static if (LOG)
+ {
+ printf("+Nspace::semantic('%s')\n", ns.toChars());
+ scope(exit) printf("-Nspace::semantic('%s')\n", ns.toChars());
+ }
+ if (ns._scope)
+ {
+ sc = ns._scope;
+ ns._scope = null;
+ }
+ if (!sc)
+ return;
+
+ bool repopulateMembers = false;
+ if (ns.identExp)
+ {
+ // resolve the namespace identifier
+ sc = sc.startCTFE();
+ Expression resolved = ns.identExp.expressionSemantic(sc);
+ resolved = resolveProperties(sc, resolved);
+ sc = sc.endCTFE();
+ resolved = resolved.ctfeInterpret();
+ StringExp name = resolved.toStringExp();
+ TupleExp tup = name ? null : resolved.isTupleExp();
+ if (!tup && !name)
+ {
+ error(ns.loc, "expected string expression for namespace name, got `%s`", ns.identExp.toChars());
+ return;
}
- //
- // - and, if one member of the pair is declared with a
- // name, the second is declared with the same name.
- //
- if (a_field.ident.isAnonymous())
+ ns.identExp = resolved; // we don't need to keep the old AST around
+ if (name)
{
- if (!b_field.ident.isAnonymous())
+ const(char)[] ident = name.toStringz();
+ if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
{
- incompatError();
- errorSupplemental(a_field.loc, "Field %zu differs in name", i);
- errorSupplemental(a_field.loc, "(anonymous)", a_field.ident.toChars());
- errorSupplemental(b_field.loc, "%s", b_field.ident.toChars());
- return false;
+ error(ns.loc, "expected valid identifier for C++ namespace but got `%.*s`", cast(int)ident.length, ident.ptr);
+ return;
}
+ ns.ident = Identifier.idPool(ident);
}
- else if (b_field.ident.isAnonymous())
- {
- incompatError();
- errorSupplemental(a_field.loc, "Field %zu differs in name", i);
- errorSupplemental(a_field.loc, "%s", a_field.ident.toChars());
- errorSupplemental(b_field.loc, "(anonymous)");
- return false;
- }
- else if (a_field.ident != b_field.ident)
+ else
{
- incompatError();
- errorSupplemental(a_field.loc, "Field %zu differs in name", i);
- errorSupplemental(a_field.loc, "%s", a_field.ident.toChars());
- errorSupplemental(b_field.loc, "%s", b_field.ident.toChars());
- return false;
+ // create namespace stack from the tuple
+ Nspace parentns = ns;
+ foreach (i, exp; *tup.exps)
+ {
+ name = exp.toStringExp();
+ if (!name)
+ {
+ error(ns.loc, "expected string expression for namespace name, got `%s`", exp.toChars());
+ return;
+ }
+ const(char)[] ident = name.toStringz();
+ if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
+ {
+ error(ns.loc, "expected valid identifier for C++ namespace but got `%.*s`", cast(int)ident.length, ident.ptr);
+ return;
+ }
+ if (i == 0)
+ {
+ ns.ident = Identifier.idPool(ident);
+ }
+ else
+ {
+ // insert the new namespace
+ Nspace childns = new Nspace(ns.loc, Identifier.idPool(ident), null, parentns.members);
+ parentns.members = new Dsymbols;
+ parentns.members.push(childns);
+ parentns = childns;
+ repopulateMembers = true;
+ }
+ }
}
+ }
- //
- // For two structures or unions, corresponding bitfields shall have the same widths.
- //
- BitFieldDeclaration bfa = a_field.isBitFieldDeclaration();
- BitFieldDeclaration bfb = b_field.isBitFieldDeclaration();
- if ((bfa is null) != (bfb is null))
+ ns.semanticRun = PASS.semantic;
+ ns.parent = sc.parent;
+ // Link does not matter here, if the UDA is present it will error
+ checkGNUABITag(ns, LINK.cpp);
+
+ if (!ns.members)
+ {
+ ns.semanticRun = PASS.semanticdone;
+ return;
+ }
+ assert(sc);
+ sc = sc.push(ns);
+ sc.linkage = LINK.cpp; // note that namespaces imply C++ linkage
+ sc.parent = ns;
+ foreach (s; *ns.members)
+ {
+ if (repopulateMembers)
{
- incompatError();
- errorSupplemental(a_field.loc, "Field %zu differs in being a bitfield", i);
- if (bfa is null)
- {
- errorSupplemental(a_field.loc, "`%s.%s` is not a bitfield",
- a.toPrettyChars(), a_field.toChars());
- errorSupplemental(b_field.loc, "`%s.%s` is a bitfield",
- b.toPrettyChars(), b_field.toChars());
- }
- else if (bfb is null)
- {
- errorSupplemental(a_field.loc, "`%s.%s` *is a bitfield",
- a.toPrettyChars(), a_field.toChars());
- errorSupplemental(b_field.loc, "`%s.%s` is not a bitfield",
- b.toPrettyChars(), b_field.toChars());
- }
- return false;
+ s.addMember(sc, sc.scopesym);
+ s.setScope(sc);
}
- if (bfa !is null && bfb !is null)
+ s.importAll(sc);
+ }
+ foreach (s; *ns.members)
+ {
+ static if (LOG)
{
- if (bfa.fieldWidth != bfb.fieldWidth)
- {
- incompatError();
- errorSupplemental(a_field.loc, "Field %zu differs in bitfield width", i);
- errorSupplemental(a_field.loc, "`%s.%s`: %u",
- a.toPrettyChars(), a_field.toChars(), bfa.fieldWidth);
- errorSupplemental(b_field.loc, "`%s.%s`: %u",
- b.toPrettyChars(), b_field.toChars(), bfb.fieldWidth);
- return false;
- }
+ printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind());
}
+ s.dsymbolSemantic(sc);
}
- return true;
+ sc.pop();
+ ns.semanticRun = PASS.semanticdone;
}
- void interfaceSemantic(ClassDeclaration cd)
+ /// Do the semantic analysis on the external interface to the function.
+ override void visit(FuncDeclaration funcdecl)
{
- cd.vtblInterfaces = new BaseClasses();
- cd.vtblInterfaces.reserve(cd.interfaces.length);
- foreach (b; cd.interfaces)
- {
- cd.vtblInterfaces.push(b);
- b.copyBaseInterfaces(cd.vtblInterfaces);
- }
+ funcDeclarationSemantic(sc, funcdecl);
}
- override void visit(ClassDeclaration cldec)
+ override void visit(CtorDeclaration ctd)
{
- //printf("ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", cldec.toChars(), cldec.type, cldec.sizeok, this);
- //printf("\tparent = %p, '%s'\n", sc.parent, sc.parent ? sc.parent.toChars() : "");
- //printf("sc.stc = %x\n", sc.stc);
-
- //{ static int n; if (++n == 20) *(char*)0=0; }
-
- if (cldec.semanticRun >= PASS.semanticdone)
+ //printf("CtorDeclaration::semantic() %p %s\n", ctd, ctd.toChars());
+ if (ctd.semanticRun >= PASS.semanticdone)
return;
- const errors = global.errors;
-
- //printf("+ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this);
-
- Scope* scx = null;
- if (cldec._scope)
+ if (ctd._scope)
{
- sc = cldec._scope;
- scx = cldec._scope; // save so we don't make redundant copies
- cldec._scope = null;
+ sc = ctd._scope;
+ ctd._scope = null;
}
- if (!cldec.parent)
+ ctd.parent = sc.parent;
+ Dsymbol p = ctd.toParentDecl();
+ AggregateDeclaration ad = p.isAggregateDeclaration();
+ if (!ad)
{
- assert(sc.parent);
- cldec.parent = sc.parent;
+ error(ctd.loc, "constructor can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
+ ctd.type = Type.terror;
+ ctd.errors = true;
+ return;
}
- if (cldec.errors)
- cldec.type = Type.terror;
- if (cldec.semanticRun == PASS.initial)
- cldec.type = cldec.type.addSTC(sc.stc | cldec.storage_class);
- cldec.type = cldec.type.typeSemantic(cldec.loc, sc);
- if (auto tc = cldec.type.isTypeClass())
- if (tc.sym != cldec)
- {
- auto ti = tc.sym.isInstantiated();
- if (ti && isError(ti))
- tc.sym = cldec;
- }
-
- // Ungag errors when not speculative
- Ungag ungag = cldec.ungagSpeculative();
+ sc = sc.push();
- if (cldec.semanticRun == PASS.initial)
+ if (sc.stc & STC.static_)
{
- cldec.visibility = sc.visibility;
+ if (sc.stc & STC.shared_)
+ error(ctd.loc, "`shared static` has no effect on a constructor inside a `shared static` block. Use `shared static this()`");
+ else
+ error(ctd.loc, "`static` has no effect on a constructor inside a `static` block. Use `static this()`");
+ }
- cldec.storage_class |= sc.stc;
- if (cldec.storage_class & STC.auto_)
- .error(cldec.loc, "%s `%s` storage class `auto` is invalid when declaring a class, did you mean to use `scope`?", cldec.kind, cldec.toPrettyChars);
- if (cldec.storage_class & STC.scope_)
- cldec.stack = true;
- if (cldec.storage_class & STC.abstract_)
- cldec.isabstract = ThreeState.yes;
+ sc.stc &= ~STC.static_; // not a static constructor
- cldec.userAttribDecl = sc.userAttribDecl;
+ funcDeclarationSemantic(sc, ctd);
- if (sc.linkage == LINK.cpp)
- cldec.classKind = ClassKind.cpp;
- cldec.cppnamespace = sc.namespace;
- cldec.cppmangle = sc.cppmangle;
- if (sc.linkage == LINK.objc)
- objc.setObjc(cldec);
- }
- else if (cldec.symtab && !scx)
- {
+ sc.pop();
+
+ if (ctd.errors)
return;
- }
- cldec.semanticRun = PASS.semantic;
- checkGNUABITag(cldec, sc.linkage);
- checkMustUseReserved(cldec);
- if (cldec.baseok < Baseok.done)
+ TypeFunction tf = ctd.type.toTypeFunction();
+ immutable dim = tf.parameterList.length;
+ auto sd = ad.isStructDeclaration();
+
+ /* See if it's the default constructor
+ * But, template constructor should not become a default constructor.
+ */
+ if (ad && (!ctd.parent.isTemplateInstance() || ctd.parent.isTemplateMixin()))
{
- /* https://issues.dlang.org/show_bug.cgi?id=12078
- * https://issues.dlang.org/show_bug.cgi?id=12143
- * https://issues.dlang.org/show_bug.cgi?id=15733
- * While resolving base classes and interfaces, a base may refer
- * the member of this derived class. In that time, if all bases of
- * this class can be determined, we can go forward the semantc process
- * beyond the Lancestorsdone. To do the recursive semantic analysis,
- * temporarily set and unset `_scope` around exp().
- */
- T resolveBase(T)(lazy T exp)
+ if (!sd)
{
- if (!scx)
- {
- scx = sc.copy();
- scx.setNoFree();
- }
- static if (!is(T == void))
- {
- cldec._scope = scx;
- auto r = exp();
- cldec._scope = null;
- return r;
- }
- else
- {
- cldec._scope = scx;
- exp();
- cldec._scope = null;
- }
+ if (dim == 0 && tf.parameterList.varargs == VarArg.none)
+ ad.defaultCtor = ctd;
+ return;
}
- cldec.baseok = Baseok.start;
-
- // Expand any tuples in baseclasses[]
- for (size_t i = 0; i < cldec.baseclasses.length;)
+ if (dim == 0 && tf.parameterList.varargs == VarArg.none) // empty default ctor w/o any varargs
{
- auto b = (*cldec.baseclasses)[i];
- b.type = resolveBase(b.type.typeSemantic(cldec.loc, sc));
-
- Type tb = b.type.toBasetype();
- if (auto tup = tb.isTypeTuple())
+ if (ctd.fbody || !(ctd.storage_class & STC.disable))
{
- cldec.baseclasses.remove(i);
- size_t dim = Parameter.dim(tup.arguments);
- for (size_t j = 0; j < dim; j++)
- {
- Parameter arg = Parameter.getNth(tup.arguments, j);
- b = new BaseClass(arg.type);
- cldec.baseclasses.insert(i + j, b);
- }
+ .error(ctd.loc, "%s `%s` default constructor for structs only allowed " ~
+ "with `@disable`, no body, and no parameters", ctd.kind, ctd.toPrettyChars);
+ ctd.storage_class |= STC.disable;
+ ctd.fbody = null;
}
- else
- i++;
+ sd.noDefaultCtor = true;
}
-
- if (cldec.baseok >= Baseok.done)
+ else if (dim == 0 && tf.parameterList.varargs != VarArg.none) // allow varargs only ctor
{
- //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun);
- if (cldec.semanticRun >= PASS.semanticdone)
- return;
- goto Lancestorsdone;
}
-
- // See if there's a base class as first in baseclasses[]
- if (cldec.baseclasses.length)
+ else if (dim && !tf.parameterList.hasArgsWithoutDefault)
{
- BaseClass* b = (*cldec.baseclasses)[0];
- Type tb = b.type.toBasetype();
- TypeClass tc = tb.isTypeClass();
- if (!tc)
+ if (ctd.storage_class & STC.disable)
{
- if (b.type != Type.terror)
- .error(cldec.loc, "%s `%s` base type must be `class` or `interface`, not `%s`", cldec.kind, cldec.toPrettyChars, b.type.toChars());
- cldec.baseclasses.remove(0);
- goto L7;
+ .error(ctd.loc, "%s `%s` is marked `@disable`, so it cannot have default "~
+ "arguments for all parameters.", ctd.kind, ctd.toPrettyChars);
+ errorSupplemental(ctd.loc, "Use `@disable this();` if you want to disable default initialization.");
}
- if (tc.sym.isDeprecated())
+ else
+ .error(ctd.loc, "%s `%s` all parameters have default arguments, "~
+ "but structs cannot have default constructors.", ctd.kind, ctd.toPrettyChars);
+ }
+ else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
+ {
+ //printf("tf: %s\n", toChars(tf));
+ auto param = tf.parameterList[0];
+ if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
{
- if (!cldec.isDeprecated())
- {
- // Deriving from deprecated class makes this one deprecated too
- cldec.setDeprecated();
- tc.checkDeprecated(cldec.loc, sc);
- }
+ //printf("copy constructor %p\n", ctd);
+ assert(!ctd.isCpCtor && !ctd.isMoveCtor);
+ if (param.storageClass & STC.ref_)
+ ctd.isCpCtor = true; // copy constructor
+ else
+ ctd.isMoveCtor = true; // move constructor
+ assert(!(ctd.isCpCtor && ctd.isMoveCtor));
}
- if (tc.sym.isInterfaceDeclaration())
- goto L7;
+ }
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=22593
+ else if (auto ti = ctd.parent.isTemplateInstance())
+ {
+ checkHasBothRvalueAndCpCtor(sd, ctd, ti);
+ }
+ }
- for (ClassDeclaration cdb = tc.sym; cdb; cdb = cdb.baseClass)
- {
- if (cdb == cldec)
- {
- .error(cldec.loc, "%s `%s` circular inheritance", cldec.kind, cldec.toPrettyChars);
- cldec.baseclasses.remove(0);
- goto L7;
- }
- }
+ override void visit(PostBlitDeclaration pbd)
+ {
+ //printf("PostBlitDeclaration::semantic() %s\n", toChars());
+ //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id.dtor.toChars(), ident, Id.dtor);
+ //printf("stc = x%llx\n", sc.stc);
+ if (pbd.semanticRun >= PASS.semanticdone)
+ return;
+ if (pbd._scope)
+ {
+ sc = pbd._scope;
+ pbd._scope = null;
+ }
- /* https://issues.dlang.org/show_bug.cgi?id=11034
- * Class inheritance hierarchy
- * and instance size of each classes are orthogonal information.
- * Therefore, even if tc.sym.sizeof == Sizeok.none,
- * we need to set baseClass field for class covariance check.
- */
- cldec.baseClass = tc.sym;
- b.sym = cldec.baseClass;
+ pbd.parent = sc.parent;
+ Dsymbol p = pbd.toParent2();
+ StructDeclaration ad = p.isStructDeclaration();
+ if (!ad)
+ {
+ error(pbd.loc, "postblit can only be a member of struct, not %s `%s`", p.kind(), p.toChars());
+ pbd.type = Type.terror;
+ pbd.errors = true;
+ return;
+ }
+ if (pbd.ident == Id.postblit && pbd.semanticRun < PASS.semantic)
+ ad.postblits.push(pbd);
+ if (!pbd.type)
+ pbd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, pbd.storage_class);
- if (tc.sym.baseok < Baseok.done)
- resolveBase(tc.sym.dsymbolSemantic(null)); // Try to resolve forward reference
- if (tc.sym.baseok < Baseok.done)
- {
- //printf("\ttry later, forward reference of base class %s\n", tc.sym.toChars());
- if (tc.sym._scope)
- Module.addDeferredSemantic(tc.sym);
- cldec.baseok = Baseok.none;
- }
- L7:
- }
+ sc = sc.push();
+ sc.stc &= ~STC.static_; // not static
+ sc.linkage = LINK.d;
- // Treat the remaining entries in baseclasses as interfaces
- // Check for errors, handle forward references
- int multiClassError = cldec.baseClass is null ? 0 : 1;
+ funcDeclarationSemantic(sc, pbd);
- BCLoop:
- for (size_t i = (cldec.baseClass ? 1 : 0); i < cldec.baseclasses.length;)
- {
- BaseClass* b = (*cldec.baseclasses)[i];
- Type tb = b.type.toBasetype();
- TypeClass tc = tb.isTypeClass();
- if (!tc || !tc.sym.isInterfaceDeclaration())
- {
- // It's a class
- if (tc)
- {
- if (multiClassError == 0)
- {
- .error(cldec.loc,"`%s`: base class must be specified first, " ~
- "before any interfaces.", cldec.toPrettyChars());
- multiClassError += 1;
- }
- else if (multiClassError >= 1)
- {
- if(multiClassError == 1)
- .error(cldec.loc, "`%s`: multiple class inheritance is not supported." ~
- " Use multiple interface inheritance and/or composition.", cldec.toPrettyChars());
- multiClassError += 1;
+ sc.pop();
+ }
- if (tc.sym.fields.length)
- errorSupplemental(cldec.loc,"`%s` has fields, consider making it a member of `%s`",
- b.type.toChars(), cldec.type.toChars());
- else
- errorSupplemental(cldec.loc,"`%s` has no fields, consider making it an `interface`",
- b.type.toChars());
- }
- }
- // It's something else: e.g. `int` in `class Foo : Bar, int { ... }`
- else if (b.type != Type.terror)
- {
- error(cldec.loc,"`%s`: base type must be `interface`, not `%s`",
- cldec.toPrettyChars(), b.type.toChars());
- }
- cldec.baseclasses.remove(i);
- continue;
- }
+ override void visit(DtorDeclaration dd)
+ {
+ //printf("DtorDeclaration::semantic() %s\n", dd.toChars());
+ //printf("ident: %s, %s, %p, %p\n", dd.ident.toChars(), Id.dtor.toChars(), dd.ident, Id.dtor);
+ if (dd.semanticRun >= PASS.semanticdone)
+ return;
+ if (dd._scope)
+ {
+ sc = dd._scope;
+ dd._scope = null;
+ }
- // Check for duplicate interfaces
- for (size_t j = (cldec.baseClass ? 1 : 0); j < i; j++)
+ dd.parent = sc.parent;
+ Dsymbol p = dd.toParent2();
+ AggregateDeclaration ad = p.isAggregateDeclaration();
+ if (!ad)
+ {
+ error(dd.loc, "destructor can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
+ dd.type = Type.terror;
+ dd.errors = true;
+ return;
+ }
+
+ if (ad.isClassDeclaration() && ad.classKind == ClassKind.d)
+ {
+ // Class destructors are implicitly `scope`
+ dd.storage_class |= STC.scope_;
+ }
+
+ if (dd.ident == Id.dtor && dd.semanticRun < PASS.semantic)
+ ad.userDtors.push(dd);
+ if (!dd.type)
+ {
+ dd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dd.storage_class);
+ if (ad.classKind == ClassKind.cpp && dd.ident == Id.dtor)
+ {
+ if (auto cldec = ad.isClassDeclaration())
{
- BaseClass* b2 = (*cldec.baseclasses)[j];
- if (b2.sym == tc.sym)
+ assert (cldec.cppDtorVtblIndex == -1); // double-call check already by dd.type
+ if (cldec.baseClass && cldec.baseClass.cppDtorVtblIndex != -1)
{
- .error(cldec.loc, "%s `%s` inherits from duplicate interface `%s`", cldec.kind, cldec.toPrettyChars, b2.sym.toChars());
- cldec.baseclasses.remove(i);
- continue BCLoop;
+ // override the base virtual
+ cldec.cppDtorVtblIndex = cldec.baseClass.cppDtorVtblIndex;
}
- }
- if (tc.sym.isDeprecated())
- {
- if (!cldec.isDeprecated())
+ else if (!dd.isFinal())
{
- // Deriving from deprecated class makes this one deprecated too
- cldec.setDeprecated();
- tc.checkDeprecated(cldec.loc, sc);
+ // reserve the dtor slot for the destructor (which we'll create later)
+ cldec.cppDtorVtblIndex = cast(int)cldec.vtbl.length;
+ cldec.vtbl.push(dd);
+ if (target.cpp.twoDtorInVtable)
+ cldec.vtbl.push(dd); // deleting destructor uses a second slot
}
}
+ }
+ }
- b.sym = tc.sym;
+ sc = sc.push();
+ sc.stc &= ~STC.static_; // not a static destructor
+ if (sc.linkage != LINK.cpp)
+ sc.linkage = LINK.d;
- if (tc.sym.baseok < Baseok.done)
- resolveBase(tc.sym.dsymbolSemantic(null)); // Try to resolve forward reference
- if (tc.sym.baseok < Baseok.done)
+ funcDeclarationSemantic(sc, dd);
+
+ sc.pop();
+ }
+
+ void visitStaticCDtorDeclaration(FuncDeclaration sd, bool isDestructor)
+ {
+ if (sd.semanticRun >= PASS.semanticdone)
+ return;
+ if (sd._scope)
+ {
+ sc = sd._scope;
+ sd._scope = null;
+ }
+ sd.parent = sc.parent;
+ Dsymbol p = sd.parent.pastMixin();
+ const bool isShared = !!(sd.isSharedStaticDtorDeclaration() || sd.isSharedStaticCtorDeclaration());
+ const(char)* what = isDestructor ? "destructor" : "constructor";
+ if (!p.isScopeDsymbol())
+ {
+ const(char)* s = isShared ? "shared " : "";
+ error(sd.loc, "`%sstatic` %s can only be member of module/aggregate/template, not %s `%s`", s, what, p.kind(), p.toChars());
+ sd.type = Type.terror;
+ sd.errors = true;
+ return;
+ }
+
+ if (!sd.type)
+ sd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sd.storage_class);
+
+ /* If the static [dc]tor appears within a template instantiation,
+ * it could get called multiple times by the module constructors
+ * for different modules. Thus, protect it with a gate.
+ */
+ if (sd.isInstantiated() && sd.semanticRun < PASS.semantic)
+ {
+ /* Add this prefix to the constructor:
+ * ```
+ * static int gate;
+ * if ([--|++]gate != [0|1]) return; // dependant on ctor/dtor
+ * ```
+ * or, for shared constructor:
+ * ```
+ * shared int gate;
+ * enum op = isDestructor ? "-=" : "+=";
+ * enum cmp = isDestructor ? 0 : 1;
+ * if (core.atomic.atomicOp!op(gate, 1) != cmp) return;
+ * ```
+ */
+
+ auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
+ v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : STC.none);
+
+ Statement s = new ExpStatement(Loc.initial, v);
+ auto sa = new Statements(s);
+
+ Expression e;
+ if (isShared)
+ {
+ e = doAtomicOp(isDestructor ? "-=" : "+=", v.ident, IntegerExp.literal!(1));
+ if (e is null)
{
- //printf("\ttry later, forward reference of base %s\n", tc.sym.toChars());
- if (tc.sym._scope)
- Module.addDeferredSemantic(tc.sym);
- cldec.baseok = Baseok.none;
+ .error(sd.loc, "%s `%s` shared static %s within a template require `core.atomic : atomicOp` to be present", sd.kind, sd.toPrettyChars, what);
+ return;
}
- i++;
}
- if (cldec.baseok == Baseok.none)
+ else
{
- // Forward referencee of one or more bases, try again later
- //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars());
- return deferDsymbolSemantic(sc, cldec, scx);
+ IntegerExp one = isDestructor ? IntegerExp.literal!(-1) : IntegerExp.literal!(1);
+ e = new AddAssignExp(
+ Loc.initial, new IdentifierExp(Loc.initial, v.ident), one);
}
- cldec.baseok = Baseok.done;
+ IntegerExp cmp = isDestructor ? IntegerExp.literal!0 : IntegerExp.literal!1;
+ e = new EqualExp(EXP.notEqual, Loc.initial, e, cmp);
+ s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
- if (cldec.classKind == ClassKind.objc || (cldec.baseClass && cldec.baseClass.classKind == ClassKind.objc))
- cldec.classKind = ClassKind.objc; // Objective-C classes do not inherit from Object
+ sa.push(s);
+ if (sd.fbody)
+ sa.push(sd.fbody);
- // If no base class, and this is not an Object, use Object as base class
- if (!cldec.baseClass && cldec.ident != Id.Object && cldec.object && cldec.classKind == ClassKind.d)
+ sd.fbody = new CompoundStatement(Loc.initial, sa);
+ if (isDestructor)
+ (cast(StaticDtorDeclaration)sd).vgate = v;
+ }
+ const LINK save = sc.linkage;
+ if (save != LINK.d)
+ {
+ const(char)* s = isShared ? "shared " : "";
+ deprecation(sd.loc, "`%sstatic` %s can only be of D linkage", s, what);
+ // Just correct it
+ sc.linkage = LINK.d;
+ }
+ funcDeclarationSemantic(sc, sd);
+ sc.linkage = save;
+
+ // We're going to need ModuleInfo
+ Module m = sd.getModule();
+ if (!m)
+ m = sc._module;
+ if (m)
+ {
+ m.needmoduleinfo = 1;
+ //printf("module2 %s needs moduleinfo\n", m.toChars());
+ }
+ }
+ override void visit(StaticCtorDeclaration scd)
+ {
+ //printf("StaticCtorDeclaration::semantic()\n");
+ visitStaticCDtorDeclaration(scd, false);
+
+ foreachUda(scd, sc, (Expression e) {
+ import dmd.attrib : isEnumAttribute;
+ if (!isEnumAttribute(e, Id.udaStandalone))
+ return 0;
+
+ if (auto sharedCtor = scd.isSharedStaticCtorDeclaration())
{
- void badObjectDotD()
- {
- ObjectNotFound(cldec.loc, cldec.ident);
- }
+ auto trust = sharedCtor.type.isTypeFunction().trust;
+ if (trust != TRUST.system && trust != TRUST.trusted)
+ error(e.loc, "a module constructor using `@%s` must be `@system` or `@trusted`", Id.udaStandalone.toChars());
+ sharedCtor.standalone = true;
+ }
+ else
+ .error(e.loc, "`@%s` can only be used on shared static constructors", Id.udaStandalone.toChars());
- if (!cldec.object || cldec.object.errors)
- badObjectDotD();
+ return 1;
+ });
+ }
- Type t = cldec.object.type;
- t = t.typeSemantic(cldec.loc, sc).toBasetype();
- if (t.ty == Terror)
- badObjectDotD();
- TypeClass tc = t.isTypeClass();
- assert(tc);
+ override void visit(StaticDtorDeclaration sdd)
+ {
+ visitStaticCDtorDeclaration(sdd, true);
+ }
- auto b = new BaseClass(tc);
- cldec.baseclasses.shift(b);
+ override void visit(InvariantDeclaration invd)
+ {
+ if (invd.semanticRun >= PASS.semanticdone)
+ return;
+ if (invd._scope)
+ {
+ sc = invd._scope;
+ invd._scope = null;
+ }
- cldec.baseClass = tc.sym;
- assert(!cldec.baseClass.isInterfaceDeclaration());
- b.sym = cldec.baseClass;
- }
- if (cldec.baseClass)
- {
- if (cldec.baseClass.storage_class & STC.final_)
- .error(cldec.loc, "%s `%s` cannot inherit from class `%s` because it is `final`", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
+ invd.parent = sc.parent;
+ Dsymbol p = invd.parent.pastMixin();
+ AggregateDeclaration ad = p.isAggregateDeclaration();
+ if (!ad)
+ {
+ error(invd.loc, "`invariant` can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
+ invd.type = Type.terror;
+ invd.errors = true;
+ return;
+ }
+ if (invd.ident != Id.classInvariant &&
+ invd.semanticRun < PASS.semantic &&
+ !ad.isUnionDeclaration() // users are on their own with union fields
+ )
+ {
+ invd.fixupInvariantIdent(ad.invs.length);
+ ad.invs.push(invd);
+ }
+ if (!invd.type)
+ invd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, invd.storage_class);
- // Inherit properties from base class
- if (cldec.baseClass.isCOMclass())
- cldec.com = true;
- if (cldec.baseClass.isCPPclass())
- cldec.classKind = ClassKind.cpp;
- if (cldec.classKind != cldec.baseClass.classKind)
- .error(cldec.loc, "%s `%s` with %s linkage cannot inherit from class `%s` with %s linkage", cldec.kind, cldec.toPrettyChars,
- ClassKindToChars(cldec.classKind), cldec.baseClass.toChars(), ClassKindToChars(cldec.baseClass.classKind));
+ sc = sc.push();
+ sc.stc &= ~STC.static_; // not a static invariant
+ sc.stc |= STC.const_; // invariant() is always const
+ sc.contract = Contract.invariant_;
+ sc.linkage = LINK.d;
+
+ funcDeclarationSemantic(sc, invd);
+
+ sc.pop();
+ }
+
+ override void visit(UnitTestDeclaration utd)
+ {
+ if (utd.semanticRun >= PASS.semanticdone)
+ return;
+ if (utd._scope)
+ {
+ sc = utd._scope;
+ utd._scope = null;
+ }
+
+ utd.visibility = sc.visibility;
+
+ utd.parent = sc.parent;
+ Dsymbol p = utd.parent.pastMixin();
+ if (!p.isScopeDsymbol())
+ {
+ error(utd.loc, "`unittest` can only be a member of module/aggregate/template, not %s `%s`", p.kind(), p.toChars());
+ utd.type = Type.terror;
+ utd.errors = true;
+ return;
+ }
- if (cldec.baseClass.stack)
- cldec.stack = true;
- cldec.enclosing = cldec.baseClass.enclosing;
- cldec.storage_class |= cldec.baseClass.storage_class & STC.TYPECTOR;
- }
+ if (global.params.useUnitTests)
+ {
+ if (!utd.type)
+ utd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, utd.storage_class);
+ Scope* sc2 = sc.push();
+ sc2.linkage = LINK.d;
+ funcDeclarationSemantic(sc, utd);
+ sc2.pop();
+ }
- cldec.interfaces = cldec.baseclasses.tdata()[(cldec.baseClass ? 1 : 0) .. cldec.baseclasses.length];
- foreach (b; cldec.interfaces)
+ version (none)
+ {
+ // We're going to need ModuleInfo even if the unit tests are not
+ // compiled in, because other modules may import this module and refer
+ // to this ModuleInfo.
+ // (This doesn't make sense to me?)
+ Module m = utd.getModule();
+ if (!m)
+ m = sc._module;
+ if (m)
{
- // If this is an interface, and it derives from a COM interface,
- // then this is a COM interface too.
- if (b.sym.isCOMinterface())
- cldec.com = true;
- if (cldec.classKind == ClassKind.cpp && !b.sym.isCPPinterface())
- {
- .error(cldec.loc, "C++ class `%s` cannot implement D interface `%s`",
- cldec.toPrettyChars(), b.sym.toPrettyChars());
- }
+ //printf("module3 %s needs moduleinfo\n", m.toChars());
+ m.needmoduleinfo = 1;
}
- interfaceSemantic(cldec);
}
- Lancestorsdone:
- //printf("\tClassDeclaration.dsymbolSemantic(%s) baseok = %d\n", toChars(), baseok);
+ }
- if (!cldec.members) // if opaque declaration
- {
- cldec.semanticRun = PASS.semanticdone;
+ override void visit(NewDeclaration nd)
+ {
+ //printf("NewDeclaration::semantic()\n");
+ if (nd.semanticRun >= PASS.semanticdone)
return;
- }
- if (!cldec.symtab)
- {
- cldec.symtab = new DsymbolTable();
+ if (!nd.type)
+ nd.type = new TypeFunction(ParameterList(), Type.tvoid.pointerTo(), LINK.d, nd.storage_class);
- /* https://issues.dlang.org/show_bug.cgi?id=12152
- * The semantic analysis of base classes should be finished
- * before the members semantic analysis of this class, in order to determine
- * vtbl in this class. However if a base class refers the member of this class,
- * it can be resolved as a normal forward reference.
- * Call addMember() and setScope() to make this class members visible from the base classes.
- */
- cldec.members.foreachDsymbol( s => s.addMember(sc, cldec) );
+ funcDeclarationSemantic(sc, nd);
+ }
- auto sc2 = cldec.newScope(sc);
+ override void visit(StructDeclaration sd)
+ {
+ enum log = false;
+ if (log) printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
- /* Set scope so if there are forward references, we still might be able to
- * resolve individual members like enums.
- */
- cldec.members.foreachDsymbol( s => s.setScope(sc2) );
+ //static int count; if (++count == 20) assert(0);
- sc2.pop();
+ if (sd.semanticRun >= PASS.semanticdone)
+ return;
+ const errors = global.errors;
+
+ //printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
+ Scope* scx = null;
+ if (sd._scope)
+ {
+ sc = sd._scope;
+ scx = sd._scope; // save so we don't make redundant copies
+ sd._scope = null;
}
- for (size_t i = 0; i < cldec.baseclasses.length; i++)
+ if (!sd.parent)
{
- BaseClass* b = (*cldec.baseclasses)[i];
- Type tb = b.type.toBasetype();
- TypeClass tc = tb.isTypeClass();
- if (tc.sym.semanticRun < PASS.semanticdone)
+ assert(sc.parent && sc.func);
+ sd.parent = sc.parent;
+ }
+ assert(sd.parent && !sd.isAnonymous());
+
+ if (sd.errors)
+ sd.type = Type.terror;
+ if (sd.semanticRun == PASS.initial)
+ sd.type = sd.type.addSTC(sc.stc | sd.storage_class);
+ sd.type = sd.type.typeSemantic(sd.loc, sc);
+ auto ts = sd.type.isTypeStruct();
+ if (ts)
+ {
+ if (ts.sym != sd)
{
- // Forward referencee of one or more bases, try again later
- if (tc.sym._scope)
- Module.addDeferredSemantic(tc.sym);
- //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars());
- return deferDsymbolSemantic(sc, cldec, scx);
+ TemplateInstance ti = ts.sym.isInstantiated();
+ if (ti && isError(ti))
+ ts.sym = sd;
+ /* For C modules, if module A contains `struct S;` and
+ * module B contains `struct S { members...}` then replace
+ * the former with the latter
+ */
+ else if (!ts.sym.members && sd.members)
+ ts.sym = sd;
}
}
- if (cldec.baseok == Baseok.done)
+ // Ungag errors when not speculative
+ Ungag ungag = sd.ungagSpeculative();
+
+ if (sd.semanticRun == PASS.initial)
{
- cldec.baseok = Baseok.semanticdone;
- objc.setMetaclass(cldec, sc);
+ sd.visibility = sc.visibility;
- // initialize vtbl
- if (cldec.baseClass)
- {
- if (cldec.classKind == ClassKind.cpp && cldec.baseClass.vtbl.length == 0)
- {
- .error(cldec.loc, "%s `%s` C++ base class `%s` needs at least one virtual function", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
- }
+ if (sd.alignment.isUnknown()) // can be set already by `struct __declspec(align(N)) Tag { ... }`
+ sd.alignment = sc.alignment();
- // Copy vtbl[] from base class
- assert(cldec.vtbl.length == 0);
- cldec.vtbl.setDim(cldec.baseClass.vtbl.length);
- memcpy(cldec.vtbl.tdata(), cldec.baseClass.vtbl.tdata(), (void*).sizeof * cldec.vtbl.length);
+ sd.storage_class |= sc.stc;
+ if (sd.storage_class & STC.abstract_)
+ .error(sd.loc, "%s `%s` structs, unions cannot be `abstract`", sd.kind, sd.toPrettyChars);
- cldec.vthis = cldec.baseClass.vthis;
- cldec.vthis2 = cldec.baseClass.vthis2;
- }
- else
- {
- // No base class, so this is the root of the class hierarchy
- cldec.vtbl.setDim(0);
- if (cldec.vtblOffset())
- cldec.vtbl.push(cldec); // leave room for classinfo as first member
- }
+ sd.userAttribDecl = sc.userAttribDecl;
- /* If this is a nested class, add the hidden 'this'
- * member which is a pointer to the enclosing scope.
- */
- if (cldec.vthis) // if inheriting from nested class
- {
- // Use the base class's 'this' member
- if (cldec.storage_class & STC.static_)
- .error(cldec.loc, "%s `%s` static class cannot inherit from nested class `%s`", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
- if (cldec.toParentLocal() != cldec.baseClass.toParentLocal() &&
- (!cldec.toParentLocal() ||
- !cldec.baseClass.toParentLocal().getType() ||
- !cldec.baseClass.toParentLocal().getType().isBaseOf(cldec.toParentLocal().getType(), null)))
- {
- if (cldec.toParentLocal())
- {
- .error(cldec.loc, "%s `%s` is nested within `%s`, but super class `%s` is nested within `%s`", cldec.kind, cldec.toPrettyChars,
- cldec.toParentLocal().toChars(),
- cldec.baseClass.toChars(),
- cldec.baseClass.toParentLocal().toChars());
- }
- else
- {
- .error(cldec.loc, "%s `%s` is not nested, but super class `%s` is nested within `%s`", cldec.kind, cldec.toPrettyChars,
- cldec.baseClass.toChars(),
- cldec.baseClass.toParentLocal().toChars());
- }
- }
- if (cldec.vthis2)
- {
- if (cldec.toParent2() != cldec.baseClass.toParent2() &&
- (!cldec.toParent2() ||
- !cldec.baseClass.toParent2().getType() ||
- !cldec.baseClass.toParent2().getType().isBaseOf(cldec.toParent2().getType(), null)))
- {
- if (cldec.toParent2() && cldec.toParent2() != cldec.toParentLocal())
- {
- .error(cldec.loc, "%s `%s` needs the frame pointer of `%s`, but super class `%s` needs the frame pointer of `%s`", cldec.kind, cldec.toPrettyChars,
- cldec.toParent2().toChars(),
- cldec.baseClass.toChars(),
- cldec.baseClass.toParent2().toChars());
- }
- else
- {
- .error(cldec.loc, "%s `%s` doesn't need a frame pointer, but super class `%s` needs the frame pointer of `%s`", cldec.kind, cldec.toPrettyChars,
- cldec.baseClass.toChars(),
- cldec.baseClass.toParent2().toChars());
- }
- }
- }
- else
- cldec.makeNested2();
- }
- else
- cldec.makeNested();
+ if (sc.linkage == LINK.cpp)
+ sd.classKind = ClassKind.cpp;
+ else if (sc.linkage == LINK.c)
+ sd.classKind = ClassKind.c;
+ sd.cppnamespace = sc.namespace;
+ sd.cppmangle = sc.cppmangle;
}
+ else if (sd.symtab && !scx)
+ return;
- auto sc2 = cldec.newScope(sc);
+ sd.semanticRun = PASS.semantic;
+ checkGNUABITag(sd, sc.linkage);
- cldec.members.foreachDsymbol( s => s.importAll(sc2) );
+ if (!sd.members) // if opaque declaration
+ {
+ if (log) printf("\topaque declaration %s\n", sd.toChars());
+ sd.semanticRun = PASS.semanticdone;
+ return;
+ }
+ if (!sd.symtab)
+ {
+ sd.symtab = new DsymbolTable();
- // Note that members.length can grow due to tuple expansion during semantic()
- cldec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
+ sd.members.foreachDsymbol( s => s.addMember(sc, sd) );
+ }
+
+ auto sc2 = sd.newScope(sc);
+
+ /* Set scope so if there are forward references, we still might be able to
+ * resolve individual members like enums.
+ */
+ sd.members.foreachDsymbol( s => s.setScope(sc2) );
+ sd.members.foreachDsymbol( s => s.importAll(sc2) );
+ sd.members.foreachDsymbol( (s) { s.dsymbolSemantic(sc2); if (sd.errors) s.errors = true; } );
+
+ if (sd.errors)
+ sd.type = Type.terror;
+
+ if (!sd.determineFields())
+ {
+ if (sd.type.ty != Terror)
+ {
+ .error(sd.loc, "%s `%s` circular or forward reference", sd.kind, sd.toPrettyChars);
+ sd.errors = true;
+ sd.type = Type.terror;
+ }
- if (!cldec.determineFields())
- {
- assert(cldec.type == Type.terror);
sc2.pop();
+ sd.semanticRun = PASS.semanticdone;
return;
}
/* Following special member functions creation needs semantic analysis
- * completion of sub-structs in each field types.
+ * completion of sub-structs in each field types. For example, buildDtor
+ * needs to check existence of elaborate dtor in type of each fields.
+ * See the case in compilable/test14838.d
*/
- foreach (v; cldec.fields)
+ foreach (v; sd.fields)
{
Type tb = v.type.baseElemOf();
if (tb.ty != Tstruct)
continue;
- auto sd = (cast(TypeStruct)tb).sym;
- if (sd.semanticRun >= PASS.semanticdone)
+ auto sdec = (cast(TypeStruct)tb).sym;
+ if (sdec.semanticRun >= PASS.semanticdone)
continue;
sc2.pop();
- //printf("\tdeferring %s\n", toChars());
- return deferDsymbolSemantic(sc, cldec, scx);
+ if (log) printf("\tdeferring %s\n", sd.toChars());
+ return deferDsymbolSemantic(sc, sd, scx);
}
/* Look for special member functions.
- * They must be in this class, not in a base class.
*/
- // Can be in base class
- cldec.disableNew = cldec.search(Loc.initial, Id.classNew) !is null;
+ sd.disableNew = sd.search(Loc.initial, Id.classNew) !is null;
// Look for the constructor
- cldec.ctor = cldec.searchCtor();
+ sd.ctor = sd.searchCtor();
- if (!cldec.ctor && cldec.noDefaultCtor)
+ buildDtors(sd, sc2);
+
+ bool hasCopyCtor;
+ bool hasMoveCtor;
+ bool needCopyCtor;
+ bool needMoveCtor;
+ needCopyOrMoveCtor(sd, hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor);
+ //printf("%s hasCopy %d hasMove %d needCopy %d needMove %d\n", sd.toChars(), hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor);
+
+ /* When generating a move ctor, generate a copy ctor too, otherwise
+ * https://github.com/s-ludwig/taggedalgebraic/issues/75
+ */
+ if (0 && needMoveCtor && !hasCopyCtor)
{
- // A class object is always created by constructor, so this check is legitimate.
- foreach (v; cldec.fields)
+ needCopyCtor = true;
+ }
+
+ if (needCopyCtor)
+ {
+ assert(hasCopyCtor == false);
+ buildCopyOrMoveCtor(sd, sc2, false); // build copy constructor
+ hasCopyCtor = true;
+ }
+ if (needMoveCtor)
+ {
+ assert(hasMoveCtor == false);
+ buildCopyOrMoveCtor(sd, sc2, true); // build move constructor
+ hasMoveCtor = true;
+ }
+ sd.hasCopyCtor = hasCopyCtor;
+ sd.hasMoveCtor = hasMoveCtor;
+
+ sd.postblit = buildPostBlit(sd, sc2);
+
+ buildOpAssign(sd, sc2);
+ buildOpEquals(sd, sc2);
+
+ if (!sc2.inCfile &&
+ global.params.useTypeInfo && Type.dtypeinfo) // these functions are used for TypeInfo
+ {
+ sd.xeq = buildXopEquals(sd, sc2);
+ sd.xcmp = buildXopCmp(sd, sc2);
+ sd.xhash = buildXtoHash(sd, sc2);
+ }
+
+ sd.inv = buildInv(sd, sc2);
+
+ sd.rtInfoScope = sc2;
+ sc2.setNoFree();
+
+ sd.semanticRun = PASS.semanticdone;
+ if (log) printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
+
+ sc2.pop();
+
+ if (sd.ctor)
+ {
+ Dsymbol scall = sd.search(Loc.initial, Id.opCall);
+ if (scall)
{
- if (v.storage_class & STC.nodefaultctor)
- error(v.loc, "field `%s` must be initialized in constructor", v.toChars());
+ const xerrors = global.startGagging();
+ sc = sc.push();
+ sc.tinst = null;
+ sc.minst = null;
+ auto fcall = resolveFuncCall(sd.loc, sc, scall, null, null, ArgumentList(), FuncResolveFlag.quiet);
+ sc = sc.pop();
+ global.endGagging(xerrors);
+
+ if (fcall && fcall.isStatic())
+ {
+ .error(fcall.loc, "%s `%s` `static opCall` is hidden by constructors and can never be called", sd.kind, sd.toPrettyChars);
+ errorSupplemental(fcall.loc, "Please use a factory method instead, or replace all constructors with `static opCall`.");
+ }
}
}
- // If this class has no constructor, but base class has a default
- // ctor, create a constructor:
- // this() { }
- if (!cldec.ctor && cldec.baseClass && cldec.baseClass.ctor)
+ if (ts && ts.sym != sd)
{
- auto fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type, ArgumentList(), FuncResolveFlag.quiet);
- if (!fd) // try shared base ctor instead
- fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type.sharedOf, ArgumentList(), FuncResolveFlag.quiet);
- if (fd && !fd.errors)
+ StructDeclaration sym = ts.sym;
+ if (sd.isCsymbol() && sym.isCsymbol())
{
- //printf("Creating default this(){} for class %s\n", toChars());
- auto btf = fd.type.toTypeFunction();
- auto tf = new TypeFunction(ParameterList(), null, LINK.d, fd.storage_class);
- tf.mod = btf.mod;
- // Don't copy @safe, ... from the base class constructor and let it be inferred instead
- // This is required if other lowerings add code to the generated constructor which
- // is less strict (e.g. `preview=dtorfields` might introduce a call to a less qualified dtor)
- auto ctor = new CtorDeclaration(cldec.loc, Loc.initial, STC.none, tf);
- ctor.storage_class |= STC.inference | (fd.storage_class & STC.scope_);
- ctor.isGenerated = true;
- ctor.fbody = new CompoundStatement(Loc.initial, new Statements());
+ if (!isCCompatible(sd, sym))
+ {
+ // Already issued an error.
+ errorSupplemental(sd.loc, "C %ss with the same name from different imports are merged", sd.kind);
+ }
+ else {
+ /* This is two structs imported from different C files.
+ * Just ignore sd, the second one. The first one will always
+ * be found when going through the type.
+ */
+ }
+ }
+ else
+ {
+ version (none)
+ {
+ printf("this = %p %s\n", sd, sd.toChars());
+ printf("type = %d sym = %p, %s\n", sd.type.ty, sym, sym.toPrettyChars());
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=19024
+ .error(sd.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", sd.kind, sd.toPrettyChars, sym.loc.toChars());
+ }
+ }
- cldec.members.push(ctor);
- ctor.addMember(sc, cldec);
- ctor.dsymbolSemantic(sc2);
+ if (global.errors != errors)
+ {
+ // The type is no good.
+ sd.type = Type.terror;
+ sd.errors = true;
+ if (sd.deferred)
+ sd.deferred.errors = true;
+ }
- cldec.ctor = ctor;
- cldec.defaultCtor = ctor;
+ if (sd.deferred && !global.gag)
+ {
+ sd.deferred.semantic2(sc);
+ sd.deferred.semantic3(sc);
+ }
+
+ version (none)
+ {
+ // @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
+ // Deprecated in 2.100
+ // Make an error in 2.110
+ if (sd.storage_class & STC.scope_)
+ deprecation(sd.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site.");
+ }
+ //printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
+ }
+
+ //
+ // Checks if two structs are compatible
+ // Implements the rules according to C23 6.2.7
+ //
+ static bool isCCompatible(StructDeclaration a, StructDeclaration b)
+ {
+ // Get the name of a type, while avoiding exposing "__tagXXX" anonymous structs
+ static const(char)* typeName(Type t)
+ {
+ if (TypeStruct ts = t.isTypeStruct())
+ {
+ if (ts.sym.ident.toString().startsWith("__tag"))
+ return ts.sym.isUnionDeclaration() ? "(anonymous union)".ptr: "(anonymous struct)".ptr;
}
- else
+ return t.toChars();
+ }
+
+ void incompatError()
+ {
+ .error(a.loc, "%s `%s` already exists with an incompatible definition.",
+ a.kind, typeName(a.type));
+ errorSupplemental(b.loc, "previously declared here");
+ }
+
+
+ // For recursive calls into unnamed structs (so Type.equals() doesn't work).
+ static bool isCCompatibleUnnamedStruct(Type a, Type b)
+ {
+ TypeStruct ats = a.isTypeStruct();
+ if (!ats) return false;
+ TypeStruct bts = b.isTypeStruct();
+ if (!bts) return false;
+ // Hack, anonymous structs within a struct are given
+ // an anonymous id starting with __tag.
+ if (!ats.sym.ident.toString().startsWith("__tag"))
+ return false;
+ if (!bts.sym.ident.toString().startsWith("__tag"))
+ return false;
+ return isCCompatible(ats.sym, bts.sym);
+ }
+
+ if (a.fields.length != b.fields.length)
+ {
+ incompatError();
+ errorSupplemental(a.loc, "`%s` has %zu field(s) while `%s` has %zu field(s)",
+ a.toPrettyChars(), a.fields.length, b.toPrettyChars(), b.fields.length);
+ return false;
+ }
+ // both are structs or both are unions
+ if ((a.isUnionDeclaration() is null) != (b.isUnionDeclaration() is null))
+ {
+ incompatError();
+ errorSupplemental(a.loc, "`%s` is a %s while `%s` is a %s",
+ a.toPrettyChars(), a.kind, b.toPrettyChars(), b.kind);
+ return false;
+ }
+ if (a.alignment != b.alignment)
+ {
+ incompatError();
+ errorSupplemental(a.loc, "`%s` has different alignment or packing", a.toPrettyChars());
+ if (a.alignment.isDefault() && ! b.alignment.isDefault())
{
- .error(cldec.loc, "%s `%s` cannot implicitly generate a default constructor when base class `%s` is missing a default constructor", cldec.kind, cldec.toPrettyChars,
- cldec.baseClass.toPrettyChars());
+ errorSupplemental(a.loc, "`%s` alignment: default", a.toPrettyChars());
+ errorSupplemental(b.loc, "`%s` alignment: %u",
+ b.toPrettyChars(), cast(uint)b.alignment.get());
+ }
+ else if (!a.alignment.isDefault() && b.alignment.isDefault())
+ {
+ errorSupplemental(a.loc, "`%s` alignment: %u",
+ a.toPrettyChars(), cast(uint)a.alignment.get());
+ errorSupplemental(b.loc, "`%s` alignment: default",
+ b.toPrettyChars());
+ }
+ else if (a.alignment.get() != b.alignment.get())
+ {
+ errorSupplemental(a.loc, "`%s` alignment: %u",
+ a.toPrettyChars(), cast(uint)a.alignment.get());
+ errorSupplemental(b.loc, "`%s` alignment: %u",
+ b.toPrettyChars(), cast(uint)b.alignment.get());
+ }
+ if (a.alignment.isPack() != b.alignment.isPack())
+ {
+ errorSupplemental(a.loc, "`%s` packed: %s",
+ a.toPrettyChars(), a.alignment.isPack()?"true".ptr:"false".ptr);
+ errorSupplemental(b.loc, "`%s` packed: %s",
+ b.toPrettyChars(), b.alignment.isPack()?"true".ptr:"false".ptr);
}
+ return false;
}
-
- buildDtors(cldec, sc2);
-
- if (cldec.classKind == ClassKind.cpp && cldec.cppDtorVtblIndex != -1)
+ foreach (size_t i, VarDeclaration a_field; a.fields[])
{
- // now we've built the aggregate destructor, we'll make it virtual and assign it to the reserved vtable slot
- cldec.dtor.vtblIndex = cldec.cppDtorVtblIndex;
- cldec.vtbl[cldec.cppDtorVtblIndex] = cldec.dtor;
+ VarDeclaration b_field = b.fields[i];
+ //
+ // — there shall be a one-to-one correspondence between
+ // their members such that each pair of corresponding
+ // members are declared with compatible types;
+ //
+ if (!a_field.type.equals(b_field.type) && !isCCompatibleUnnamedStruct(a_field.type, b_field.type))
+ {
+ // Already errored, just bail
+ incompatError();
+ if (a_field.type.isTypeError()) return false;
+ if (b_field.type.isTypeError()) return false;
- if (target.cpp.twoDtorInVtable)
+ errorSupplemental(a_field.loc, "Field %zu differs in type", i);
+ errorSupplemental(a_field.loc, "typeof(%s): %s",
+ a_field.toChars(), typeName(a_field.type));
+ errorSupplemental(b_field.loc, "typeof(%s): %s",
+ b_field.toChars(), typeName(b_field.type));
+ return false;
+ }
+ //
+ // — if one member of the pair is declared with an
+ // alignment specifier, the second is declared with an
+ // equivalent alignment specifier;
+ //
+ if (a_field.alignment != b_field.alignment)
{
- // TODO: create a C++ compatible deleting destructor (call out to `operator delete`)
- // for the moment, we'll call the non-deleting destructor and leak
- cldec.vtbl[cldec.cppDtorVtblIndex + 1] = cldec.dtor;
+ incompatError();
+ errorSupplemental(a_field.loc, "Field %zu differs in alignment or packing", i);
+ if (a_field.alignment.isDefault() && ! b_field.alignment.isDefault())
+ {
+ errorSupplemental(a_field.loc, "`%s.%s` alignment: default",
+ a.toPrettyChars(),a_field.toChars());
+ errorSupplemental(b_field.loc, "`%s.%s` alignment: %u",
+ b.toPrettyChars(), b_field.toChars(), cast(uint)b_field.alignment.get());
+ }
+ else if (!a_field.alignment.isDefault() && b_field.alignment.isDefault())
+ {
+ errorSupplemental(a_field.loc, "`%s.%s` alignment: %u",
+ a.toPrettyChars(), a_field.toChars(), cast(uint)a_field.alignment.get());
+ errorSupplemental(b_field.loc, "`%s.%s` alignment: default",
+ b.toPrettyChars(), b_field.toChars());
+ }
+ else if (a_field.alignment.get() != b_field.alignment.get())
+ {
+ errorSupplemental(a_field.loc, "`%s.%s` alignment: %u",
+ a.toPrettyChars(), a_field.toChars(),
+ cast(uint)a_field.alignment.get());
+ errorSupplemental(b_field.loc, "`%s.%s` alignment: %u",
+ b.toPrettyChars(), b_field.toChars(),
+ cast(uint)b_field.alignment.get());
+ }
+ if (a_field.alignment.isPack() != b_field.alignment.isPack())
+ {
+ errorSupplemental(a_field.loc, "`%s.%s` packed: %s",
+ a.toPrettyChars(), a_field.toChars(),
+ a_field.alignment.isPack()?"true".ptr:"false".ptr);
+ errorSupplemental(b_field.loc, "`%s.%s` packed: %s",
+ b.toPrettyChars(), b_field.toChars(),
+ b_field.alignment.isPack()?"true".ptr:"false".ptr);
+ }
+ return false;
}
- }
-
- if (auto f = hasIdentityOpAssign(cldec, sc2))
- {
- if (!(f.storage_class & STC.disable))
- .error(f.loc, "%s `%s` identity assignment operator overload is illegal", cldec.kind, cldec.toPrettyChars);
- }
-
- cldec.inv = buildInv(cldec, sc2);
-
- cldec.semanticRun = PASS.semanticdone;
- //printf("-ClassDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
-
- sc2.pop();
-
- /* isAbstract() is undecidable in some cases because of circular dependencies.
- * Now that semantic is finished, get a definitive result, and error if it is not the same.
- */
- if (cldec.isabstract != ThreeState.none) // if evaluated it before completion
- {
- const isabstractsave = cldec.isabstract;
- cldec.isabstract = ThreeState.none;
- cldec.isAbstract(); // recalculate
- if (cldec.isabstract != isabstractsave)
+ //
+ // - and, if one member of the pair is declared with a
+ // name, the second is declared with the same name.
+ //
+ if (a_field.ident.isAnonymous())
{
- .error(cldec.loc, "%s `%s` cannot infer `abstract` attribute due to circular dependencies", cldec.kind, cldec.toPrettyChars);
+ if (!b_field.ident.isAnonymous())
+ {
+ incompatError();
+ errorSupplemental(a_field.loc, "Field %zu differs in name", i);
+ errorSupplemental(a_field.loc, "(anonymous)", a_field.ident.toChars());
+ errorSupplemental(b_field.loc, "%s", b_field.ident.toChars());
+ return false;
+ }
}
- }
-
- if (cldec.type.ty == Tclass && (cast(TypeClass)cldec.type).sym != cldec)
- {
- // https://issues.dlang.org/show_bug.cgi?id=17492
- ClassDeclaration cd = (cast(TypeClass)cldec.type).sym;
- version (none)
+ else if (b_field.ident.isAnonymous())
{
- printf("this = %p %s\n", cldec, cldec.toPrettyChars());
- printf("type = %d sym = %p, %s\n", cldec.type.ty, cd, cd.toPrettyChars());
+ incompatError();
+ errorSupplemental(a_field.loc, "Field %zu differs in name", i);
+ errorSupplemental(a_field.loc, "%s", a_field.ident.toChars());
+ errorSupplemental(b_field.loc, "(anonymous)");
+ return false;
+ }
+ else if (a_field.ident != b_field.ident)
+ {
+ incompatError();
+ errorSupplemental(a_field.loc, "Field %zu differs in name", i);
+ errorSupplemental(a_field.loc, "%s", a_field.ident.toChars());
+ errorSupplemental(b_field.loc, "%s", b_field.ident.toChars());
+ return false;
}
- .error(cldec.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", cldec.kind, cldec.toPrettyChars, cd.loc.toChars());
- }
-
- if (global.errors != errors || (cldec.baseClass && cldec.baseClass.errors))
- {
- // The type is no good, but we should keep the
- // the type so that we have more accurate error messages
- // See: https://issues.dlang.org/show_bug.cgi?id=23552
- cldec.errors = true;
- if (cldec.deferred)
- cldec.deferred.errors = true;
- }
- // Verify fields of a synchronized class are not public
- if (cldec.storage_class & STC.synchronized_)
- {
- foreach (vd; cldec.fields)
+ //
+ // For two structures or unions, corresponding bitfields shall have the same widths.
+ //
+ BitFieldDeclaration bfa = a_field.isBitFieldDeclaration();
+ BitFieldDeclaration bfb = b_field.isBitFieldDeclaration();
+ if ((bfa is null) != (bfb is null))
{
- if (!vd.isThisDeclaration() &&
- vd.visible() >= Visibility(Visibility.Kind.public_))
+ incompatError();
+ errorSupplemental(a_field.loc, "Field %zu differs in being a bitfield", i);
+ if (bfa is null)
{
- .error(vd.loc, "%s `%s` Field members of a `synchronized` class cannot be `%s`", vd.kind, vd.toPrettyChars,
- visibilityToChars(vd.visible().kind));
+ errorSupplemental(a_field.loc, "`%s.%s` is not a bitfield",
+ a.toPrettyChars(), a_field.toChars());
+ errorSupplemental(b_field.loc, "`%s.%s` is a bitfield",
+ b.toPrettyChars(), b_field.toChars());
+ }
+ else if (bfb is null)
+ {
+ errorSupplemental(a_field.loc, "`%s.%s` *is a bitfield",
+ a.toPrettyChars(), a_field.toChars());
+ errorSupplemental(b_field.loc, "`%s.%s` is not a bitfield",
+ b.toPrettyChars(), b_field.toChars());
+ }
+ return false;
+ }
+ if (bfa !is null && bfb !is null)
+ {
+ if (bfa.fieldWidth != bfb.fieldWidth)
+ {
+ incompatError();
+ errorSupplemental(a_field.loc, "Field %zu differs in bitfield width", i);
+ errorSupplemental(a_field.loc, "`%s.%s`: %u",
+ a.toPrettyChars(), a_field.toChars(), bfa.fieldWidth);
+ errorSupplemental(b_field.loc, "`%s.%s`: %u",
+ b.toPrettyChars(), b_field.toChars(), bfb.fieldWidth);
+ return false;
}
}
}
+ return true;
+ }
- if (cldec.deferred && !global.gag)
- {
- cldec.deferred.semantic2(sc);
- cldec.deferred.semantic3(sc);
- }
- //printf("-ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this);
-
- version (none)
+ void interfaceSemantic(ClassDeclaration cd)
+ {
+ cd.vtblInterfaces = new BaseClasses();
+ cd.vtblInterfaces.reserve(cd.interfaces.length);
+ foreach (b; cd.interfaces)
{
- // @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
- // Deprecated in 2.100
- // Make an error in 2.110
- // Don't forget to remove code at https://github.com/dlang/dmd/blob/b2f8274ba76358607fc3297a1e9f361480f9bcf9/src/dmd/dsymbolsem.d#L1032-L1036
- if (cldec.storage_class & STC.scope_)
- deprecation(cldec.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site.");
+ cd.vtblInterfaces.push(b);
+ b.copyBaseInterfaces(cd.vtblInterfaces);
}
}
- override void visit(InterfaceDeclaration idec)
+ override void visit(ClassDeclaration cldec)
{
- /// Returns: `true` is this is an anonymous Objective-C metaclass
- static bool isAnonymousMetaclass(InterfaceDeclaration idec)
- {
- return idec.classKind == ClassKind.objc &&
- idec.objc.isMeta &&
- idec.isAnonymous;
- }
+ //printf("ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", cldec.toChars(), cldec.type, cldec.sizeok, this);
+ //printf("\tparent = %p, '%s'\n", sc.parent, sc.parent ? sc.parent.toChars() : "");
+ //printf("sc.stc = %x\n", sc.stc);
- //printf("InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
- if (idec.semanticRun >= PASS.semanticdone)
+ //{ static int n; if (++n == 20) *(char*)0=0; }
+
+ if (cldec.semanticRun >= PASS.semanticdone)
return;
const errors = global.errors;
- //printf("+InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
+ //printf("+ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this);
Scope* scx = null;
- if (idec._scope)
- {
- sc = idec._scope;
- scx = idec._scope; // save so we don't make redundant copies
- idec._scope = null;
- }
-
- if (!idec.parent)
+ if (cldec._scope)
{
- assert(sc.parent && sc.func);
- idec.parent = sc.parent;
+ sc = cldec._scope;
+ scx = cldec._scope; // save so we don't make redundant copies
+ cldec._scope = null;
}
- // Objective-C metaclasses are anonymous
- assert(idec.parent && !idec.isAnonymous || isAnonymousMetaclass(idec));
-
- if (idec.errors)
- idec.type = Type.terror;
- idec.type = idec.type.typeSemantic(idec.loc, sc);
- if (idec.type.ty == Tclass && (cast(TypeClass)idec.type).sym != idec)
+
+ if (!cldec.parent)
{
- auto ti = (cast(TypeClass)idec.type).sym.isInstantiated();
- if (ti && isError(ti))
- (cast(TypeClass)idec.type).sym = idec;
+ assert(sc.parent);
+ cldec.parent = sc.parent;
}
+ if (cldec.errors)
+ cldec.type = Type.terror;
+ if (cldec.semanticRun == PASS.initial)
+ cldec.type = cldec.type.addSTC(sc.stc | cldec.storage_class);
+ cldec.type = cldec.type.typeSemantic(cldec.loc, sc);
+ if (auto tc = cldec.type.isTypeClass())
+ if (tc.sym != cldec)
+ {
+ auto ti = tc.sym.isInstantiated();
+ if (ti && isError(ti))
+ tc.sym = cldec;
+ }
+
// Ungag errors when not speculative
- Ungag ungag = idec.ungagSpeculative();
+ Ungag ungag = cldec.ungagSpeculative();
- if (idec.semanticRun == PASS.initial)
+ if (cldec.semanticRun == PASS.initial)
{
- idec.visibility = sc.visibility;
+ cldec.visibility = sc.visibility;
- idec.storage_class |= sc.stc;
- idec.userAttribDecl = sc.userAttribDecl;
+ cldec.storage_class |= sc.stc;
+ if (cldec.storage_class & STC.auto_)
+ .error(cldec.loc, "%s `%s` storage class `auto` is invalid when declaring a class, did you mean to use `scope`?", cldec.kind, cldec.toPrettyChars);
+ if (cldec.storage_class & STC.scope_)
+ cldec.stack = true;
+ if (cldec.storage_class & STC.abstract_)
+ cldec.isabstract = ThreeState.yes;
+
+ cldec.userAttribDecl = sc.userAttribDecl;
+
+ if (sc.linkage == LINK.cpp)
+ cldec.classKind = ClassKind.cpp;
+ cldec.cppnamespace = sc.namespace;
+ cldec.cppmangle = sc.cppmangle;
+ if (sc.linkage == LINK.objc)
+ objc.setObjc(cldec);
}
- else if (idec.symtab)
+ else if (cldec.symtab && !scx)
{
- if (idec.sizeok == Sizeok.done || !scx)
- {
- idec.semanticRun = PASS.semanticdone;
- return;
- }
+ return;
}
- idec.semanticRun = PASS.semantic;
+ cldec.rtInfoScope = sc;
+ sc.setNoFree();
- if (idec.baseok < Baseok.done)
+ cldec.semanticRun = PASS.semantic;
+ checkGNUABITag(cldec, sc.linkage);
+ checkMustUseReserved(cldec);
+
+ if (cldec.baseok < Baseok.done)
{
+ /* https://issues.dlang.org/show_bug.cgi?id=12078
+ * https://issues.dlang.org/show_bug.cgi?id=12143
+ * https://issues.dlang.org/show_bug.cgi?id=15733
+ * While resolving base classes and interfaces, a base may refer
+ * the member of this derived class. In that time, if all bases of
+ * this class can be determined, we can go forward the semantc process
+ * beyond the Lancestorsdone. To do the recursive semantic analysis,
+ * temporarily set and unset `_scope` around exp().
+ */
T resolveBase(T)(lazy T exp)
{
if (!scx)
}
static if (!is(T == void))
{
- idec._scope = scx;
+ cldec._scope = scx;
auto r = exp();
- idec._scope = null;
+ cldec._scope = null;
return r;
}
else
{
- idec._scope = scx;
+ cldec._scope = scx;
exp();
- idec._scope = null;
+ cldec._scope = null;
}
}
- idec.baseok = Baseok.start;
+ cldec.baseok = Baseok.start;
// Expand any tuples in baseclasses[]
- for (size_t i = 0; i < idec.baseclasses.length;)
+ for (size_t i = 0; i < cldec.baseclasses.length;)
{
- auto b = (*idec.baseclasses)[i];
- b.type = resolveBase(b.type.typeSemantic(idec.loc, sc));
+ auto b = (*cldec.baseclasses)[i];
+ b.type = resolveBase(b.type.typeSemantic(cldec.loc, sc));
Type tb = b.type.toBasetype();
if (auto tup = tb.isTypeTuple())
{
- idec.baseclasses.remove(i);
+ cldec.baseclasses.remove(i);
size_t dim = Parameter.dim(tup.arguments);
for (size_t j = 0; j < dim; j++)
{
Parameter arg = Parameter.getNth(tup.arguments, j);
b = new BaseClass(arg.type);
- idec.baseclasses.insert(i + j, b);
+ cldec.baseclasses.insert(i + j, b);
}
}
else
i++;
}
- if (idec.baseok >= Baseok.done)
+ if (cldec.baseok >= Baseok.done)
{
//printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun);
- if (idec.semanticRun >= PASS.semanticdone)
+ if (cldec.semanticRun >= PASS.semanticdone)
return;
goto Lancestorsdone;
}
- if (!idec.baseclasses.length && sc.linkage == LINK.cpp)
- idec.classKind = ClassKind.cpp;
- idec.cppnamespace = sc.namespace;
- checkGNUABITag(idec, sc.linkage);
- checkMustUseReserved(idec);
+ // See if there's a base class as first in baseclasses[]
+ if (cldec.baseclasses.length)
+ {
+ BaseClass* b = (*cldec.baseclasses)[0];
+ Type tb = b.type.toBasetype();
+ TypeClass tc = tb.isTypeClass();
+ if (!tc)
+ {
+ if (b.type != Type.terror)
+ .error(cldec.loc, "%s `%s` base type must be `class` or `interface`, not `%s`", cldec.kind, cldec.toPrettyChars, b.type.toChars());
+ cldec.baseclasses.remove(0);
+ goto L7;
+ }
+ if (tc.sym.isDeprecated())
+ {
+ if (!cldec.isDeprecated())
+ {
+ // Deriving from deprecated class makes this one deprecated too
+ cldec.setDeprecated();
+ tc.checkDeprecated(cldec.loc, sc);
+ }
+ }
+ if (tc.sym.isInterfaceDeclaration())
+ goto L7;
- if (sc.linkage == LINK.objc)
- objc.setObjc(idec);
+ for (ClassDeclaration cdb = tc.sym; cdb; cdb = cdb.baseClass)
+ {
+ if (cdb == cldec)
+ {
+ .error(cldec.loc, "%s `%s` circular inheritance", cldec.kind, cldec.toPrettyChars);
+ cldec.baseclasses.remove(0);
+ goto L7;
+ }
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=11034
+ * Class inheritance hierarchy
+ * and instance size of each classes are orthogonal information.
+ * Therefore, even if tc.sym.sizeof == Sizeok.none,
+ * we need to set baseClass field for class covariance check.
+ */
+ cldec.baseClass = tc.sym;
+ b.sym = cldec.baseClass;
+
+ if (tc.sym.baseok < Baseok.done)
+ resolveBase(tc.sym.dsymbolSemantic(null)); // Try to resolve forward reference
+ if (tc.sym.baseok < Baseok.done)
+ {
+ //printf("\ttry later, forward reference of base class %s\n", tc.sym.toChars());
+ if (tc.sym._scope)
+ addDeferredSemantic(tc.sym);
+ cldec.baseok = Baseok.none;
+ }
+ L7:
+ }
+ // Treat the remaining entries in baseclasses as interfaces
// Check for errors, handle forward references
+ int multiClassError = cldec.baseClass is null ? 0 : 1;
+
BCLoop:
- for (size_t i = 0; i < idec.baseclasses.length;)
+ for (size_t i = (cldec.baseClass ? 1 : 0); i < cldec.baseclasses.length;)
{
- BaseClass* b = (*idec.baseclasses)[i];
+ BaseClass* b = (*cldec.baseclasses)[i];
Type tb = b.type.toBasetype();
- TypeClass tc = (tb.ty == Tclass) ? cast(TypeClass)tb : null;
+ TypeClass tc = tb.isTypeClass();
if (!tc || !tc.sym.isInterfaceDeclaration())
{
- if (b.type != Type.terror)
- .error(idec.loc, "%s `%s` base type must be `interface`, not `%s`", idec.kind, idec.toPrettyChars, b.type.toChars());
- idec.baseclasses.remove(i);
+ // It's a class
+ if (tc)
+ {
+ if (multiClassError == 0)
+ {
+ .error(cldec.loc,"`%s`: base class must be specified first, " ~
+ "before any interfaces.", cldec.toPrettyChars());
+ multiClassError += 1;
+ }
+ else if (multiClassError >= 1)
+ {
+ if(multiClassError == 1)
+ .error(cldec.loc, "`%s`: multiple class inheritance is not supported." ~
+ " Use multiple interface inheritance and/or composition.", cldec.toPrettyChars());
+ multiClassError += 1;
+
+ if (tc.sym.fields.length)
+ errorSupplemental(cldec.loc,"`%s` has fields, consider making it a member of `%s`",
+ b.type.toChars(), cldec.type.toChars());
+ else
+ errorSupplemental(cldec.loc,"`%s` has no fields, consider making it an `interface`",
+ b.type.toChars());
+ }
+ }
+ // It's something else: e.g. `int` in `class Foo : Bar, int { ... }`
+ else if (b.type != Type.terror)
+ {
+ error(cldec.loc,"`%s`: base type must be `interface`, not `%s`",
+ cldec.toPrettyChars(), b.type.toChars());
+ }
+ cldec.baseclasses.remove(i);
continue;
}
// Check for duplicate interfaces
- for (size_t j = 0; j < i; j++)
+ for (size_t j = (cldec.baseClass ? 1 : 0); j < i; j++)
{
- BaseClass* b2 = (*idec.baseclasses)[j];
+ BaseClass* b2 = (*cldec.baseclasses)[j];
if (b2.sym == tc.sym)
{
- .error(idec.loc, "%s `%s` inherits from duplicate interface `%s`", idec.kind, idec.toPrettyChars, b2.sym.toChars());
- idec.baseclasses.remove(i);
+ .error(cldec.loc, "%s `%s` inherits from duplicate interface `%s`", cldec.kind, cldec.toPrettyChars, b2.sym.toChars());
+ cldec.baseclasses.remove(i);
continue BCLoop;
}
}
- if (tc.sym == idec || idec.isBaseOf2(tc.sym))
- {
- .error(idec.loc, "%s `%s` circular inheritance of interface", idec.kind, idec.toPrettyChars);
- idec.baseclasses.remove(i);
- continue;
- }
if (tc.sym.isDeprecated())
{
- if (!idec.isDeprecated())
+ if (!cldec.isDeprecated())
{
- // Deriving from deprecated interface makes this one deprecated too
- idec.setDeprecated();
- tc.checkDeprecated(idec.loc, sc);
+ // Deriving from deprecated class makes this one deprecated too
+ cldec.setDeprecated();
+ tc.checkDeprecated(cldec.loc, sc);
}
}
{
//printf("\ttry later, forward reference of base %s\n", tc.sym.toChars());
if (tc.sym._scope)
- Module.addDeferredSemantic(tc.sym);
- idec.baseok = Baseok.none;
+ addDeferredSemantic(tc.sym);
+ cldec.baseok = Baseok.none;
}
i++;
}
- if (idec.baseok == Baseok.none)
+ if (cldec.baseok == Baseok.none)
+ {
+ // Forward referencee of one or more bases, try again later
+ //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars());
+ return deferDsymbolSemantic(sc, cldec, scx);
+ }
+ cldec.baseok = Baseok.done;
+
+ if (cldec.classKind == ClassKind.objc || (cldec.baseClass && cldec.baseClass.classKind == ClassKind.objc))
+ cldec.classKind = ClassKind.objc; // Objective-C classes do not inherit from Object
+
+ // If no base class, and this is not an Object, use Object as base class
+ if (!cldec.baseClass && cldec.ident != Id.Object && cldec.object && cldec.classKind == ClassKind.d)
+ {
+ void badObjectDotD()
+ {
+ ObjectNotFound(cldec.loc, cldec.ident);
+ }
+
+ if (!cldec.object || cldec.object.errors)
+ badObjectDotD();
+
+ Type t = cldec.object.type;
+ t = t.typeSemantic(cldec.loc, sc).toBasetype();
+ if (t.ty == Terror)
+ badObjectDotD();
+ TypeClass tc = t.isTypeClass();
+ assert(tc);
+
+ auto b = new BaseClass(tc);
+ cldec.baseclasses.shift(b);
+
+ cldec.baseClass = tc.sym;
+ assert(!cldec.baseClass.isInterfaceDeclaration());
+ b.sym = cldec.baseClass;
+ }
+ if (cldec.baseClass)
{
- // Forward referencee of one or more bases, try again later
- return deferDsymbolSemantic(sc, idec, scx);
+ if (cldec.baseClass.storage_class & STC.final_)
+ .error(cldec.loc, "%s `%s` cannot inherit from class `%s` because it is `final`", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
+
+ // Inherit properties from base class
+ if (cldec.baseClass.isCOMclass())
+ cldec.com = true;
+ if (cldec.baseClass.isCPPclass())
+ cldec.classKind = ClassKind.cpp;
+ if (cldec.classKind != cldec.baseClass.classKind)
+ .error(cldec.loc, "%s `%s` with %s linkage cannot inherit from class `%s` with %s linkage", cldec.kind, cldec.toPrettyChars,
+ ClassKindToChars(cldec.classKind), cldec.baseClass.toChars(), ClassKindToChars(cldec.baseClass.classKind));
+
+ if (cldec.baseClass.stack)
+ cldec.stack = true;
+ cldec.enclosing = cldec.baseClass.enclosing;
+ cldec.storage_class |= cldec.baseClass.storage_class & STC.TYPECTOR;
}
- idec.baseok = Baseok.done;
- idec.interfaces = idec.baseclasses.tdata()[0 .. idec.baseclasses.length];
- foreach (b; idec.interfaces)
+ cldec.interfaces = cldec.baseclasses.tdata()[(cldec.baseClass ? 1 : 0) .. cldec.baseclasses.length];
+ foreach (b; cldec.interfaces)
{
// If this is an interface, and it derives from a COM interface,
// then this is a COM interface too.
if (b.sym.isCOMinterface())
- idec.com = true;
- if (b.sym.isCPPinterface())
- idec.classKind = ClassKind.cpp;
+ cldec.com = true;
+ if (cldec.classKind == ClassKind.cpp && !b.sym.isCPPinterface())
+ {
+ .error(cldec.loc, "C++ class `%s` cannot implement D interface `%s`",
+ cldec.toPrettyChars(), b.sym.toPrettyChars());
+ }
}
-
- interfaceSemantic(idec);
+ interfaceSemantic(cldec);
}
Lancestorsdone:
+ //printf("\tClassDeclaration.dsymbolSemantic(%s) baseok = %d\n", toChars(), baseok);
- if (!idec.members) // if opaque declaration
+ if (!cldec.members) // if opaque declaration
{
- idec.semanticRun = PASS.semanticdone;
+ cldec.semanticRun = PASS.semanticdone;
return;
}
- if (!idec.symtab)
- idec.symtab = new DsymbolTable();
+ if (!cldec.symtab)
+ {
+ cldec.symtab = new DsymbolTable();
- for (size_t i = 0; i < idec.baseclasses.length; i++)
+ /* https://issues.dlang.org/show_bug.cgi?id=12152
+ * The semantic analysis of base classes should be finished
+ * before the members semantic analysis of this class, in order to determine
+ * vtbl in this class. However if a base class refers the member of this class,
+ * it can be resolved as a normal forward reference.
+ * Call addMember() and setScope() to make this class members visible from the base classes.
+ */
+ cldec.members.foreachDsymbol( s => s.addMember(sc, cldec) );
+
+ auto sc2 = cldec.newScope(sc);
+
+ /* Set scope so if there are forward references, we still might be able to
+ * resolve individual members like enums.
+ */
+ cldec.members.foreachDsymbol( s => s.setScope(sc2) );
+
+ sc2.pop();
+ }
+
+ for (size_t i = 0; i < cldec.baseclasses.length; i++)
{
- BaseClass* b = (*idec.baseclasses)[i];
+ BaseClass* b = (*cldec.baseclasses)[i];
Type tb = b.type.toBasetype();
TypeClass tc = tb.isTypeClass();
if (tc.sym.semanticRun < PASS.semanticdone)
{
// Forward referencee of one or more bases, try again later
if (tc.sym._scope)
- Module.addDeferredSemantic(tc.sym);
- return deferDsymbolSemantic(sc, idec, scx);
+ addDeferredSemantic(tc.sym);
+ //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars());
+ return deferDsymbolSemantic(sc, cldec, scx);
}
}
- if (idec.baseok == Baseok.done)
+ if (cldec.baseok == Baseok.done)
{
- idec.baseok = Baseok.semanticdone;
- objc.setMetaclass(idec, sc);
+ cldec.baseok = Baseok.semanticdone;
+ objc.setMetaclass(cldec, sc);
// initialize vtbl
- if (idec.vtblOffset())
- idec.vtbl.push(idec); // leave room at vtbl[0] for classinfo
-
- // Cat together the vtbl[]'s from base interfaces
- foreach (i, b; idec.interfaces)
+ if (cldec.baseClass)
{
- // Skip if b has already appeared
- for (size_t k = 0; k < i; k++)
+ if (cldec.classKind == ClassKind.cpp && cldec.baseClass.vtbl.length == 0)
{
- if (b == idec.interfaces[k])
- goto Lcontinue;
+ .error(cldec.loc, "%s `%s` C++ base class `%s` needs at least one virtual function", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
}
// Copy vtbl[] from base class
- if (b.sym.vtblOffset())
+ assert(cldec.vtbl.length == 0);
+ cldec.vtbl.setDim(cldec.baseClass.vtbl.length);
+ memcpy(cldec.vtbl.tdata(), cldec.baseClass.vtbl.tdata(), (void*).sizeof * cldec.vtbl.length);
+
+ cldec.vthis = cldec.baseClass.vthis;
+ cldec.vthis2 = cldec.baseClass.vthis2;
+ }
+ else
+ {
+ // No base class, so this is the root of the class hierarchy
+ cldec.vtbl.setDim(0);
+ if (cldec.vtblOffset())
+ cldec.vtbl.push(cldec); // leave room for classinfo as first member
+ }
+
+ /* If this is a nested class, add the hidden 'this'
+ * member which is a pointer to the enclosing scope.
+ */
+ if (cldec.vthis) // if inheriting from nested class
+ {
+ // Use the base class's 'this' member
+ if (cldec.storage_class & STC.static_)
+ .error(cldec.loc, "%s `%s` static class cannot inherit from nested class `%s`", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
+ if (cldec.toParentLocal() != cldec.baseClass.toParentLocal() &&
+ (!cldec.toParentLocal() ||
+ !cldec.baseClass.toParentLocal().getType() ||
+ !cldec.baseClass.toParentLocal().getType().isBaseOf(cldec.toParentLocal().getType(), null)))
{
- size_t d = b.sym.vtbl.length;
- if (d > 1)
+ if (cldec.toParentLocal())
{
- idec.vtbl.pushSlice(b.sym.vtbl[1 .. d]);
+ .error(cldec.loc, "%s `%s` is nested within `%s`, but super class `%s` is nested within `%s`", cldec.kind, cldec.toPrettyChars,
+ cldec.toParentLocal().toChars(),
+ cldec.baseClass.toChars(),
+ cldec.baseClass.toParentLocal().toChars());
+ }
+ else
+ {
+ .error(cldec.loc, "%s `%s` is not nested, but super class `%s` is nested within `%s`", cldec.kind, cldec.toPrettyChars,
+ cldec.baseClass.toChars(),
+ cldec.baseClass.toParentLocal().toChars());
}
}
- else
+ if (cldec.vthis2)
{
- idec.vtbl.append(&b.sym.vtbl);
+ if (cldec.toParent2() != cldec.baseClass.toParent2() &&
+ (!cldec.toParent2() ||
+ !cldec.baseClass.toParent2().getType() ||
+ !cldec.baseClass.toParent2().getType().isBaseOf(cldec.toParent2().getType(), null)))
+ {
+ if (cldec.toParent2() && cldec.toParent2() != cldec.toParentLocal())
+ {
+ .error(cldec.loc, "%s `%s` needs the frame pointer of `%s`, but super class `%s` needs the frame pointer of `%s`", cldec.kind, cldec.toPrettyChars,
+ cldec.toParent2().toChars(),
+ cldec.baseClass.toChars(),
+ cldec.baseClass.toParent2().toChars());
+ }
+ else
+ {
+ .error(cldec.loc, "%s `%s` doesn't need a frame pointer, but super class `%s` needs the frame pointer of `%s`", cldec.kind, cldec.toPrettyChars,
+ cldec.baseClass.toChars(),
+ cldec.baseClass.toParent2().toChars());
+ }
+ }
}
-
- Lcontinue:
+ else
+ cldec.makeNested2();
}
+ else
+ cldec.makeNested();
}
- idec.members.foreachDsymbol( s => s.addMember(sc, idec) );
-
- auto sc2 = idec.newScope(sc);
-
- /* Set scope so if there are forward references, we still might be able to
- * resolve individual members like enums.
- */
- idec.members.foreachDsymbol( s => s.setScope(sc2) );
-
- idec.members.foreachDsymbol( s => s.importAll(sc2) );
-
- idec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
-
- idec.semanticRun = PASS.semanticdone;
- //printf("-InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
+ auto sc2 = cldec.newScope(sc);
- sc2.pop();
+ cldec.members.foreachDsymbol( s => s.importAll(sc2) );
- if (global.errors != errors)
- {
- // The type is no good.
- idec.type = Type.terror;
- }
+ // Note that members.length can grow due to tuple expansion during semantic()
+ cldec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
- version (none)
+ if (!cldec.determineFields())
{
- if (type.ty == Tclass && (cast(TypeClass)idec.type).sym != idec)
- {
- printf("this = %p %s\n", idec, idec.toChars());
- printf("type = %d sym = %p\n", idec.type.ty, (cast(TypeClass)idec.type).sym);
- }
+ assert(cldec.type == Type.terror);
+ sc2.pop();
+ return;
}
- assert(idec.type.ty != Tclass || (cast(TypeClass)idec.type).sym == idec);
-
- version (none)
+ /* Following special member functions creation needs semantic analysis
+ * completion of sub-structs in each field types.
+ */
+ foreach (v; cldec.fields)
{
- // @@@DEPRECATED_2.120@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
- // Deprecated in 2.087
- // Made an error in 2.100, but removal depends on `scope class` being removed too
- // Don't forget to remove code at https://github.com/dlang/dmd/blob/b2f8274ba76358607fc3297a1e9f361480f9bcf9/src/dmd/dsymbolsem.d#L1032-L1036
- if (idec.storage_class & STC.scope_)
- error(idec.loc, "`scope` as a type constraint is obsolete. Use `scope` at the usage site.");
- }
- }
-}
-
-/*
-Adds dsym as a member of scope sds.
-
-Params:
- dsym = dsymbol to inserted
- sc = scope where the dsymbol is declared
- sds = ScopeDsymbol where dsym is inserted
-*/
-void addMember(Dsymbol dsym, Scope* sc, ScopeDsymbol sds)
-{
- auto addMemberVisitor = new AddMemberVisitor(sc, sds);
- dsym.accept(addMemberVisitor);
-}
+ Type tb = v.type.baseElemOf();
+ if (tb.ty != Tstruct)
+ continue;
+ auto sd = (cast(TypeStruct)tb).sym;
+ if (sd.semanticRun >= PASS.semanticdone)
+ continue;
-private void attribAddMember(AttribDeclaration atb, Scope* sc, ScopeDsymbol sds)
-{
- Dsymbols* d = atb.include(sc);
- if (d)
- {
- Scope* sc2 = atb.newScope(sc);
- d.foreachDsymbol( s => s.addMember(sc2, sds) );
- if (sc2 != sc)
sc2.pop();
- }
-}
-
-/****************************************************
- * Declare parameters of template instance, initialize them with the
- * template instance arguments.
- */
-private void declareParameters(TemplateInstance ti, Scope* sc)
-{
- TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration();
- assert(tempdecl);
-
- //printf("TemplateInstance.declareParameters()\n");
- foreach (i, o; ti.tdtypes) // initializer for tp
- {
- TemplateParameter tp = (*tempdecl.parameters)[i];
- //printf("\ttdtypes[%d] = %p\n", i, o);
- declareParameter(tempdecl, sc, tp, o);
- }
-}
-private extern(C++) class AddMemberVisitor : Visitor
-{
- alias visit = Visitor.visit;
- Scope* sc;
- ScopeDsymbol sds;
+ //printf("\tdeferring %s\n", toChars());
+ return deferDsymbolSemantic(sc, cldec, scx);
+ }
- this(Scope* sc, ScopeDsymbol sds) @safe
- {
- this.sc = sc;
- this.sds = sds;
- }
+ /* Look for special member functions.
+ * They must be in this class, not in a base class.
+ */
+ // Can be in base class
+ cldec.disableNew = cldec.search(Loc.initial, Id.classNew) !is null;
- override void visit(Dsymbol dsym)
- {
- //printf("Dsymbol::addMember('%s')\n", toChars());
- //printf("Dsymbol::addMember(this = %p, '%s' scopesym = '%s')\n", this, toChars(), sds.toChars());
- //printf("Dsymbol::addMember(this = %p, '%s' sds = %p, sds.symtab = %p)\n", this, toChars(), sds, sds.symtab);
- dsym.parent = sds;
- if (dsym.isAnonymous()) // no name, so can't add it to symbol table
- return;
+ // Look for the constructor
+ cldec.ctor = cldec.searchCtor();
- if (!sds.symtabInsert(dsym)) // if name is already defined
+ if (!cldec.ctor && cldec.noDefaultCtor)
{
- if (dsym.isAliasDeclaration() && !dsym._scope)
- dsym.setScope(sc);
- Dsymbol s2 = sds.symtabLookup(dsym, dsym.ident);
- /* https://issues.dlang.org/show_bug.cgi?id=17434
- *
- * If we are trying to add an import to the symbol table
- * that has already been introduced, then keep the one with
- * larger visibility. This is fine for imports because if
- * we have multiple imports of the same file, if a single one
- * is public then the symbol is reachable.
- */
- if (auto i1 = dsym.isImport())
+ // A class object is always created by constructor, so this check is legitimate.
+ foreach (v; cldec.fields)
{
- if (auto i2 = s2.isImport())
- {
- if (sc.explicitVisibility && sc.visibility > i2.visibility)
- sds.symtab.update(dsym);
- }
+ if (v.storage_class & STC.nodefaultctor)
+ error(v.loc, "field `%s` must be initialized in constructor", v.toChars());
}
+ }
- // If using C tag/prototype/forward declaration rules
- if (sc && sc.inCfile && !dsym.isImport())
- // When merging master, replace with: if (sc && sc.inCfile && !dsym.isImport())
+ // If this class has no constructor, but base class has a default
+ // ctor, create a constructor:
+ // this() { }
+ if (!cldec.ctor && cldec.baseClass && cldec.baseClass.ctor)
+ {
+ auto fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type, ArgumentList(), FuncResolveFlag.quiet);
+ if (!fd) // try shared base ctor instead
+ fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type.sharedOf, ArgumentList(), FuncResolveFlag.quiet);
+ if (fd && !fd.errors)
{
- if (handleTagSymbols(*sc, dsym, s2, sds))
- return;
- if (handleSymbolRedeclarations(*sc, dsym, s2, sds))
- return;
+ //printf("Creating default this(){} for class %s\n", toChars());
+ auto btf = fd.type.toTypeFunction();
+ auto tf = new TypeFunction(ParameterList(), null, LINK.d, fd.storage_class);
+ tf.mod = btf.mod;
+ // Don't copy @safe, ... from the base class constructor and let it be inferred instead
+ // This is required if other lowerings add code to the generated constructor which
+ // is less strict (e.g. `preview=dtorfields` might introduce a call to a less qualified dtor)
- sds.multiplyDefined(Loc.initial, dsym, s2); // ImportC doesn't allow overloading
- dsym.errors = true;
- return;
- }
+ auto ctor = new CtorDeclaration(cldec.loc, Loc.initial, STC.none, tf);
+ ctor.storage_class |= STC.inference | (fd.storage_class & STC.scope_);
+ ctor.isGenerated = true;
+ ctor.fbody = new CompoundStatement(Loc.initial, new Statements());
- if (!s2.overloadInsert(dsym))
+ cldec.members.push(ctor);
+ ctor.addMember(sc, cldec);
+ ctor.dsymbolSemantic(sc2);
+
+ cldec.ctor = ctor;
+ cldec.defaultCtor = ctor;
+ }
+ else
{
- sds.multiplyDefined(Loc.initial, dsym, s2);
- dsym.errors = true;
+ .error(cldec.loc, "%s `%s` cannot implicitly generate a default constructor when base class `%s` is missing a default constructor", cldec.kind, cldec.toPrettyChars,
+ cldec.baseClass.toPrettyChars());
}
}
- if (sds.isAggregateDeclaration() || sds.isEnumDeclaration())
+
+ buildDtors(cldec, sc2);
+
+ if (cldec.classKind == ClassKind.cpp && cldec.cppDtorVtblIndex != -1)
{
- if (dsym.ident == Id.__sizeof ||
- !(sc && sc.inCfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof))
+ // now we've built the aggregate destructor, we'll make it virtual and assign it to the reserved vtable slot
+ cldec.dtor.vtblIndex = cldec.cppDtorVtblIndex;
+ cldec.vtbl[cldec.cppDtorVtblIndex] = cldec.dtor;
+
+ if (target.cpp.twoDtorInVtable)
{
- .error(dsym.loc, "%s `%s` `.%s` property cannot be redefined", dsym.kind, dsym.toPrettyChars, dsym.ident.toChars());
- dsym.errors = true;
+ // TODO: create a C++ compatible deleting destructor (call out to `operator delete`)
+ // for the moment, we'll call the non-deleting destructor and leak
+ cldec.vtbl[cldec.cppDtorVtblIndex + 1] = cldec.dtor;
}
}
- }
+ if (auto f = hasIdentityOpAssign(cldec, sc2))
+ {
+ if (!(f.storage_class & STC.disable))
+ .error(f.loc, "%s `%s` identity assignment operator overload is illegal", cldec.kind, cldec.toPrettyChars);
+ }
- override void visit(StaticAssert _)
- {
- // we didn't add anything
- }
+ cldec.inv = buildInv(cldec, sc2);
- /*****************************
- * Add import to sd's symbol table.
- */
- override void visit(Import imp)
- {
- //printf("Import.addMember(this=%s, sds=%s, sc=%p)\n", imp.toChars(), sds.toChars(), sc);
- if (imp.names.length == 0)
- return visit(cast(Dsymbol)imp);
- if (imp.aliasId)
- visit(cast(Dsymbol)imp);
+ cldec.semanticRun = PASS.semanticdone;
+ //printf("-ClassDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
- /* Instead of adding the import to sds's symbol table,
- * add each of the alias=name pairs
+ sc2.pop();
+
+ /* isAbstract() is undecidable in some cases because of circular dependencies.
+ * Now that semantic is finished, get a definitive result, and error if it is not the same.
*/
- for (size_t i = 0; i < imp.names.length; i++)
+ if (cldec.isabstract != ThreeState.none) // if evaluated it before completion
{
- Identifier name = imp.names[i];
- Identifier _alias = imp.aliases[i];
- if (!_alias)
- _alias = name;
- auto tname = new TypeIdentifier(imp.loc, name);
- auto ad = new AliasDeclaration(imp.loc, _alias, tname);
- ad._import = imp;
- addMember(ad, sc, sds);
- imp.aliasdecls.push(ad);
+ const isabstractsave = cldec.isabstract;
+ cldec.isabstract = ThreeState.none;
+ cldec.isAbstract(); // recalculate
+ if (cldec.isabstract != isabstractsave)
+ {
+ .error(cldec.loc, "%s `%s` cannot infer `abstract` attribute due to circular dependencies", cldec.kind, cldec.toPrettyChars);
+ }
}
- }
-
- override void visit(AttribDeclaration atb)
- {
- attribAddMember(atb, sc, sds);
- }
- override void visit(StorageClassDeclaration stcd)
- {
- Dsymbols* d = stcd.include(sc);
- if (d)
+ if (cldec.type.ty == Tclass && (cast(TypeClass)cldec.type).sym != cldec)
{
- Scope* sc2 = stcd.newScope(sc);
-
- d.foreachDsymbol( (s)
+ // https://issues.dlang.org/show_bug.cgi?id=17492
+ ClassDeclaration cd = (cast(TypeClass)cldec.type).sym;
+ version (none)
{
- //printf("\taddMember %s to %s\n", s.toChars(), sds.toChars());
- // STC.local needs to be attached before the member is added to the scope (because it influences the parent symbol)
- if (auto decl = s.isDeclaration())
- {
- decl.storage_class |= stcd.stc & STC.local;
- if (auto sdecl = s.isStorageClassDeclaration()) // TODO: why is this not enough to deal with the nested case?
- {
- sdecl.stc |= stcd.stc & STC.local;
- }
- }
- s.addMember(sc2, sds);
- });
-
- if (sc2 != sc)
- sc2.pop();
+ printf("this = %p %s\n", cldec, cldec.toPrettyChars());
+ printf("type = %d sym = %p, %s\n", cldec.type.ty, cd, cd.toPrettyChars());
+ }
+ .error(cldec.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", cldec.kind, cldec.toPrettyChars, cd.loc.toChars());
}
- }
- override void visit(VisibilityDeclaration visd)
- {
- if (visd.pkg_identifiers)
+ if (global.errors != errors || (cldec.baseClass && cldec.baseClass.errors))
{
- Dsymbol tmp;
- Package.resolve(visd.pkg_identifiers, &tmp, null);
- visd.visibility.pkg = tmp ? tmp.isPackage() : null;
- visd.pkg_identifiers = null;
+ // The type is no good, but we should keep the
+ // the type so that we have more accurate error messages
+ // See: https://issues.dlang.org/show_bug.cgi?id=23552
+ cldec.errors = true;
+ if (cldec.deferred)
+ cldec.deferred.errors = true;
}
- if (visd.visibility.kind == Visibility.Kind.package_ && visd.visibility.pkg && sc._module)
- {
- Module m = sc._module;
- // https://issues.dlang.org/show_bug.cgi?id=17441
- // While isAncestorPackageOf does an equality check, the fix for the issue adds a check to see if
- // each package's .isModule() properites are equal.
- //
- // Properties generated from `package(foo)` i.e. visibility.pkg have .isModule() == null.
- // This breaks package declarations of the package in question if they are declared in
- // the same package.d file, which _do_ have a module associated with them, and hence a non-null
- // isModule()
- if (!m.isPackage() || !visd.visibility.pkg.ident.equals(m.isPackage().ident))
+ // Verify fields of a synchronized class are not public
+ if (cldec.storage_class & STC.synchronized_)
+ {
+ foreach (vd; cldec.fields)
{
- Package pkg = m.parent ? m.parent.isPackage() : null;
- if (!pkg || !visd.visibility.pkg.isAncestorPackageOf(pkg))
- .error(visd.loc, "%s `%s` does not bind to one of ancestor packages of module `%s`", visd.kind(), visd.toPrettyChars(false), m.toPrettyChars(true));
+ if (!vd.isThisDeclaration() &&
+ vd.visible() >= Visibility(Visibility.Kind.public_))
+ {
+ .error(vd.loc, "%s `%s` Field members of a `synchronized` class cannot be `%s`", vd.kind, vd.toPrettyChars,
+ visibilityToChars(vd.visible().kind));
+ }
}
}
- attribAddMember(visd, sc, sds);
- }
- override void visit(StaticIfDeclaration sid)
- {
- //printf("StaticIfDeclaration::addMember() '%s'\n", sid.toChars());
- /* This is deferred until the condition evaluated later (by the include() call),
- * so that expressions in the condition can refer to declarations
- * in the same scope, such as:
- *
- * template Foo(int i)
- * {
- * const int j = i + 1;
- * static if (j == 3)
- * const int k;
- * }
- */
- sid.scopesym = sds;
+ if (cldec.deferred && !global.gag)
+ {
+ cldec.deferred.semantic2(sc);
+ cldec.deferred.semantic3(sc);
+ }
+ //printf("-ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this);
+
+ version (none)
+ {
+ // @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
+ // Deprecated in 2.100
+ // Make an error in 2.110
+ // Don't forget to remove code at https://github.com/dlang/dmd/blob/b2f8274ba76358607fc3297a1e9f361480f9bcf9/src/dmd/dsymbolsem.d#L1032-L1036
+ if (cldec.storage_class & STC.scope_)
+ deprecation(cldec.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site.");
+ }
}
-
- override void visit(StaticForeachDeclaration sfd)
+ override void visit(InterfaceDeclaration idec)
{
- // used only for caching the enclosing symbol
- sfd.scopesym = sds;
- }
+ /// Returns: `true` is this is an anonymous Objective-C metaclass
+ static bool isAnonymousMetaclass(InterfaceDeclaration idec)
+ {
+ return idec.classKind == ClassKind.objc &&
+ idec.objc.isMeta &&
+ idec.isAnonymous;
+ }
- /***************************************
- * Lazily initializes the scope to forward to.
- */
- override void visit(ForwardingAttribDeclaration fad)
- {
- fad.sym.parent = sds;
- sds = fad.sym;
- attribAddMember(fad, sc, fad.sym);
- }
+ //printf("InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
+ if (idec.semanticRun >= PASS.semanticdone)
+ return;
+ const errors = global.errors;
- override void visit(MixinDeclaration md)
- {
- //printf("MixinDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, md.memnum);
- md.scopesym = sds;
- }
+ //printf("+InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
- override void visit(DebugSymbol ds)
- {
- //printf("DebugSymbol::addMember('%s') %s\n", sds.toChars(), ds.toChars());
- Module m = sds.isModule();
- // Do not add the member to the symbol table,
- // just make sure subsequent debug declarations work.
- if (!m)
+ Scope* scx = null;
+ if (idec._scope)
{
- .error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars);
- ds.errors = true;
+ sc = idec._scope;
+ scx = idec._scope; // save so we don't make redundant copies
+ idec._scope = null;
}
- else
+
+ if (!idec.parent)
{
- if (m.debugidsNot && findCondition(*m.debugidsNot, ds.ident))
- {
- .error(ds.loc, "%s `%s` defined after use", ds.kind, ds.toPrettyChars);
- ds.errors = true;
- }
- if (!m.debugids)
- m.debugids = new Identifiers();
- m.debugids.push(ds.ident);
+ assert(sc.parent && sc.func);
+ idec.parent = sc.parent;
}
- }
+ // Objective-C metaclasses are anonymous
+ assert(idec.parent && !idec.isAnonymous || isAnonymousMetaclass(idec));
- override void visit(VersionSymbol vs)
- {
- //printf("VersionSymbol::addMember('%s') %s\n", sds.toChars(), vs.toChars());
- Module m = sds.isModule();
- // Do not add the member to the symbol table,
- // just make sure subsequent debug declarations work.
- VersionCondition.checkReserved(vs.loc, vs.ident.toString());
- if (!m)
+ if (idec.errors)
+ idec.type = Type.terror;
+ idec.type = idec.type.typeSemantic(idec.loc, sc);
+ if (idec.type.ty == Tclass && (cast(TypeClass)idec.type).sym != idec)
{
- .error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars);
- vs.errors = true;
+ auto ti = (cast(TypeClass)idec.type).sym.isInstantiated();
+ if (ti && isError(ti))
+ (cast(TypeClass)idec.type).sym = idec;
}
- else
+
+ // Ungag errors when not speculative
+ Ungag ungag = idec.ungagSpeculative();
+
+ if (idec.semanticRun == PASS.initial)
{
- if (m.versionidsNot && findCondition(*m.versionidsNot, vs.ident))
+ idec.visibility = sc.visibility;
+
+ idec.storage_class |= sc.stc;
+ idec.userAttribDecl = sc.userAttribDecl;
+ }
+ else if (idec.symtab)
+ {
+ if (idec.sizeok == Sizeok.done || !scx)
{
- .error(vs.loc, "%s `%s` defined after use", vs.kind, vs.toPrettyChars);
- vs.errors = true;
+ idec.semanticRun = PASS.semanticdone;
+ return;
}
- if (!m.versionids)
- m.versionids = new Identifiers();
- m.versionids.push(vs.ident);
}
+ idec.semanticRun = PASS.semantic;
- }
-
- override void visit(Nspace ns)
- {
- visit(cast(Dsymbol)ns);
-
- if (ns.members)
+ if (idec.baseok < Baseok.done)
{
- if (!ns.symtab)
- ns.symtab = new DsymbolTable();
- // The namespace becomes 'imported' into the enclosing scope
- for (Scope* sce = sc; 1; sce = sce.enclosing)
+ T resolveBase(T)(lazy T exp)
{
- ScopeDsymbol sds2 = sce.scopesym;
- if (sds2)
+ if (!scx)
{
- sds2.importScope(ns, Visibility(Visibility.Kind.public_));
- break;
+ scx = sc.copy();
+ scx.setNoFree();
+ }
+ static if (!is(T == void))
+ {
+ idec._scope = scx;
+ auto r = exp();
+ idec._scope = null;
+ return r;
+ }
+ else
+ {
+ idec._scope = scx;
+ exp();
+ idec._scope = null;
}
}
- assert(sc);
- sc = sc.push(ns);
- sc.linkage = LINK.cpp; // namespaces default to C++ linkage
- sc.parent = ns;
- ns.members.foreachDsymbol(s => s.addMember(sc, ns));
- sc.pop();
- }
- }
- override void visit(EnumDeclaration ed)
- {
- version (none)
- {
- printf("EnumDeclaration::addMember() %s\n", ed.toChars());
- for (size_t i = 0; i < ed.members.length; i++)
+ idec.baseok = Baseok.start;
+
+ // Expand any tuples in baseclasses[]
+ for (size_t i = 0; i < idec.baseclasses.length;)
{
- EnumMember em = (*ed.members)[i].isEnumMember();
- printf(" member %s\n", em.toChars());
+ auto b = (*idec.baseclasses)[i];
+ b.type = resolveBase(b.type.typeSemantic(idec.loc, sc));
+
+ Type tb = b.type.toBasetype();
+ if (auto tup = tb.isTypeTuple())
+ {
+ idec.baseclasses.remove(i);
+ size_t dim = Parameter.dim(tup.arguments);
+ for (size_t j = 0; j < dim; j++)
+ {
+ Parameter arg = Parameter.getNth(tup.arguments, j);
+ b = new BaseClass(arg.type);
+ idec.baseclasses.insert(i + j, b);
+ }
+ }
+ else
+ i++;
}
- }
- if (!ed.isAnonymous())
- {
- visit(cast(Dsymbol)ed);
- }
- addEnumMembersToSymtab(ed, sc, sds);
- }
-}
+ if (idec.baseok >= Baseok.done)
+ {
+ //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun);
+ if (idec.semanticRun >= PASS.semanticdone)
+ return;
+ goto Lancestorsdone;
+ }
-/*******************************************
- * Add members of EnumDeclaration to the symbol table(s).
- * Params:
- * ed = EnumDeclaration
- * sc = context of `ed`
- * sds = symbol table that `ed` resides in
- */
-void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds)
-{
- const bool isCEnum = sc.inCfile; // it's an ImportC enum
- //printf("addEnumMembersToSymtab(ed: %s added: %d Cfile: %d)\n", ed.toChars(), ed.added, isCEnum);
- if (ed.added)
- return;
- ed.added = true;
+ if (!idec.baseclasses.length && sc.linkage == LINK.cpp)
+ idec.classKind = ClassKind.cpp;
+ idec.cppnamespace = sc.namespace;
+ checkGNUABITag(idec, sc.linkage);
+ checkMustUseReserved(idec);
- if (!ed.members)
- return;
+ if (sc.linkage == LINK.objc)
+ objc.setObjc(idec);
- const bool isAnon = ed.isAnonymous();
+ // Check for errors, handle forward references
+ BCLoop:
+ for (size_t i = 0; i < idec.baseclasses.length;)
+ {
+ BaseClass* b = (*idec.baseclasses)[i];
+ Type tb = b.type.toBasetype();
+ TypeClass tc = (tb.ty == Tclass) ? cast(TypeClass)tb : null;
+ if (!tc || !tc.sym.isInterfaceDeclaration())
+ {
+ if (b.type != Type.terror)
+ .error(idec.loc, "%s `%s` base type must be `interface`, not `%s`", idec.kind, idec.toPrettyChars, b.type.toChars());
+ idec.baseclasses.remove(i);
+ continue;
+ }
- if ((isCEnum || isAnon) && !sds.symtab)
- sds.symtab = new DsymbolTable();
+ // Check for duplicate interfaces
+ for (size_t j = 0; j < i; j++)
+ {
+ BaseClass* b2 = (*idec.baseclasses)[j];
+ if (b2.sym == tc.sym)
+ {
+ .error(idec.loc, "%s `%s` inherits from duplicate interface `%s`", idec.kind, idec.toPrettyChars, b2.sym.toChars());
+ idec.baseclasses.remove(i);
+ continue BCLoop;
+ }
+ }
+ if (tc.sym == idec || idec.isBaseOf2(tc.sym))
+ {
+ .error(idec.loc, "%s `%s` circular inheritance of interface", idec.kind, idec.toPrettyChars);
+ idec.baseclasses.remove(i);
+ continue;
+ }
+ if (tc.sym.isDeprecated())
+ {
+ if (!idec.isDeprecated())
+ {
+ // Deriving from deprecated interface makes this one deprecated too
+ idec.setDeprecated();
+ tc.checkDeprecated(idec.loc, sc);
+ }
+ }
- if ((isCEnum || !isAnon) && !ed.symtab)
- ed.symtab = new DsymbolTable();
+ b.sym = tc.sym;
- ed.members.foreachDsymbol( (s)
- {
- if (EnumMember em = s.isEnumMember())
- {
- //printf("adding EnumMember %s to %s %d\n", em.toChars(), ed.toChars(), isCEnum);
- em.ed = ed;
- if (isCEnum)
+ if (tc.sym.baseok < Baseok.done)
+ resolveBase(tc.sym.dsymbolSemantic(null)); // Try to resolve forward reference
+ if (tc.sym.baseok < Baseok.done)
+ {
+ //printf("\ttry later, forward reference of base %s\n", tc.sym.toChars());
+ if (tc.sym._scope)
+ addDeferredSemantic(tc.sym);
+ idec.baseok = Baseok.none;
+ }
+ i++;
+ }
+ if (idec.baseok == Baseok.none)
{
- /* C doesn't add the enum member to the symbol table of the enum tag, it adds
- * it to the symbol table that the tag is in. This is in contrast to D, where enum
- * members become members of the enum tag. To accommodate this, we add
- * the enum members to both symbol tables.
- */
- em.addMember(sc, ed); // add em to ed's symbol table
- if (em.errors)
- return;
- em.addMember(sc, sds); // add em to symbol table that ed is in
- em.parent = ed; // restore it after previous addMember() changed it
+ // Forward referencee of one or more bases, try again later
+ return deferDsymbolSemantic(sc, idec, scx);
}
- else
+ idec.baseok = Baseok.done;
+
+ idec.interfaces = idec.baseclasses.tdata()[0 .. idec.baseclasses.length];
+ foreach (b; idec.interfaces)
{
- em.addMember(sc, isAnon ? sds : ed);
+ // If this is an interface, and it derives from a COM interface,
+ // then this is a COM interface too.
+ if (b.sym.isCOMinterface())
+ idec.com = true;
+ if (b.sym.isCPPinterface())
+ idec.classKind = ClassKind.cpp;
}
- }
- });
-}
-/******************************************************
- * Verifies if the given Identifier is a DRuntime hook. It uses the hooks
- * defined in `id.d`.
- *
- * Params:
- * id = Identifier to verify
- * Returns:
- * true if `id` is a DRuntime hook
- * false otherwise
- */
-private bool isDRuntimeHook(Identifier id)
-{
- return id == Id._d_HookTraceImpl ||
- id == Id._d_newclassT || id == Id._d_newclassTTrace ||
- id == Id._d_arraycatnTX || id == Id._d_arraycatnTXTrace ||
- id == Id._d_newThrowable || id == Id._d_delThrowable ||
- id == Id._d_arrayassign_l || id == Id._d_arrayassign_r ||
- id == Id._d_arraysetassign || id == Id._d_arraysetctor ||
- id == Id._d_arrayctor ||
- id == Id._d_arraysetlengthT ||
- id == Id._d_arraysetlengthTTrace ||
- id == Id._d_arrayappendT || id == Id._d_arrayappendTTrace ||
- id == Id._d_arrayappendcTX;
-}
+ interfaceSemantic(idec);
+ }
+ Lancestorsdone:
-/*****************************************
- * Append `ti` to the specific module `ti.members[]`
- */
-private Dsymbols* appendToModuleMember(TemplateInstance ti)
-{
- Module mi = ti.minst; // instantiated . inserted module
-
- //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n",
- // toPrettyChars(),
- // enclosing ? enclosing.toPrettyChars() : null,
- // mi ? mi.toPrettyChars() : null);
- if (global.params.allInst || !mi || mi.isRoot())
- {
- /* If the instantiated module is speculative or root, insert to the
- * member of a root module. Then:
- * - semantic3 pass will get called on the instance members.
- * - codegen pass will get a selection chance to do/skip it (needsCodegen()).
- */
- static Dsymbol getStrictEnclosing(TemplateInstance ti)
+ if (!idec.members) // if opaque declaration
{
- do
- {
- if (ti.enclosing)
- return ti.enclosing;
- ti = ti.tempdecl.isInstantiated();
- } while (ti);
- return null;
+ idec.semanticRun = PASS.semanticdone;
+ return;
}
+ if (!idec.symtab)
+ idec.symtab = new DsymbolTable();
- Dsymbol enc = getStrictEnclosing(ti);
- // insert target is made stable by using the module
- // where tempdecl is declared.
- mi = (enc ? enc : ti.tempdecl).getModule();
- if (!mi.isRoot())
+ for (size_t i = 0; i < idec.baseclasses.length; i++)
{
- if (mi.importedFrom)
+ BaseClass* b = (*idec.baseclasses)[i];
+ Type tb = b.type.toBasetype();
+ TypeClass tc = tb.isTypeClass();
+ if (tc.sym.semanticRun < PASS.semanticdone)
{
- mi = mi.importedFrom;
- assert(mi.isRoot());
+ // Forward referencee of one or more bases, try again later
+ if (tc.sym._scope)
+ addDeferredSemantic(tc.sym);
+ return deferDsymbolSemantic(sc, idec, scx);
}
- else
+ }
+
+ if (idec.baseok == Baseok.done)
+ {
+ idec.baseok = Baseok.semanticdone;
+ objc.setMetaclass(idec, sc);
+
+ // initialize vtbl
+ if (idec.vtblOffset())
+ idec.vtbl.push(idec); // leave room at vtbl[0] for classinfo
+
+ // Cat together the vtbl[]'s from base interfaces
+ foreach (i, b; idec.interfaces)
{
- // This can happen when using the frontend as a library.
- // Append it to the non-root module.
+ // Skip if b has already appeared
+ for (size_t k = 0; k < i; k++)
+ {
+ if (b == idec.interfaces[k])
+ goto Lcontinue;
+ }
+
+ // Copy vtbl[] from base class
+ if (b.sym.vtblOffset())
+ {
+ size_t d = b.sym.vtbl.length;
+ if (d > 1)
+ {
+ idec.vtbl.pushSlice(b.sym.vtbl[1 .. d]);
+ }
+ }
+ else
+ {
+ idec.vtbl.append(&b.sym.vtbl);
+ }
+
+ Lcontinue:
}
}
- }
- else
- {
- /* If the instantiated module is non-root, insert to the member of the
- * non-root module. Then:
- * - semantic3 pass won't be called on the instance.
- * - codegen pass won't reach to the instance.
- * Unless it is re-appended to a root module later (with changed minst).
+
+ idec.members.foreachDsymbol( s => s.addMember(sc, idec) );
+
+ auto sc2 = idec.newScope(sc);
+
+ /* Set scope so if there are forward references, we still might be able to
+ * resolve individual members like enums.
*/
- }
- //printf("\t-. mi = %s\n", mi.toPrettyChars());
+ idec.members.foreachDsymbol( s => s.setScope(sc2) );
- if (ti.memberOf) // already appended to some module
- {
- assert(mi.isRoot(), "can only re-append to a root module");
- if (ti.memberOf.isRoot())
- return null; // no need to move to another root module
- }
+ idec.members.foreachDsymbol( s => s.importAll(sc2) );
- Dsymbols* a = mi.members;
- a.push(ti);
- ti.memberOf = mi;
- if (mi.semanticRun >= PASS.semantic2done && mi.isRoot())
- Module.addDeferredSemantic2(ti);
- if (mi.semanticRun >= PASS.semantic3done && mi.isRoot())
- Module.addDeferredSemantic3(ti);
- return a;
-}
+ idec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
-private void expandMembers(TemplateInstance ti,Scope* sc2)
-{
- ti.members.foreachDsymbol( (s) { s.setScope (sc2); } );
+ idec.semanticRun = PASS.semanticdone;
+ //printf("-InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
- ti.members.foreachDsymbol( (s) { s.importAll(sc2); } );
+ sc2.pop();
- if (!ti.aliasdecl)
- {
- /* static if's are crucial to evaluating aliasdecl correctly. But
- * evaluating the if/else bodies may require aliasdecl.
- * So, evaluate the condition for static if's, but not their if/else bodies.
- * Then try to set aliasdecl.
- * Later do the if/else bodies.
- * https://issues.dlang.org/show_bug.cgi?id=23598
- * It might be better to do this by attaching a lambda to the StaticIfDeclaration
- * to do the oneMembers call after the sid.include(sc2) is run as part of dsymbolSemantic().
- */
- bool done;
- void staticIfDg(Dsymbol s)
+ if (global.errors != errors)
{
- if (done || ti.aliasdecl)
- return;
- //printf("\t staticIfDg on '%s %s' in '%s'\n", s.kind(), s.toChars(), this.toChars());
- if (!s.isStaticIfDeclaration())
- {
- //s.dsymbolSemantic(sc2);
- done = true;
- return;
- }
- auto sid = s.isStaticIfDeclaration();
- sid.include(sc2);
- if (ti.members.length)
+ // The type is no good.
+ idec.type = Type.terror;
+ }
+
+ version (none)
+ {
+ if (type.ty == Tclass && (cast(TypeClass)idec.type).sym != idec)
{
- Dsymbol sa;
- if (oneMembers(ti.members, sa, ti.tempdecl.ident) && sa)
- ti.aliasdecl = sa;
+ printf("this = %p %s\n", idec, idec.toChars());
+ printf("type = %d sym = %p\n", idec.type.ty, (cast(TypeClass)idec.type).sym);
}
- done = true;
}
+ assert(idec.type.ty != Tclass || (cast(TypeClass)idec.type).sym == idec);
- ti.members.foreachDsymbol(&staticIfDg);
+ version (none)
+ {
+ // @@@DEPRECATED_2.120@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
+ // Deprecated in 2.087
+ // Made an error in 2.100, but removal depends on `scope class` being removed too
+ // Don't forget to remove code at https://github.com/dlang/dmd/blob/b2f8274ba76358607fc3297a1e9f361480f9bcf9/src/dmd/dsymbolsem.d#L1032-L1036
+ if (idec.storage_class & STC.scope_)
+ error(idec.loc, "`scope` as a type constraint is obsolete. Use `scope` at the usage site.");
+ }
}
+}
- void symbolDg(Dsymbol s)
- {
- //printf("\t semantic on '%s' %p kind %s in '%s'\n", s.toChars(), s, s.kind(), this.toChars());
- //printf("test: enclosing = %d, sc2.parent = %s\n", enclosing, sc2.parent.toChars());
- //if (enclosing)
- // s.parent = sc.parent;
- //printf("test3: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
- s.dsymbolSemantic(sc2);
- //printf("test4: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
- Module.runDeferredSemantic();
- }
+/*
+Adds dsym as a member of scope sds.
- ti.members.foreachDsymbol(&symbolDg);
+Params:
+ dsym = dsymbol to inserted
+ sc = scope where the dsymbol is declared
+ sds = ScopeDsymbol where dsym is inserted
+*/
+void addMember(Dsymbol dsym, Scope* sc, ScopeDsymbol sds)
+{
+ auto addMemberVisitor = new AddMemberVisitor(sc, sds);
+ dsym.accept(addMemberVisitor);
}
-private void tryExpandMembers(TemplateInstance ti, Scope* sc2)
+private void attribAddMember(AttribDeclaration atb, Scope* sc, ScopeDsymbol sds)
{
- __gshared int nest;
- // extracted to a function to allow windows SEH to work without destructors in the same function
- //printf("%d\n", nest);
- if (++nest > global.recursionLimit)
+ Dsymbols* d = atb.include(sc);
+ if (d)
{
- global.gag = 0; // ensure error message gets printed
- .error(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
- fatal();
+ Scope* sc2 = atb.newScope(sc);
+ d.foreachDsymbol( s => s.addMember(sc2, sds) );
+ if (sc2 != sc)
+ sc2.pop();
}
-
- ti.expandMembers(sc2);
-
- nest--;
}
-private void trySemantic3(TemplateInstance ti, Scope* sc2)
+private extern(C++) class AddMemberVisitor : Visitor
{
- // extracted to a function to allow windows SEH to work without destructors in the same function
- __gshared int nest;
- //printf("%d\n", nest);
- if (++nest > global.recursionLimit)
- {
- global.gag = 0; // ensure error message gets printed
- .error(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
- fatal();
- }
-
- semantic3(ti, sc2);
+ alias visit = Visitor.visit;
- --nest;
-}
+ Scope* sc;
+ ScopeDsymbol sds;
-void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList argumentList)
-{
- //printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc);
- version (none)
+ this(Scope* sc, ScopeDsymbol sds) @safe
{
- for (Dsymbol s = tempinst; s; s = s.parent)
- {
- printf("\t%s\n", s.toChars());
- }
- printf("Scope\n");
- for (Scope* scx = sc; scx; scx = scx.enclosing)
- {
- printf("\t%s parent %s\n", scx._module ? scx._module.toChars() : "null", scx.parent ? scx.parent.toChars() : "null");
- }
+ this.sc = sc;
+ this.sds = sds;
}
- static if (LOG)
- {
- printf("\n+TemplateInstance.dsymbolSemantic('%s', this=%p)\n", tempinst.toChars(), tempinst);
- }
- if (tempinst.inst) // if semantic() was already run
+ override void visit(Dsymbol dsym)
{
- static if (LOG)
+ //printf("Dsymbol::addMember('%s')\n", toChars());
+ //printf("Dsymbol::addMember(this = %p, '%s' scopesym = '%s')\n", this, toChars(), sds.toChars());
+ //printf("Dsymbol::addMember(this = %p, '%s' sds = %p, sds.symtab = %p)\n", this, toChars(), sds, sds.symtab);
+ dsym.parent = sds;
+ if (dsym.isAnonymous()) // no name, so can't add it to symbol table
+ return;
+
+ if (!sds.symtabInsert(dsym)) // if name is already defined
{
- printf("-TemplateInstance.dsymbolSemantic('%s', this=%p) already run\n",
- tempinst.inst.toChars(), tempinst.inst);
- }
- return;
- }
- if (tempinst.semanticRun != PASS.initial)
- {
- static if (LOG)
+ if (dsym.isAliasDeclaration() && !dsym._scope)
+ dsym.setScope(sc);
+ Dsymbol s2 = sds.symtabLookup(dsym, dsym.ident);
+ /* https://issues.dlang.org/show_bug.cgi?id=17434
+ *
+ * If we are trying to add an import to the symbol table
+ * that has already been introduced, then keep the one with
+ * larger visibility. This is fine for imports because if
+ * we have multiple imports of the same file, if a single one
+ * is public then the symbol is reachable.
+ */
+ if (auto i1 = dsym.isImport())
+ {
+ if (auto i2 = s2.isImport())
+ {
+ if (sc.explicitVisibility && sc.visibility > i2.visibility)
+ sds.symtab.update(dsym);
+ }
+ }
+
+ // If using C tag/prototype/forward declaration rules
+ if (sc && sc.inCfile && !dsym.isImport())
+ // When merging master, replace with: if (sc && sc.inCfile && !dsym.isImport())
+ {
+ if (handleTagSymbols(*sc, dsym, s2, sds))
+ return;
+ if (handleSymbolRedeclarations(*sc, dsym, s2, sds))
+ return;
+
+ sds.multiplyDefined(Loc.initial, dsym, s2); // ImportC doesn't allow overloading
+ dsym.errors = true;
+ return;
+ }
+
+ if (!s2.overloadInsert(dsym))
+ {
+ if (auto _td = s2.isTemplateDeclaration())
+ _td.computeOneMember();
+ sds.multiplyDefined(Loc.initial, dsym, s2);
+ dsym.errors = true;
+ }
+ }
+ if (sds.isAggregateDeclaration() || sds.isEnumDeclaration())
{
- printf("Recursive template expansion\n");
+ if (dsym.ident == Id.__sizeof ||
+ !(sc && sc.inCfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof))
+ {
+ .error(dsym.loc, "%s `%s` `.%s` property cannot be redefined", dsym.kind, dsym.toPrettyChars, dsym.ident.toChars());
+ dsym.errors = true;
+ }
}
- auto ungag = Ungag(global.gag);
- if (!tempinst.gagged)
- global.gag = 0;
- .error(tempinst.loc, "%s `%s` recursive template expansion", tempinst.kind, tempinst.toPrettyChars);
- if (tempinst.gagged)
- tempinst.semanticRun = PASS.initial;
- else
- tempinst.inst = tempinst;
- tempinst.errors = true;
- return;
}
- // Get the enclosing template instance from the scope tinst
- tempinst.tinst = sc.tinst;
- // Get the instantiating module from the scope minst
- tempinst.minst = sc.minst;
- // https://issues.dlang.org/show_bug.cgi?id=10920
- // If the enclosing function is non-root symbol,
- // this instance should be speculative.
- if (!tempinst.tinst && sc.func && sc.func.inNonRoot())
+ override void visit(StaticAssert _)
{
- tempinst.minst = null;
+ // we didn't add anything
}
- tempinst.gagged = (global.gag > 0);
-
- tempinst.semanticRun = PASS.semantic;
-
- static if (LOG)
- {
- printf("\tdo semantic\n");
- }
- /* Find template declaration first,
- * then run semantic on each argument (place results in tiargs[]),
- * last find most specialized template from overload list/set.
+ /*****************************
+ * Add import to sd's symbol table.
*/
- if (!tempinst.findTempDecl(sc, null) || !tempinst.semanticTiargs(sc) || !tempinst.findBestMatch(sc, argumentList))
+ override void visit(Import imp)
{
- Lerror:
- if (tempinst.gagged)
+ //printf("Import.addMember(this=%s, sds=%s, sc=%p)\n", imp.toChars(), sds.toChars(), sc);
+ if (imp.names.length == 0)
+ return visit(cast(Dsymbol)imp);
+ if (imp.aliasId)
+ visit(cast(Dsymbol)imp);
+
+ /* Instead of adding the import to sds's symbol table,
+ * add each of the alias=name pairs
+ */
+ for (size_t i = 0; i < imp.names.length; i++)
{
- // https://issues.dlang.org/show_bug.cgi?id=13220
- // Roll back status for later semantic re-running
- tempinst.semanticRun = PASS.initial;
+ Identifier name = imp.names[i];
+ Identifier _alias = imp.aliases[i];
+ if (!_alias)
+ _alias = name;
+ auto tname = new TypeIdentifier(imp.loc, name);
+ auto ad = new AliasDeclaration(imp.loc, _alias, tname);
+ ad._import = imp;
+ addMember(ad, sc, sds);
+ imp.aliasdecls.push(ad);
}
- else
- tempinst.inst = tempinst;
- tempinst.errors = true;
- return;
- }
- TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration();
- assert(tempdecl);
-
- if (global.params.v.templates)
- TemplateStats.incInstance(tempdecl, tempinst, global.params.v.templatesListInstances);
-
- tempdecl.checkDeprecated(tempinst.loc, sc);
-
- // If tempdecl is a mixin, disallow it
- if (tempdecl.ismixin)
- {
- .error(tempinst.loc, "%s `%s` mixin templates are not regular templates", tempinst.kind, tempinst.toPrettyChars);
- goto Lerror;
}
- tempinst.hasNestedArgs(tempinst.tiargs, tempdecl.isstatic);
- if (tempinst.errors)
- goto Lerror;
-
- // Copy the tempdecl namespace (not the scope one)
- tempinst.cppnamespace = tempdecl.cppnamespace;
- if (tempinst.cppnamespace)
- tempinst.cppnamespace.dsymbolSemantic(sc);
-
- /* Greatly simplified semantic processing for AliasSeq templates
- */
- if (tempdecl.isTrivialAliasSeq)
+ override void visit(AttribDeclaration atb)
{
- tempinst.inst = tempinst;
- return aliasSeqInstanceSemantic(tempinst, sc, tempdecl);
+ attribAddMember(atb, sc, sds);
}
- /* Greatly simplified semantic processing for Alias templates
- */
- else if (tempdecl.isTrivialAlias)
+ override void visit(StorageClassDeclaration stcd)
{
- tempinst.inst = tempinst;
- return aliasInstanceSemantic(tempinst, sc, tempdecl);
- }
+ Dsymbols* d = stcd.include(sc);
+ if (d)
+ {
+ Scope* sc2 = stcd.newScope(sc);
+ d.foreachDsymbol( (s)
+ {
+ //printf("\taddMember %s to %s\n", s.toChars(), sds.toChars());
+ // STC.local needs to be attached before the member is added to the scope (because it influences the parent symbol)
+ if (auto decl = s.isDeclaration())
+ {
+ decl.storage_class |= stcd.stc & STC.local;
+ if (auto sdecl = s.isStorageClassDeclaration()) // TODO: why is this not enough to deal with the nested case?
+ {
+ sdecl.stc |= stcd.stc & STC.local;
+ }
+ }
+ s.addMember(sc2, sds);
+ });
- /* See if there is an existing TemplateInstantiation that already
- * implements the typeargs. If so, just refer to that one instead.
- */
- tempinst.inst = tempdecl.findExistingInstance(tempinst, argumentList);
- TemplateInstance errinst = null;
- if (!tempinst.inst)
- {
- // So, we need to implement 'this' instance.
- }
- else if (tempinst.inst.gagged && !tempinst.gagged && tempinst.inst.errors)
- {
- // If the first instantiation had failed, re-run semantic,
- // so that error messages are shown.
- errinst = tempinst.inst;
+ if (sc2 != sc)
+ sc2.pop();
+ }
}
- else
- {
- // It's a match
- tempinst.parent = tempinst.inst.parent;
- tempinst.errors = tempinst.inst.errors;
-
- // If both this and the previous instantiation were gagged,
- // use the number of errors that happened last time.
- global.errors += tempinst.errors;
- global.gaggedErrors += tempinst.errors;
- // If the first instantiation was gagged, but this is not:
- if (tempinst.inst.gagged)
+ override void visit(VisibilityDeclaration visd)
+ {
+ if (visd.pkg_identifiers)
{
- // It had succeeded, mark it is a non-gagged instantiation,
- // and reuse it.
- tempinst.inst.gagged = tempinst.gagged;
+ Dsymbol tmp;
+ Package.resolve(visd.pkg_identifiers, &tmp, null);
+ visd.visibility.pkg = tmp ? tmp.isPackage() : null;
+ visd.pkg_identifiers = null;
}
-
- tempinst.tnext = tempinst.inst.tnext;
- tempinst.inst.tnext = tempinst;
-
- /* A module can have explicit template instance and its alias
- * in module scope (e,g, `alias Base64 = Base64Impl!('+', '/');`).
- * If the first instantiation 'inst' had happened in non-root module,
- * compiler can assume that its instantiated code would be included
- * in the separately compiled obj/lib file (e.g. phobos.lib).
- *
- * However, if 'this' second instantiation happened in root module,
- * compiler might need to invoke its codegen
- * (https://issues.dlang.org/show_bug.cgi?id=2500 & https://issues.dlang.org/show_bug.cgi?id=2644).
- * But whole import graph is not determined until all semantic pass finished,
- * so 'inst' should conservatively finish the semantic3 pass for the codegen.
- */
- if (tempinst.minst && tempinst.minst.isRoot() && !(tempinst.inst.minst && tempinst.inst.minst.isRoot()))
+ if (visd.visibility.kind == Visibility.Kind.package_ && visd.visibility.pkg && sc._module)
{
- /* Swap the position of 'inst' and 'this' in the instantiation graph.
- * Then, the primary instance `inst` will be changed to a root instance,
- * along with all members of `inst` having their scopes updated.
- *
- * Before:
- * non-root -> A!() -> B!()[inst] -> C!() { members[non-root] }
- * |
- * root -> D!() -> B!()[this]
- *
- * After:
- * non-root -> A!() -> B!()[this]
- * |
- * root -> D!() -> B!()[inst] -> C!() { members[root] }
- */
- Module mi = tempinst.minst;
- TemplateInstance ti = tempinst.tinst;
- tempinst.minst = tempinst.inst.minst;
- tempinst.tinst = tempinst.inst.tinst;
- tempinst.inst.minst = mi;
- tempinst.inst.tinst = ti;
-
- /* https://issues.dlang.org/show_bug.cgi?id=21299
- `minst` has been updated on the primary instance `inst` so it is
- now coming from a root module, however all Dsymbol `inst.members`
- of the instance still have their `_scope.minst` pointing at the
- original non-root module. We must now propagate `minst` to all
- members so that forward referenced dependencies that get
- instantiated will also be appended to the root module, otherwise
- there will be undefined references at link-time. */
- extern (C++) final class InstMemberWalker : Visitor
- {
- alias visit = Visitor.visit;
- TemplateInstance inst;
-
- extern (D) this(TemplateInstance inst) scope @safe
- {
- this.inst = inst;
- }
-
- override void visit(Dsymbol d)
- {
- if (d._scope)
- d._scope.minst = inst.minst;
- }
-
- override void visit(ScopeDsymbol sds)
- {
- sds.members.foreachDsymbol( s => s.accept(this) );
- 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) );
- visit(cast(Dsymbol)ad);
- }
-
- override void visit(ConditionalDeclaration cd)
- {
- if (cd.condition.inc)
- visit(cast(AttribDeclaration)cd);
- else
- visit(cast(Dsymbol)cd);
- }
- }
- scope v = new InstMemberWalker(tempinst.inst);
- tempinst.inst.accept(v);
+ Module m = sc._module;
- if (!global.params.allInst &&
- tempinst.minst) // if inst was not speculative...
+ // https://issues.dlang.org/show_bug.cgi?id=17441
+ // While isAncestorPackageOf does an equality check, the fix for the issue adds a check to see if
+ // each package's .isModule() properites are equal.
+ //
+ // Properties generated from `package(foo)` i.e. visibility.pkg have .isModule() == null.
+ // This breaks package declarations of the package in question if they are declared in
+ // the same package.d file, which _do_ have a module associated with them, and hence a non-null
+ // isModule()
+ if (!m.isPackage() || !visd.visibility.pkg.ident.equals(m.isPackage().ident))
{
- assert(!tempinst.minst.isRoot()); // ... it was previously appended to a non-root module
- // Append again to the root module members[], so that the instance will
- // get codegen chances (depending on `tempinst.inst.needsCodegen()`).
- tempinst.inst.appendToModuleMember();
+ Package pkg = m.parent ? m.parent.isPackage() : null;
+ if (!pkg || !visd.visibility.pkg.isAncestorPackageOf(pkg))
+ .error(visd.loc, "%s `%s` does not bind to one of ancestor packages of module `%s`", visd.kind(), visd.toPrettyChars(false), m.toPrettyChars(true));
}
-
- assert(tempinst.inst.memberOf && tempinst.inst.memberOf.isRoot(), "no codegen chances");
- }
-
- // modules imported by an existing instance should be added to the module
- // that instantiates the instance.
- if (tempinst.minst)
- foreach(imp; tempinst.inst.importedModules)
- if (!tempinst.minst.aimports.contains(imp))
- tempinst.minst.aimports.push(imp);
-
- static if (LOG)
- {
- printf("\tit's a match with instance %p, %d\n", tempinst.inst, tempinst.inst.semanticRun);
}
- return;
+ attribAddMember(visd, sc, sds);
}
- static if (LOG)
+
+ override void visit(StaticIfDeclaration sid)
{
- printf("\timplement template instance %s '%s'\n", tempdecl.parent.toChars(), tempinst.toChars());
- printf("\ttempdecl %s\n", tempdecl.toChars());
+ //printf("StaticIfDeclaration::addMember() '%s'\n", sid.toChars());
+ /* This is deferred until the condition evaluated later (by the include() call),
+ * so that expressions in the condition can refer to declarations
+ * in the same scope, such as:
+ *
+ * template Foo(int i)
+ * {
+ * const int j = i + 1;
+ * static if (j == 3)
+ * const int k;
+ * }
+ */
+ sid.scopesym = sds;
}
- const errorsave = global.errors;
-
- tempinst.inst = tempinst;
- tempinst.parent = tempinst.enclosing ? tempinst.enclosing : tempdecl.parent;
- //printf("parent = '%s'\n", parent.kind());
-
- if (global.params.v.templates)
- TemplateStats.incUnique(tempdecl, tempinst);
- TemplateInstance tempdecl_instance_idx = tempdecl.addInstance(tempinst);
- //getIdent();
-
- // Store the place we added it to in target_symbol_list(_idx) so we can
- // remove it later if we encounter an error.
- Dsymbols* target_symbol_list = tempinst.appendToModuleMember();
- size_t target_symbol_list_idx = target_symbol_list ? target_symbol_list.length - 1 : 0;
-
- // Copy the syntax trees from the TemplateDeclaration
- tempinst.members = Dsymbol.arraySyntaxCopy(tempdecl.members);
-
- // resolve TemplateThisParameter
- for (size_t i = 0; i < tempdecl.parameters.length; i++)
+ override void visit(StaticForeachDeclaration sfd)
{
- if ((*tempdecl.parameters)[i].isTemplateThisParameter() is null)
- continue;
- Type t = isType((*tempinst.tiargs)[i]);
- assert(t);
- if (STC stc = ModToStc(t.mod))
- {
- //printf("t = %s, stc = x%llx\n", t.toChars(), stc);
- auto s = new Dsymbols(new StorageClassDeclaration(stc, tempinst.members));
- tempinst.members = s;
- }
- break;
+ // used only for caching the enclosing symbol
+ sfd.scopesym = sds;
}
- // Create our own scope for the template parameters
- Scope* _scope = tempdecl._scope;
- if (tempdecl.semanticRun == PASS.initial)
+ /***************************************
+ * Lazily initializes the scope to forward to.
+ */
+ override void visit(ForwardingAttribDeclaration fad)
{
- .error(tempinst.loc, "%s `%s` template instantiation `%s` forward references template declaration `%s`",
- tempinst.kind, tempinst.toPrettyChars, tempinst.toChars(), tempdecl.toChars());
- return;
+ fad.sym.parent = sds;
+ sds = fad.sym;
+ attribAddMember(fad, sc, fad.sym);
}
- static if (LOG)
+ override void visit(MixinDeclaration md)
{
- printf("\tcreate scope for template parameters '%s'\n", tempinst.toChars());
+ //printf("MixinDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, md.memnum);
+ md.scopesym = sds;
}
- tempinst.argsym = new ScopeDsymbol();
- tempinst.argsym.parent = _scope.parent;
- _scope = _scope.push(tempinst.argsym);
- _scope.tinst = tempinst;
- _scope.minst = tempinst.minst;
- //scope.stc = STC.none;
-
- // Declare each template parameter as an alias for the argument type
- Scope* paramscope = _scope.push();
- paramscope.stc = STC.none;
- paramscope.visibility = Visibility(Visibility.Kind.public_); // https://issues.dlang.org/show_bug.cgi?id=14169
- // template parameters should be public
- tempinst.declareParameters(paramscope);
- paramscope.pop();
- // Add members of template instance to template instance symbol table
- //parent = scope.scopesym;
- tempinst.symtab = new DsymbolTable();
-
- tempinst.members.foreachDsymbol( (s)
+ override void visit(DebugSymbol ds)
{
- static if (LOG)
+ //printf("DebugSymbol::addMember('%s') %s\n", sds.toChars(), ds.toChars());
+ Module m = sds.isModule();
+ // Do not add the member to the symbol table,
+ // just make sure subsequent debug declarations work.
+ if (!m)
{
- printf("\t adding member '%s' %p kind %s to '%s'\n", s.toChars(), s, s.kind(), tempinst.toChars());
+ .error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars);
+ ds.errors = true;
}
- s.addMember(_scope, tempinst);
- });
-
- static if (LOG)
- {
- printf("adding members done\n");
- }
-
- /* See if there is only one member of template instance, and that
- * member has the same name as the template instance.
- * If so, this template instance becomes an alias for that member.
- */
- //printf("members.length = %d\n", tempinst.members.length);
- if (tempinst.members.length)
- {
- Dsymbol s;
- if (oneMembers(tempinst.members, s, tempdecl.ident) && s)
+ else
{
- //printf("tempdecl.ident = %s, s = `%s %s`\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
- //printf("setting aliasdecl\n");
- tempinst.aliasdecl = s;
+ if (m.debugidsNot && findCondition(*m.debugidsNot, ds.ident))
+ {
+ .error(ds.loc, "%s `%s` defined after use", ds.kind, ds.toPrettyChars);
+ ds.errors = true;
+ }
+ if (!m.debugids)
+ m.debugids = new Identifiers();
+ m.debugids.push(ds.ident);
}
}
- /* If function template declaration
- */
- if (argumentList.length > 0 && tempinst.aliasdecl)
+ override void visit(VersionSymbol vs)
{
- if (auto fd = tempinst.aliasdecl.isFuncDeclaration())
+ //printf("VersionSymbol::addMember('%s') %s\n", sds.toChars(), vs.toChars());
+ Module m = sds.isModule();
+ // Do not add the member to the symbol table,
+ // just make sure subsequent debug declarations work.
+ VersionCondition.checkReserved(vs.loc, vs.ident.toString());
+ if (!m)
{
- /* Transmit fargs to type so that TypeFunction.dsymbolSemantic() can
- * resolve any "auto ref" storage classes.
- */
- if (fd.type)
- if (auto tf = fd.type.isTypeFunction())
- tf.inferenceArguments = argumentList;
+ .error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars);
+ vs.errors = true;
+ }
+ else
+ {
+ if (m.versionidsNot && findCondition(*m.versionidsNot, vs.ident))
+ {
+ .error(vs.loc, "%s `%s` defined after use", vs.kind, vs.toPrettyChars);
+ vs.errors = true;
+ }
+ if (!m.versionids)
+ m.versionids = new Identifiers();
+ m.versionids.push(vs.ident);
}
- }
- // Do semantic() analysis on template instance members
- static if (LOG)
- {
- printf("\tdo semantic() on template instance members '%s'\n", tempinst.toChars());
}
- Scope* sc2;
- sc2 = _scope.push(tempinst);
- //printf("enclosing = %d, sc.parent = %s\n", tempinst.enclosing, sc.parent.toChars());
- sc2.parent = tempinst;
- sc2.tinst = tempinst;
- sc2.minst = tempinst.minst;
- sc2.stc &= ~STC.deprecated_;
- tempinst.tryExpandMembers(sc2);
-
- tempinst.semanticRun = PASS.semanticdone;
- /* ConditionalDeclaration may introduce eponymous declaration,
- * so we should find it once again after semantic.
- */
- if (tempinst.members.length)
+ override void visit(Nspace ns)
{
- Dsymbol s;
- if (oneMembers(tempinst.members, s, tempdecl.ident) && s)
+ visit(cast(Dsymbol)ns);
+
+ if (ns.members)
{
- if (!tempinst.aliasdecl || tempinst.aliasdecl != s)
+ if (!ns.symtab)
+ ns.symtab = new DsymbolTable();
+ // The namespace becomes 'imported' into the enclosing scope
+ for (Scope* sce = sc; 1; sce = sce.enclosing)
{
- //printf("tempdecl.ident = %s, s = `%s %s`\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
- //printf("setting aliasdecl 2\n");
- tempinst.aliasdecl = s;
+ ScopeDsymbol sds2 = sce.scopesym;
+ if (sds2)
+ {
+ sds2.importScope(ns, Visibility(Visibility.Kind.public_));
+ break;
+ }
}
+ assert(sc);
+ sc = sc.push(ns);
+ sc.linkage = LINK.cpp; // namespaces default to C++ linkage
+ sc.parent = ns;
+ ns.members.foreachDsymbol(s => s.addMember(sc, ns));
+ sc.pop();
}
}
- if (global.errors != errorsave)
- goto Laftersemantic;
-
- /* If any of the instantiation members didn't get semantic() run
- * on them due to forward references, we cannot run semantic2()
- * or semantic3() yet.
- */
+ override void visit(EnumDeclaration ed)
{
- bool found_deferred_ad = false;
- for (size_t i = 0; i < Module.deferred.length; i++)
+ version (none)
{
- Dsymbol sd = Module.deferred[i];
- AggregateDeclaration ad = sd.isAggregateDeclaration();
- if (ad && ad.parent && ad.parent.isTemplateInstance())
+ printf("EnumDeclaration::addMember() %s\n", ed.toChars());
+ for (size_t i = 0; i < ed.members.length; i++)
{
- //printf("deferred template aggregate: %s %s\n",
- // sd.parent.toChars(), sd.toChars());
- found_deferred_ad = true;
- if (ad.parent == tempinst)
- {
- ad.deferred = tempinst;
- break;
- }
+ EnumMember em = (*ed.members)[i].isEnumMember();
+ printf(" member %s\n", em.toChars());
}
}
- if (found_deferred_ad || Module.deferred.length)
- goto Laftersemantic;
- }
+ if (!ed.isAnonymous())
+ {
+ visit(cast(Dsymbol)ed);
+ }
- /* The problem is when to parse the initializer for a variable.
- * Perhaps VarDeclaration.dsymbolSemantic() should do it like it does
- * for initializers inside a function.
- */
- //if (sc.parent.isFuncDeclaration())
- {
- /* https://issues.dlang.org/show_bug.cgi?id=782
- * this has problems if the classes this depends on
- * are forward referenced. Find a way to defer semantic()
- * on this template.
- */
- tempinst.semantic2(sc2);
+ addEnumMembersToSymtab(ed, sc, sds);
}
- if (global.errors != errorsave)
- goto Laftersemantic;
+}
- if ((sc.func || sc.fullinst) && !tempinst.tinst)
- {
- /* If a template is instantiated inside function, the whole instantiation
- * should be done at that position. But, immediate running semantic3 of
- * dependent templates may cause unresolved forward reference.
- * https://issues.dlang.org/show_bug.cgi?id=9050
- * To avoid the issue, don't run semantic3 until semantic and semantic2 done.
- */
- TemplateInstances deferred;
- tempinst.deferred = &deferred;
+/*******************************************
+ * Add members of EnumDeclaration to the symbol table(s).
+ * Params:
+ * ed = EnumDeclaration
+ * sc = context of `ed`
+ * sds = symbol table that `ed` resides in
+ */
+void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds)
+{
+ const bool isCEnum = sc.inCfile; // it's an ImportC enum
+ //printf("addEnumMembersToSymtab(ed: %s added: %d Cfile: %d)\n", ed.toChars(), ed.added, isCEnum);
+ if (ed.added)
+ return;
+ ed.added = true;
- //printf("Run semantic3 on %s\n", toChars());
+ if (!ed.members)
+ return;
- /* https://issues.dlang.org/show_bug.cgi?id=23965
- * DRuntime hooks are not deprecated, but may be used for deprecated
- * types. Deprecations are disabled while analysing hooks to avoid
- * spurious error messages.
- */
- auto saveUseDeprecated = global.params.useDeprecated;
- if (sc.isDeprecated() && isDRuntimeHook(tempinst.name))
- global.params.useDeprecated = DiagnosticReporting.off;
+ const bool isAnon = ed.isAnonymous();
- tempinst.trySemantic3(sc2);
+ if ((isCEnum || isAnon) && !sds.symtab)
+ sds.symtab = new DsymbolTable();
- global.params.useDeprecated = saveUseDeprecated;
+ if ((isCEnum || !isAnon) && !ed.symtab)
+ ed.symtab = new DsymbolTable();
- for (size_t i = 0; i < deferred.length; i++)
+ ed.members.foreachDsymbol( (s)
+ {
+ if (EnumMember em = s.isEnumMember())
{
- //printf("+ run deferred semantic3 on %s\n", deferred[i].toChars());
- deferred[i].semantic3(null);
+ //printf("adding EnumMember %s to %s %d\n", em.toChars(), ed.toChars(), isCEnum);
+ em.ed = ed;
+ if (isCEnum)
+ {
+ /* C doesn't add the enum member to the symbol table of the enum tag, it adds
+ * it to the symbol table that the tag is in. This is in contrast to D, where enum
+ * members become members of the enum tag. To accommodate this, we add
+ * the enum members to both symbol tables.
+ */
+ em.addMember(sc, ed); // add em to ed's symbol table
+ if (em.errors)
+ return;
+ em.addMember(sc, sds); // add em to symbol table that ed is in
+ em.parent = ed; // restore it after previous addMember() changed it
+ }
+ else
+ {
+ em.addMember(sc, isAnon ? sds : ed);
+ }
}
+ });
+}
- tempinst.deferred = null;
+private OverloadSet mergeOverloadSet(ScopeDsymbol sds, Identifier ident, OverloadSet os, Dsymbol s)
+{
+ if (!os)
+ {
+ os = new OverloadSet(ident);
+ os.parent = sds;
}
- else if (tempinst.tinst)
+ if (OverloadSet os2 = s.isOverloadSet())
{
- bool doSemantic3 = false;
- FuncDeclaration fd;
- if (tempinst.aliasdecl)
- fd = tempinst.aliasdecl.toAlias2().isFuncDeclaration();
-
- if (fd)
+ // Merge the cross-module overload set 'os2' into 'os'
+ if (os.a.length == 0)
{
- /* Template function instantiation should run semantic3 immediately
- * for attribute inference.
- */
- scope fld = fd.isFuncLiteralDeclaration();
- if (fld && fld.tok == TOK.reserved)
- doSemantic3 = true;
- else if (sc.func)
- doSemantic3 = true;
- }
- else if (sc.func)
- {
- /* A lambda function in template arguments might capture the
- * instantiated scope context. For the correct context inference,
- * all instantiated functions should run the semantic3 immediately.
- * See also compilable/test14973.d
- */
- foreach (oarg; tempinst.tdtypes)
- {
- auto s = getDsymbol(oarg);
- if (!s)
- continue;
-
- if (auto td = s.isTemplateDeclaration())
- {
- if (!td.literal)
- continue;
- assert(td.members && td.members.length == 1);
- s = (*td.members)[0];
- }
- if (auto fld = s.isFuncLiteralDeclaration())
- {
- if (fld.tok == TOK.reserved)
- {
- doSemantic3 = true;
- break;
- }
- }
- }
- //printf("[%s] %s doSemantic3 = %d\n", tempinst.tinst.loc.toChars(), tempinst.tinst.toChars(), doSemantic3);
+ os.a.setDim(os2.a.length);
+ memcpy(os.a.tdata(), os2.a.tdata(), (os.a[0]).sizeof * os2.a.length);
}
- if (doSemantic3)
- tempinst.trySemantic3(sc2);
-
- TemplateInstance ti = tempinst.tinst;
- int nest = 0;
- while (ti && !ti.deferred && ti.tinst)
+ else
{
- ti = ti.tinst;
- if (++nest > global.recursionLimit)
+ for (size_t i = 0; i < os2.a.length; i++)
{
- global.gag = 0; // ensure error message gets printed
- .error(tempinst.loc, "%s `%s` recursive expansion", tempinst.kind, tempinst.toPrettyChars);
- fatal();
+ os = mergeOverloadSet(sds, ident, os, os2.a[i]);
}
}
- if (ti && ti.deferred)
+ return os;
+ }
+
+ assert(s.isOverloadable());
+ /* Don't add to os[] if s is alias of previous sym
+ */
+ for (size_t j = 0; j < os.a.length; j++)
+ {
+ Dsymbol s2 = os.a[j];
+ if (s.toAlias() == s2.toAlias())
{
- //printf("deferred semantic3 of %p %s, ti = %s, ti.deferred = %p\n", this, toChars(), ti.toChars());
- for (size_t i = 0;; i++)
+ if (s2.isDeprecated() || (s2.visible() < s.visible() && s.visible().kind != Visibility.Kind.none))
{
- if (i == ti.deferred.length)
- {
- ti.deferred.push(tempinst);
- break;
- }
- if ((*ti.deferred)[i] == tempinst)
- break;
+ os.a[j] = s;
}
+ return os;
}
}
+ os.push(s);
+ return os;
+}
- if (tempinst.aliasdecl)
+/***
+ * Returns true if any of the symbols `p1` or `p2` resides in the enclosing
+ * instantiation scope of `this`.
+ */
+bool followInstantiationContext(Dsymbol d,Dsymbol p1, Dsymbol p2 = null)
+{
+ static bool has2This(Dsymbol s)
{
- /* https://issues.dlang.org/show_bug.cgi?id=13816
- * AliasDeclaration tries to resolve forward reference
- * twice (See inuse check in AliasDeclaration.toAlias()). It's
- * necessary to resolve mutual references of instantiated symbols, but
- * it will left a true recursive alias in tuple declaration - an
- * AliasDeclaration A refers TupleDeclaration B, and B contains A
- * in its elements. To correctly make it an error, we strictly need to
- * resolve the alias of eponymous member.
- */
- tempinst.aliasdecl = tempinst.aliasdecl.toAlias2();
-
- // stop AliasAssign tuple building
- if (auto td = tempinst.aliasdecl.isTupleDeclaration())
- td.building = false;
+ if (auto f = s.isFuncDeclaration())
+ return f.hasDualContext;
+ if (auto ad = s.isAggregateDeclaration())
+ return ad.vthis2 !is null;
+ return false;
}
-Laftersemantic:
- sc2.pop();
- _scope.pop();
+ if (!has2This(d))
+ return false;
- // Give additional context info if error occurred during instantiation
- if (global.errors != errorsave)
+ assert(p1);
+ auto outer = d.toParent();
+ while (outer)
{
- if (!tempinst.errors)
+ auto ti = outer.isTemplateInstance();
+ if (!ti)
+ return false;
+ foreach (oarg; *ti.tiargs)
{
- if (!tempdecl.literal)
- .error(tempinst.loc, "%s `%s` error instantiating", tempinst.kind, tempinst.toPrettyChars);
- if (tempinst.tinst)
- tempinst.tinst.printInstantiationTrace();
+ auto sa = getDsymbol(oarg);
+ if (!sa)
+ continue;
+ sa = sa.toAlias().toParent2();
+ if (!sa)
+ continue;
+ if (sa == p1)
+ return true;
+ if (p2 && sa == p2)
+ return true;
}
- tempinst.errors = true;
- if (tempinst.gagged)
- {
- // Errors are gagged, so remove the template instance from the
- // instance/symbol lists we added it to and reset our state to
- // finish clean and so we can try to instantiate it again later
- // (see https://issues.dlang.org/show_bug.cgi?id=4302 and https://issues.dlang.org/show_bug.cgi?id=6602).
- tempdecl.removeInstance(tempdecl_instance_idx);
- if (target_symbol_list)
- {
- // Because we added 'this' in the last position above, we
- // should be able to remove it without messing other indices up.
- assert((*target_symbol_list)[target_symbol_list_idx] == tempinst);
- target_symbol_list.remove(target_symbol_list_idx);
- tempinst.memberOf = null; // no longer a member
- }
- tempinst.semanticRun = PASS.initial;
- tempinst.inst = null;
- tempinst.symtab = null;
- }
- }
- else if (errinst)
- {
- /* https://issues.dlang.org/show_bug.cgi?id=14541
- * If the previous gagged instance had failed by
- * circular references, currrent "error reproduction instantiation"
- * might succeed, because of the difference of instantiated context.
- * On such case, the cached error instance needs to be overridden by the
- * succeeded instance.
- */
- //printf("replaceInstance()\n");
- assert(errinst.errors);
- auto ti1 = TemplateInstanceBox(errinst);
- tempdecl.instances.remove(ti1);
-
- auto ti2 = TemplateInstanceBox(tempinst);
- tempdecl.instances[ti2] = tempinst;
- }
-
- static if (LOG)
- {
- printf("-TemplateInstance.dsymbolSemantic('%s', this=%p)\n", tempinst.toChars(), tempinst);
+ outer = ti.tempdecl.toParent();
}
+ return false;
}
-/******************************************************
- * Do template instance semantic for isAliasSeq templates.
- * This is a greatly simplified version of templateInstanceSemantic().
- */
-private
-void aliasSeqInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclaration tempdecl)
-{
- //printf("[%s] aliasSeqInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars());
- Scope* paramscope = sc.push();
- paramscope.stc = STC.none;
- paramscope.visibility = Visibility(Visibility.Kind.public_);
-
- TemplateTupleParameter ttp = (*tempdecl.parameters)[0].isTemplateTupleParameter();
- Tuple va = tempinst.tdtypes[0].isTuple();
- Declaration d = new TupleDeclaration(tempinst.loc, ttp.ident, &va.objects);
- d.storage_class |= STC.templateparameter;
- d.dsymbolSemantic(sc);
-
- paramscope.pop();
-
- tempinst.aliasdecl = d;
-
- tempinst.semanticRun = PASS.semanticdone;
-}
-
-/******************************************************
- * Do template instance semantic for isAlias templates.
- * This is a greatly simplified version of templateInstanceSemantic().
+/**
+ * Returns the declaration scope scope of `this` unless any of the symbols
+ * `p1` or `p2` resides in its enclosing instantiation scope then the
+ * latter is returned.
*/
-private
-void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclaration tempdecl)
+Dsymbol toParentP(Dsymbol d, Dsymbol p1, Dsymbol p2 = null)
{
- //printf("[%s] aliasInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars());
- Scope* paramscope = sc.push();
- paramscope.stc = STC.none;
- paramscope.visibility = Visibility(Visibility.Kind.public_);
-
- TemplateTypeParameter ttp = (*tempdecl.parameters)[0].isTemplateTypeParameter();
- Type ta = tempinst.tdtypes[0].isType();
- auto ad = tempdecl.onemember.isAliasDeclaration();
-
- // Note: qualifiers can be in both 'ad.type.mod' and 'ad.storage_class'
- Declaration d = new AliasDeclaration(tempinst.loc, ttp.ident, ta.addMod(ad.type.mod));
- d.storage_class |= STC.templateparameter | ad.storage_class;
- d.dsymbolSemantic(sc);
-
- paramscope.pop();
-
- tempinst.aliasdecl = d;
-
- tempinst.semanticRun = PASS.semanticdone;
+ return followInstantiationContext(d, p1, p2) ? d.toParent2() : d.toParentLocal();
}
// function used to perform semantic on AliasDeclaration
normalRet();
}
-/**********************************************
- * Find template declaration corresponding to template instance.
- *
- * Returns:
- * false if finding fails.
- * Note:
- * This function is reentrant against error occurrence. If returns false,
- * any members of this object won't be modified, and repetition call will
- * reproduce same error.
- */
-bool findTempDecl(TemplateInstance ti, Scope* sc, WithScopeSymbol* pwithsym)
-{
- if (pwithsym)
- *pwithsym = null;
-
- if (ti.havetempdecl)
- return true;
-
- //printf("TemplateInstance.findTempDecl() %s\n", toChars());
- if (!ti.tempdecl)
- {
- /* Given:
- * foo!( ... )
- * figure out which TemplateDeclaration foo refers to.
- */
- Identifier id = ti.name;
- Dsymbol scopesym;
- Dsymbol s = sc.search(ti.loc, id, scopesym);
- if (!s)
- {
- s = sc.search_correct(id);
- if (s)
- .error(ti.loc, "%s `%s` template `%s` is not defined, did you mean %s?", ti.kind, ti.toPrettyChars(), id.toChars(), s.toChars());
- else
- .error(ti.loc, "%s `%s` template `%s` is not defined", ti.kind, ti.toPrettyChars(), id.toChars());
- return false;
- }
- static if (LOG)
- {
- printf("It's an instance of '%s' kind '%s'\n", s.toChars(), s.kind());
- if (s.parent)
- printf("s.parent = '%s'\n", s.parent.toChars());
- }
- if (pwithsym)
- *pwithsym = scopesym.isWithScopeSymbol();
-
- /* We might have found an alias within a template when
- * we really want the template.
- */
- TemplateInstance ti2;
- if (s.parent && (ti2 = s.parent.isTemplateInstance()) !is null)
- {
- if (ti2.tempdecl && ti2.tempdecl.ident == id)
- {
- /* This is so that one can refer to the enclosing
- * template, even if it has the same name as a member
- * of the template, if it has a !(arguments)
- */
- TemplateDeclaration td = ti2.tempdecl.isTemplateDeclaration();
- assert(td);
- if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
- td = td.overroot; // then get the start
- s = td;
- }
- }
-
- // The template might originate from a selective import which implies that
- // s is a lowered AliasDeclaration of the actual TemplateDeclaration.
- // This is the last place where we see the deprecated alias because it is
- // stripped below, so check if the selective import was deprecated.
- // See https://issues.dlang.org/show_bug.cgi?id=20840.
- if (s.isAliasDeclaration())
- s.checkDeprecated(ti.loc, sc);
-
- if (!ti.updateTempDecl(sc, s))
- {
- return false;
- }
- }
- assert(ti.tempdecl);
-
- // Look for forward references
- 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 (td.semanticRun == PASS.initial)
- {
- if (td._scope)
- {
- // Try to fix forward reference. Ungag errors while doing so.
- auto 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;
- }
- }
- return 0;
- });
- if (r)
- return false;
- }
- return true;
-}
-
-private bool findMixinTempDecl(TemplateMixin tm, Scope* sc)
-{
- // Follow qualifications to find the TemplateDeclaration
- if (!tm.tempdecl)
- {
- Expression e;
- Type t;
- Dsymbol s;
- tm.tqual.resolve(tm.loc, sc, e, t, s);
- if (!s)
- {
- .error(tm.loc, "%s `%s` is not defined", tm.kind, tm.toPrettyChars);
- return false;
- }
- s = s.toAlias();
- tm.tempdecl = s.isTemplateDeclaration();
- OverloadSet os = s.isOverloadSet();
-
- /* If an OverloadSet, look for a unique member that is a template declaration
- */
- if (os)
- {
- Dsymbol ds = null;
- foreach (i, sym; os.a)
- {
- Dsymbol s2 = sym.isTemplateDeclaration();
- if (s2)
- {
- if (ds)
- {
- tm.tempdecl = os;
- break;
- }
- ds = s2;
- }
- }
- }
- if (!tm.tempdecl)
- {
- .error(tm.loc, "%s `%s` - `%s` is a %s, not a template", tm.kind,
- tm.toPrettyChars, s.toChars(), s.kind());
- return false;
- }
- }
- assert(tm.tempdecl);
-
- // Look for forward references
- auto tovers = tm.tempdecl.isOverloadSet();
- foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
- {
- Dsymbol dstart = tovers ? tovers.a[oi] : tm.tempdecl;
- int r = overloadApply(dstart, (Dsymbol s)
- {
- auto td = s.isTemplateDeclaration();
- if (!td)
- return 0;
-
- if (td.semanticRun == PASS.initial)
- {
- if (td._scope)
- td.dsymbolSemantic(td._scope);
- else
- {
- tm.semanticRun = PASS.initial;
- return 1;
- }
- }
- return 0;
- });
- if (r)
- return false;
- }
- return true;
-}
-
/********************
* Perform semantic on AliasAssignment.
* Has a lot of similarities to aliasSemantic(). Perhaps they should share code.
// Try AliasSeq optimization
if (auto ti = ds.type.isTypeInstance())
{
- if (!ti.tempinst.findTempDecl(sc, null))
+ import dmd.templatesem : findTempDecl;
+ if (!findTempDecl(ti.tempinst, sc, null))
return errorRet();
if (auto tempinst = isAliasSeq(sc, ti))
{
int r = overloadApply(dstart, (Dsymbol s)
{
auto td = s.isTemplateDeclaration();
- if (!td || !td.isTrivialAliasSeq)
- return 1;
- return 0;
+ return (td && td.isTrivialAliasSeq) ? 0 : 1;
});
if (r)
return null;
* gets imported, it is unaffected by context.
* Ignore prevsc.
*/
- Scope* sc = Scope.createGlobal(m, global.errorSink); // create root scope
+ Scope* sc = scopeCreateGlobal(m, global.errorSink); // create root scope
if (m.md && m.md.msg)
m.md.msg = semanticString(sc, m.md.msg, "deprecation message");
s.importAll(sc);
}
sc = sc.pop();
- sc.pop(); // 2 pops because Scope.createGlobal() created 2
+ sc.pop(); // 2 pops because scopeCreateGlobal() created 2
}
override void visit(AttribDeclaration atb)
private extern(C++) class SetFieldOffsetVisitor : Visitor
{
+ import dmd.typesem: size;
+
alias visit = Visitor.visit;
AggregateDeclaration ad;
*/
private Expression callScopeDtor(VarDeclaration vd, Scope* sc)
{
+ import dmd.typesem: size;
+
//printf("VarDeclaration::callScopeDtor() %s\n", toChars());
// Destruction of STC.field's is handled by buildDtor()
static int virtualSemantic(Dsymbol s, void*)
{
auto fd = s.isFuncDeclaration();
- if (fd && !(fd.storage_class & STC.static_) && !fd.isUnitTestDeclaration())
+ if (fd && !(fd.storage_class & STC.static_) && !fd.isUnitTestDeclaration() && !fd.isCtorDeclaration())
fd.dsymbolSemantic(null);
return 0;
}
*/
bool _isZeroInit(Expression exp)
{
+ import dmd.typesem: size;
+
switch (exp.op)
{
case EXP.int64:
private extern(C++) class FinalizeSizeVisitor : Visitor
{
+ import dmd.typesem: size;
+
alias visit = Visitor.visit;
override void visit(ClassDeclaration outerCd)
for (size_t i = 0; i < members.length; i++)
{
Dsymbol sx = (*members)[i];
- bool x = sx.oneMember(ps, ident); //MYTODO: this temporarily creates a new dependency to dsymbolsem, will need to extract oneMembers() later
+ bool x = sx.oneMember(ps, ident);
//printf("\t[%d] kind %s = %d, s = %p\n", i, sx.kind(), x, *ps);
if (!x)
{
assert(ps is null);
return false;
}
- if (ps)
+ if (!ps)
+ continue;
+
+ assert(ident);
+ if (!ps.ident || !ps.ident.equals(ident))
+ continue;
+ if (!s)
+ s = ps;
+ else if (s.isOverloadable() && ps.isOverloadable())
{
- assert(ident);
- if (!ps.ident || !ps.ident.equals(ident))
- continue;
- if (!s)
- s = ps;
- else if (s.isOverloadable() && ps.isOverloadable())
+ // keep head of overload set
+ FuncDeclaration f1 = s.isFuncDeclaration();
+ FuncDeclaration f2 = ps.isFuncDeclaration();
+ if (f1 && f2)
{
- // keep head of overload set
- FuncDeclaration f1 = s.isFuncDeclaration();
- FuncDeclaration f2 = ps.isFuncDeclaration();
- if (f1 && f2)
+ assert(!f1.isFuncAliasDeclaration());
+ assert(!f2.isFuncAliasDeclaration());
+ for (; f1 != f2; f1 = f1.overnext0)
{
- assert(!f1.isFuncAliasDeclaration());
- assert(!f2.isFuncAliasDeclaration());
- for (; f1 != f2; f1 = f1.overnext0)
+ if (f1.overnext0 is null)
{
- if (f1.overnext0 is null)
- {
- f1.overnext0 = f2;
- break;
- }
+ f1.overnext0 = f2;
+ break;
}
}
}
- else // more than one symbol
- {
- ps = null;
- //printf("\tfalse 2\n");
- return false;
- }
+ }
+ else // more than one symbol
+ {
+ ps = null;
+ //printf("\tfalse 2\n");
+ return false;
}
}
ps = s; // s is the one symbol, null if none
import core.stdc.stdio;
import core.stdc.string;
-import dmd.aggregate;
-import dmd.aliasthis;
import dmd.arraytypes;
import dmd.astenums;
import dmd.ast_node;
-import dmd.attrib;
-import dmd.dcast;
-import dmd.dclass;
import dmd.declaration;
-import dmd.dinterpret;
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem : aliasSemantic, oneMembers, toAlias;
import dmd.errors;
import dmd.errorsink;
import dmd.expression;
import dmd.func;
import dmd.globals;
import dmd.hdrgen;
-import dmd.id;
import dmd.identifier;
-import dmd.impcnvtab;
-import dmd.init;
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.templatesem : getExpression, TemplateInstance_semanticTiargs;
import dmd.tokens;
-import dmd.typesem : typeSemantic, isBaseOf, resolveNamedArgs;
import dmd.visitor;
//debug = FindExistingInstance; // print debug stats of findExistingInstance
}
-/******************************
- * See if two objects match
- * Params:
- * o1 = first object
- * o2 = second object
- * Returns: true if they match
- */
-bool match(RootObject o1, RootObject o2)
-{
- enum log = false;
-
- static if (log)
- {
- printf("match() o1 = %p %s (%d), o2 = %p %s (%d)\n",
- o1, o1.toChars(), o1.dyncast(), o2, o2.toChars(), o2.dyncast());
- }
-
- bool yes()
- {
- static if (log)
- printf("\t. match\n");
- return true;
- }
- bool no()
- {
- static if (log)
- printf("\t. nomatch\n");
- return false;
- }
- /* A proper implementation of the various equals() overrides
- * should make it possible to just do o1.equals(o2), but
- * we'll do that another day.
- */
- /* Manifest constants should be compared by their values,
- * at least in template arguments.
- */
-
- if (auto t1 = isType(o1))
- {
- auto t2 = isType(o2);
- if (!t2)
- return no();
-
- static if (log)
- {
- printf("\tt1 = %s\n", t1.toChars());
- printf("\tt2 = %s\n", t2.toChars());
- }
- if (!t1.equals(t2))
- return no();
-
- return yes();
- }
- if (auto e1 = getExpression(o1))
- {
- auto e2 = getExpression(o2);
- if (!e2)
- return no();
-
- static if (log)
- {
- printf("\te1 = %s '%s' %s\n", e1.type ? e1.type.toChars() : "null", EXPtoString(e1.op).ptr, e1.toChars());
- printf("\te2 = %s '%s' %s\n", e2.type ? e2.type.toChars() : "null", EXPtoString(e2.op).ptr, e2.toChars());
- }
-
- // two expressions can be equal although they do not have the same
- // type; that happens when they have the same value. So check type
- // as well as expression equality to ensure templates are properly
- // matched.
- if (!(e1.type && e2.type && e1.type.equals(e2.type)) || !e1.equals(e2))
- return no();
-
- return yes();
- }
- if (auto s1 = isDsymbol(o1))
- {
- auto s2 = isDsymbol(o2);
- if (!s2)
- return no();
-
- static if (log)
- {
- printf("\ts1 = %s \n", s1.kind(), s1.toChars());
- printf("\ts2 = %s \n", s2.kind(), s2.toChars());
- }
- if (!s1.equals(s2))
- return no();
- if (s1.parent != s2.parent && !s1.isFuncDeclaration() && !s2.isFuncDeclaration())
- return no();
-
- return yes();
- }
- if (auto u1 = isTuple(o1))
- {
- auto u2 = isTuple(o2);
- if (!u2)
- return no();
-
- static if (log)
- {
- printf("\tu1 = %s\n", u1.toChars());
- printf("\tu2 = %s\n", u2.toChars());
- }
- if (!arrayObjectMatch(u1.objects, u2.objects))
- return no();
-
- return yes();
- }
- return yes();
-}
-
-/************************************
- * Match an array of them.
- */
-bool arrayObjectMatch(ref Objects oa1, ref Objects oa2)
-{
- if (&oa1 == &oa2)
- return true;
- if (oa1.length != oa2.length)
- return false;
- immutable oa1dim = oa1.length;
- auto oa1d = oa1[].ptr;
- auto oa2d = oa2[].ptr;
- foreach (j; 0 .. oa1dim)
- {
- RootObject o1 = oa1d[j];
- RootObject o2 = oa2d[j];
- if (!match(o1, o2))
- {
- return false;
- }
- }
- return true;
-}
-
-/************************************
- * Return hash of Objects.
- */
-private size_t arrayObjectHash(ref Objects oa1)
-{
- import dmd.root.hash : mixHash;
-
- size_t hash = 0;
- foreach (o1; oa1)
- {
- /* Must follow the logic of match()
- */
- if (auto t1 = isType(o1))
- hash = mixHash(hash, cast(size_t)t1.deco);
- else if (auto e1 = getExpression(o1))
- hash = mixHash(hash, expressionHash(e1));
- else if (auto s1 = isDsymbol(o1))
- {
- if (auto fa1 = s1.isFuncAliasDeclaration())
- s1 = fa1.toAliasFunc();
- hash = mixHash(hash, mixHash(cast(size_t)cast(void*)s1.getIdent(), cast(size_t)cast(void*)s1.parent));
- }
- else if (auto u1 = isTuple(o1))
- hash = mixHash(hash, arrayObjectHash(u1.objects));
- }
- return hash;
-}
-
-
-/************************************
- * Computes hash of expression.
- * Handles all Expression classes and MUST match their equals method,
- * i.e. e1.equals(e2) implies expressionHash(e1) == expressionHash(e2).
- */
-private size_t expressionHash(Expression e)
-{
- import dmd.root.ctfloat : CTFloat;
- import dmd.root.hash : calcHash, mixHash;
-
- switch (e.op)
- {
- case EXP.int64:
- return cast(size_t) e.isIntegerExp().getInteger();
-
- case EXP.float64:
- return CTFloat.hash(e.isRealExp().value);
-
- case EXP.complex80:
- auto ce = e.isComplexExp();
- return mixHash(CTFloat.hash(ce.toReal), CTFloat.hash(ce.toImaginary));
-
- case EXP.identifier:
- return cast(size_t)cast(void*) e.isIdentifierExp().ident;
-
- case EXP.null_:
- return cast(size_t)cast(void*) e.isNullExp().type;
-
- case EXP.string_:
- return calcHash(e.isStringExp.peekData());
-
- case EXP.tuple:
- {
- auto te = e.isTupleExp();
- size_t hash = 0;
- hash += te.e0 ? expressionHash(te.e0) : 0;
- foreach (elem; *te.exps)
- hash = mixHash(hash, expressionHash(elem));
- return hash;
- }
-
- case EXP.arrayLiteral:
- {
- auto ae = e.isArrayLiteralExp();
- size_t hash;
- foreach (i; 0 .. ae.elements.length)
- hash = mixHash(hash, expressionHash(ae[i]));
- return hash;
- }
-
- case EXP.assocArrayLiteral:
- {
- auto ae = e.isAssocArrayLiteralExp();
- size_t hash;
- foreach (i; 0 .. ae.keys.length)
- // reduction needs associative op as keys are unsorted (use XOR)
- hash ^= mixHash(expressionHash((*ae.keys)[i]), expressionHash((*ae.values)[i]));
- return hash;
- }
-
- case EXP.structLiteral:
- {
- auto se = e.isStructLiteralExp();
- size_t hash;
- foreach (elem; *se.elements)
- hash = mixHash(hash, elem ? expressionHash(elem) : 0);
- return hash;
- }
-
- case EXP.variable:
- return cast(size_t)cast(void*) e.isVarExp().var;
-
- case EXP.function_:
- return cast(size_t)cast(void*) e.isFuncExp().fd;
-
- default:
- // no custom equals for this expression
- assert((&e.equals).funcptr is &RootObject.equals);
- // equals based on identity
- return cast(size_t)cast(void*) e;
- }
-}
-
RootObject objectSyntaxCopy(RootObject o)
{
if (!o)
Expression constraint;
// Hash table to look up TemplateInstance's of this TemplateDeclaration
- TemplateInstance[TemplateInstanceBox] instances;
+ void* instances;
TemplateDeclaration overnext; // next overloaded TemplateDeclaration
TemplateDeclaration overroot; // first in overnext list
bool isTrivialAlias; /// matches pattern `template Alias(T) { alias Alias = qualifiers(T); }`
bool deprecated_; /// this template declaration is deprecated
bool isCmacro; /// Whether this template is a translation of a C macro
+ bool haveComputedOneMember; /// Whether computeOneMeber has been called
Visibility visibility;
// threaded list of previous instantiation attempts on stack
this.literal = literal;
this.ismixin = ismixin;
this.isstatic = true;
+ this.haveComputedOneMember = false;
this.visibility = Visibility(Visibility.Kind.undefined);
+ }
- // Compute in advance for Ddoc's use
- // https://issues.dlang.org/show_bug.cgi?id=11153: ident could be NULL if parsing fails.
- if (!members || !ident)
- return;
-
- Dsymbol s;
- if (!oneMembers(members, s, ident) || !s)
- return;
-
- onemember = s;
- s.parent = this;
-
+ extern(D) void computeIsTrivialAlias(Dsymbol s)
+ {
/* Set isTrivialAliasSeq if this fits the pattern:
* template AliasSeq(T...) { alias AliasSeq = T; }
* or set isTrivialAlias if this fits the pattern:
return new TemplateDeclaration(loc, ident, p, constraint ? constraint.syntaxCopy() : null, Dsymbol.arraySyntaxCopy(members), ismixin, literal);
}
- /**********************************
- * Overload existing TemplateDeclaration 'this' with the new one 's'.
- * Params:
- * s = symbol to be inserted
- * Return: true if successful; i.e. no conflict.
- */
- override bool overloadInsert(Dsymbol s)
- {
- static if (LOG)
- {
- printf("TemplateDeclaration.overloadInsert('%s')\n", s.toChars());
- }
- FuncDeclaration fd = s.isFuncDeclaration();
- if (fd)
- {
- if (funcroot)
- return funcroot.overloadInsert(fd);
- funcroot = fd;
- return funcroot.overloadInsert(this);
- }
-
- // https://issues.dlang.org/show_bug.cgi?id=15795
- // if candidate is an alias and its sema is not run then
- // insertion can fail because the thing it alias is not known
- if (AliasDeclaration ad = s.isAliasDeclaration())
- {
- if (s._scope)
- aliasSemantic(ad, s._scope);
- if (ad.aliassym && ad.aliassym is this)
- return false;
- }
- TemplateDeclaration td = s.toAlias().isTemplateDeclaration();
- if (!td)
- return false;
-
- TemplateDeclaration pthis = this;
- TemplateDeclaration* ptd;
- for (ptd = &pthis; *ptd; ptd = &(*ptd).overnext)
- {
- }
-
- td.overroot = this;
- *ptd = td;
- static if (LOG)
- {
- printf("\ttrue: no conflict\n");
- }
- return true;
- }
-
override const(char)* kind() const
{
return (onemember && onemember.isAggregateDeclaration()) ? onemember.kind() : "template";
return visibility;
}
- debug (FindExistingInstance)
- {
- __gshared uint nFound, nNotFound, nAdded, nRemoved;
-
- shared static ~this()
- {
- printf("debug (FindExistingInstance) nFound %u, nNotFound: %u, nAdded: %u, nRemoved: %u\n",
- nFound, nNotFound, nAdded, nRemoved);
- }
- }
-
- /****************************************************
- * Given a new instance `tithis` of this TemplateDeclaration,
- * see if there already exists an instance.
- *
- * Params:
- * tithis = template instance to check
- * argumentList = For function templates, needed because different
- * `auto ref` resolutions create different instances,
- * even when template parameters are identical
- *
- * Returns: that existing instance, or `null` when it doesn't exist
- */
- extern (D) TemplateInstance findExistingInstance(TemplateInstance tithis, ArgumentList argumentList)
- {
- //printf("findExistingInstance() %s\n", tithis.toChars());
- tithis.fargs = argumentList.arguments;
- tithis.fnames = argumentList.names;
- auto tibox = TemplateInstanceBox(tithis);
- auto p = tibox in this.instances;
- debug (FindExistingInstance) ++(p ? nFound : nNotFound);
- //if (p) printf("\tfound %p\n", *p); else printf("\tnot found\n");
- return p ? *p : null;
- }
-
- /********************************************
- * Add instance ti to TemplateDeclaration's table of instances.
- * Return a handle we can use to later remove it if it fails instantiation.
- */
- extern (D) TemplateInstance addInstance(TemplateInstance ti)
- {
- //printf("addInstance() %p %s\n", instances, ti.toChars());
- auto tibox = TemplateInstanceBox(ti);
- instances[tibox] = ti;
- debug (FindExistingInstance) ++nAdded;
- return ti;
- }
-
- /*******************************************
- * Remove TemplateInstance from table of instances.
- * Input:
- * handle returned by addInstance()
- */
- extern (D) void removeInstance(TemplateInstance ti)
- {
- //printf("removeInstance() %s\n", ti.toChars());
- auto tibox = TemplateInstanceBox(ti);
- debug (FindExistingInstance) ++nRemoved;
- instances.remove(tibox);
- }
-
/**
* Check if the last template parameter is a tuple one,
* and returns it if so, else returns `null`.
}
}
-/* ======================== Type ============================================ */
-
-/***********************************************************
- * Check whether the type t representation relies on one or more the template parameters.
- * Params:
- * t = Tested type, if null, returns false.
- * tparams = Template parameters.
- * iStart = Start index of tparams to limit the tested parameters. If it's
- * nonzero, tparams[0..iStart] will be excluded from the test target.
- */
-bool reliesOnTident(Type t, TemplateParameters* tparams, size_t iStart = 0)
-{
- return reliesOnTemplateParameters(t, (*tparams)[0 .. tparams.length]);
-}
-
-/***********************************************************
- * Check whether the type t representation relies on one or more the template parameters.
- * Params:
- * t = Tested type, if null, returns false.
- * tparams = Template parameters.
- */
-bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams)
-{
- bool visitVector(TypeVector t)
- {
- return t.basetype.reliesOnTemplateParameters(tparams);
- }
-
- bool visitAArray(TypeAArray t)
- {
- return t.next.reliesOnTemplateParameters(tparams) ||
- t.index.reliesOnTemplateParameters(tparams);
- }
-
- bool visitFunction(TypeFunction t)
- {
- foreach (i, fparam; t.parameterList)
- {
- if (fparam.type.reliesOnTemplateParameters(tparams))
- return true;
- }
- return t.next.reliesOnTemplateParameters(tparams);
- }
-
- bool visitIdentifier(TypeIdentifier t)
- {
- foreach (tp; tparams)
- {
- if (tp.ident.equals(t.ident))
- return true;
- }
- return false;
- }
-
- bool visitInstance(TypeInstance t)
- {
- foreach (tp; tparams)
- {
- if (t.tempinst.name == tp.ident)
- return true;
- }
-
- if (t.tempinst.tiargs)
- foreach (arg; *t.tempinst.tiargs)
- {
- if (Type ta = isType(arg))
- {
- if (ta.reliesOnTemplateParameters(tparams))
- return true;
- }
- }
-
- return false;
- }
-
- bool visitTypeof(TypeTypeof t)
- {
- //printf("TypeTypeof.reliesOnTemplateParameters('%s')\n", t.toChars());
- return t.exp.reliesOnTemplateParameters(tparams);
- }
-
- bool visitTuple(TypeTuple t)
- {
- if (t.arguments)
- foreach (arg; *t.arguments)
- {
- if (arg.type.reliesOnTemplateParameters(tparams))
- return true;
- }
-
- return false;
- }
-
- if (!t)
- return false;
-
- Type tb = t.toBasetype();
- switch (tb.ty)
- {
- case Tvector: return visitVector(tb.isTypeVector());
- case Taarray: return visitAArray(tb.isTypeAArray());
- case Tfunction: return visitFunction(tb.isTypeFunction());
- case Tident: return visitIdentifier(tb.isTypeIdentifier());
- case Tinstance: return visitInstance(tb.isTypeInstance());
- case Ttypeof: return visitTypeof(tb.isTypeTypeof());
- case Ttuple: return visitTuple(tb.isTypeTuple());
- case Tenum: return false;
- default: return tb.nextOf().reliesOnTemplateParameters(tparams);
- }
-}
-
-/***********************************************************
- * Check whether the expression representation relies on one or more the template parameters.
- * Params:
- * e = expression to test
- * tparams = Template parameters.
- * Returns:
- * true if it does
- */
-private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparams)
-{
- extern (C++) final class ReliesOnTemplateParameters : Visitor
- {
- alias visit = Visitor.visit;
- public:
- TemplateParameter[] tparams;
- bool result;
-
- extern (D) this(TemplateParameter[] tparams) @safe
- {
- this.tparams = tparams;
- }
-
- override void visit(Expression e)
- {
- //printf("Expression.reliesOnTemplateParameters('%s')\n", e.toChars());
- }
-
- override void visit(IdentifierExp e)
- {
- //printf("IdentifierExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- foreach (tp; tparams)
- {
- if (e.ident == tp.ident)
- {
- result = true;
- return;
- }
- }
- }
-
- override void visit(TupleExp e)
- {
- //printf("TupleExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (e.exps)
- {
- foreach (ea; *e.exps)
- {
- ea.accept(this);
- if (result)
- return;
- }
- }
- }
-
- override void visit(ArrayLiteralExp e)
- {
- //printf("ArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (e.elements)
- {
- foreach (el; *e.elements)
- {
- el.accept(this);
- if (result)
- return;
- }
- }
- }
-
- override void visit(AssocArrayLiteralExp e)
- {
- //printf("AssocArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- foreach (ek; *e.keys)
- {
- ek.accept(this);
- if (result)
- return;
- }
- foreach (ev; *e.values)
- {
- ev.accept(this);
- if (result)
- return;
- }
- }
-
- override void visit(StructLiteralExp e)
- {
- //printf("StructLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (e.elements)
- {
- foreach (ea; *e.elements)
- {
- ea.accept(this);
- if (result)
- return;
- }
- }
- }
-
- override void visit(TypeExp e)
- {
- //printf("TypeExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- result = e.type.reliesOnTemplateParameters(tparams);
- }
-
- override void visit(NewExp e)
- {
- //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (e.placement)
- e.placement.accept(this);
- if (e.thisexp)
- e.thisexp.accept(this);
- result = e.newtype.reliesOnTemplateParameters(tparams);
- if (!result && e.arguments)
- {
- foreach (ea; *e.arguments)
- {
- ea.accept(this);
- if (result)
- return;
- }
- }
- }
-
- override void visit(NewAnonClassExp e)
- {
- //printf("NewAnonClassExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- result = true;
- }
-
- override void visit(FuncExp e)
- {
- //printf("FuncExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- result = true;
- }
-
- override void visit(TypeidExp e)
- {
- //printf("TypeidExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (auto ea = isExpression(e.obj))
- ea.accept(this);
- else if (auto ta = isType(e.obj))
- result = ta.reliesOnTemplateParameters(tparams);
- }
-
- override void visit(TraitsExp e)
- {
- //printf("TraitsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (e.args)
- {
- foreach (oa; *e.args)
- {
- if (auto ea = isExpression(oa))
- ea.accept(this);
- else if (auto ta = isType(oa))
- result = ta.reliesOnTemplateParameters(tparams);
- if (result)
- return;
- }
- }
- }
-
- override void visit(IsExp e)
- {
- //printf("IsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- result = e.targ.reliesOnTemplateParameters(tparams);
- }
-
- override void visit(UnaExp e)
- {
- //printf("UnaExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- e.e1.accept(this);
- }
-
- override void visit(DotTemplateInstanceExp e)
- {
- //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- visit(e.isUnaExp());
- if (!result && e.ti.tiargs)
- {
- foreach (oa; *e.ti.tiargs)
- {
- if (auto ea = isExpression(oa))
- ea.accept(this);
- else if (auto ta = isType(oa))
- result = ta.reliesOnTemplateParameters(tparams);
- if (result)
- return;
- }
- }
- }
-
- override void visit(CallExp e)
- {
- //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- visit(e.isUnaExp());
- if (!result && e.arguments)
- {
- foreach (ea; *e.arguments)
- {
- ea.accept(this);
- if (result)
- return;
- }
- }
- }
-
- override void visit(CastExp e)
- {
- //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- visit(e.isUnaExp());
- // e.to can be null for cast() with no type
- if (!result && e.to)
- result = e.to.reliesOnTemplateParameters(tparams);
- }
-
- override void visit(SliceExp e)
- {
- //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- visit(e.isUnaExp());
- if (!result && e.lwr)
- e.lwr.accept(this);
- if (!result && e.upr)
- e.upr.accept(this);
- }
-
- override void visit(IntervalExp e)
- {
- //printf("IntervalExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- e.lwr.accept(this);
- if (!result)
- e.upr.accept(this);
- }
-
- override void visit(ArrayExp e)
- {
- //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- visit(e.isUnaExp());
- if (!result && e.arguments)
- {
- foreach (ea; *e.arguments)
- ea.accept(this);
- }
- }
-
- override void visit(BinExp e)
- {
- //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- e.e1.accept(this);
- if (!result)
- e.e2.accept(this);
- }
-
- override void visit(CondExp e)
- {
- //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- e.econd.accept(this);
- if (!result)
- visit(e.isBinExp());
- }
- }
-
- scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams);
- e.accept(v);
- return v.result;
-}
-
/***********************************************************
* https://dlang.org/spec/template.html#TemplateParameter
*/
abstract TemplateParameter syntaxCopy();
- abstract bool declareParameter(Scope* sc);
-
abstract void print(RootObject oarg, RootObject oded);
abstract RootObject specialization();
return new TemplateTypeParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
}
- override final bool declareParameter(Scope* sc)
- {
- //printf("TemplateTypeParameter.declareParameter('%s')\n", ident.toChars());
- auto ti = new TypeIdentifier(loc, ident);
- Declaration ad = new AliasDeclaration(loc, ident, ti);
- return sc.insert(ad) !is null;
- }
-
override final void print(RootObject oarg, RootObject oded)
{
printf(" %s\n", ident.toChars());
defaultValue ? defaultValue.syntaxCopy() : null);
}
- override bool declareParameter(Scope* sc)
- {
- /*
- Do type semantic earlier.
-
- This means for certain erroneous value parameters
- their "type" can be known earlier and thus a better
- error message given.
-
- For example:
- `template test(x* x) {}`
- now yields "undefined identifier" rather than the opaque
- "variable `x` is used as a type".
- */
- if (valType)
- valType = valType.typeSemantic(loc, sc);
- auto v = new VarDeclaration(loc, valType, ident, null);
- v.storage_class = STC.templateparameter;
- return sc.insert(v) !is null;
- }
-
override void print(RootObject oarg, RootObject oded)
{
printf(" %s\n", ident.toChars());
return new TemplateAliasParameter(loc, ident, specType ? specType.syntaxCopy() : null, objectSyntaxCopy(specAlias), objectSyntaxCopy(defaultAlias));
}
- override bool declareParameter(Scope* sc)
- {
- auto ti = new TypeIdentifier(loc, ident);
- Declaration ad = new AliasDeclaration(loc, ident, ti);
- return sc.insert(ad) !is null;
- }
-
override void print(RootObject oarg, RootObject oded)
{
printf(" %s\n", ident.toChars());
return new TemplateTupleParameter(loc, ident);
}
- override bool declareParameter(Scope* sc)
- {
- auto ti = new TypeIdentifier(loc, ident);
- Declaration ad = new AliasDeclaration(loc, ident, ti);
- return sc.insert(ad) !is null;
- }
-
override void print(RootObject oarg, RootObject oded)
{
printf(" %s... [", ident.toChars());
TemplateInstance inst;
ScopeDsymbol argsym; // argument symbol table
- size_t hash; // cached result of toHash()
/// For function template, these are the function fnames(name and loc of it) and arguments
/// Relevant because different resolutions of `auto ref` parameters
return ident;
}
- /*************************************
- * Compare proposed template instantiation with existing template instantiation.
- * Note that this is not commutative because of the auto ref check.
- * Params:
- * ti = existing template instantiation
- * Returns:
- * true for match
- */
- final bool equalsx(TemplateInstance ti)
- {
- //printf("this = %p, ti = %p\n", this, ti);
- assert(tdtypes.length == ti.tdtypes.length);
-
- // Nesting must match
- if (enclosing != ti.enclosing)
- {
- //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : "");
- return false;
- }
- //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars());
-
- if (!arrayObjectMatch(tdtypes, ti.tdtypes))
- return false;
-
- /* Template functions may have different instantiations based on
- * "auto ref" parameters.
- */
- auto fd = ti.toAlias().isFuncDeclaration();
- if (!fd)
- return true;
- if (fd.errors)
- return true;
-
- auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs(
- ArgumentList(this.fargs, this.fnames), null);
-
- // 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;
-
- 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;
- }
-
- extern (D) final size_t toHash()
- {
- if (!hash)
- {
- hash = cast(size_t)cast(void*)enclosing;
- hash += arrayObjectHash(tdtypes);
- hash += hash == 0;
- }
- return hash;
- }
-
- /**********************************
- * Run semantic on the elements of tiargs.
- * Input:
- * sc
- * Returns:
- * false if one or more arguments have errors.
- * Note:
- * This function is reentrant against error occurrence. If returns false,
- * all elements of tiargs won't be modified.
- */
- extern (D) final bool semanticTiargs(Scope* sc)
- {
- //printf("+TemplateInstance.semanticTiargs() %s\n", toChars());
- if (semantictiargsdone)
- return true;
- if (TemplateInstance_semanticTiargs(loc, sc, tiargs, 0))
- {
- // cache the result iff semantic analysis succeeded entirely
- semantictiargsdone = 1;
- return true;
- }
- return false;
- }
-
- /*****************************************
- * Determines if a TemplateInstance will need a nested
- * generation of the TemplateDeclaration.
- * Sets enclosing property if so, and returns != 0;
- */
- extern (D) final bool hasNestedArgs(Objects* args, bool isstatic)
- {
- int nested = 0;
- //printf("TemplateInstance.hasNestedArgs('%s')\n", tempdecl.ident.toChars());
-
- // arguments from parent instances are also accessible
- if (!enclosing)
- {
- if (TemplateInstance ti = tempdecl.toParent().isTemplateInstance())
- enclosing = ti.enclosing;
- }
-
- /* A nested instance happens when an argument references a local
- * symbol that is on the stack.
- */
- foreach (o; *args)
- {
- Expression ea = isExpression(o);
- Dsymbol sa = isDsymbol(o);
- Tuple va = isTuple(o);
- if (ea)
- {
- if (auto ve = ea.isVarExp())
- {
- sa = ve.var;
- goto Lsa;
- }
- if (auto te = ea.isThisExp())
- {
- sa = te.var;
- goto Lsa;
- }
- if (auto fe = ea.isFuncExp())
- {
- if (fe.td)
- sa = fe.td;
- else
- sa = fe.fd;
- goto Lsa;
- }
- // Emulate Expression.toMangleBuffer call that had exist in TemplateInstance.genIdent.
- if (ea.op != EXP.int64 && ea.op != EXP.float64 && ea.op != EXP.complex80 && ea.op != EXP.null_ && ea.op != EXP.string_ && ea.op != EXP.arrayLiteral && ea.op != EXP.assocArrayLiteral && ea.op != EXP.structLiteral)
- {
- if (!ea.type.isTypeError())
- .error(ea.loc, "%s `%s` expression `%s` is not a valid template value argument", kind, toPrettyChars, ea.toChars());
- errors = true;
- }
- }
- else if (sa)
- {
- Lsa:
- sa = sa.toAlias();
- TemplateDeclaration td = sa.isTemplateDeclaration();
- if (td)
- {
- TemplateInstance ti = sa.toParent().isTemplateInstance();
- if (ti && ti.enclosing)
- sa = ti;
- }
- TemplateInstance ti = sa.isTemplateInstance();
- Declaration d = sa.isDeclaration();
- if ((td && td.literal) || (ti && ti.enclosing) || (d && !d.isDataseg() && !(d.storage_class & STC.manifest) && (!d.isFuncDeclaration() || d.isFuncDeclaration().isNested()) && !isTemplateMixin()))
- {
- Dsymbol dparent = sa.toParent2();
- if (!dparent || dparent.isModule)
- goto L1;
- else if (!enclosing)
- enclosing = dparent;
- else if (enclosing != dparent)
- {
- /* Select the more deeply nested of the two.
- * Error if one is not nested inside the other.
- */
- for (Dsymbol p = enclosing; p; p = p.parent)
- {
- if (p == dparent)
- goto L1; // enclosing is most nested
- }
- for (Dsymbol p = dparent; p; p = p.parent)
- {
- if (p == enclosing)
- {
- enclosing = dparent;
- goto L1; // dparent is most nested
- }
- }
- //https://issues.dlang.org/show_bug.cgi?id=17870
- if (dparent.isClassDeclaration() && enclosing.isClassDeclaration())
- {
- auto pc = dparent.isClassDeclaration();
- auto ec = enclosing.isClassDeclaration();
- if (pc.isBaseOf(ec, null))
- goto L1;
- else if (ec.isBaseOf(pc, null))
- {
- enclosing = dparent;
- goto L1;
- }
- }
- .error(loc, "%s `%s` `%s` is nested in both `%s` and `%s`", kind, toPrettyChars, toChars(), enclosing.toChars(), dparent.toChars());
- errors = true;
- }
- L1:
- //printf("\tnested inside %s as it references %s\n", enclosing.toChars(), sa.toChars());
- nested |= 1;
- }
- }
- else if (va)
- {
- nested |= cast(int)hasNestedArgs(&va.objects, isstatic);
- }
- }
- //printf("-TemplateInstance.hasNestedArgs('%s') = %d\n", tempdecl.ident.toChars(), nested);
- return nested != 0;
- }
-
/****************************************
* This instance needs an identifier for name mangling purposes.
* Create one by taking the template declaration name and adding
}
}
-/************************************
- * This struct is needed for TemplateInstance to be the key in an associative array.
- * Fixing https://issues.dlang.org/show_bug.cgi?id=15813 would make it unnecessary.
- */
-struct TemplateInstanceBox
-{
- TemplateInstance ti;
-
- this(TemplateInstance ti)
- {
- this.ti = ti;
- this.ti.toHash();
- assert(this.ti.hash);
- }
-
- size_t toHash() const @safe pure nothrow
- {
- assert(ti.hash);
- return ti.hash;
- }
-
- bool opEquals(ref const TemplateInstanceBox s) @trusted const
- {
- bool res = void;
- if (ti.inst && s.ti.inst)
- {
- /* This clause is only used when an instance with errors
- * is replaced with a correct instance.
- */
- res = ti is s.ti;
- }
- else
- {
- /* Used when a proposed instance is used to see if there's
- * an existing instance.
- */
- static if (__VERSION__ < 2099) // https://issues.dlang.org/show_bug.cgi?id=22717
- res = (cast()s.ti).equalsx(cast()ti);
- else
- res = (cast()ti).equalsx(cast()s.ti);
- }
-
- debug (FindExistingInstance) ++(res ? nHits : nCollisions);
- return res;
- }
-
- debug (FindExistingInstance)
- {
- __gshared uint nHits, nCollisions;
-
- shared static ~this()
- {
- printf("debug (FindExistingInstance) TemplateInstanceBox.equals hits: %u collisions: %u\n",
- nHits, nCollisions);
- }
- }
-}
-
/***********************************************
* Collect and print statistics on template instantiations.
*/
}
}
-/// Pair of MATCHes
-struct MATCHpair
-{
- MATCH mta; /// match template parameters by initial template arguments
- MATCH mfa; /// match template parameters by inferred template arguments
-
- debug this(MATCH mta, MATCH mfa)
- {
- assert(MATCH.min <= mta && mta <= MATCH.max);
- assert(MATCH.min <= mfa && mfa <= MATCH.max);
- this.mta = mta;
- this.mfa = mfa;
- }
-}
-
void write(ref OutBuffer buf, RootObject obj)
{
if (obj)
import dmd.astcodegen;
import dmd.astenums;
import dmd.arraytypes;
-import dmd.attrib;
-import dmd.dsymbol;
import dmd.dsymbolsem;
+import dmd.templatesem : computeOneMember;
+import dmd.expressionsem : toInteger;
import dmd.errors;
+import dmd.errorsink;
import dmd.globals;
import dmd.hdrgen;
import dmd.id;
*
* Params:
* ms = the modules
+ * eSink = where to report errors
*
* Notes:
* - the header is written to `<global.params.cxxhdrdir>/<global.params.cxxhdrfile>`
* - ignored declarations are mentioned in a comment if `global.params.doCxxHdrGeneration`
* is set to `CxxHeaderMode.verbose`
*/
-void genCppHdrFiles(ref Modules ms)
+void genCppHdrFiles(ref Modules ms, ErrorSink eSink)
{
initialize();
decl.doindent = true;
decl.spaces = true;
- scope v = new ToCppBuffer(&fwd, &done, &decl);
+ scope v = new ToCppBuffer(&fwd, &done, &decl, eSink);
// Conditionally include another buffer for sanity checks
debug (Debug_DtoH_Checks)
/// Default buffer for the currently visited declaration
OutBuffer* buf;
+ /// Sink for error reporting
+ ErrorSink eSink;
+
/// The generated header uses `real` emitted as `_d_real`?
bool hasReal = false;
}
mixin(generateMembers());
- this(OutBuffer* fwdbuf, OutBuffer* donebuf, OutBuffer* buf) scope
+ this(OutBuffer* fwdbuf, OutBuffer* donebuf, OutBuffer* buf, ErrorSink eSink) scope
{
this.fwdbuf = fwdbuf;
this.donebuf = donebuf;
this.buf = buf;
this.printIgnored = global.params.cxxhdr.fullOutput;
+ this.eSink = eSink;
}
/**
}
__gshared bool warned = false;
- warning(loc, "%s `%s` is a %s", kind, ident.toChars(), reason);
+ eSink.warning(loc, "%s `%s` is a %s", kind, ident.toChars(), reason);
if (!warned)
{
- warningSupplemental(loc, "The generated C++ header will contain " ~
- "identifiers that are keywords in C++");
+ eSink.warningSupplemental(loc, "The generated C++ header will contain " ~
+ "identifiers that are keywords in C++");
warned = true;
}
}
debug (Debug_DtoH)
{
mixin(traceVisit!s);
- import dmd.asttypename;
printf("[AST.Dsymbol enter] %s\n", s.astTypeName().ptr);
}
}
// `this` but accessible via `outer`
if (auto td = s.isThisDeclaration())
{
- import dmd.id;
this.ident = Id.outer;
}
else
if (!shouldEmitAndMarkVisited(td))
return;
+ td.computeOneMember();
if (!td.parameters || !td.onemember || (!td.onemember.isStructDeclaration && !td.onemember.isClassDeclaration && !td.onemember.isFuncDeclaration))
{
visit(cast(AST.Dsymbol)td);
}
else
{
- import dmd.hdrgen;
// Hex floating point literals were introduced in C++ 17
const allowHex = global.params.cplusplus >= CppStdRevision.cpp17;
floatToBuffer(e.type, e.value, *buf, allowHex);
module dmd.dversion;
-import dmd.arraytypes;
-import dmd.cond;
-import dmd.dmodule;
-import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem;
import dmd.identifier;
import dmd.location;
-import dmd.common.outbuffer;
import dmd.visitor;
/***********************************************************
bool inuse(bool v);
EnumDeclaration *syntaxCopy(Dsymbol *s) override;
- Type *getType() override;
const char *kind() const override;
bool isDeprecated() const override; // is Dsymbol deprecated?
Visibility visible() override;
else if (first)
{
Type t;
+ bool terror;
if (em.ed.memtype)
+ {
t = em.ed.memtype;
+ if (!t.isScalar())
+ {
+ t = Type.terror; // print more relevant error message later
+ terror = true;
+ }
+ }
else
{
t = Type.tint32;
if (!em.ed.isAnonymous())
em.ed.memtype = t;
}
+
const errors = global.startGagging();
Expression e = new IntegerExp(em.loc, 0, t);
e = e.ctfeInterpret();
- if (global.endGagging(errors))
+ if (global.endGagging(errors) || terror)
{
error(em.loc, "cannot generate 0 value of type `%s` for `%s`",
- t.toChars(), em.toChars());
+ em.ed.memtype.toChars(), em.toChars());
}
// save origValue for better json output
em.origValue = e;
void verror(Loc loc, const(char)* format, va_list ap)
{
- verrorReport(loc, format, ap, ErrorKind.error);
+ vreportDiagnostic(loc, format, ap, ErrorKind.error);
}
void verrorSupplemental(Loc loc, const(char)* format, va_list ap)
{
- verrorReportSupplemental(loc, format, ap, ErrorKind.error);
+ vsupplementalDiagnostic(loc, format, ap, ErrorKind.error);
}
void vwarning(Loc loc, const(char)* format, va_list ap)
{
- verrorReport(loc, format, ap, ErrorKind.warning);
+ vreportDiagnostic(loc, format, ap, ErrorKind.warning);
}
void vwarningSupplemental(Loc loc, const(char)* format, va_list ap)
{
- verrorReportSupplemental(loc, format, ap, ErrorKind.warning);
+ vsupplementalDiagnostic(loc, format, ap, ErrorKind.warning);
}
void vdeprecation(Loc loc, const(char)* format, va_list ap)
{
- verrorReport(loc, format, ap, ErrorKind.deprecation);
+ vreportDiagnostic(loc, format, ap, ErrorKind.deprecation);
}
void vdeprecationSupplemental(Loc loc, const(char)* format, va_list ap)
{
- verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation);
+ vsupplementalDiagnostic(loc, format, ap, ErrorKind.deprecation);
}
void vmessage(Loc loc, const(char)* format, va_list ap)
{
- verrorReport(loc, format, ap, ErrorKind.message);
+ vreportDiagnostic(loc, format, ap, ErrorKind.message);
}
}
{
va_list ap;
va_start(ap, format);
- verrorReport(loc, format, ap, ErrorKind.error);
+ vreportDiagnostic(loc, format, ap, ErrorKind.error);
va_end(ap);
}
else
{
va_list ap;
va_start(ap, format);
- verrorReport(loc, format, ap, ErrorKind.error);
+ vreportDiagnostic(loc, format, ap, ErrorKind.error);
va_end(ap);
}
const loc = SourceLoc(filename.toDString, linnum, charnum);
va_list ap;
va_start(ap, format);
- verrorReport(loc, format, ap, ErrorKind.error);
+ vreportDiagnostic(loc, format, ap, ErrorKind.error);
va_end(ap);
}
else
const loc = SourceLoc(filename.toDString, linnum, charnum);
va_list ap;
va_start(ap, format);
- verrorReport(loc, format, ap, ErrorKind.error);
+ vreportDiagnostic(loc, format, ap, ErrorKind.error);
va_end(ap);
}
const loc = SourceLoc(filename.toDString, linnum, charnum);
va_list ap;
va_start(ap, format);
- verrorReport(loc, format, ap, ErrorKind.error);
+ vreportDiagnostic(loc, format, ap, ErrorKind.error);
va_end(ap);
}
{
va_list ap;
va_start(ap, format);
- verrorReportSupplemental(loc, format, ap, ErrorKind.error);
+ vsupplementalDiagnostic(loc, format, ap, ErrorKind.error);
va_end(ap);
}
else
{
va_list ap;
va_start(ap, format);
- verrorReportSupplemental(loc, format, ap, ErrorKind.error);
+ vsupplementalDiagnostic(loc, format, ap, ErrorKind.error);
va_end(ap);
}
{
va_list ap;
va_start(ap, format);
- verrorReport(loc, format, ap, ErrorKind.warning);
+ vreportDiagnostic(loc, format, ap, ErrorKind.warning);
va_end(ap);
}
else
{
va_list ap;
va_start(ap, format);
- verrorReport(loc, format, ap, ErrorKind.warning);
+ vreportDiagnostic(loc, format, ap, ErrorKind.warning);
va_end(ap);
}
{
va_list ap;
va_start(ap, format);
- verrorReportSupplemental(loc, format, ap, ErrorKind.warning);
+ vsupplementalDiagnostic(loc, format, ap, ErrorKind.warning);
va_end(ap);
}
else
{
va_list ap;
va_start(ap, format);
- verrorReportSupplemental(loc, format, ap, ErrorKind.warning);
+ vsupplementalDiagnostic(loc, format, ap, ErrorKind.warning);
va_end(ap);
}
{
va_list ap;
va_start(ap, format);
- verrorReport(loc, format, ap, ErrorKind.deprecation);
+ vreportDiagnostic(loc, format, ap, ErrorKind.deprecation);
va_end(ap);
}
else
{
va_list ap;
va_start(ap, format);
- verrorReport(loc, format, ap, ErrorKind.deprecation);
+ vreportDiagnostic(loc, format, ap, ErrorKind.deprecation);
va_end(ap);
}
{
va_list ap;
va_start(ap, format);
- verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation);
+ vsupplementalDiagnostic(loc, format, ap, ErrorKind.deprecation);
va_end(ap);
}
else
{
va_list ap;
va_start(ap, format);
- verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation);
+ vsupplementalDiagnostic(loc, format, ap, ErrorKind.deprecation);
va_end(ap);
}
{
va_list ap;
va_start(ap, format);
- verrorReport(loc, format, ap, ErrorKind.message);
+ vreportDiagnostic(loc, format, ap, ErrorKind.message);
va_end(ap);
}
else
{
va_list ap;
va_start(ap, format);
- verrorReport(loc, format, ap, ErrorKind.message);
+ vreportDiagnostic(loc, format, ap, ErrorKind.message);
va_end(ap);
}
{
va_list ap;
va_start(ap, format);
- verrorReport(Loc.initial, format, ap, ErrorKind.message);
+ vreportDiagnostic(Loc.initial, format, ap, ErrorKind.message);
va_end(ap);
}
else
{
va_list ap;
va_start(ap, format);
- verrorReport(Loc.initial, format, ap, ErrorKind.message);
+ vreportDiagnostic(Loc.initial, format, ap, ErrorKind.message);
va_end(ap);
}
/**
* The type of the diagnostic handler
- * see verrorReport for arguments
+ * see vreportDiagnostic for arguments
* Returns: true if error handling is done, false to continue printing to stderr
*/
alias DiagnosticHandler = bool delegate(const ref SourceLoc location, Color headerColor, const(char)* header, const(char)* messageFormat, va_list args, const(char)* prefix1, const(char)* prefix2);
{
va_list ap;
va_start(ap, format);
- verrorReport(Loc.initial, format, ap, ErrorKind.tip);
+ vreportDiagnostic(Loc.initial, format, ap, ErrorKind.tip);
va_end(ap);
}
else
{
va_list ap;
va_start(ap, format);
- verrorReport(Loc.initial, format, ap, ErrorKind.tip);
+ vreportDiagnostic(Loc.initial, format, ap, ErrorKind.tip);
va_end(ap);
}
* p1 = additional message prefix
* p2 = additional message prefix
*/
-private extern(C++) void verrorReport(Loc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null)
+private extern(C++) void vreportDiagnostic(Loc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null)
{
- return verrorReport(loc.SourceLoc, format, ap, kind, p1, p2);
+ return vreportDiagnostic(loc.SourceLoc, format, ap, kind, p1, p2);
}
/// ditto
-private extern(C++) void verrorReport(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null);
+private extern(C++) void vreportDiagnostic(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null);
/**
* Implements $(D errorSupplemental), $(D warningSupplemental), and
* ap = printf-style variadic arguments
* kind = kind of error being printed
*/
-private extern(C++) void verrorReportSupplemental(Loc loc, const(char)* format, va_list ap, ErrorKind kind)
+private extern(C++) void vsupplementalDiagnostic(Loc loc, const(char)* format, va_list ap, ErrorKind kind)
{
- return verrorReportSupplemental(loc.SourceLoc, format, ap, kind);
+ return vsupplementalDiagnostic(loc.SourceLoc, format, ap, kind);
}
/// ditto
-private extern(C++) void verrorReportSupplemental(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind);
+private extern(C++) void vsupplementalDiagnostic(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind);
/**
* The type of the fatal error handler
import dmd.dsymbol;
import dmd.errors;
import dmd.expression;
+import dmd.expressionsem;
import dmd.func;
import dmd.funcsem;
import dmd.globals : FeatureState;
+import dmd.hdrgen : toErrMsg;
import dmd.id;
import dmd.identifier;
import dmd.init;
import dmd.rootobject;
import dmd.safe;
import dmd.tokens;
-import dmd.typesem : hasPointers, parameterStorageClass;
+import dmd.typesem;
import dmd.visitor;
import dmd.arraytypes;
package(dmd) struct EscapeState
{
// Maps `sequenceNumber` of a `VarDeclaration` to an object that contains the
- // reason it failed to infer `scope`
+ // reason it inferred or didn't infer `scope` for supplemental error messages
// https://issues.dlang.org/show_bug.cgi?id=23295
- private __gshared RootObject[int] scopeInferFailure;
+ private __gshared RootObject[int] scopeInferReason;
/// Called by `initDMD` / `deinitializeDMD` to reset global state
static void reset()
{
- scopeInferFailure = null;
+ scopeInferReason = null;
}
}
}
/**
- * A `scope` variable was assigned to non-scope parameter `v`.
- * If applicable, print why the parameter was not inferred `scope`.
+ * An error occured due to `v` either being or not being `scope`.
+ * If applicable, print why the `v` was inferred that way.
*
* Params:
* printFunc = error/deprecation print function to use
* v = parameter that was not inferred
* recursionLimit = recursion limit for printing the reason
+ * positive = true for telling why v is scope, false for telling why v is not scope
*/
-private
-void printScopeFailure(E)(E printFunc, VarDeclaration v, int recursionLimit)
+public
+void printScopeReason(E)(E printFunc, VarDeclaration v, int recursionLimit, bool positive)
{
recursionLimit--;
if (recursionLimit < 0 || !v)
return;
- if (RootObject* o = v.sequenceNumber in EscapeState.scopeInferFailure)
+ if (RootObject* o = v.sequenceNumber in EscapeState.scopeInferReason)
{
switch ((*o).dyncast())
{
case DYNCAST.expression:
Expression e = cast(Expression) *o;
- printFunc(e.loc, "which is not `scope` because of `%s`", e.toChars());
+ if (positive)
+ printFunc(e.loc, "`%s` inferred `scope` because of `%s`", v.toErrMsg(), e.toErrMsg());
+ else
+ printFunc(e.loc, "`%s` is not `scope` because of `%s`", v.toErrMsg(), e.toErrMsg());
break;
case DYNCAST.dsymbol:
VarDeclaration v1 = cast(VarDeclaration) *o;
- printFunc(v1.loc, "which is assigned to non-scope parameter `%s`", v1.toChars());
- printScopeFailure(printFunc, v1, recursionLimit);
+ assert(!positive);
+ printFunc(v1.loc, "`%s` is assigned to non-scope parameter `%s`", v.toErrMsg(), v1.toErrMsg());
+ printScopeReason(printFunc, v1, recursionLimit, positive);
break;
default:
assert(0);
(desc ~ " `%s` assigned to non-scope anonymous parameter");
if (isThis ?
- sc.setUnsafeDIP1000(gag, arg.loc, msg, arg, fdc.toParent2(), fdc) :
- sc.setUnsafeDIP1000(gag, arg.loc, msg, v, parId ? parId : fdc, fdc))
+ sc.setUnsafeDIP1000(gag, arg.loc, vPar, msg, arg, fdc.toParent2(), fdc) :
+ sc.setUnsafeDIP1000(gag, arg.loc, vPar, msg, v, parId ? parId : fdc, fdc))
{
result = true;
- printScopeFailure(previewSupplementalFunc(sc.isDeprecated(), sc.useDIP1000), vPar, 10);
+ printScopeReason(previewSupplementalFunc(sc.isDeprecated(), sc.useDIP1000), vPar, 10, false);
+ printScopeReason(previewSupplementalFunc(sc.isDeprecated(), sc.useDIP1000), v, 10, true);
}
}
if (vaIsFirstRef && v.isParameter() && v.isReturn())
{
// va=v, where v is `return scope`
- if (inferScope(va))
+ if (inferScope(va, e))
return;
}
// v = scope, va should be scope as well
const vaWasScope = va && va.isScope();
- if (inferScope(va))
+ if (inferScope(va, e))
{
// In case of `scope local = returnScopeParam`, do not infer return scope for `x`
if (!vaWasScope && v.isReturn() && !va.isReturn())
if (p != sc.func)
return;
- if (inferScope(va))
+ if (inferScope(va, e))
{
if (v.isReturn() && !va.isReturn())
va.storage_class |= STC.return_ | STC.returninferred;
else
{
// https://issues.dlang.org/show_bug.cgi?id=17029
- result |= sc.setUnsafeDIP1000(gag, e.loc, "returning scope variable `%s`", v);
- return;
+ if (sc.setUnsafeDIP1000(gag, e.loc, "returning scope variable `%s`", v))
+ {
+ printScopeReason(previewSupplementalFunc(sc.isDeprecated(), sc.useDIP1000), v, 10, true);
+ result = true;
+ return;
+ }
}
}
}
*
* Params:
* va = variable to infer scope for
+ * reason = optional Expression that causes `va` to infer scope, used for supplemental error message
* Returns: `true` if succesful or already `scope`
*/
private
-bool inferScope(VarDeclaration va)
+bool inferScope(VarDeclaration va, RootObject reason)
{
if (!va)
return false;
if (!va.isDataseg() && va.maybeScope && !va.isScope())
{
//printf("inferring scope for %s\n", va.toChars());
+ if (reason)
+ EscapeState.scopeInferReason[va.sequenceNumber] = reason;
va.maybeScope = false;
va.storage_class |= STC.scope_ | STC.scopeinferred;
return true;
{
v.maybeScope = false;
if (o && v.isParameter())
- EscapeState.scopeInferFailure[v.sequenceNumber] = o;
+ EscapeState.scopeInferReason[v.sequenceNumber] = o;
}
}
foreach (u, p; f.parameterList)
{
auto v = (*funcdecl.parameters)[u];
- if (!v.isScope() && v.type.hasPointers() && inferScope(v))
+ if (!v.isScope() && v.type.hasPointers() && inferScope(v, null))
{
//printf("Inferring scope for %s\n", v.toChars());
p.storageClass |= STC.scope_ | STC.scopeinferred;
if (funcdecl.vthis)
{
- inferScope(funcdecl.vthis);
+ inferScope(funcdecl.vthis, null);
f.isScopeQual = funcdecl.vthis.isScope();
f.isScopeInferred = !!(funcdecl.vthis.storage_class & STC.scopeinferred);
}
return setUnsafePreview(&sc, sc.useDIP1000, gag, loc, msg, args);
}
+/// Overload for scope violations that also stores the variable whose scope status caused the issue
+private
+bool setUnsafeDIP1000(ref Scope sc, bool gag, Loc loc, VarDeclaration scopeVar,
+ const(char)* msg, RootObject[] args...)
+{
+ return setUnsafePreview(&sc, sc.useDIP1000, gag, loc, scopeVar, msg, args);
+}
+
/***************************************
* Check that taking the address of `v` is `@safe`
*
import core.stdc.stdio;
import core.stdc.string;
-import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
import dmd.ast_node;
-import dmd.ctfeexpr : isCtfeReferenceValid;
-import dmd.dcast : implicitConvTo;
import dmd.dclass;
import dmd.declaration;
-import dmd.dimport;
-import dmd.dmodule;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
import dmd.errors;
-import dmd.errorsink;
-import dmd.expressionsem : getDsymbol;
import dmd.func;
import dmd.globals;
-import dmd.hdrgen;
+import dmd.hdrgen : toChars;
import dmd.id;
import dmd.identifier;
import dmd.init;
import dmd.mtype;
import dmd.root.complex;
import dmd.root.ctfloat;
-import dmd.common.outbuffer;
-import dmd.root.optional;
import dmd.root.rmem;
import dmd.rootobject;
import dmd.root.string;
import dmd.root.utf;
-import dmd.target;
import dmd.tokens;
-import dmd.typesem : toHeadMutable, size, mutableOf, unSharedOf;
import dmd.visitor;
enum LOGSEMANTIC = false;
}
-/****************************************
- * Expand tuples in-place.
- *
- * Example:
- * When there's a call `f(10, pair: AliasSeq!(20, 30), single: 40)`, the input is:
- * `exps = [10, (20, 30), 40]`
- * `names = [null, "pair", "single"]`
- * The arrays will be modified to:
- * `exps = [10, 20, 30, 40]`
- * `names = [null, "pair", null, "single"]`
- *
- * Params:
- * exps = array of Expressions
- * names = optional array of names corresponding to Expressions
- */
-void expandTuples(Expressions* exps, ArgumentLabels* names = null)
-{
- //printf("expandTuples()\n");
- if (exps is null)
- return;
-
- if (names)
- {
- if (exps.length != names.length)
- {
- printf("exps.length = %d, names.length = %d\n", cast(int) exps.length, cast(int) names.length);
- printf("exps = %s, names = %s\n", exps.toChars(), names.toChars());
- if (exps.length > 0)
- printf("%s\n", (*exps)[0].loc.toChars());
- assert(0);
- }
- }
-
- // At `index`, a tuple of length `length` is expanded. Insert corresponding nulls in `names`.
- void expandNames(size_t index, size_t length)
- {
- if (names)
- {
- if (length == 0)
- {
- names.remove(index);
- return;
- }
- foreach (i; 1 .. length)
- {
- names.insert(index + i, ArgumentLabel(cast(Identifier) null, Loc.init));
- }
- }
- }
-
- for (size_t i = 0; i < exps.length; i++)
- {
- Expression arg = (*exps)[i];
- if (!arg)
- continue;
-
- // Look for tuple with 0 members
- if (auto e = arg.isTypeExp())
- {
- if (auto tt = e.type.toBasetype().isTypeTuple())
- {
- if (!tt.arguments || tt.arguments.length == 0)
- {
- exps.remove(i);
- expandNames(i, 0);
- if (i == exps.length)
- return;
- }
- else // Expand a TypeTuple
- {
- exps.remove(i);
- auto texps = new Expressions(tt.arguments.length);
- foreach (j, a; *tt.arguments)
- (*texps)[j] = new TypeExp(e.loc, a.type);
- exps.insert(i, texps);
- expandNames(i, texps.length);
- }
- i--;
- continue;
- }
- }
-
- // Inline expand all the tuples
- while (arg.op == EXP.tuple)
- {
- TupleExp te = cast(TupleExp)arg;
- exps.remove(i); // remove arg
- exps.insert(i, te.exps); // replace with tuple contents
- expandNames(i, te.exps.length);
- if (i == exps.length)
- return; // empty tuple, no more arguments
- (*exps)[i] = Expression.combine(te.e0, (*exps)[i]);
- arg = (*exps)[i];
- }
- }
-}
-
/****************************************
* If `s` is a function template, i.e. the only member of a template
* and that member is a function, return that template.
return new DotIdExp(loc, new TypeExp(loc, type), ident);
}
-/***************************************************
- * Given an Expression, find the variable it really is.
- *
- * For example, `a[index]` is really `a`, and `s.f` is really `s`.
- * Params:
- * e = Expression to look at
- * deref = number of dereferences encountered
- * Returns:
- * variable if there is one, null if not
- */
-VarDeclaration expToVariable(Expression e, out int deref)
-{
- deref = 0;
- while (1)
- {
- switch (e.op)
- {
- case EXP.variable:
- return e.isVarExp().var.isVarDeclaration();
-
- case EXP.dotVariable:
- e = e.isDotVarExp().e1;
- if (e.type.toBasetype().isTypeClass())
- deref++;
-
- continue;
-
- case EXP.index:
- {
- e = e.isIndexExp().e1;
- if (!e.type.toBasetype().isTypeSArray())
- deref++;
-
- continue;
- }
-
- case EXP.slice:
- {
- e = e.isSliceExp().e1;
- if (!e.type.toBasetype().isTypeSArray())
- deref++;
-
- continue;
- }
-
- case EXP.super_:
- return e.isSuperExp().var.isVarDeclaration();
- case EXP.this_:
- return e.isThisExp().var.isVarDeclaration();
-
- // Temporaries for rvalues that need destruction
- // are of form: (T s = rvalue, s). For these cases
- // we can just return var declaration of `s`. However,
- // this is intentionally not calling `Expression.extractLast`
- // because at this point we cannot infer the var declaration
- // of more complex generated comma expressions such as the
- // one for the array append hook.
- case EXP.comma:
- {
- if (auto ve = e.isCommaExp().e2.isVarExp())
- return ve.var.isVarDeclaration();
-
- return null;
- }
- default:
- return null;
- }
- }
-}
-
enum OwnedBy : ubyte
{
code, // normal code expression in AST
return a;
}
- dinteger_t toInteger()
- {
- //printf("Expression %s\n", EXPtoString(op).ptr);
- if (!type || !type.isTypeError())
- error(loc, "integer constant expression expected instead of `%s`", toChars());
- return 0;
- }
-
- uinteger_t toUInteger()
- {
- //printf("Expression %s\n", EXPtoString(op).ptr);
- return cast(uinteger_t)toInteger();
- }
-
- real_t toReal()
- {
- error(loc, "floating point constant expression expected instead of `%s`", toChars());
- return CTFloat.zero;
- }
-
- real_t toImaginary()
- {
- error(loc, "floating point constant expression expected instead of `%s`", toChars());
- return CTFloat.zero;
- }
-
- complex_t toComplex()
- {
- error(loc, "floating point constant expression expected instead of `%s`", toChars());
- return complex_t(CTFloat.zero);
- }
-
- StringExp toStringExp()
- {
- return null;
- }
-
- /***************************************
- * Return !=0 if expression is an lvalue.
- */
- bool isLvalue()
- {
- return false;
- }
-
- /****************************************
- * Check that the expression has a valid type.
- * If not, generates an error "... has no type".
- * Returns:
- * true if the expression is not valid.
- * Note:
- * When this function returns true, `checkValue()` should also return true.
- */
- bool checkType()
- {
- return false;
- }
-
/******************************
* If this is a reference, dereference it.
*/
assert(0);
}
- /******
- * Identical, not just equal. I.e. NaNs with different bit patterns are not identical
- */
- bool isIdentical(const Expression e) const
- {
- return equals(e);
- }
-
-
- /// Statically evaluate this expression to a `bool` if possible
- /// Returns: an optional thath either contains the value or is empty
- Optional!bool toBool()
- {
- return typeof(return)();
- }
-
bool hasCode()
{
return true;
}
}
+// Approximate Non-semantic version of the `Type.isScalar` function in `typesem`
+bool _isRoughlyScalar(Type _this)
+{
+ if (auto tb = _this.isTypeBasic())
+ return (tb.flags & TFlags.integral | TFlags.floating) != 0;
+ else if (_this.ty == Tenum || _this.ty == Tpointer) // the enum is possibly scalar
+ return true;
+ return false;
+}
+
/***********************************************************
* A compile-time known integer value
*/
extern (C++) final class IntegerExp : Expression
{
- private dinteger_t value;
+ dinteger_t value;
extern (D) this(Loc loc, dinteger_t value, Type type)
{
super(loc, EXP.int64);
//printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type.toChars() : "");
assert(type);
- if (!type.isScalar())
- {
- //printf("%s, loc = %d\n", toChars(), loc.linnum);
- if (type.ty != Terror)
- error(loc, "integral constant must be scalar type, not `%s`", type.toChars());
- type = Type.terror;
- }
+
+ /* Verify no path to the following assert failure.
+ * Weirdly, the isScalar() includes floats - see enumsem.enumMemberSemantic() for the
+ * base type. This is possibly a bug.
+ */
+ assert(_isRoughlyScalar(type) || type.ty == Terror);
+
this.type = type;
- this.value = normalize(type.toBasetype().ty, value);
+ this.value = normalize(type.toBaseTypeNonSemantic().ty, value);
}
extern (D) this(dinteger_t value)
return new IntegerExp(loc, value, type);
}
- override bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
- if (auto ne = (cast(Expression)o).isIntegerExp())
- {
- if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && value == ne.value)
- {
- return true;
- }
- }
- return false;
- }
-
- override dinteger_t toInteger()
- {
- // normalize() is necessary until we fix all the paints of 'type'
- return value = normalize(type.toBasetype().ty, value);
- }
-
- override real_t toReal()
- {
- // normalize() is necessary until we fix all the paints of 'type'
- const ty = type.toBasetype().ty;
- const val = normalize(ty, value);
- value = val;
- return (ty == Tuns64)
- ? real_t(cast(ulong)val)
- : real_t(cast(long)val);
- }
-
- override real_t toImaginary()
- {
- return CTFloat.zero;
- }
-
- override complex_t toComplex()
- {
- return complex_t(toReal());
- }
-
- override Optional!bool toBool()
- {
- bool r = toInteger() != 0;
- return typeof(return)(r);
- }
-
override void accept(Visitor v)
{
v.visit(this);
extern (D) void setInteger(dinteger_t value)
{
- this.value = normalize(type.toBasetype().ty, value);
+ this.value = normalize(type.toBaseTypeNonSemantic().ty, value);
}
extern (D) static dinteger_t normalize(TY ty, dinteger_t value)
/* 'Normalize' the value of the integer to be in range of the type
*/
dinteger_t result;
+ if (ty == Tpointer)
+ ty = Type.tsize_t.ty;
switch (ty)
{
case Tbool:
result = cast(ulong)value;
break;
- case Tpointer:
- if (target.ptrsize == 8)
- goto case Tuns64;
- if (target.ptrsize == 4)
- goto case Tuns32;
- if (target.ptrsize == 2)
- goto case Tuns16;
- assert(0);
-
default:
break;
}
return new RealExp(loc, value, type);
}
- /********************************
- * Test to see if two reals are the same.
- * Regard NaN's as equivalent.
- * Regard +0 and -0 as different.
- * Params:
- * x1 = first operand
- * x2 = second operand
- * Returns:
- * true if x1 is x2
- * else false
- */
- private static bool RealIdentical(real_t x1, real_t x2) @safe
- {
- return (CTFloat.isNaN(x1) && CTFloat.isNaN(x2)) || CTFloat.isIdentical(x1, x2);
- }
- override bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
- if (auto ne = (cast(Expression)o).isRealExp())
- {
- if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && RealIdentical(value, ne.value))
- {
- return true;
- }
- }
- return false;
- }
-
- override bool isIdentical(const Expression e) const
- {
- if (!equals(e))
- return false;
- return CTFloat.isIdentical(value, e.isRealExp().value);
- }
-
- override dinteger_t toInteger()
- {
- return cast(sinteger_t)toReal();
- }
-
- override uinteger_t toUInteger()
- {
- return cast(uinteger_t)toReal();
- }
-
- override real_t toReal()
- {
- return type.isReal() ? value : CTFloat.zero;
- }
-
- override real_t toImaginary()
- {
- return type.isReal() ? CTFloat.zero : value;
- }
-
- override complex_t toComplex()
- {
- return complex_t(toReal(), toImaginary());
- }
-
- override Optional!bool toBool()
- {
- return typeof(return)(!!value);
- }
-
override void accept(Visitor v)
{
v.visit(this);
return new ComplexExp(loc, value, type);
}
- override bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
- if (auto ne = (cast(Expression)o).isComplexExp())
- {
- if (type.toHeadMutable().equals(ne.type.toHeadMutable()) &&
- RealExp.RealIdentical(creall(value), creall(ne.value)) &&
- RealExp.RealIdentical(cimagl(value), cimagl(ne.value)))
- {
- return true;
- }
- }
- return false;
- }
-
- override bool isIdentical(const Expression e) const
- {
- if (!equals(e))
- return false;
- // equals() regards different NaN values as 'equals'
- auto c = e.isComplexExp();
- return CTFloat.isIdentical(creall(value), creall(c.value)) &&
- CTFloat.isIdentical(cimagl(value), cimagl(c.value));
- }
-
- override dinteger_t toInteger()
- {
- return cast(sinteger_t)toReal();
- }
-
- override uinteger_t toUInteger()
- {
- return cast(uinteger_t)toReal();
- }
-
- override real_t toReal()
- {
- return creall(value);
- }
-
- override real_t toImaginary()
- {
- return cimagl(value);
- }
-
- override complex_t toComplex()
- {
- return value;
- }
-
- override Optional!bool toBool()
- {
- return typeof(return)(!!value);
- }
-
override void accept(Visitor v)
{
v.visit(this);
return new IdentifierExp(loc, ident);
}
- override final bool isLvalue()
- {
- return !this.rvalue;
- }
-
override void accept(Visitor v)
{
v.visit(this);
this.hasOverloads = hasOverloads;
}
- override bool isLvalue()
- {
- return !rvalue;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return r;
}
- override Optional!bool toBool()
- {
- // `this` is never null (what about structs?)
- return typeof(return)(true);
- }
-
- override final bool isLvalue()
- {
- // Class `this` should be an rvalue; struct `this` should be an lvalue.
- return !rvalue && type.toBasetype().ty != Tclass;
- }
-
override void accept(Visitor v)
{
v.visit(this);
this.type = type;
}
- override bool equals(const RootObject o) const
- {
- if (auto e = o.isExpression())
- {
- if (e.op == EXP.null_ && type.equals(e.type))
- {
- return true;
- }
- }
- return false;
- }
-
- override Optional!bool toBool()
- {
- // null in any type is false
- return typeof(return)(false);
- }
-
- override StringExp toStringExp()
- {
- if (this.type.implicitConvTo(Type.tstring))
- {
- auto se = new StringExp(loc, (cast(char*)mem.xcalloc(1, 1))[0 .. 0]);
- se.type = Type.tstring;
- return se;
- }
-
- return null;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return new StringExp(loc, string[0 .. len]);
}
- override bool equals(const RootObject o) const
- {
- //printf("StringExp::equals('%s') %s\n", o.toChars(), toChars());
- if (auto e = o.isExpression())
- {
- if (auto se = e.isStringExp())
- {
- return compare(se) == 0;
- }
- }
- return false;
- }
-
/**********************************
* Return the number of code units the string would be if it were re-encoded
* as tynto.
* Params:
* tynto = code unit type of the target encoding
+ * s = set to error message on invalid string
* Returns:
* number of code units
*/
- size_t numberOfCodeUnits(int tynto = 0) const
+ extern (D) size_t numberOfCodeUnits(int tynto, out .string s) const
{
int encSize;
switch (tynto)
case 1:
for (size_t u = 0; u < len;)
{
- if (const s = utf_decodeChar(string[0 .. len], u, c))
- {
- error(loc, "%.*s", cast(int)s.length, s.ptr);
+ s = utf_decodeChar(string[0 .. len], u, c);
+ if (s)
return 0;
- }
result += utf_codeLength(encSize, c);
}
break;
case 2:
for (size_t u = 0; u < len;)
{
- if (const s = utf_decodeWchar(wstring[0 .. len], u, c))
- {
- error(loc, "%.*s", cast(int)s.length, s.ptr);
+ s = utf_decodeWchar(wstring[0 .. len], u, c);
+ if (s)
return 0;
- }
result += utf_codeLength(encSize, c);
}
break;
}
}
- override StringExp toStringExp()
- {
- return this;
- }
-
-
/**
* Compare two `StringExp` by length, then value
*
assert(this.sz == se2.sz, "Comparing string expressions of different sizes");
//printf("sz = %d, len1 = %d, len2 = %d\n", sz, cast(int)len1, cast(int)len2);
- if (len1 == len2)
+ if (len1 != len2)
+ return cast(int)(len1 - len2);
+ switch (sz)
{
- switch (sz)
- {
- case 1:
- return memcmp(string, se2.string, len1);
+ case 1:
+ return memcmp(string, se2.string, len1);
- case 2:
+ case 2:
+ {
+ wchar* s1 = cast(wchar*)string;
+ wchar* s2 = cast(wchar*)se2.string;
+ foreach (u; 0 .. len)
{
- wchar* s1 = cast(wchar*)string;
- wchar* s2 = cast(wchar*)se2.string;
- foreach (u; 0 .. len)
- {
- if (s1[u] != s2[u])
- return s1[u] - s2[u];
- }
+ if (s1[u] != s2[u])
+ return s1[u] - s2[u];
}
- break;
- case 4:
+ }
+ break;
+ case 4:
+ {
+ dchar* s1 = cast(dchar*)string;
+ dchar* s2 = cast(dchar*)se2.string;
+ foreach (u; 0 .. len)
{
- dchar* s1 = cast(dchar*)string;
- dchar* s2 = cast(dchar*)se2.string;
- foreach (u; 0 .. len)
- {
- if (s1[u] != s2[u])
- return s1[u] - s2[u];
- }
+ if (s1[u] != s2[u])
+ return s1[u] - s2[u];
}
- break;
- default:
- assert(0);
}
+ break;
+ default:
+ assert(0);
}
- return cast(int)(len1 - len2);
- }
-
- override Optional!bool toBool()
- {
- // Keep the old behaviour for this refactoring
- // Should probably match language spec instead and check for length
- return typeof(return)(true);
- }
-
- override bool isLvalue()
- {
- /* string literal is rvalue in default, but
- * conversion to reference of static array is only allowed.
- */
- return !rvalue && (type && type.toBasetype().ty == Tsarray);
+ return 0;
}
/********************************
{
super(loc, EXP.tuple);
this.exps = new Expressions();
-
- this.exps.reserve(tup.objects.length);
- foreach (o; *tup.objects)
- {
- if (Dsymbol s = getDsymbol(o))
- {
- /* If tuple element represents a symbol, translate to DsymbolExp
- * to supply implicit 'this' if needed later.
- */
- Expression e = new DsymbolExp(loc, s);
- this.exps.push(e);
- }
- else if (auto eo = o.isExpression())
- {
- auto e = eo.copy();
- e.loc = loc; // https://issues.dlang.org/show_bug.cgi?id=15669
- this.exps.push(e);
- }
- else if (auto t = o.isType())
- {
- Expression e = new TypeExp(loc, t);
- this.exps.push(e);
- }
- else
- {
- error(loc, "`%s` is not an expression", o.toChars());
- }
- }
+ // the rest of the constructor is moved to expressionsem.d in fillTupleExpExps function
}
static TupleExp create(Loc loc, Expressions* exps) @safe
return new TupleExp(loc, exps);
}
- override TupleExp syntaxCopy()
- {
- return new TupleExp(loc, e0 ? e0.syntaxCopy() : null, arraySyntaxCopy(exps));
- }
-
- override bool equals(const RootObject o) const
+ override TupleExp syntaxCopy()
{
- if (this == o)
- return true;
- if (auto e = o.isExpression())
- if (auto te = e.isTupleExp())
- {
- if (exps.length != te.exps.length)
- return false;
- if (e0 && !e0.equals(te.e0) || !e0 && te.e0)
- return false;
- foreach (i, e1; *exps)
- {
- auto e2 = (*te.exps)[i];
- if (!e1.equals(e2))
- return false;
- }
- return true;
- }
- return false;
+ return new TupleExp(loc, e0 ? e0.syntaxCopy() : null, arraySyntaxCopy(exps));
}
override void accept(Visitor v)
arraySyntaxCopy(elements));
}
- override bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
- auto e = o.isExpression();
- if (!e)
- return false;
- if (auto ae = e.isArrayLiteralExp())
- {
- if (elements.length != ae.elements.length)
- return false;
- if (elements.length == 0 && !type.equals(ae.type))
- {
- return false;
- }
-
- foreach (i, e1; *elements)
- {
- auto e2 = (*ae.elements)[i];
- auto e1x = e1 ? e1 : basis;
- auto e2x = e2 ? e2 : ae.basis;
-
- if (e1x != e2x && (!e1x || !e2x || !e1x.equals(e2x)))
- return false;
- }
- return true;
- }
- return false;
- }
-
Expression getElement(size_t i) // use opIndex instead
{
return this[i];
return el ? el : basis;
}
- override Optional!bool toBool()
- {
- size_t dim = elements ? elements.length : 0;
- return typeof(return)(dim != 0);
- }
-
- override StringExp toStringExp()
- {
- TY telem = type.nextOf().toBasetype().ty;
- if (telem.isSomeChar || (telem == Tvoid && (!elements || elements.length == 0)))
- {
- ubyte sz = 1;
- if (telem == Twchar)
- sz = 2;
- else if (telem == Tdchar)
- sz = 4;
-
- OutBuffer buf;
- if (elements)
- {
- foreach (i; 0 .. elements.length)
- {
- auto ch = this[i];
- if (ch.op != EXP.int64)
- return null;
- if (sz == 1)
- buf.writeByte(cast(ubyte)ch.toInteger());
- else if (sz == 2)
- buf.writeword(cast(uint)ch.toInteger());
- else
- buf.write4(cast(uint)ch.toInteger());
- }
- }
- char prefix;
- if (sz == 1)
- {
- prefix = 'c';
- buf.writeByte(0);
- }
- else if (sz == 2)
- {
- prefix = 'w';
- buf.writeword(0);
- }
- else
- {
- prefix = 'd';
- buf.write4(0);
- }
-
- const size_t len = buf.length / sz - 1;
- auto se = new StringExp(loc, buf.extractSlice()[0 .. len * sz], len, sz, prefix);
- se.sz = sz;
- se.type = type;
- return se;
- }
- return null;
- }
-
override void accept(Visitor v)
{
v.visit(this);
this.values = values;
}
- override bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
- auto e = o.isExpression();
- if (!e)
- return false;
- if (auto ae = e.isAssocArrayLiteralExp())
- {
- if (keys.length != ae.keys.length)
- return false;
- size_t count = 0;
- foreach (i, key; *keys)
- {
- foreach (j, akey; *ae.keys)
- {
- if (key.equals(akey))
- {
- if (!(*values)[i].equals((*ae.values)[j]))
- return false;
- ++count;
- }
- }
- }
- return count == keys.length;
- }
- return false;
- }
-
override AssocArrayLiteralExp syntaxCopy()
{
return new AssocArrayLiteralExp(loc, arraySyntaxCopy(keys), arraySyntaxCopy(values));
}
- override Optional!bool toBool()
- {
- size_t dim = keys.length;
- return typeof(return)(dim != 0);
- }
-
override void accept(Visitor v)
{
v.visit(this);
return new StructLiteralExp(loc, sd, cast(Expressions*)elements, stype);
}
- override bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
- auto e = o.isExpression();
- if (!e)
- return false;
- if (auto se = e.isStructLiteralExp())
- {
- if (!type.equals(se.type))
- return false;
- if (elements.length != se.elements.length)
- return false;
- foreach (i, e1; *elements)
- {
- auto e2 = (*se.elements)[i];
- if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
- return false;
- }
- return true;
- }
- return false;
- }
-
override StructLiteralExp syntaxCopy()
{
auto exp = new StructLiteralExp(loc, sd, arraySyntaxCopy(elements), type ? type : stype);
return exp;
}
- /************************************
- * Get index of field.
- * Returns -1 if not found.
- */
- extern (D) int getFieldIndex(Type type, uint offset)
- {
- /* Find which field offset is by looking at the field offsets
- */
- if (elements.length)
- {
- const sz = type.size();
- if (sz == SIZE_INVALID)
- return -1;
- foreach (i, v; sd.fields)
- {
- if (offset == v.offset && sz == v.type.size())
- {
- /* context fields might not be filled. */
- if (i >= sd.nonHiddenFields())
- return cast(int)i;
- if (auto e = (*elements)[i])
- {
- return cast(int)i;
- }
- break;
- }
- }
- }
- return -1;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return new TypeExp(loc, type.syntaxCopy());
}
- override bool checkType()
- {
- error(loc, "type `%s` is not an expression", toChars());
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return new ScopeExp(loc, sds.syntaxCopy(null));
}
- override bool checkType()
- {
- if (sds.isPackage())
- {
- error(loc, "%s `%s` has no type", sds.kind(), sds.toChars());
- return true;
- }
- if (auto ti = sds.isTemplateInstance())
- {
- //assert(ti.needsTypeInference(sc));
- if (ti.tempdecl &&
- ti.semantictiargsdone &&
- ti.semanticRun == PASS.initial)
- {
- error(loc, "partial %s `%s` has no type", sds.kind(), toChars());
- return true;
- }
- }
- return false;
- }
-
override void accept(Visitor v)
{
v.visit(this);
this.fd = fd;
}
- override bool isLvalue()
- {
- return fd !is null;
- }
-
- override bool checkType()
- {
- error(loc, "%s `%s` has no type", td.kind(), toChars());
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
{
if (auto v = var.isVarDeclaration())
{
- // FIXME: This error report will never be handled anyone.
- // It should be done before the SymOffExp construction.
- if (v.needThis())
- {
- auto t = v.isThis();
- assert(t);
- .error(loc, "taking the address of non-static variable `%s` requires an instance of `%s`", v.toChars(), t.toChars());
- }
+ assert(!v.needThis()); // make sure the error message is no longer necessary
hasOverloads = false;
}
super(loc, EXP.symbolOffset, var, hasOverloads);
this.offset = offset;
}
- override Optional!bool toBool()
- {
- return typeof(return)(true);
- }
-
override void accept(Visitor v)
{
v.visit(this);
return new VarExp(loc, var, hasOverloads);
}
- override bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
- if (auto ne = o.isExpression().isVarExp())
- {
- if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && var == ne.var)
- {
- return true;
- }
- }
- return false;
- }
-
- override bool isLvalue()
- {
- if (rvalue || var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest))
- return false;
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
type = Type.tvoid;
}
- override bool isLvalue()
- {
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
assert(fd.fbody);
}
- override bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
- auto e = o.isExpression();
- if (!e)
- return false;
- if (auto fe = e.isFuncExp())
- {
- return fd == fe.fd;
- }
- return false;
- }
-
override FuncExp syntaxCopy()
{
if (td)
return new FuncExp(loc, fd);
}
- override bool checkType()
- {
- if (td)
- {
- error(loc, "template lambda has no type");
- return true;
- }
- return false;
- }
-
override void accept(Visitor v)
{
v.visit(this);
super(loc, op, e1, e2);
}
- override final bool isLvalue()
- {
- return !rvalue;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return new MixinExp(loc, arraySyntaxCopy(exps));
}
- override bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
- auto e = o.isExpression();
- if (!e)
- return false;
- if (auto ce = e.isMixinExp())
- {
- if (exps.length != ce.exps.length)
- return false;
- foreach (i, e1; *exps)
- {
- auto e2 = (*ce.exps)[i];
- if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
- return false;
- }
- return true;
- }
- return false;
- }
-
override void accept(Visitor v)
{
v.visit(this);
this.td = td;
}
- override bool checkType()
- {
- error(loc, "%s `%s` has no type", td.kind(), toChars());
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
this.hasOverloads = hasOverloads;
}
- override bool isLvalue()
- {
- if (rvalue)
- return false;
- if (e1.op != EXP.structLiteral)
- return true;
- auto vd = var.isVarDeclaration();
- return !(vd && vd.isField());
- }
-
override void accept(Visitor v)
{
v.visit(this);
{
TemplateInstance ti;
- extern (D) this(Loc loc, Expression e, Identifier name, Objects* tiargs)
- {
- super(loc, EXP.dotTemplateInstance, e);
- //printf("DotTemplateInstanceExp()\n");
- this.ti = new TemplateInstance(loc, name, tiargs);
- }
-
extern (D) this(Loc loc, Expression e, TemplateInstance ti) @safe
{
super(loc, EXP.dotTemplateInstance, e);
this.ti = ti;
}
- override DotTemplateInstanceExp syntaxCopy()
+ extern (D) this(Loc loc, Expression e, Identifier name, Objects* tiargs)
{
- return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs));
+ //printf("DotTemplateInstanceExp()\n");
+ this(loc, e, new TemplateInstance(loc, name, tiargs));
}
- override bool checkType()
+ override DotTemplateInstanceExp syntaxCopy()
{
- // Same logic as ScopeExp.checkType()
- if (ti.tempdecl &&
- ti.semantictiargsdone &&
- ti.semanticRun == PASS.initial)
- {
- error(loc, "partial %s `%s` has no type", ti.kind(), toChars());
- return true;
- }
- return false;
+ return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs));
}
override void accept(Visitor v)
return new CallExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments), names ? names.copy() : null);
}
- override bool isLvalue()
- {
- if (rvalue)
- return false;
- Type tb = e1.type.toBasetype();
- if (tb.ty == Tdelegate || tb.ty == Tpointer)
- tb = tb.nextOf();
- auto tf = tb.isTypeFunction();
- if (tf && tf.isRef)
- {
- if (auto dve = e1.isDotVarExp())
- if (dve.var.isCtorDeclaration())
- return false;
- return true; // function returns a reference
- }
- return false;
- }
-
override void accept(Visitor v)
{
v.visit(this);
}
}
-/**
- * Get the called function type from a call expression
- * Params:
- * ce = function call expression. Must have had semantic analysis done.
- * Returns: called function type, or `null` if error / no semantic analysis done
- */
-TypeFunction calledFunctionType(CallExp ce)
-{
- Type t = ce.e1.type;
- if (!t)
- return null;
- t = t.toBasetype();
- if (auto tf = t.isTypeFunction())
- return tf;
- if (auto td = t.isTypeDelegate())
- return td.nextOf().isTypeFunction();
- return null;
-}
-
FuncDeclaration isFuncAddress(Expression e, bool* hasOverloads = null) @safe
{
if (auto ae = e.isAddrExp())
type = t;
}
- override Optional!bool toBool()
- {
- if (isCtfeReferenceValid(e1))
- return typeof(return)(true);
- return UnaExp.toBool();
- }
-
override void accept(Visitor v)
{
v.visit(this);
type = t;
}
- override bool isLvalue()
- {
- return !rvalue;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return to ? new CastExp(loc, e1.syntaxCopy(), to.syntaxCopy()) : new CastExp(loc, e1.syntaxCopy(), mod);
}
- override bool isLvalue()
- {
- //printf("e1.type = %s, to.type = %s\n", e1.type.toChars(), to.toChars());
- if (rvalue || !e1.isLvalue())
- return false;
- return (to.ty == Tsarray && (e1.type.ty == Tvector || e1.type.ty == Tsarray)) ||
- (to.ty == Taarray && e1.type.ty == Taarray) ||
- e1.type.mutableOf.unSharedOf().equals(to.mutableOf().unSharedOf());
- }
-
override void accept(Visitor v)
{
v.visit(this);
super(loc, EXP.vectorArray, e1);
}
- override bool isLvalue()
- {
- return !rvalue && e1.isLvalue();
- }
-
override void accept(Visitor v)
{
v.visit(this);
return se;
}
- override bool isLvalue()
- {
- /* slice expression is rvalue in default, but
- * conversion to reference of static array is only allowed.
- */
- return !rvalue && (type && type.toBasetype().ty == Tsarray);
- }
-
- override Optional!bool toBool()
- {
- return e1.toBool();
- }
-
override void accept(Visitor v)
{
v.visit(this);
return ae;
}
- override bool isLvalue()
- {
- if (rvalue)
- return false;
- if (type && type.toBasetype().ty == Tvoid)
- return false;
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
originalExp = oe;
}
- override bool isLvalue()
- {
- return !rvalue && e2.isLvalue();
- }
-
- override Optional!bool toBool()
- {
- return e2.toBool();
- }
-
override void accept(Visitor v)
{
v.visit(this);
super(loc, EXP.delegatePointer, e1);
}
- override bool isLvalue()
- {
- return !rvalue && e1.isLvalue();
- }
-
override void accept(Visitor v)
{
v.visit(this);
super(loc, EXP.delegateFunctionPointer, e1);
}
- override bool isLvalue()
- {
- return !rvalue && e1.isLvalue();
- }
-
override void accept(Visitor v)
{
v.visit(this);
return ie;
}
- override bool isLvalue()
- {
- if (rvalue)
- return false;
- auto t1b = e1.type.toBasetype();
- if (t1b.isTypeAArray() || t1b.isTypeSArray() ||
- (e1.isIndexExp() && t1b != t1b.isTypeDArray()))
- {
- return e1.isLvalue();
- }
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
super(loc, tok, e1, e2);
}
- override final bool isLvalue()
- {
- // Array-op 'x[] = y[]' should make an rvalue.
- // Setting array length 'x.length = v' should make an rvalue.
- if (e1.op == EXP.slice || e1.op == EXP.arrayLength)
- {
- return false;
- }
- return !rvalue;
- }
-
override void accept(Visitor v)
{
v.visit(this);
*/
extern (C++) final class ConstructExp : AssignExp
{
+ Expression lowering;
+
extern (D) this(Loc loc, Expression e1, Expression e2) @safe
{
super(loc, EXP.construct, e1, e2);
return new CondExp(loc, econd.syntaxCopy(), e1.syntaxCopy(), e2.syntaxCopy());
}
- override bool isLvalue()
- {
- return !rvalue && e1.isLvalue() && e2.isLvalue();
- }
-
override void accept(Visitor v)
{
v.visit(this);
return value.sd.isClassDeclaration();
}
- // Return index of the field, or -1 if not found
- int getFieldIndex(Type fieldtype, uint fieldoffset)
- {
- ClassDeclaration cd = originalClass();
- uint fieldsSoFar = 0;
- for (size_t j = 0; j < value.elements.length; j++)
- {
- while (j - fieldsSoFar >= cd.fields.length)
- {
- fieldsSoFar += cd.fields.length;
- cd = cd.baseClass;
- }
- VarDeclaration v2 = cd.fields[j - fieldsSoFar];
- if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size())
- {
- return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
- }
- }
- return -1;
- }
-
// Return index of the field, or -1 if not found
// Same as getFieldIndex, but checks for a direct match with the VarDeclaration
int findFieldIndexByName(VarDeclaration v)
Expression *ctfeInterpret(Expression *e);
void expandTuples(Expressions *exps, ArgumentLabels *names = nullptr);
Expression *optimize(Expression *exp, int result, bool keepLvalue = false);
+ bool isIdentical(const Expression *exp, const Expression *e);
+ bool equals(const Expression *exp, const Expression *e);
+ bool isLvalue(Expression *exp);
+ int32_t getFieldIndex(ClassReferenceExp *cre, Type *fieldtype, uint32_t fieldoffset);
+ void fillTupleExpExps(TupleExp *te, TupleDeclaration *tup);
+ Optional<bool> toBool(Expression *exp);
+ StringExp *toStringExp(Expression *exp);
+ dinteger_t toInteger(Expression *exp);
+ uinteger_t toUInteger(Expression *exp);
+ real_t toReal(Expression *exp);
+ real_t toImaginary(Expression *exp);
+ complex_t toComplex(Expression *exp);
}
typedef unsigned char OwnedBy;
const char* toChars() const final override;
- virtual dinteger_t toInteger();
- virtual uinteger_t toUInteger();
- virtual real_t toReal();
- virtual real_t toImaginary();
- virtual complex_t toComplex();
- virtual StringExp *toStringExp();
- virtual bool isLvalue();
- virtual bool checkType();
Expression *addressOf();
Expression *deref();
int isConst();
- virtual bool isIdentical(const Expression *e) const;
- virtual Optional<bool> toBool();
virtual bool hasCode()
{
return true;
dinteger_t value;
static IntegerExp *create(Loc loc, dinteger_t value, Type *type);
- bool equals(const RootObject * const o) const override;
- dinteger_t toInteger() override;
- real_t toReal() override;
- real_t toImaginary() override;
- complex_t toComplex() override;
- Optional<bool> toBool() override;
void accept(Visitor *v) override { v->visit(this); }
dinteger_t getInteger() { return value; }
template<int v>
real_t value;
static RealExp *create(Loc loc, real_t value, Type *type);
- bool equals(const RootObject * const o) const override;
- bool isIdentical(const Expression *e) const override;
- dinteger_t toInteger() override;
- uinteger_t toUInteger() override;
- real_t toReal() override;
- real_t toImaginary() override;
- complex_t toComplex() override;
- Optional<bool> toBool() override;
void accept(Visitor *v) override { v->visit(this); }
};
complex_t value;
static ComplexExp *create(Loc loc, complex_t value, Type *type);
- bool equals(const RootObject * const o) const override;
- bool isIdentical(const Expression *e) const override;
- dinteger_t toInteger() override;
- uinteger_t toUInteger() override;
- real_t toReal() override;
- real_t toImaginary() override;
- complex_t toComplex() override;
- Optional<bool> toBool() override;
void accept(Visitor *v) override { v->visit(this); }
};
Identifier *ident;
static IdentifierExp *create(Loc loc, Identifier *ident);
- bool isLvalue() override final;
void accept(Visitor *v) override { v->visit(this); }
};
d_bool hasOverloads;
DsymbolExp *syntaxCopy() override;
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
VarDeclaration *var;
ThisExp *syntaxCopy() override;
- Optional<bool> toBool() override;
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
class NullExp final : public Expression
{
public:
- bool equals(const RootObject * const o) const override;
- Optional<bool> toBool() override;
- StringExp *toStringExp() override;
void accept(Visitor *v) override { v->visit(this); }
};
static StringExp *create(Loc loc, const char *s);
static StringExp *create(Loc loc, const void *s, d_size_t len);
- bool equals(const RootObject * const o) const override;
char32_t getCodeUnit(d_size_t i) const;
dinteger_t getIndex(d_size_t i) const;
- StringExp *toStringExp() override;
- Optional<bool> toBool() override;
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
size_t numberOfCodeUnits(int tynto = 0) const;
void writeTo(void* dest, bool zero, int tyto = 0) const;
static TupleExp *create(Loc loc, Expressions *exps);
TupleExp *syntaxCopy() override;
- bool equals(const RootObject * const o) const override;
void accept(Visitor *v) override { v->visit(this); }
};
static ArrayLiteralExp *create(Loc loc, Expressions *elements);
ArrayLiteralExp *syntaxCopy() override;
- bool equals(const RootObject * const o) const override;
Expression *getElement(d_size_t i);
- Optional<bool> toBool() override;
- StringExp *toStringExp() override;
void accept(Visitor *v) override { v->visit(this); }
};
Expression* lowering;
Expression* loweringCtfe;
- bool equals(const RootObject * const o) const override;
AssocArrayLiteralExp *syntaxCopy() override;
- Optional<bool> toBool() override;
void accept(Visitor *v) override { v->visit(this); }
};
static StructLiteralExp *create(Loc loc, StructDeclaration *sd, void *elements, Type *stype = nullptr);
- bool equals(const RootObject * const o) const override;
StructLiteralExp *syntaxCopy() override;
void accept(Visitor *v) override { v->visit(this); }
{
public:
TypeExp *syntaxCopy() override;
- bool checkType() override;
void accept(Visitor *v) override { v->visit(this); }
};
ScopeDsymbol *sds;
ScopeExp *syntaxCopy() override;
- bool checkType() override;
void accept(Visitor *v) override { v->visit(this); }
};
TemplateDeclaration *td;
FuncDeclaration *fd;
- bool isLvalue() override;
- bool checkType() override;
void accept(Visitor *v) override { v->visit(this); }
};
public:
dinteger_t offset;
- Optional<bool> toBool() override;
-
void accept(Visitor *v) override { v->visit(this); }
};
public:
d_bool delegateWasExtracted;
static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = true);
- bool equals(const RootObject * const o) const override;
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
public:
OverloadSet *vars;
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
TemplateDeclaration *td;
TOK tok;
- bool equals(const RootObject * const o) const override;
FuncExp *syntaxCopy() override;
- bool checkType() override;
void accept(Visitor *v) override { v->visit(this); }
};
class BinAssignExp : public BinExp
{
public:
- bool isLvalue() override final;
void accept(Visitor *v) override { v->visit(this); }
};
public:
TemplateDeclaration *td;
- bool checkType() override;
void accept(Visitor *v) override { v->visit(this); }
};
Declaration *var;
d_bool hasOverloads;
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
TemplateInstance *ti;
DotTemplateInstanceExp *syntaxCopy() override;
- bool checkType() override;
void accept(Visitor *v) override { v->visit(this); }
};
static CallExp *create(Loc loc, FuncDeclaration *fd, Expression *earg1);
CallExp *syntaxCopy() override;
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
class AddrExp final : public UnaExp
{
public:
- Optional<bool> toBool() override;
void accept(Visitor *v) override { v->visit(this); }
};
class PtrExp final : public UnaExp
{
public:
- bool isLvalue() override;
-
void accept(Visitor *v) override { v->visit(this); }
};
Expression* lowering;
CastExp *syntaxCopy() override;
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
class VectorArrayExp final : public UnaExp
{
public:
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
public:
SliceExp *syntaxCopy() override;
- bool isLvalue() override;
- Optional<bool> toBool() override;
void accept(Visitor *v) override { v->visit(this); }
};
class DelegatePtrExp final : public UnaExp
{
public:
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
class DelegateFuncptrExp final : public UnaExp
{
public:
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
d_bool modifiable;
ArrayExp *syntaxCopy() override;
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
d_bool isGenerated;
d_bool allowCommaExp;
Expression* originalExp;
- bool isLvalue() override;
- Optional<bool> toBool() override;
void accept(Visitor *v) override { v->visit(this); }
};
d_bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1
IndexExp *syntaxCopy() override;
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
public:
MemorySet memset;
- bool isLvalue() override final;
-
void accept(Visitor *v) override { v->visit(this); }
};
class ConstructExp final : public AssignExp
{
public:
+ Expression *lowering;
void accept(Visitor *v) override { v->visit(this); }
};
Expression *econd;
CondExp *syntaxCopy() override;
- bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
import dmd.chkformat;
import dmd.cond;
import dmd.ctorflow;
+import dmd.ctfeexpr : isCtfeReferenceValid;
import dmd.dscope;
import dmd.dsymbol;
import dmd.dsymbolsem;
+import dmd.templatesem : computeOneMember;
import dmd.declaration;
import dmd.dclass;
import dmd.dcast;
import dmd.dinterpret;
import dmd.dmodule;
import dmd.dstruct;
-import dmd.dsymbolsem : findTempDecl;
import dmd.dtemplate;
import dmd.errors;
import dmd.errorsink;
import dmd.parse;
import dmd.printast;
import dmd.root.array;
+import dmd.root.complex;
import dmd.root.ctfloat;
import dmd.root.filename;
+import dmd.root.optional;
import dmd.common.outbuffer;
import dmd.rootobject;
import dmd.root.string;
import dmd.root.utf;
+import dmd.root.rmem;
import dmd.semantic2;
import dmd.semantic3;
import dmd.sideeffect;
import dmd.safe;
import dmd.target;
-import dmd.templatesem : matchWithInstance, deduceType, matchArg, updateTempDecl;
+import dmd.targetcompiler;
+import dmd.templatesem : matchWithInstance, deduceType, matchArg, updateTempDecl, semanticTiargs, findTempDecl;
import dmd.tokens;
import dmd.traits;
import dmd.typesem;
enum LOGSEMANTIC = false;
+/*******************************
+ * Merge results of `ctorflow` into `_this`.
+ * Params:
+ * _this = scope to merge the results into
+ * loc = for error messages
+ * ctorflow = flow results to merge in
+ */
+void merge(Scope* _this, Loc loc, const ref CtorFlow ctorflow)
+{
+ if (!mergeCallSuper(_this.ctorflow.callSuper, ctorflow.callSuper))
+ error(loc, "one path skips constructor");
+
+ const fies = ctorflow.fieldinit;
+ if (!_this.ctorflow.fieldinit.length || !fies.length)
+ return;
+ FuncDeclaration f = _this.func;
+ if (_this.fes)
+ f = _this.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)
+ {
+ error(loc, "one path skips field `%s`", v.toChars());
+ }
+ }
+}
+
+real_t toImaginary(Expression _this)
+{
+ if (auto ie = _this.isIntegerExp())
+ return CTFloat.zero;
+ else if (auto re = _this.isRealExp)
+ return re.type.isReal() ? CTFloat.zero : re.value;
+ else if (auto ce = _this.isComplexExp())
+ return cimagl(ce.value);
+
+ error(_this.loc, "floating point constant expression expected instead of `%s`", _this.toChars());
+ return CTFloat.zero;
+}
+
+real_t toReal(Expression _this)
+{
+ if (auto iexp = _this.isIntegerExp())
+ {
+ // normalize() is necessary until we fix all the paints of 'type'
+ const ty = iexp.type.toBasetype().ty;
+ const val = iexp.normalize(ty, iexp.value);
+ iexp.value = val;
+ return (ty == Tuns64)
+ ? real_t(cast(ulong)val)
+ : real_t(cast(long)val);
+ }
+ else if (auto rexp = _this.isRealExp())
+ {
+ return rexp.type.isReal() ? rexp.value : CTFloat.zero;
+ }
+ else if (auto cexp = _this.isComplexExp())
+ {
+ return creall(cexp.value);
+ }
+ error(_this.loc, "floating point constant expression expected instead of `%s`", _this.toChars());
+ return CTFloat.zero;
+}
+
+complex_t toComplex(Expression _this)
+{
+ if (auto iexp = _this.isIntegerExp())
+ {
+ return complex_t(iexp.toReal());
+ }
+ else if (auto rexp = _this.isRealExp())
+ {
+ return complex_t(rexp.toReal(), rexp.toImaginary());
+ }
+ else if (auto cexp = _this.isComplexExp())
+ {
+ return cexp.value;
+ }
+ error(_this.loc, "floating point constant expression expected instead of `%s`", _this.toChars());
+ return complex_t(CTFloat.zero);
+}
+
+dinteger_t toInteger(Expression _this)
+{
+ if (auto iexp = _this.isIntegerExp())
+ {
+ // normalize() is necessary until we fix all the paints of 'type'
+ return iexp.value = IntegerExp.normalize(iexp.type.toBasetype().ty, iexp.value);
+ }
+ else if (auto rexp = _this.isRealExp())
+ {
+ return cast(sinteger_t)rexp.toReal();
+ }
+
+ else if (auto cexp = _this.isComplexExp())
+ {
+ return cast(sinteger_t)cexp.toReal();
+ }
+
+ // import dmd.hdrgen : EXPtoString;
+ //printf("Expression %s\n", EXPtoString(op).ptr);
+ if (!_this.type || !_this.type.isTypeError())
+ error(_this.loc, "integer constant expression expected instead of `%s`", _this.toChars());
+ return 0;
+}
+
+uinteger_t toUInteger(Expression _this)
+{
+ if (auto rexp = _this.isRealExp())
+ {
+ return cast(uinteger_t)rexp.toReal();
+ }
+ else if (auto cexp = _this.isComplexExp())
+ {
+ return cast(uinteger_t)cexp.toReal();
+ }
+ // import dmd.hdrgen : EXPtoString;
+ //printf("Expression %s\n", EXPtoString(op).ptr);
+ return cast(uinteger_t)_this.toInteger();
+}
+
+
+/***************************************************
+ * Given an Expression, find the variable it really is.
+ *
+ * For example, `a[index]` is really `a`, and `s.f` is really `s`.
+ * Params:
+ * e = Expression to look at
+ * deref = number of dereferences encountered
+ * Returns:
+ * variable if there is one, null if not
+ */
+VarDeclaration expToVariable(Expression e, out int deref)
+{
+ deref = 0;
+ while (1)
+ {
+ switch (e.op)
+ {
+ case EXP.variable:
+ return e.isVarExp().var.isVarDeclaration();
+
+ case EXP.dotVariable:
+ e = e.isDotVarExp().e1;
+ if (e.type.toBasetype().isTypeClass())
+ deref++;
+
+ continue;
+
+ case EXP.index:
+ {
+ e = e.isIndexExp().e1;
+ if (!e.type.toBasetype().isTypeSArray())
+ deref++;
+
+ continue;
+ }
+
+ case EXP.slice:
+ {
+ e = e.isSliceExp().e1;
+ if (!e.type.toBasetype().isTypeSArray())
+ deref++;
+
+ continue;
+ }
+
+ case EXP.super_:
+ return e.isSuperExp().var.isVarDeclaration();
+ case EXP.this_:
+ return e.isThisExp().var.isVarDeclaration();
+
+ // Temporaries for rvalues that need destruction
+ // are of form: (T s = rvalue, s). For these cases
+ // we can just return var declaration of `s`. However,
+ // this is intentionally not calling `Expression.extractLast`
+ // because at this point we cannot infer the var declaration
+ // of more complex generated comma expressions such as the
+ // one for the array append hook.
+ case EXP.comma:
+ {
+ if (auto ve = e.isCommaExp().e2.isVarExp())
+ return ve.var.isVarDeclaration();
+
+ return null;
+ }
+ default:
+ return null;
+ }
+ }
+}
+
+/**
+ * Get the called function type from a call expression
+ * Params:
+ * ce = function call expression. Must have had semantic analysis done.
+ * Returns: called function type, or `null` if error / no semantic analysis done
+ */
+TypeFunction calledFunctionType(CallExp ce)
+{
+ Type t = ce.e1.type;
+ if (!t)
+ return null;
+ t = t.toBasetype();
+ if (auto tf = t.isTypeFunction())
+ return tf;
+ if (auto td = t.isTypeDelegate())
+ return td.nextOf().isTypeFunction();
+ return null;
+}
+
+/****************************************
+ * Expand tuples in-place.
+ *
+ * Example:
+ * When there's a call `f(10, pair: AliasSeq!(20, 30), single: 40)`, the input is:
+ * `exps = [10, (20, 30), 40]`
+ * `names = [null, "pair", "single"]`
+ * The arrays will be modified to:
+ * `exps = [10, 20, 30, 40]`
+ * `names = [null, "pair", null, "single"]`
+ *
+ * Params:
+ * exps = array of Expressions
+ * names = optional array of names corresponding to Expressions
+ */
+void expandTuples(Expressions* exps, ArgumentLabels* names = null)
+{
+ //printf("expandTuples()\n");
+ if (exps is null)
+ return;
+
+ if (names)
+ {
+ if (exps.length != names.length)
+ {
+ printf("exps.length = %d, names.length = %d\n", cast(int) exps.length, cast(int) names.length);
+ printf("exps = %s, names = %s\n", exps.toChars(), names.toChars());
+ if (exps.length > 0)
+ printf("%s\n", (*exps)[0].loc.toChars());
+ assert(0);
+ }
+ }
+
+ // At `index`, a tuple of length `length` is expanded. Insert corresponding nulls in `names`.
+ void expandNames(size_t index, size_t length)
+ {
+ if (names)
+ {
+ if (length == 0)
+ {
+ names.remove(index);
+ return;
+ }
+ foreach (i; 1 .. length)
+ {
+ names.insert(index + i, ArgumentLabel(cast(Identifier) null, Loc.init));
+ }
+ }
+ }
+
+ for (size_t i = 0; i < exps.length; i++)
+ {
+ Expression arg = (*exps)[i];
+ if (!arg)
+ continue;
+
+ // Look for tuple with 0 members
+ if (auto e = arg.isTypeExp())
+ {
+ if (auto tt = e.type.toBasetype().isTypeTuple())
+ {
+ if (!tt.arguments || tt.arguments.length == 0)
+ {
+ exps.remove(i);
+ expandNames(i, 0);
+ if (i == exps.length)
+ return;
+ }
+ else // Expand a TypeTuple
+ {
+ exps.remove(i);
+ auto texps = new Expressions(tt.arguments.length);
+ foreach (j, a; *tt.arguments)
+ (*texps)[j] = new TypeExp(e.loc, a.type);
+ exps.insert(i, texps);
+ expandNames(i, texps.length);
+ }
+ i--;
+ continue;
+ }
+ }
+
+ // Inline expand all the tuples
+ while (arg.op == EXP.tuple)
+ {
+ TupleExp te = cast(TupleExp)arg;
+ exps.remove(i); // remove arg
+ exps.insert(i, te.exps); // replace with tuple contents
+ expandNames(i, te.exps.length);
+ if (i == exps.length)
+ return; // empty tuple, no more arguments
+ (*exps)[i] = Expression.combine(te.e0, (*exps)[i]);
+ arg = (*exps)[i];
+ }
+ }
+}
+
+StringExp toStringExp(Expression _this)
+{
+ static StringExp nullExpToStringExp(NullExp _this)
+ {
+ if (_this.type.implicitConvTo(Type.tstring))
+ {
+ auto se = new StringExp(_this.loc, (cast(char*).mem.xcalloc(1, 1))[0 .. 0]);
+ se.type = Type.tstring;
+ return se;
+ }
+ return null;
+ }
+
+ static StringExp arrayLiteralToStringExp(ArrayLiteralExp _this)
+ {
+ TY telem = _this.type.nextOf().toBasetype().ty;
+ if (!(telem.isSomeChar || (telem == Tvoid && (!_this.elements || _this.elements.length == 0))))
+ return null;
+
+ ubyte sz = 1;
+ if (telem == Twchar)
+ sz = 2;
+ else if (telem == Tdchar)
+ sz = 4;
+
+ OutBuffer buf;
+ if (_this.elements)
+ {
+ foreach (i; 0 .. _this.elements.length)
+ {
+ auto ch = _this[i];
+ if (ch.op != EXP.int64)
+ return null;
+ if (sz == 1)
+ buf.writeByte(cast(ubyte)ch.toInteger());
+ else if (sz == 2)
+ buf.writeword(cast(uint)ch.toInteger());
+ else
+ buf.write4(cast(uint)ch.toInteger());
+ }
+ }
+ char prefix;
+ if (sz == 1)
+ {
+ prefix = 'c';
+ buf.writeByte(0);
+ }
+ else if (sz == 2)
+ {
+ prefix = 'w';
+ buf.writeword(0);
+ }
+ else
+ {
+ prefix = 'd';
+ buf.write4(0);
+ }
+
+ const size_t len = buf.length / sz - 1;
+ auto se = new StringExp(_this.loc, buf.extractSlice()[0 .. len * sz], len, sz, prefix);
+ se.sz = sz;
+ se.type = _this.type;
+ return se;
+ }
+
+ switch(_this.op)
+ {
+ case EXP.null_: return nullExpToStringExp(_this.isNullExp());
+ case EXP.string_: return _this.isStringExp();
+ case EXP.arrayLiteral: return arrayLiteralToStringExp(_this.isArrayLiteralExp());
+ default: return null;
+ }
+}
+
+Optional!bool toBool(Expression _this)
+{
+ static Optional!bool integerToBool(IntegerExp _this)
+ {
+ bool r = _this.toInteger() != 0;
+ return typeof(return)(r);
+ }
+
+ static Optional!bool arrayLiteralToBool(ArrayLiteralExp _this)
+ {
+ size_t dim = _this.elements ? _this.elements.length : 0;
+ return typeof(return)(dim != 0);
+ }
+
+ static Optional!bool assocArrayLiteralToBool(AssocArrayLiteralExp _this)
+ {
+ size_t dim = _this.keys.length;
+ return typeof(return)(dim != 0);
+ }
+
+ static Optional!bool addrToBool(AddrExp _this)
+ {
+ if (isCtfeReferenceValid(_this.e1))
+ return typeof(return)(true);
+ return typeof(return)();
+ }
+
+ switch(_this.op)
+ {
+ case EXP.int64: return integerToBool(_this.isIntegerExp());
+ case EXP.float64: return typeof(return)(!!_this.isRealExp().value);
+ case EXP.complex80: return typeof(return)(!!_this.isComplexExp().value);
+ // `this` is never null (what about structs?)
+ case EXP.this_, EXP.super_: return typeof(return)(true);
+ // null in any type is false
+ case EXP.null_: return typeof(return)(false);
+ // Keep the old behaviour for this refactoring
+ // Should probably match language spec instead and check for length
+ case EXP.string_: return typeof(return)(true);
+ case EXP.arrayLiteral: return arrayLiteralToBool(_this.isArrayLiteralExp());
+ case EXP.assocArrayLiteral: return assocArrayLiteralToBool(_this.isAssocArrayLiteralExp());
+ case EXP.symbolOffset: return typeof(return)(true);
+ case EXP.address: return addrToBool(_this.isAddrExp());
+ case EXP.slice: return _this.isSliceExp().e1.toBool();
+ case EXP.comma: return _this.isCommaExp().e2.toBool();
+ // Statically evaluate this expression to a `bool` if possible
+ // Returns: an optional that either contains the value or is empty
+ default: return typeof(return)();
+ }
+}
+
+/****************************************
+ * Check that the expression `e` has a valid type.
+ * If not, generates an error "... has no type".
+ * Params:
+ * e = Expression to check
+ * Returns:
+ * true if the expression has a valid type.
+ * Note:
+ * When this function returns false, `checkValue()` should also return true.
+ */
+bool hasValidType(Expression e)
+{
+ static bool visitTypeExp(TypeExp e)
+ {
+ error(e.loc, "type `%s` is not an expression", e.toChars());
+ return false;
+ }
+
+ static bool visitScopeExp(ScopeExp e)
+ {
+ if (e.sds.isPackage())
+ {
+ error(e.loc, "%s `%s` has no type", e.sds.kind(), e.sds.toChars());
+ return false;
+ }
+ auto ti = e.sds.isTemplateInstance();
+ if (!ti)
+ return true;
+ //assert(ti.needsTypeInference(sc));
+ if (ti.tempdecl &&
+ ti.semantictiargsdone &&
+ ti.semanticRun == PASS.initial)
+ {
+ error(e.loc, "partial %s `%s` has no type", e.sds.kind(), e.toChars());
+ return false;
+ }
+ return true;
+ }
+
+ static bool visitTemplateExp(TemplateExp e)
+ {
+ error(e.loc, "%s `%s` has no type", e.td.kind(), e.toChars());
+ return false;
+ }
+
+ static bool visitFuncExp(FuncExp e)
+ {
+ if (e.td)
+ {
+ error(e.loc, "template lambda has no type");
+ return false;
+ }
+ return true;
+ }
+
+ static bool visitDotTemplateExp(DotTemplateExp e)
+ {
+ error(e.loc, "%s `%s` has no type", e.td.kind(), e.toChars());
+ return false;
+ }
+
+ static bool visitDotTemplateInstanceExp(DotTemplateInstanceExp e)
+ {
+ // Same logic as ScopeExp.hasValidType()
+ if (e.ti.tempdecl &&
+ e.ti.semantictiargsdone &&
+ e.ti.semanticRun == PASS.initial)
+ {
+ error(e.loc, "partial %s `%s` has no type", e.ti.kind(), e.toChars());
+ return false;
+ }
+ return true;
+ }
+
+ switch (e.op)
+ {
+ case EXP.type: return visitTypeExp(e.isTypeExp());
+ case EXP.scope_: return visitScopeExp(e.isScopeExp());
+ case EXP.template_: return visitTemplateExp(e.isTemplateExp());
+ case EXP.function_: return visitFuncExp(e.isFuncExp());
+ case EXP.dotTemplateDeclaration: return visitDotTemplateExp(e.isDotTemplateExp());
+ case EXP.dotTemplateInstance: return visitDotTemplateInstanceExp(e.isDotTemplateInstanceExp());
+
+ default: return true;
+ }
+}
+
+void fillTupleExpExps(TupleExp _this, TupleDeclaration tup)
+{
+ _this.exps.reserve(tup.objects.length);
+ foreach (o; *tup.objects)
+ {
+ if (Dsymbol s = getDsymbol(o))
+ {
+ /* If tuple element represents a symbol, translate to DsymbolExp
+ * to supply implicit 'this' if needed later.
+ */
+ Expression e = new DsymbolExp(_this.loc, s);
+ _this.exps.push(e);
+ }
+ else if (auto eo = o.isExpression())
+ {
+ auto e = eo.copy();
+ e.loc = _this.loc; // https://issues.dlang.org/show_bug.cgi?id=15669
+ _this.exps.push(e);
+ }
+ else if (auto t = o.isType())
+ {
+ Expression e = new TypeExp(_this.loc, t);
+ _this.exps.push(e);
+ }
+ else
+ {
+ error(_this.loc, "`%s` is not an expression", o.toChars());
+ }
+ }
+}
+
+bool isLvalue(Expression _this)
+{
+ static bool dotVarExpIsLvalue(DotVarExp _this)
+ {
+ if (_this.rvalue)
+ return false;
+ if (_this.e1.op != EXP.structLiteral)
+ return true;
+ auto vd = _this.var.isVarDeclaration();
+ return !(vd && vd.isField());
+ }
+
+ static bool callExpIsLvalue(CallExp _this)
+ {
+ if (_this.rvalue)
+ return false;
+ Type tb = _this.e1.type.toBasetype();
+ if (tb.ty == Tdelegate || tb.ty == Tpointer)
+ tb = tb.nextOf();
+ auto tf = tb.isTypeFunction();
+ if (tf && tf.isRef)
+ {
+ if (auto dve = _this.e1.isDotVarExp())
+ if (dve.var.isCtorDeclaration())
+ return false;
+ return true; // function returns a reference
+ }
+ return false;
+ }
+
+ static bool castExpIsLvalue(CastExp _this)
+ {
+ //printf("e1.type = %s, to.type = %s\n", e1.type.toChars(), to.toChars());
+ if (_this.rvalue || !_this.e1.isLvalue())
+ return false;
+ return (_this.to.ty == Tsarray && (_this.e1.type.ty == Tvector || _this.e1.type.ty == Tsarray)) ||
+ (_this.to.ty == Taarray && _this.e1.type.ty == Taarray) ||
+ _this.e1.type.mutableOf.unSharedOf().equals(_this.to.mutableOf().unSharedOf());
+ }
+
+ static bool indexExpIsLvalue(IndexExp _this)
+ {
+ if (_this.rvalue)
+ return false;
+ auto t1b = _this.e1.type.toBasetype();
+ if (t1b.isTypeAArray() || t1b.isTypeSArray() ||
+ (_this.e1.isIndexExp() && t1b != t1b.isTypeDArray()))
+ {
+ return _this.e1.isLvalue();
+ }
+ return true;
+ }
+
+ static bool assignExpIsLvalue(AssignExp _this)
+ {
+ // Array-op 'x[] = y[]' should make an rvalue.
+ // Setting array length 'x.length = v' should make an rvalue.
+ if (_this.e1.op == EXP.slice || _this.e1.op == EXP.arrayLength)
+ {
+ return false;
+ }
+ return !_this.rvalue;
+ }
+
+ bool not_rvalue = _this.rvalue == false;
+
+ if (_this.isBinAssignExp())
+ return not_rvalue;
+
+ switch(_this.op)
+ {
+ case EXP.overloadSet: return true;
+ case EXP.identifier, EXP.dollar, EXP.dSymbol, EXP.star: return not_rvalue;
+ // Class `this` should be an rvalue; struct `this` should be an lvalue.
+ case EXP.this_, EXP.super_: return not_rvalue && _this.type.toBasetype().ty != Tclass;
+ /* string literal/slice is rvalue in default, but
+ * conversion to reference of static array is only allowed.
+ */
+ case EXP.string_, EXP.slice: return not_rvalue && (_this.type && _this.type.toBasetype().ty == Tsarray);
+ case EXP.question: return not_rvalue && _this.isCondExp().e1.isLvalue() && _this.isCondExp().e2.isLvalue();
+ case EXP.array: return !( _this.rvalue || (_this.type && _this.type.toBasetype().ty == Tvoid));
+
+ case EXP.vectorArray: return not_rvalue && _this.isVectorArrayExp().e1.isLvalue();
+ case EXP.delegatePointer: return not_rvalue && _this.isDelegatePtrExp().e1.isLvalue();
+ case EXP.delegateFunctionPointer: return not_rvalue && _this.isDelegateFuncptrExp().e1.isLvalue();
+
+ case EXP.comma: return not_rvalue && _this.isCommaExp().e2.isLvalue();
+ case EXP.variable: return !(_this.rvalue || (_this.isVarExp().var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest)));
+ case EXP.template_: return _this.isTemplateExp().fd !is null;
+
+ case EXP.dotVariable: return dotVarExpIsLvalue(_this.isDotVarExp());
+ case EXP.call: return callExpIsLvalue(_this.isCallExp());
+ case EXP.cast_: return castExpIsLvalue(_this.isCastExp());
+ case EXP.index: return indexExpIsLvalue(_this.isIndexExp());
+ case EXP.assign, EXP.loweredAssignExp, EXP.construct, EXP.blit: return assignExpIsLvalue(cast(AssignExp) _this);
+ default: return false;
+ }
+}
+
+/***********************************************************
+ * Determine if copy elision is allowed when copying an expression to
+ * a typed storage. This basically elides a restricted subset of so-called
+ * "pure" rvalues, i.e. expressions with no reference semantics.
+ */
+bool canElideCopy(Expression e, Type to, bool checkMod = true)
+{
+ if (checkMod && !MODimplicitConv(e.type.mod, to.mod))
+ return false;
+
+ static bool visitCallExp(CallExp e)
+ {
+ if (auto dve = e.e1.isDotVarExp())
+ if (dve.var.isCtorDeclaration())
+ return true;
+
+ auto tb = e.e1.type.toBasetype();
+ if (tb.ty == Tdelegate || tb.ty == Tpointer)
+ tb = tb.nextOf();
+ auto tf = tb.isTypeFunction();
+ return tf && !tf.isRef;
+ }
+
+ static bool visitDotVarExp(DotVarExp e)
+ {
+ auto vd = e.var.isVarDeclaration();
+ if (!vd || !vd.isField())
+ return false;
+
+ auto sd = vd.type.isTypeStruct();
+ if (!sd || sd.needsCopyOrPostblit() || sd.sym.hasMoveCtor)
+ return false;
+
+ // If an aggregate can be elided, so are its fields
+ return canElideCopy(e.e1, e.e1.type, false);
+ }
+
+ switch (e.op)
+ {
+ case EXP.comma:
+ auto ce = e.isCommaExp();
+ return canElideCopy(ce.e2, to, checkMod);
+ case EXP.question:
+ auto ce = e.isCondExp();
+ return canElideCopy(ce.e1, to, checkMod) && canElideCopy(ce.e2, to, checkMod);
+
+ case EXP.call:
+ return visitCallExp(e.isCallExp());
+ case EXP.dotVariable:
+ return visitDotVarExp(e.isDotVarExp());
+ case EXP.structLiteral:
+ auto sle = e.isStructLiteralExp();
+ return !(checkMod && sle.useStaticInit && to.isMutable());
+ case EXP.variable:
+ return (e.isVarExp().var.storage_class & STC.rvalue) != 0;
+ default:
+ return false;
+ }
+}
+
+// Return index of the field, or -1 if not found
+int getFieldIndex(ClassReferenceExp _this, Type fieldtype, uint fieldoffset)
+{
+ ClassDeclaration cd = _this.originalClass();
+ uint fieldsSoFar = 0;
+ for (size_t j = 0; j < _this.value.elements.length; j++)
+ {
+ while (j - fieldsSoFar >= cd.fields.length)
+ {
+ fieldsSoFar += cd.fields.length;
+ cd = cd.baseClass;
+ }
+ VarDeclaration v2 = cd.fields[j - fieldsSoFar];
+ if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size())
+ {
+ return cast(int)( _this.value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
+ }
+ }
+ return -1;
+}
+
+/************************************
+ * Get index of field.
+ * Returns -1 if not found.
+ */
+int getFieldIndex(StructLiteralExp _this, Type type, uint offset)
+{
+ /* Find which field offset is by looking at the field offsets
+ */
+ if (!_this.elements.length)
+ return -1;
+
+ const sz = type.size();
+ if (sz == SIZE_INVALID)
+ return -1;
+ foreach (i, v; _this.sd.fields)
+ {
+ if (offset != v.offset)
+ continue;
+ if (sz != v.type.size())
+ continue;
+ /* context fields might not be filled. */
+ if (i >= _this.sd.nonHiddenFields())
+ return cast(int)i;
+ if (auto e = (*_this.elements)[i])
+ {
+ return cast(int)i;
+ }
+ return -1;
+ }
+ assert(0);
+}
+
+bool equals(const Expression _this, const Expression e)
+{
+ static bool intExpEquals(const IntegerExp _this, const IntegerExp e)
+ {
+ return _this.type.toHeadMutable().equals(e.type.toHeadMutable()) && _this.value == e.value;
+ }
+
+ static bool realExpEquals(const RealExp _this, const RealExp e)
+ {
+ return _this.type.toHeadMutable().equals(e.type.toHeadMutable()) && RealIdentical(_this.value, e.value);
+ }
+
+ static bool complexExpEquals(const ComplexExp _this, const ComplexExp e)
+ {
+ return _this.type.toHeadMutable().equals(e.type.toHeadMutable()) &&
+ RealIdentical(creall(_this.value), creall(e.value)) &&
+ RealIdentical(cimagl(_this.value), cimagl(e.value));
+ }
+
+ static bool nullExpEquals(const NullExp _this, const NullExp e)
+ {
+ return e.op == EXP.null_ && _this.type.equals(e.type);
+ }
+
+ static bool stringExpEquals(const StringExp _this, const StringExp e)
+ {
+ //printf("StringExp::equals('%s') %s\n", o.toChars(), toChars());
+ return _this.compare(e) == 0;
+ }
+
+ static bool tupleExpEquals(const TupleExp _this, const TupleExp e)
+ {
+ if (_this.exps.length != e.exps.length)
+ return false;
+ if (_this.e0 && !_this.e0.equals(e.e0) || !_this.e0 && e.e0)
+ return false;
+ foreach (i, e1; *_this.exps)
+ {
+ auto e2 = (*e.exps)[i];
+ if (!e1.equals(e2))
+ return false;
+ }
+ return true;
+ }
+
+ static bool arrayLiteralExpEquals(const ArrayLiteralExp _this, const ArrayLiteralExp e)
+ {
+ if (_this.elements.length != e.elements.length)
+ return false;
+ if (_this.elements.length == 0 && !_this.type.equals(e.type))
+ {
+ return false;
+ }
+
+ foreach (i, e1; *_this.elements)
+ {
+ auto e2 = (*e.elements)[i];
+ auto e1x = e1 ? e1 : _this.basis;
+ auto e2x = e2 ? e2 : e.basis;
+
+ if (e1x != e2x && (!e1x || !e2x || !e1x.equals(e2x)))
+ return false;
+ }
+ return true;
+ }
+
+ static bool assocArrayLiteralExpEquals(const AssocArrayLiteralExp _this, const AssocArrayLiteralExp e)
+ {
+ if (_this.keys.length != e.keys.length)
+ return false;
+ size_t count = 0;
+ foreach (i, key; *_this.keys)
+ {
+ foreach (j, akey; *e.keys)
+ {
+ if (key.equals(akey))
+ {
+ if (!(*_this.values)[i].equals((*e.values)[j]))
+ return false;
+ ++count;
+ }
+ }
+ }
+ return count == _this.keys.length;
+ }
+
+ static bool structLiteralExpEquals(const StructLiteralExp _this, const StructLiteralExp e)
+ {
+ if (!_this.type.equals(e.type))
+ return false;
+ if (_this.elements.length != e.elements.length)
+ return false;
+ foreach (i, e1; *_this.elements)
+ {
+ auto e2 = (*e.elements)[i];
+ if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
+ return false;
+ }
+ return true;
+ }
+
+ static bool varExpEquals(const VarExp _this, const VarExp e)
+ {
+ return _this.type.toHeadMutable().equals(e.type.toHeadMutable()) && _this.var == e.var;
+ }
+
+ static bool funcExpEquals(const FuncExp _this, const FuncExp e)
+ {
+ return _this.fd == e.fd;
+ }
+
+ static bool mixinExpEquals(const MixinExp _this, const MixinExp e)
+ {
+ if (_this.exps.length != e.exps.length)
+ return false;
+ foreach (i, e1; *_this.exps)
+ {
+ auto e2 = (*e.exps)[i];
+ if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
+ return false;
+ }
+ return true;
+ }
+
+ if (_this == e)
+ return true;
+
+ if (_this.op != e.op)
+ return false;
+
+ switch(_this.op)
+ {
+ case EXP.int64: return intExpEquals(_this.isIntegerExp(), e.isIntegerExp());
+ case EXP.float64: return realExpEquals(_this.isRealExp(), e.isRealExp());
+ case EXP.complex80: return complexExpEquals(_this.isComplexExp(), e.isComplexExp());
+ case EXP.null_: return nullExpEquals(_this.isNullExp(), e.isNullExp());
+ case EXP.string_: return stringExpEquals(_this.isStringExp(), e.isStringExp());
+ case EXP.tuple: return tupleExpEquals(_this.isTupleExp(), e.isTupleExp());
+ case EXP.arrayLiteral: return arrayLiteralExpEquals(_this.isArrayLiteralExp(), e.isArrayLiteralExp());
+ case EXP.assocArrayLiteral: return assocArrayLiteralExpEquals(_this.isAssocArrayLiteralExp(), e.isAssocArrayLiteralExp());
+ case EXP.structLiteral: return structLiteralExpEquals(_this.isStructLiteralExp(), e.isStructLiteralExp());
+ case EXP.variable: return varExpEquals(_this.isVarExp(), e.isVarExp());
+ case EXP.function_: return funcExpEquals(_this.isFuncExp(), e.isFuncExp());
+ case EXP.mixin_: return mixinExpEquals(_this.isMixinExp(), e.isMixinExp());
+
+ default: return _this is e;
+ }
+}
+
+
+/********************************
+ * Test to see if two reals are the same.
+ * Regard NaN's as equivalent.
+ * Regard +0 and -0 as different.
+ * Params:
+ * x1 = first operand
+ * x2 = second operand
+ * Returns:
+ * true if x1 is x2
+ * else false
+ */
+@safe private bool RealIdentical(real_t x1, real_t x2)
+{
+ return (CTFloat.isNaN(x1) && CTFloat.isNaN(x2)) || CTFloat.isIdentical(x1, x2);
+}
+
+private bool realExpIsIdentical(const RealExp _this, const Expression e)
+{
+ if (!equals(_this, e))
+ return false;
+ return CTFloat.isIdentical(_this.value, e.isRealExp().value);
+}
+
+private bool complexExpIsIdentical(const ComplexExp _this, const Expression e)
+{
+ if (!equals(_this, e))
+ return false;
+ // equals() regards different NaN values as 'equals'
+ auto c = e.isComplexExp();
+ return CTFloat.isIdentical(creall(_this.value), creall(c.value)) &&
+ CTFloat.isIdentical(cimagl(_this.value), cimagl(c.value));
+}
+
+/******
+ * Identical, not just equal. I.e. NaNs with different bit patterns are not identical
+ */
+bool isIdentical(const Expression _this, const Expression e)
+{
+ if (auto re = _this.isRealExp())
+ return realExpIsIdentical(re, e);
+ else if (auto ce = _this.isComplexExp())
+ return complexExpIsIdentical(ce, e);
+ return equals(_this, e);
+}
+
/***********************************
* Determine if a `this` is needed to access `d`.
* Params:
{
if (auto td = s.isTemplateDeclaration())
{
+ td.computeOneMember();
if (ignoreDisabled && td.onemember)
{
if (auto ctorDecl = td.onemember.isCtorDeclaration())
return result;
}
+/// Returns: whether `s` is a method which can possibly be called without a struct instance.
+/// Used to check whether S() should try to call `S.opCall()` rather than construct a struct literal
+bool hasStaticOverload(Dsymbol s)
+{
+ bool result = false;
+ overloadApply(s, (Dsymbol sym) {
+ if (auto fd = sym.isFuncDeclaration())
+ {
+ if (fd.isStatic)
+ {
+ result = true;
+ return 1;
+ }
+ }
+ else if (auto td = sym.isTemplateDeclaration())
+ {
+ // Consider both `template opCall { static opCall() {} }` and `static opCall()() {}`
+ if (td._scope.stc & STC.static_)
+ {
+ result = true;
+ return 1;
+ }
+ if (auto fd = td.onemember.isFuncDeclaration())
+ {
+ if (fd.isStatic)
+ {
+ result = true;
+ return 1;
+ }
+ }
+ }
+ return 0;
+ });
+ return result;
+}
+
/*****************************************
* Determine if `this` is available by walking up the enclosing
* scopes until a function is found.
Type t1 = e1.type;
Type t2 = e2.type;
+ // @@@DEPRECATED_2.122@@@
+ // Deprecated in 2.112, make it an error in 2.122
// T opAssign floating yields a floating. Prevent truncating conversions (float to int).
// See https://issues.dlang.org/show_bug.cgi?id=3841.
// Should we also prevent double to float (type.isFloating() && type.size() < t2.size()) ?
- if (op == EXP.addAssign || op == EXP.minAssign ||
- op == EXP.mulAssign || op == EXP.divAssign || op == EXP.modAssign ||
- op == EXP.powAssign)
+ if (!sc.inCfile &&
+ (op == EXP.addAssign || op == EXP.minAssign ||
+ op == EXP.mulAssign || op == EXP.divAssign || op == EXP.modAssign ||
+ op == EXP.powAssign))
{
if ((type.isIntegral() && t2.isFloating()))
{
- warning(loc, "`%s %s %s` is performing truncating conversion", type.toChars(), EXPtoString(op).ptr, t2.toChars());
+ deprecation(loc, "`%s %s %s` is performing truncating conversion", type.toChars(), EXPtoString(op).ptr, t2.toChars());
}
}
{
e = callCpCtor(sc, e, t, nrvo);
}
- else if (move && sd && sd.hasMoveCtor && !e.isCallExp() && !e.isStructLiteralExp())
+ else if (move && sd && sd.hasMoveCtor && !canElideCopy(e, t ? t : e.type, false))
{
// #move
/* Rewrite as:
VarDeclaration vd = new VarDeclaration(e.loc, e.type, Identifier.generateId("__copyrvalue"), null);
if (nrvo)
vd.nrvo = true;
- vd.storage_class |= STC.nodtor;
+ vd.storage_class |= STC.temp | STC.rvalue | STC.nodtor;
vd.dsymbolSemantic(sc);
Expression de = new DeclarationExp(e.loc, vd);
Expression ve = new VarExp(e.loc, vd);
{
//printf("e1 = %s %s\n", Token.toChars(e1.op), e1.toChars());
- Expression handleOverloadSet(OverloadSet os)
- {
- assert(os);
- foreach (s; os.a)
- {
- auto fd = s.isFuncDeclaration();
- auto td = s.isTemplateDeclaration();
- if (fd)
- {
- if (fd.type.isTypeFunction().isProperty)
- return resolveProperties(sc, e1);
- }
- else if (td && td.onemember && (fd = td.onemember.isFuncDeclaration()) !is null)
- {
- if (fd.type.isTypeFunction().isProperty ||
- (fd.storage_class2 & STC.property) ||
- (td._scope.stc & STC.property))
- return resolveProperties(sc, e1);
- }
- }
- return e1;
- }
-
Expression handleTemplateDecl(TemplateDeclaration td)
{
assert(td);
- if (td.onemember)
+ td.computeOneMember();
+ if (!td.onemember)
+ return e1;
+
+ if (auto fd = td.onemember.isFuncDeclaration())
{
- if (auto fd = td.onemember.isFuncDeclaration())
- {
- if (fd.type.isTypeFunction().isProperty ||
- (fd.storage_class2 & STC.property) ||
- (td._scope.stc & STC.property))
- return resolveProperties(sc, e1);
- }
+ if (fd.type.isTypeFunction().isProperty ||
+ (fd.storage_class2 & STC.property) ||
+ (td._scope.stc & STC.property))
+ return resolveProperties(sc, e1);
}
return e1;
}
return e1;
}
+ Expression handleOverloadSet(OverloadSet os)
+ {
+ assert(os);
+ foreach (s; os.a)
+ {
+ if (auto fd = s.isFuncDeclaration())
+ return handleFuncDecl(fd);
+
+ if (auto td = s.isTemplateDeclaration())
+ return handleTemplateDecl(td);
+ }
+ return e1;
+ }
+
if (auto de = e1.isDotExp())
{
if (auto os = de.e2.isOverExp())
return ie.expressionSemantic(sc);
}
- if (Type t = s.getType())
+ if (Type t = dmd.dsymbolsem.getType(s))
{
return (new TypeExp(loc, t)).expressionSemantic(sc);
}
if (tup.needThis() && hasThis(sc))
e = new DotVarExp(loc, new ThisExp(loc), tup);
else
+ {
e = new TupleExp(loc, tup);
+ fillTupleExpExps(e.isTupleExp(), tup);
+ }
e = e.expressionSemantic(sc);
return e;
}
{
auto cls = ad.isClassDeclaration();
auto classObj = new ObjcClassReferenceExp(e1.loc, cls);
- classObj.type = objc.getRuntimeMetaclass(cls).getType();
+ classObj.type = dmd.dsymbolsem.getType(objc.getRuntimeMetaclass(cls));
return classObj;
}
if (s.action.length > 0)
{
errorFunc(s.loc, "and %.*s makes it fail to infer `%.*s`", s.action.fTuple.expand, attr.fTuple.expand);
+ // For scope violations, also print why the target parameter is not scope
+ if (s.scopeVar)
+ {
+ import dmd.escape : printScopeReason;
+ printScopeReason(errorFunc, s.scopeVar, 10, false);
+ }
}
else if (s.fd)
{
/*********************************************
* Calling function f.
- * Check the safety, i.e. if we're in a @safe function
+ * Check the safety, i.e. if we are in a @safe function
* we can only call @safe or @trusted functions.
- * Returns true if error occurs.
+ * Params:
+ * f = function being called
+ * loc = location for error messages
+ * sc = context
+ * arguments = array of actual arguments to function call, null for none
+ * Returns: true if unsafe (and diagnostic is generated)
*/
-private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc)
+private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc, Expressions* arguments)
{
+ //printf("checkSafety() %s\n", f.toChars());
if (sc.func == f)
return false;
if (sc.intypeof == 1)
return false;
}
+ if (f.printf)
+ {
+ TypeFunction tf = f.type.isTypeFunction();
+ assert(tf);
+ const isVa_list = tf.parameterList.varargs == VarArg.none;
+ const nparams = tf.parameterList.length;
+ const nargs = arguments ? arguments.length : 0;
+ if (nparams == 1 && nargs)
+ {
+ if (auto se = (*arguments)[nparams - 1 - isVa_list].isStringExp())
+ {
+ if (isFormatSafe(se.peekString()))
+ return false;
+ }
+ }
+ }
+
if (!f.isSafe() && !f.isTrusted())
{
if (isRootTraitsCompilesScope(sc) ? sc.func.isSafeBypassingInference() : sc.func.setUnsafeCall(f))
if (f.isNogc())
return false;
- if (isRootTraitsCompilesScope(sc) ? !sc.func.isNogcBypassingInference() : !sc.func.setGCCall(f))
+ if (isRootTraitsCompilesScope(sc) ? !sc.func.isNogcBypassingInference() : !sc.setGCCall(sc.func, f))
return false;
if (loc == Loc.initial) // e.g. implicitly generated dtor
//checkAccess(sd, loc, sc, sd.postblit); // necessary?
bool result = false;
result |= sd.postblit.checkPurity(loc, sc);
- result |= sd.postblit.checkSafety(loc, sc);
+ result |= sd.postblit.checkSafety(loc, sc, null);
result |= sd.postblit.checkNogc(loc, sc);
return result;
}
*/
private bool functionParameters(Loc loc, Scope* sc,
TypeFunction tf, Expression ethis, Type tthis, ArgumentList argumentList, FuncDeclaration fd,
- Type* prettype, Expression* peprefix)
+ out Type prettype, out Expression peprefix)
{
Expressions* arguments = argumentList.arguments;
//printf("functionParameters() fd: %s tf: %s\n", fd ? fd.ident.toChars() : "", toChars(tf));
const olderrors = global.errors;
bool err = false;
Expression eprefix = null;
- *peprefix = null;
if (argumentList.names)
{
* Start with the `this` argument, later on merge into wildmatch the mod bits of the rest
* of the arguments.
*/
- MOD wildmatch = (tthis && !isCtorCall) ? tthis.Type.deduceWild(tf, false) : 0;
+ MOD wildmatch = (tthis && !isCtorCall) ? tthis.typeDeduceWild(tf, false) : 0;
bool done = false;
foreach (const i; 0 .. n)
tret = tret.substWildTo(wildmatch);
}
- *prettype = tret;
- *peprefix = eprefix;
+ prettype = tret;
+ peprefix = eprefix;
return (err || olderrors != global.errors);
}
public:
bool result = false;
+ static Type loweredBaseElemOf(Type t)
+ {
+ t = t.baseElemOf(); // skip over static-array parents
+ switch (t.ty)
+ {
+ case Tvoid:
+ return Type.tuns8;
+ case Tpointer:
+ return Type.tsize_t;
+ default:
+ return t;
+ }
+ }
+
+ static bool isTriviallyMemcmpable(Type t)
+ {
+ return loweredBaseElemOf(t).isIntegral();
+ }
+
override void visit(Type t)
{
- result = t.ty == Tvoid || (t.isScalar() && !t.isFloating());
+ result = isTriviallyMemcmpable(t);
}
override void visit(TypeStruct ts)
}
}
const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd);
- if (lv == fd.LevelError)
+ if (lv == LevelError)
return true; // error
if (lv == -1)
return false; // downlevel call
o = vd.type;
else if (AliasDeclaration ad = d.isAliasDeclaration())
{
- o = ad.getType();
+ o = dmd.dsymbolsem.getType(ad);
if (!o)
o = ad.toAlias();
}
{
if (!e.type)
e.type = Type.tfloat64;
- else if (!e.type.isImaginary || !sc.inCfile)
- {
- e.type = e.type.typeSemantic(e.loc, sc);
- result = e;
- return;
- }
-
- /* Convert to core.stdc.config.complex
- */
- Type t = getComplexLibraryType(e.loc, sc, e.type.ty);
- if (t.ty == Terror)
- return setError();
-
- 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);
- }
- /* 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);
+ e.type = e.type.typeSemantic(e.loc, sc);
+ result = e;
+ return;
}
override void visit(ComplexExp e)
setError();
}
}
- scope (exit) result.rvalue = exp.rvalue;
+ scope (exit)
+ {
+ if (result !is null)
+ result.rvalue = exp.rvalue;
+ }
Dsymbol scopesym;
Dsymbol s = sc.search(exp.loc, exp.ident, scopesym);
error(exp.loc, "`%s` is not defined, perhaps `import %.*s;` is needed?", exp.ident.toChars(), cast(int)n.length, n.ptr);
else if (auto s2 = sc.search_correct(exp.ident))
error(exp.loc, "undefined identifier `%s`, did you mean %s `%s`?", exp.ident.toChars(), s2.kind(), s2.toChars());
- else if (const p = Scope.search_correct_C(exp.ident))
+ else if (const p = search_correct_C(exp.ident))
error(exp.loc, "undefined identifier `%s`, did you mean `%s`?", exp.ident.toChars(), p);
else if (exp.ident == Id.dollar)
error(exp.loc, "undefined identifier `$`");
error(cle.loc, "cannot convert initializer `%s` to expression", toChars(init));
return setError();
}
- result = e;
- return;
+
+ // leave these to be handled by their respective functions
+ if (e.isStringExp() || e.isStructLiteralExp() || e.isArrayLiteralExp() || e.isAssocArrayLiteralExp())
+ {
+ result = e;
+ return;
+ }
+
+ if (sc.func)
+ {
+ Expression e0;
+ auto ve = extractSideEffect(sc, "__cl", e0, e, true);
+
+ // export (int a = 5, a) so a doesn't forward ref
+ result = new CommaExp(cle.loc, e0, ve).expressionSemantic(sc);
+ return;
+ }
+ else //global variables
+ {
+ Identifier ident = Identifier.generateId("__cl");
+ auto tmp = new VarDeclaration(cle.loc, t, ident, new ExpInitializer(cle.loc, e));
+
+ // static const type ???
+ tmp.storage_class = STC.static_ | STC.const_ | STC.ctfe;
+
+ // for our global variables, we need them in our symbol table
+ if (sc._module)
+ {
+ sc._module.members.push(tmp);
+ }
+ else if (sc.minst)
+ {
+ sc.minst.members.push(tmp);
+ }
+
+ dsymbolSemantic(tmp, sc);
+
+ auto ve = new VarExp(cle.loc, tmp).expressionSemantic(sc);
+ result = ve;
+ return;
+ }
}
override void visit(TypeExp exp)
sds2.dsymbolSemantic(sc);
// (Aggregate|Enum)Declaration
- if (auto t = sds2.getType())
+ if (auto t = dmd.dsymbolsem.getType(sds2))
{
result = (new TypeExp(exp.loc, t)).expressionSemantic(sc);
return;
error(p.loc, "PlacementExpression `%s` is an rvalue, but must be an lvalue", p.toChars());
return setError();
}
- if (sc.setUnsafe(false, p.loc, "`@safe` function `%s` cannot use placement `new`", sc.func))
+ if (sc.setUnsafe(false, p.loc, "placement `new`", sc.func))
+ {
+ return setError();
+ }
+ if (!p.type.isNaked)
{
+ error(p.loc, "PlacementExpression `%s` of type `%s` must be unshared and mutable", p.toChars(), toChars(p.type));
return setError();
}
- if (!exp.placement.type.isNaked)
+ if (p.type.ty == Tarray)
{
- error(p.loc, "PlacementExpression `%s` of type `%s` be unshared and mutable", p.toChars(), toChars(p.type));
+ error(p.loc, "PlacementExpression cannot be a dynamic array");
return setError();
}
checkModifiable(exp.placement, sc);
TypeFunction tf = f.type.isTypeFunction();
if (!exp.arguments)
exp.arguments = new Expressions();
- if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.argumentList, f, &exp.type, &exp.argprefix))
+ if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.argumentList, f, exp.type, exp.argprefix))
return setError();
exp.member = f.isCtorDeclaration();
TypeFunction tf = f.type.isTypeFunction();
if (!exp.arguments)
exp.arguments = new Expressions();
- if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.argumentList, f, &exp.type, &exp.argprefix))
+ if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.argumentList, f, exp.type, exp.argprefix))
return setError();
exp.member = f.isCtorDeclaration();
if (!global.params.useGC && sc.needsCodegen())
{
- version(IN_GCC)
- error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-fno-rtti`", exp.toErrMsg());
- else
- error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-betterC`", exp.toErrMsg());
+ error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-%s`", exp.toErrMsg(), SwitchVariadic.ptr);
return setError();
}
//printf("td = %p, treq = %p\n", td, fd.treq);
if (exp.td)
{
- assert(exp.td.parameters && exp.td.parameters.length);
+ assert(exp.td.parameters);
exp.td.dsymbolSemantic(sc);
exp.type = Type.tvoid; // temporary type
{
setError();
}
- else
+ else if (result !is null)
{
result.rvalue = true;
}
return;
}
}
- if (sc.inCfile)
- {
- /* See if need to rewrite the AST because of cast/call ambiguity
- */
- if (auto e = castCallAmbiguity(exp, sc))
- {
- result = expressionSemantic(e, sc);
- return;
- }
- }
-
if (Expression ex = resolveUFCS(sc, exp))
{
result = ex;
return;
}
// No constructor, look for overload of opCall
- if (search_function(sd, Id.opCall))
- goto L1;
+ if (auto sym = search_function(sd, Id.opCall))
+ {
+ // Don't consider opCall on a type with no instance
+ // if there's no static overload for it
+ if (!exp.e1.isTypeExp() || hasStaticOverload(sym))
+ goto L1;
+ }
// overload of opCall, therefore it's a call
if (exp.e1.op != EXP.type)
{
if (exp.f)
{
exp.f.checkPurity(exp.loc, sc);
- exp.f.checkSafety(exp.loc, sc);
+ exp.f.checkSafety(exp.loc, sc, exp.arguments);
exp.f.checkNogc(exp.loc, sc);
if (exp.f.checkNestedFuncReference(sc, exp.loc))
return setError();
sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toErrMsg());
err = true;
}
- if (!tf.isNogc && sc.func.setGC(exp.loc, "calling non-@nogc `%s`", exp.e1))
+ if (!tf.isNogc && sc.setGC(sc.func, exp.loc, "calling non-@nogc `%s`", exp.e1))
{
error(exp.loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toErrMsg());
Expression argprefix;
if (!exp.arguments)
exp.arguments = new Expressions();
- if (functionParameters(exp.loc, sc, cast(TypeFunction)t1, ethis, tthis, exp.argumentList, exp.f, &exp.type, &argprefix))
+ if (functionParameters(exp.loc, sc, cast(TypeFunction)t1, ethis, tthis, exp.argumentList, exp.f, exp.type, argprefix))
return setError();
if (!exp.type)
//printf("inserting '%s' %p into sc = %p\n", s.toChars(), s, sc);
// Insert into both local scope and function scope.
- // Must be unique in both.
+ // Must be unique in both (except for importC).
if (s.ident)
{
VarDeclaration v = s.isVarDeclaration();
if (sc.inCfile)
{
- /* See if need to rewrite the AST because of cast/call ambiguity
- */
- if (auto e = castCallAmbiguity(exp, sc))
- {
- result = expressionSemantic(e, sc);
- return;
- }
-
if (exp.arrow) // ImportC only
exp.e1 = exp.e1.expressionSemantic(sc).arrayFuncConv(sc);
}
else if (auto ad = exp.var.isAliasDeclaration())
{
- if (auto t = ad.getType())
+ if (auto t = dmd.dsymbolsem.getType(ad))
{
result = new TypeExp(exp.loc, t).expressionSemantic(sc);
return;
return;
}
- exp.type = exp.sym.getType().addMod(exp.e1.type.mod);
+ exp.type = dmd.dsymbolsem.getType(exp.sym).addMod(exp.e1.type.mod);
result = exp;
}
* since C regards string/array literals as lvalues
*/
auto e = exp.e1;
- if(e.isStringExp() || e.isArrayLiteralExp())
+ if (e.isStringExp() || e.isArrayLiteralExp())
{
e.type = typeSemantic(e.type, Loc.initial, sc);
// if type is already a pointer exp is an illegal expression of the form `&(&"")`
{
err |= !functionSemantic(cd.dtor);
err |= cd.dtor.checkPurity(exp.loc, sc);
- err |= cd.dtor.checkSafety(exp.loc, sc);
+ err |= cd.dtor.checkSafety(exp.loc, sc, null);
err |= cd.dtor.checkNogc(exp.loc, sc);
}
if (err)
result = e1x;
return;
}
- if (e1x.checkType())
+ if (!e1x.hasValidType())
return setError();
exp.e1 = e1x;
printf("ArrayExp::semantic('%s')\n", exp.toChars());
}
- if (sc.inCfile)
- {
- /* See if need to rewrite the AST because of cast/call ambiguity
- */
- if (auto e = castCallAmbiguity(exp, sc))
- {
- result = expressionSemantic(e, sc);
- return;
- }
- }
-
result = exp.carraySemantic(sc); // C semantics
if (result)
return;
* https://issues.dlang.org/show_bug.cgi?id=2684
* see also bug https://issues.dlang.org/show_bug.cgi?id=2954 b
*/
- if (!arrayTypeCompatibleWithoutCasting(exp.e2.type, taa.index))
+ if (!keyCompatibleWithoutCasting(exp.e2, taa.index))
{
exp.e2 = exp.e2.implicitCastTo(sc, taa.index); // type checking
if (exp.e2.type == Type.terror)
printf("PostExp::semantic('%s')\n", exp.toChars());
}
- if (sc.inCfile)
- {
- /* See if need to rewrite the AST because of cast/call ambiguity
- */
- if (auto e = castCallAmbiguity(exp, sc))
- {
- result = expressionSemantic(e, sc);
- return;
- }
- }
-
if (auto ae = exp.e1.isArrayExp())
markArrayExpModifiable(ae);
return setError();
// Lower to object._d_array{,set}ctor(e1, e2)
- Expression id = new IdentifierExp(exp.loc, Id.empty);
- id = new DotIdExp(exp.loc, id, Id.object);
- id = new DotIdExp(exp.loc, id, func);
+ Expression lowering = new IdentifierExp(ae.loc, Id.empty);
+ lowering = new DotIdExp(ae.loc, lowering, Id.object);
+ lowering = new DotIdExp(ae.loc, lowering, func);
auto arguments = new Expressions(new CastExp(ae.loc, ae.e1, t1e.arrayOf).expressionSemantic(sc));
if (lowerToArrayCtor)
{
arguments.push(new CastExp(ae.loc, rhs, t2b.nextOf.arrayOf).expressionSemantic(sc));
- Expression ce = new CallExp(exp.loc, id, arguments);
- res = ce.expressionSemantic(sc);
+ lowering = new CallExp(ae.loc, lowering, arguments);
+ lowering = lowering.expressionSemantic(sc);
}
else
{
else
arguments.push(ae.e2);
- Expression ce = new CallExp(exp.loc, id, arguments);
- res = Expression.combine(e0, ce).expressionSemantic(sc);
+ lowering = new CallExp(ae.loc, lowering, arguments);
+ lowering = Expression.combine(e0, lowering).expressionSemantic(sc);
}
+ ae.lowering = lowering;
+
if (global.params.v.verbose)
- message("lowered %s =>\n %s", exp.toChars(), res.toChars());
+ message("lowered %s =>\n %s", exp.toChars(), lowering.toChars());
}
}
else if (auto ae = res.isAssignExp())
TypeAArray ta = cast(TypeAArray)t2b;
// Special handling for array keys
- if (!arrayTypeCompatibleWithoutCasting(exp.e1.type, ta.index))
+ if (!keyCompatibleWithoutCasting(exp.e1, ta.index))
{
// Convert key to type of key
exp.e1 = exp.e1.implicitCastTo(sc, ta.index);
{
Type t1n = t1.nextOf().toBasetype();
Type t2n = t2.nextOf().toBasetype();
- const t1nsz = t1n.size();
- const t2nsz = t2n.size();
- if ((t1n.ty == Tvoid || (t1n.isScalar() && !t1n.isFloating())) &&
- (t2n.ty == Tvoid || (t2n.isScalar() && !t1n.isFloating())) &&
- t1nsz == t2nsz && t1n.isUnsigned() == t2n.isUnsigned())
+ if (t1n.size() != t2n.size())
+ return false;
+
+ if (IsMemcmpableVisitor.isTriviallyMemcmpable(t1n) &&
+ IsMemcmpableVisitor.isTriviallyMemcmpable(t2n))
{
- return true;
+ // due to int promotion, disallow small integers of diverging signed-ness
+ Type e1 = IsMemcmpableVisitor.loweredBaseElemOf(t1n);
+ Type e2 = IsMemcmpableVisitor.loweredBaseElemOf(t2n);
+ if ((e1.size() >= 4 && e2.size() >= 4) || e1.isUnsigned() == e2.isUnsigned())
+ return true;
}
+
if (t1n.constOf() != t2n.constOf())
{
return false;
if (global.params.useTypeInfo && Type.dtypeinfo)
semanticTypeInfo(sc, ts);
- auto v = new IsMemcmpableVisitor();
+ scope v = new IsMemcmpableVisitor();
ts.accept(v);
return v.result;
}
return new OverExp(exp.loc, o);
}
- if (auto t = s.getType())
+ if (auto t = dmd.dsymbolsem.getType(s))
{
return (new TypeExp(exp.loc, t)).expressionSemantic(sc);
}
return e;
}
Expression e = new TupleExp(exp.loc, tup);
+ fillTupleExpExps(e.isTupleExp(), tup);
e = e.expressionSemantic(sc);
return e;
}
MATCH matchType(FuncExp funcExp, Type to, Scope* sc, FuncExp* presult, ErrorSink eSink)
{
+ import dmd.typesem : merge;
+
auto loc = funcExp.loc;
auto tok = funcExp.tok;
auto td = funcExp.td;
}
if (!_this.isLvalue())
return visit(_this);
- if (e1.op == EXP.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
+ if (e1.op == EXP.this_ && sc && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
{
if (VarDeclaration vd = var.isVarDeclaration())
{
"taking the address of local variable `%s`" :
"taking the address of stack-allocated local variable `%s`";
if (sc.useDIP1000 != FeatureState.enabled &&
- !(v.storage_class & STC.temp) &&
+ (!(v.storage_class & STC.temp) || v.storage_class & STC.result) &&
sc.setUnsafe(false, exp.loc, msg.ptr, v))
{
return false;
* and usage of `deprecated` and `@disabled`-ed symbols are checked.
*
* Params:
- * exp = expression to check attributes for
+ * exp = expression to check attributes for (CallExp or NewExp)
* sc = scope of the function
* f = function to be checked
* Returns: `true` if error occur.
bool error = f.checkDisabled(exp.loc, sc);
error |= f.checkDeprecated(exp.loc, sc);
error |= f.checkPurity(exp.loc, sc);
- error |= f.checkSafety(exp.loc, sc);
+ Expressions* arguments = exp.isCallExp() ? exp.isCallExp().arguments : null;
+ error |= f.checkSafety(exp.loc, sc, arguments);
error |= f.checkNogc(exp.loc, sc);
return error;
}
bool verifyHookExist(Loc loc, ref Scope sc, Identifier id, string description, Identifier module_ = Id.object)
{
Dsymbol pscopesym;
- auto rootSymbol = sc.search(loc, Id.empty, pscopesym);
+ auto rootSymbol = search(&sc, loc, Id.empty, pscopesym);
if (auto moduleSymbol = rootSymbol.search(loc, module_))
if (moduleSymbol.search(loc, id))
return true;
Type typeb = se.type.toBasetype();
TY tynto = tb.nextOf().ty;
if (!se.committed &&
- typeb.isStaticOrDynamicArray() && tynto.isSomeChar &&
- se.numberOfCodeUnits(tynto) < (cast(TypeSArray)tb).dim.toInteger())
+ typeb.isStaticOrDynamicArray() && tynto.isSomeChar)
{
- e = se.castTo(sc, t);
- goto L1;
+ string s;
+ size_t len = se.numberOfCodeUnits(tynto, s);
+ if (s)
+ error(se.loc, "%.*s", cast(int)s.length, s.ptr);
+ if (len < (cast(TypeSArray)tb).dim.toInteger())
+ {
+ e = se.castTo(sc, t);
+ goto L1;
+ }
}
}
else if (TemplateInstance ti = sd.isInstantiated())
{
if (ti.minst && !ti.minst.isRoot())
- Module.addDeferredSemantic3(sd);
+ addDeferredSemantic3(sd);
}
else
{
if (sd.inNonRoot())
{
//printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot());
- Module.addDeferredSemantic3(sd);
+ addDeferredSemantic3(sd);
}
}
}
auto td = s.isTemplateDeclaration();
if (!td)
return 0;
-
/* If any of the overloaded template declarations need inference,
* then return true
*/
+ td.computeOneMember();
if (!td.onemember)
return 0;
if (auto td2 = td.onemember.isTemplateDeclaration())
{
+ td2.computeOneMember();
if (!td2.onemember || !td2.onemember.isFuncDeclaration())
return 0;
if (ti.tiargs.length >= td.parameters.length - (td.isVariadic() ? 1 : 0))
*/
extern(D) void prepare(StaticForeach sfe, Scope* sc)
{
- assert(sc);
+ import dmd.statementsem : ready;
+ assert(sc);
if (sfe.aggrfe)
{
sc = sc.startCTFE();
foreach (ekey; ekeys)
{
Type tidx = ekey.type.toBasetype();
- if (tidx.ty == Tarray && tidx.nextOf().isMutable())
+ if (tidx.ty == Tarray && tidx.nextOf().isMutable() && !ekey.isArrayLiteralExp())
{
error(loc, "associative arrays can only be assigned values with immutable keys, not `%s`", tidx.toChars());
return ErrorExp.get();
// extract side effects in lexical order
for (size_t i = ekeys.length; i > 0; --i)
ekeys[i-1] = extractSideEffect(sc, "__aakey", e0, ekeys[i-1]);
- Expression ev = extractSideEffect(sc, "__aaval", e0, exp.e2);
+ // some implicit conversions are lost when assigning to a temporary, e.g. from array literal
+ auto taa = eaa.type.isTypeAArray();
+ auto match = exp.e2.implicitConvTo(taa.next);
+ auto e2 = match == MATCH.exact || match == MATCH.nomatch ? exp.e2 : exp.e2.implicitCastTo(sc, taa.next);
+ Expression ev = extractSideEffect(sc, "__aaval", e0, e2); // must be evaluated before the insertion
// generate series of calls to _d_aaGetY
for (size_t i = ekeys.length; i > 0; --i)
{
- auto taa = eaa.type.isTypeAArray();
+ taa = eaa.type.isTypeAArray();
assert (taa); // type must not have changed during rewrite
Expression func = new IdentifierExp(loc, Id.empty);
func = new DotIdExp(loc, func, Id.object);
import core.stdc.stdio;
import dmd.common.outbuffer;
import dmd.root.stringtable : StringTable;
-import dmd.root.file : File, Buffer;
+import dmd.root.file : File;
import dmd.root.filename : FileName, isDirSeparator;
import dmd.root.string : toDString;
import dmd.globals;
import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
-import dmd.blockexit;
-import dmd.dcast;
import dmd.dclass;
import dmd.declaration;
-import dmd.delegatize;
-import dmd.dmodule;
-import dmd.dscope;
-import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
-import dmd.escape;
-import dmd.expression;
-import dmd.funcsem : isUnique;
import dmd.globals;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
-import dmd.init;
import dmd.location;
import dmd.mtype;
import dmd.objc;
import dmd.common.outbuffer;
import dmd.rootobject;
-import dmd.root.string;
-import dmd.root.stringtable;
import dmd.statement;
+import dmd.targetcompiler;
import dmd.tokens;
import dmd.visitor;
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
-
/// Inline Status
enum ILS : ubyte
{
Expressions* fdensureParams; /// argument list for __ensure
}
+/// Information for data flow analysis regarding parameters
+extern(D) struct ParametersDFAInfo
+{
+ ParameterDFAInfo thisPointer;
+ ParameterDFAInfo returnValue;
+ ParameterDFAInfo[] parameters;
+}
+
+/// Information for data flow analysis per parameter
+extern(D) struct ParameterDFAInfo
+{
+ /// Parameter id: -1 this, -2 return, otherwise it is FuncDeclaration.parameters index
+ int parameterId;
+
+ /// Is the parameter non-null upon input, only applies to pointers.
+ Fact notNullIn;
+ /// Is the parameter non-null, applies only for by-ref parameters that are pointers.
+ Fact notNullOut;
+
+ /// Was the attributes for this parameter specified by the user?
+ bool specifiedByUser;
+
+ /// Given a property, has it been specificed and is it guaranteed?
+ enum Fact : ubyte
+ {
+ ///
+ Unspecified,
+ ///
+ NotGuaranteed,
+ ///
+ Guaranteed
+ }
+}
+
/***********************************************************
*/
extern (C++) class FuncDeclaration : Declaration
GotoStatements* gotos; /// Gotos with forward references
- version (MARS)
- {
- VarDeclarations* alignSectionVars; /// local variables with alignment needs larger than stackAlign
- void* salignSection; /// pointer to aligned section, if any
- }
+ mixin FuncDeclarationExtra;
/// set if this is a known, builtin function we can evaluate at compile time
BUILTIN builtin = BUILTIN.unknown;
AttributeViolation* pureViolation;
AttributeViolation* nothrowViolation;
+ ParametersDFAInfo* parametersDFAInfo;
+
/// See the `FUNCFLAG` struct
import dmd.common.bitfields;
mixin(generateBitFields!(FUNCFLAG, uint));
/* The type given for "infer the return type" is a TypeFunction with
* NULL for the return type.
*/
- if (type && type.nextOf() is null)
- this.inferRetType = true;
+ if (type)
+ {
+ if (auto tf = type.isTypeFunction())
+ this.inferRetType = tf.next is null;
+ else
+ assert(0); // unreachable
+ }
}
static FuncDeclaration create(Loc loc, Loc endloc, Identifier id, StorageClass storage_class, Type type, bool noreturn = false)
return f;
}
- override final bool equals(const RootObject o) const
- {
- if (this == o)
- return true;
-
- auto s = isDsymbol(o);
- if (!s)
- return false;
-
- auto fd1 = this;
- auto fd2 = s.isFuncDeclaration();
- if (!fd2)
- return false;
-
- auto fa1 = fd1.isFuncAliasDeclaration();
- auto faf1 = fa1 ? fa1.toAliasFunc() : fd1;
-
- auto fa2 = fd2.isFuncAliasDeclaration();
- auto faf2 = fa2 ? fa2.toAliasFunc() : fd2;
-
- if (fa1 && fa2)
- return faf1.equals(faf2) && fa1.hasOverloads == fa2.hasOverloads;
-
- bool b1 = fa1 !is null;
- if (b1 && faf1.isUnique() && !fa1.hasOverloads)
- b1 = false;
-
- bool b2 = fa2 !is null;
- if (b2 && faf2.isUnique() && !fa2.hasOverloads)
- b2 = false;
-
- if (b1 != b2)
- return false;
-
- return faf1.toParent().equals(faf2.toParent()) &&
- faf1.ident.equals(faf2.ident) &&
- faf1.type.equals(faf2.type);
- }
-
- /****************************************************
- * Overload this FuncDeclaration with the new one f.
- * Return true if successful; i.e. no conflict.
- */
- override bool overloadInsert(Dsymbol s)
- {
- //printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s.toChars(), toChars());
- assert(s != this);
- if (AliasDeclaration ad = s.isAliasDeclaration())
- {
- if (overnext)
- return overnext.overloadInsert(ad);
- if (!ad.aliassym && ad.type.ty != Tident && ad.type.ty != Tinstance && ad.type.ty != Ttypeof)
- {
- //printf("\tad = '%s'\n", ad.type.toChars());
- return false;
- }
- overnext = ad;
- //printf("\ttrue: no conflict\n");
- return true;
- }
- TemplateDeclaration td = s.isTemplateDeclaration();
- if (td)
- {
- if (!td.funcroot)
- td.funcroot = this;
- if (overnext)
- return overnext.overloadInsert(td);
- overnext = td;
- return true;
- }
- FuncDeclaration fd = s.isFuncDeclaration();
- if (!fd)
- return false;
-
- if (overnext)
- {
- td = overnext.isTemplateDeclaration();
- if (td)
- fd.overloadInsert(td);
- else
- return overnext.overloadInsert(fd);
- }
- overnext = fd;
- //printf("\ttrue: no conflict\n");
- return true;
- }
-
/********************************************
* find function template root in overload list
*/
return cast(LabelDsymbol)s;
}
- /*****************************************
- * Determine lexical level difference from `this` to nested function `fd`.
- * Params:
- * fd = target of call
- * intypeof = !=0 if inside typeof
- * Returns:
- * 0 same level
- * >0 decrease nesting by number
- * -1 increase nesting by 1 (`fd` is nested within `this`)
- * LevelError error, `this` cannot call `fd`
- */
- extern (D) final int getLevel(FuncDeclaration fd, int intypeof)
- {
- //printf("FuncDeclaration::getLevel(fd = '%s')\n", fd.toChars());
- Dsymbol fdparent = fd.toParent2();
- if (fdparent == this)
- return -1;
-
- Dsymbol s = this;
- int level = 0;
- while (fd != s && fdparent != s.toParent2())
- {
- //printf("\ts = %s, '%s'\n", s.kind(), s.toChars());
- if (auto thisfd = s.isFuncDeclaration())
- {
- if (!thisfd.isNested() && !thisfd.vthis && !intypeof)
- return LevelError;
- }
- else
- {
- if (auto thiscd = s.isAggregateDeclaration())
- {
- /* AggregateDeclaration::isNested returns true only when
- * it has a hidden pointer.
- * But, calling the function belongs unrelated lexical scope
- * is still allowed inside typeof.
- *
- * struct Map(alias fun) {
- * typeof({ return fun(); }) RetType;
- * // No member function makes Map struct 'not nested'.
- * }
- */
- if (!thiscd.isNested() && !intypeof)
- return LevelError;
- }
- else
- return LevelError;
- }
-
- s = s.toParentP(fd);
- assert(s);
- level++;
- }
- return level;
- }
-
- enum LevelError = -2;
-
override const(char)* toPrettyChars(bool QualifyTypes = false)
{
if (isMain())
return this.isGenerated ? "generated function" : "function";
}
- /*******************************
- * Look at all the variables in this function that are referenced
- * by nested functions, and determine if a closure needs to be
- * created for them.
- */
- final bool needsClosure()
- {
- /* Need a closure for all the closureVars[] if any of the
- * closureVars[] are accessed by a
- * function that escapes the scope of this function.
- * We take the conservative approach and decide that a function needs
- * a closure if it:
- * 1) is a virtual function
- * 2) has its address taken
- * 3) has a parent that escapes
- * 4) calls another nested function that needs a closure
- *
- * Note that since a non-virtual function can be called by
- * a virtual one, if that non-virtual function accesses a closure
- * var, the closure still has to be taken. Hence, we check for isThis()
- * instead of isVirtual(). (thanks to David Friedman)
- *
- * When the function returns a local struct or class, `requiresClosure`
- * is already set to `true` upon entering this function when the
- * struct/class refers to a local variable and a closure is needed.
- */
- //printf("FuncDeclaration::needsClosure() %s\n", toPrettyChars());
-
- if (requiresClosure)
- goto Lyes;
-
- for (size_t i = 0; i < closureVars.length; i++)
- {
- VarDeclaration v = closureVars[i];
- //printf("\tv = %s\n", v.toChars());
-
- for (size_t j = 0; j < v.nestedrefs.length; j++)
- {
- FuncDeclaration f = v.nestedrefs[j];
- assert(f != this);
-
- /* __require and __ensure will always get called directly,
- * so they never make outer functions closure.
- */
- if (f.ident == Id.require || f.ident == Id.ensure)
- continue;
-
- //printf("\t\tf = %p, %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f, f.toChars(), f.isVirtual(), f.isThis(), f.tookAddressOf);
-
- /* Look to see if f escapes. We consider all parents of f within
- * this, and also all siblings which call f; if any of them escape,
- * so does f.
- * Mark all affected functions as requiring closures.
- */
- for (Dsymbol s = f; s && s != this; s = s.toParentP(this))
- {
- FuncDeclaration fx = s.isFuncDeclaration();
- if (!fx)
- continue;
- if (fx.isThis() || fx.tookAddressOf)
- {
- //printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx.toChars(), fx.isVirtual(), fx.isThis(), fx.tookAddressOf);
-
- /* Mark as needing closure any functions between this and f
- */
- markAsNeedingClosure((fx == f) ? fx.toParentP(this) : fx, this);
-
- requiresClosure = true;
- }
-
- /* We also need to check if any sibling functions that
- * called us, have escaped. This is recursive: we need
- * to check the callers of our siblings.
- */
- if (checkEscapingSiblings(fx, this))
- requiresClosure = true;
-
- /* https://issues.dlang.org/show_bug.cgi?id=12406
- * Iterate all closureVars to mark all descendant
- * nested functions that access to the closing context of this function.
- */
- }
- }
- }
- if (requiresClosure)
- goto Lyes;
-
- return false;
-
- Lyes:
- return true;
- }
-
/***********************************************
* Determine if function's variables are referenced by a function
* nested within it.
return ParameterList(null, VarArg.none);
}
- /**********************************
- * Generate a FuncDeclaration for a runtime library function.
- */
- extern(D) static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, STC stc = STC.none)
- {
- return genCfunc(fparams, treturn, Identifier.idPool(name[0 .. strlen(name)]), stc);
- }
-
- extern(D) static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, STC stc = STC.none)
- {
- FuncDeclaration fd;
- TypeFunction tf;
- Dsymbol s;
- __gshared DsymbolTable st = null;
-
- //printf("genCfunc(name = '%s')\n", id.toChars());
- //printf("treturn\n\t"); treturn.print();
-
- // See if already in table
- if (!st)
- st = new DsymbolTable();
- s = st.lookup(id);
- if (s)
- {
- fd = s.isFuncDeclaration();
- assert(fd);
- assert(fd.type.nextOf().equals(treturn));
- }
- else
- {
- tf = new TypeFunction(ParameterList(fparams), treturn, LINK.c, stc);
- fd = new FuncDeclaration(Loc.initial, Loc.initial, id, STC.static_, tf);
- fd.visibility = Visibility(Visibility.Kind.public_);
- fd._linkage = LINK.c;
-
- st.insert(fd);
- }
- return fd;
- }
-
inout(FuncDeclaration) toAliasFunc() inout @safe
{
return this;
assert(mismatches.isMutable);
}
-/* For all functions between outerFunc and f, mark them as needing
- * a closure.
- */
-private void markAsNeedingClosure(Dsymbol f, FuncDeclaration outerFunc)
-{
- for (Dsymbol sx = f; sx && sx != outerFunc; sx = sx.toParentP(outerFunc))
- {
- FuncDeclaration fy = sx.isFuncDeclaration();
- if (fy && fy.closureVars.length)
- {
- /* fy needs a closure if it has closureVars[],
- * because the frame pointer in the closure will be accessed.
- */
- fy.requiresClosure = true;
- }
- }
-}
-
-/********
- * Given a nested function f inside a function outerFunc, check
- * if any sibling callers of f have escaped. If so, mark
- * all the enclosing functions as needing closures.
- * This is recursive: we need to check the callers of our siblings.
- * Note that nested functions can only call lexically earlier nested
- * functions, so loops are impossible.
- * Params:
- * f = inner function (nested within outerFunc)
- * outerFunc = outer function
- * p = for internal recursion use
- * Returns:
- * true if any closures were needed
- */
-bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null)
-{
- static struct PrevSibling
- {
- PrevSibling* p;
- FuncDeclaration f;
- }
-
- if (f.computedEscapingSiblings)
- return f.hasEscapingSiblings;
-
- PrevSibling ps;
- ps.p = cast(PrevSibling*)p;
- ps.f = f;
-
- //printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f.toChars(), outerFunc.toChars());
- bool bAnyClosures = false;
- for (size_t i = 0; i < f.siblingCallers.length; ++i)
- {
- FuncDeclaration g = f.siblingCallers[i];
- if (g.isThis() || g.tookAddressOf)
- {
- markAsNeedingClosure(g, outerFunc);
- bAnyClosures = true;
- }
-
- for (auto parent = g.toParentP(outerFunc); parent && parent !is outerFunc; parent = parent.toParentP(outerFunc))
- {
- // A parent of the sibling had its address taken.
- // Assume escaping of parent affects its children, so needs propagating.
- // see https://issues.dlang.org/show_bug.cgi?id=19679
- FuncDeclaration parentFunc = parent.isFuncDeclaration;
- if (parentFunc && parentFunc.tookAddressOf)
- {
- markAsNeedingClosure(parentFunc, outerFunc);
- bAnyClosures = true;
- }
- }
-
- PrevSibling* prev = cast(PrevSibling*)p;
- while (1)
- {
- if (!prev)
- {
- bAnyClosures |= checkEscapingSiblings(g, outerFunc, &ps);
- break;
- }
- if (prev.f == g)
- break;
- prev = prev.p;
- }
- }
- f.hasEscapingSiblings = bAnyClosures;
- f.computedEscapingSiblings = true;
- //printf("\t%d\n", bAnyClosures);
- return bAnyClosures;
-}
-
/***********************************************************
* Used as a way to import a set of functions from another scope into this one.
*/
return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on);
}
- override bool overloadInsert(Dsymbol s)
- {
- return false; // cannot overload postblits
- }
-
override void accept(Visitor v)
{
v.visit(this);
return false;
}
- override bool overloadInsert(Dsymbol s)
- {
- return false; // cannot overload destructors
- }
-
override void accept(Visitor v)
{
v.visit(this);
string action; /// Action that made the attribute fail to get inferred
+ VarDeclaration scopeVar; /// For scope violations: the parameter whose scope status caused the issue
+
this(Loc loc, FuncDeclaration fd) { this.loc = loc; this.fd = fd; }
this(Loc loc, const(char)* fmt, RootObject[] args)
);
this.action = buf.extractSlice();
}
+
+ this(Loc loc, const(char)* fmt, VarDeclaration scopeVar, RootObject[] args)
+ {
+ this(loc, fmt, args);
+ this.scopeVar = scopeVar;
+ }
}
module dmd.funcsem;
import core.stdc.stdio;
+import core.stdc.string;
import dmd.aggregate;
import dmd.arraytypes;
import dmd.statement;
import dmd.statementsem;
import dmd.target;
+import dmd.targetcompiler;
import dmd.templatesem;
import dmd.tokens;
import dmd.typesem;
import dmd.visitor;
import dmd.visitor.statement_rewrite_walker;
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
+
+/**********************************
+ * Generate a FuncDeclaration for a runtime library function.
+ */
+FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, STC stc = STC.none)
+{
+ return genCfunc(fparams, treturn, Identifier.idPool(name[0 .. strlen(name)]), stc);
+}
+
+FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, STC stc = STC.none)
+{
+ FuncDeclaration fd;
+ TypeFunction tf;
+ Dsymbol s;
+ __gshared DsymbolTable st = null;
+
+ //printf("genCfunc(name = '%s')\n", id.toChars());
+ //printf("treturn\n\t"); treturn.print();
+
+ // See if already in table
+ if (!st)
+ st = new DsymbolTable();
+ s = st.lookup(id);
+ if (s)
+ {
+ fd = s.isFuncDeclaration();
+ assert(fd);
+ assert(fd.type.nextOf().equals(treturn));
+ }
+ else
+ {
+ tf = new TypeFunction(ParameterList(fparams), treturn, LINK.c, stc);
+ fd = new FuncDeclaration(Loc.initial, Loc.initial, id, STC.static_, tf);
+ fd.visibility = Visibility(Visibility.Kind.public_);
+ fd._linkage = LINK.c;
+
+ st.insert(fd);
+ }
+ return fd;
+}
/* Tweak all return statements and dtor call for nrvo_var, for correct NRVO.
*/
printf("type: %p, %s\n", funcdecl.type, funcdecl.type.toChars());
}
+ import dmd.timetrace;
+ timeTraceBeginEvent(TimeTraceEventType.sema1Function);
+ scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Function, funcdecl);
+
if (funcdecl.semanticRun != PASS.initial && funcdecl.isFuncLiteralDeclaration())
{
/* Member functions that have return types that are
sc = sc.push();
sc.stc |= funcdecl.storage_class & (STC.disable | STC.deprecated_); // forward to function type
+ // Parameters don't inherit UDAs from outside, https://github.com/dlang/dmd/issues/19788
+ sc.userAttribDecl = null;
+
if (sc.func)
{
/* If the nesting parent is pure without inference,
funcdecl.type = funcdecl.type.addSTC(stc);
funcdecl.type = funcdecl.type.typeSemantic(funcdecl.loc, sc);
+
+ // semantic for parameters' UDAs
+ if (auto f = getFunctionType(funcdecl))
+ foreach (i, param; f.parameterList)
+ {
+ sc.userAttribDecl = null;
+ if (param && param.userAttribDecl)
+ param.userAttribDecl.dsymbolSemantic(sc);
+ }
+
sc = sc.pop();
}
auto fnext = funcdecl.overnext.isFuncDeclaration();
funcDeclarationSemantic(sc, fnext);
auto fn = fnext.type.isTypeFunction();
- if (!fn || !cFuncEquivalence(f, fn))
+ if (!fn || !cFuncEquivalence(f, fn) || !cTypeEquivalence(f.next, fn.next))
{
.error(funcdecl.loc, "%s `%s` redeclaration with different type", funcdecl.kind, funcdecl.toPrettyChars);
//printf("t1: %s\n", f.toChars());
}
assert(funcdecl.type.ty != Terror || funcdecl.errors);
-
- // semantic for parameters' UDAs
- foreach (i, param; f.parameterList)
- {
- if (param && param.userAttribDecl)
- param.userAttribDecl.dsymbolSemantic(sc);
- }
}
/**
functionToBufferFull(cast(TypeFunction)(fd.type), buf1,
new Identifier(fd.toPrettyChars()), hgs, null);
- error(funcdecl.loc, "function `%s` does not override any function, did you mean to override `%s`?",
- funcdeclToChars, buf1.peekChars());
+ error(funcdecl.loc, "function `%s` does not override any function", funcdeclToChars);
+ errorSupplemental(fd.loc, "did you mean to override `%s`?", buf1.peekChars());
// Supplemental error for parameter scope differences
auto tf1 = cast(TypeFunction)funcdecl.type;
if (params1.length == params2.length)
{
- bool hasScopeDifference = false;
-
for (size_t i = 0; i < params1.length; i++)
{
auto p1 = params1[i];
if (!(p2.storageClass & STC.scope_))
continue;
- if (!hasScopeDifference)
- {
- // Intended signature
- errorSupplemental(funcdecl.loc, "Did you intend to override:");
- errorSupplemental(funcdecl.loc, "`%s`", buf1.peekChars());
- hasScopeDifference = true;
- }
- errorSupplemental(funcdecl.loc, "Parameter %d is missing `scope`",
+ errorSupplemental(funcdecl.loc, "parameter %d is missing `scope`",
cast(int)(i + 1));
}
}
{
if (!argumentList.hasArgNames())
return;
-
+ td.computeOneMember();
auto tf = td.onemember ? td.onemember.isFuncDeclaration() : null;
if (tf && tf.type && tf.type.ty == Tfunction)
{
if (auto fd = s.isFuncDeclaration())
tf = fd;
else if (auto td = s.isTemplateDeclaration())
+ {
+ td.computeOneMember();
if (td.onemember)
tf = td.onemember.isFuncDeclaration();
+ }
}
return 0;
});
// td.onemember may not have overloads set
// (see fail_compilation/onemember_overloads.d)
// assume if more than one member it is overloaded internally
+ td.computeOneMember();
bool recurse = td.onemember && (!td.onemember.isFuncDeclaration ||
td.members.length > 1);
OutBuffer buf;
Declaration decl)
{
int level = fd.getLevel(target, sc.intypeof);
- if (level != fd.LevelError)
+ if (level != LevelError)
return level;
// Don't give error if in template constraint
if (!sc.inTemplateConstraint)
xstatic, fd.kind(), fd.toPrettyChars(), decl.kind(), decl.toChars(),
target.toPrettyChars());
.errorSupplemental(decl.loc, "`%s` declared here", decl.toChars());
- return fd.LevelError;
+ return LevelError;
}
return 1;
}
TypeFunction tf = fd.type.toTypeFunction();
if (tf.isRef)
fd.vresult.storage_class |= STC.ref_;
+ else if (target.isReturnOnStack(tf, fd.needThis()))
+ fd.vresult.nrvo = true;
fd.vresult.type = tret;
fd.vresult.dsymbolSemantic(sc);
if (!sc.insert(fd.vresult))
.error(fd.loc, "%s `%s` must return `int`, `void` or `noreturn`, not `%s`", fd.kind, fd.toPrettyChars, tf.nextOf().toChars());
}
+enum LevelError = -2;
+
+/*****************************************
+ * Determine lexical level difference from `fd1` to nested function `fd2`.
+ * Params:
+ * fd1 = function
+ * fd2 = target of call
+ * intypeof = !=0 if inside typeof
+ * Returns:
+ * 0 same level
+ * >0 decrease nesting by number
+ * -1 increase nesting by 1 (`fd2` is nested within `fd1`)
+ * LevelError error, `this` cannot call `fd2`
+ */
+extern (D) int getLevel(FuncDeclaration fd1, FuncDeclaration fd2, int intypeof)
+{
+ //printf("FuncDeclaration::getLevel(fd2 = '%s')\n", fd2.toChars());
+ Dsymbol fd2parent = fd2.toParent2();
+ if (fd2parent == fd1)
+ return -1;
+
+ Dsymbol s = fd1;
+ int level = 0;
+ while (fd2 != s && fd2parent != s.toParent2())
+ {
+ //printf("\ts = %s, '%s'\n", s.kind(), s.toChars());
+ if (auto thisfd = s.isFuncDeclaration())
+ {
+ if (!thisfd.isNested() && !thisfd.vthis && !intypeof)
+ return LevelError;
+ }
+ else
+ {
+ if (auto thiscd = s.isAggregateDeclaration())
+ {
+ /* AggregateDeclaration::isNested returns true only when
+ * it has a hidden pointer.
+ * But, calling the function belongs unrelated lexical scope
+ * is still allowed inside typeof.
+ *
+ * struct Map(alias fun) {
+ * typeof({ return fun(); }) RetType;
+ * // No member function makes Map struct 'not nested'.
+ * }
+ */
+ if (!thiscd.isNested() && !intypeof)
+ return LevelError;
+ }
+ else
+ return LevelError;
+ }
+
+ s = s.toParentP(fd2);
+ assert(s);
+ level++;
+ }
+ return level;
+}
+
+/* For all functions between outerFunc and f, mark them as needing
+ * a closure.
+ */
+private void markAsNeedingClosure(Dsymbol f, FuncDeclaration outerFunc)
+{
+ for (Dsymbol sx = f; sx && sx != outerFunc; sx = sx.toParentP(outerFunc))
+ {
+ FuncDeclaration fy = sx.isFuncDeclaration();
+ if (fy && fy.closureVars.length)
+ {
+ /* fy needs a closure if it has closureVars[],
+ * because the frame pointer in the closure will be accessed.
+ */
+ fy.requiresClosure = true;
+ }
+ }
+}
+
+/********
+ * Given a nested function f inside a function outerFunc, check
+ * if any sibling callers of f have escaped. If so, mark
+ * all the enclosing functions as needing closures.
+ * This is recursive: we need to check the callers of our siblings.
+ * Note that nested functions can only call lexically earlier nested
+ * functions, so loops are impossible.
+ * Params:
+ * f = inner function (nested within outerFunc)
+ * outerFunc = outer function
+ * p = for internal recursion use
+ * Returns:
+ * true if any closures were needed
+ */
+bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null)
+{
+ static struct PrevSibling
+ {
+ PrevSibling* p;
+ FuncDeclaration f;
+ }
+
+ if (f.computedEscapingSiblings)
+ return f.hasEscapingSiblings;
+
+ PrevSibling ps;
+ ps.p = cast(PrevSibling*)p;
+ ps.f = f;
+
+ //printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f.toChars(), outerFunc.toChars());
+ bool bAnyClosures = false;
+ for (size_t i = 0; i < f.siblingCallers.length; ++i)
+ {
+ FuncDeclaration g = f.siblingCallers[i];
+ if (g.isThis() || g.tookAddressOf)
+ {
+ markAsNeedingClosure(g, outerFunc);
+ bAnyClosures = true;
+ }
+
+ for (auto parent = g.toParentP(outerFunc); parent && parent !is outerFunc; parent = parent.toParentP(outerFunc))
+ {
+ // A parent of the sibling had its address taken.
+ // Assume escaping of parent affects its children, so needs propagating.
+ // see https://issues.dlang.org/show_bug.cgi?id=19679
+ FuncDeclaration parentFunc = parent.isFuncDeclaration;
+ if (parentFunc && parentFunc.tookAddressOf)
+ {
+ markAsNeedingClosure(parentFunc, outerFunc);
+ bAnyClosures = true;
+ }
+ }
+
+ PrevSibling* prev = cast(PrevSibling*)p;
+ while (1)
+ {
+ if (!prev)
+ {
+ bAnyClosures |= checkEscapingSiblings(g, outerFunc, &ps);
+ break;
+ }
+ if (prev.f == g)
+ break;
+ prev = prev.p;
+ }
+ }
+ f.hasEscapingSiblings = bAnyClosures;
+ f.computedEscapingSiblings = true;
+ //printf("\t%d\n", bAnyClosures);
+ return bAnyClosures;
+}
+
+/*******************************
+ * Look at all the variables in this function that are referenced
+ * by nested functions, and determine if a closure needs to be
+ * created for them.
+ */
+bool needsClosure(FuncDeclaration fd)
+{
+ /* Need a closure for all the closureVars[] if any of the
+ * closureVars[] are accessed by a
+ * function that escapes the scope of this function.
+ * We take the conservative approach and decide that a function needs
+ * a closure if it:
+ * 1) is a virtual function
+ * 2) has its address taken
+ * 3) has a parent that escapes
+ * 4) calls another nested function that needs a closure
+ *
+ * Note that since a non-virtual function can be called by
+ * a virtual one, if that non-virtual function accesses a closure
+ * var, the closure still has to be taken. Hence, we check for isThis()
+ * instead of isVirtual(). (thanks to David Friedman)
+ *
+ * When the function returns a local struct or class, `requiresClosure`
+ * is already set to `true` upon entering this function when the
+ * struct/class refers to a local variable and a closure is needed.
+ */
+ //printf("FuncDeclaration::needsClosure() %s\n", toPrettyChars());
+
+ if (fd.requiresClosure)
+ goto Lyes;
+
+ for (size_t i = 0; i < fd.closureVars.length; i++)
+ {
+ VarDeclaration v = fd.closureVars[i];
+ //printf("\tv = %s\n", v.toChars());
+
+ for (size_t j = 0; j < v.nestedrefs.length; j++)
+ {
+ FuncDeclaration f = v.nestedrefs[j];
+ assert(f != fd);
+
+ /* __require and __ensure will always get called directly,
+ * so they never make outer functions closure.
+ */
+ if (f.ident == Id.require || f.ident == Id.ensure)
+ continue;
+
+ //printf("\t\tf = %p, %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f, f.toChars(), f.isVirtual(), f.isThis(), f.tookAddressOf);
+
+ /* Look to see if f escapes. We consider all parents of f within
+ * this, and also all siblings which call f; if any of them escape,
+ * so does f.
+ * Mark all affected functions as requiring closures.
+ */
+ for (Dsymbol s = f; s && s != fd; s = s.toParentP(fd))
+ {
+ FuncDeclaration fx = s.isFuncDeclaration();
+ if (!fx)
+ continue;
+ if (fx.isThis() || fx.tookAddressOf)
+ {
+ //printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx.toChars(), fx.isVirtual(), fx.isThis(), fx.tookAddressOf);
+
+ /* Mark as needing closure any functions between this and f
+ */
+ markAsNeedingClosure((fx == f) ? fx.toParentP(fd) : fx, fd);
+
+ fd.requiresClosure = true;
+ }
+
+ /* We also need to check if any sibling functions that
+ * called us, have escaped. This is recursive: we need
+ * to check the callers of our siblings.
+ */
+ if (checkEscapingSiblings(fx, fd))
+ fd.requiresClosure = true;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=12406
+ * Iterate all closureVars to mark all descendant
+ * nested functions that access to the closing context of this function.
+ */
+ }
+ }
+ }
+ if (fd.requiresClosure)
+ goto Lyes;
+
+ return false;
+
+Lyes:
+ return true;
+}
+
/***********************************************
* Check all return statements for a function to verify that returning
* using NRVO is possible.
return false;
if (v.nestedrefs.length && fd.needsClosure())
return false;
+
// don't know if the return storage is aligned
- version (MARS)
- {
- if (fd.alignSectionVars && (*fd.alignSectionVars).contains(v))
- return false;
- }
+ mixin alignSectionVarsContains;
+ if (isAlignSectionVar(v))
+ return false;
+
// The variable type needs to be equivalent to the return type.
if (!v.type.equivalent(tf.next))
return false;
// The 'this' reference is a parameter, too
if (AggregateDeclaration ad = fd.isCtorDeclaration() ? null : fd.isThis())
{
- Type tthis = ad.getType().addMod(tf.mod);
+ Type tthis = dmd.dsymbolsem.getType(ad).addMod(tf.mod);
//printf("\ttthis = %s\n", tthis.toChars());
if (!traverseIndirections(tthis, t))
return false;
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)
{
+ // Detect cyclic calls.
+ if (visited.contains(d))
+ return 0;
+ visited.push(d);
+
import dmd.access : checkSymbolAccess;
if (auto od = d.isOverDeclaration())
{
}
else if (auto td = d.isTemplateDeclaration())
{
+ // Sometimes funcroot is not in the .overnext chain
+ // in such case we have to recurse into it
+ if(td.funcroot)
+ {
+ if (int r = overloadApplyRecurse(td.funcroot, dg, sc))
+ return r;
+ }
if (int r = dg(td))
return r;
next = td.overnext;
if (!fdv || fdv == fdthis)
return false;
- // Add fdthis to nestedrefs[] if not already there
- if (!vd.nestedrefs.contains(fdthis))
- vd.nestedrefs.push(fdthis);
-
//printf("\tfdv = %s\n", fdv.toChars());
//printf("\tfdthis = %s\n", fdthis.toChars());
if (loc.isValid())
{
- if (fdthis.getLevelAndCheck(loc, sc, fdv, vd) == fdthis.LevelError)
+ if (fdthis.getLevelAndCheck(loc, sc, fdv, vd) == LevelError)
return true;
}
- // Add this VarDeclaration to fdv.closureVars[] if not already there
if (!sc.intypeof && !sc.traitsCompiles &&
// https://issues.dlang.org/show_bug.cgi?id=17605
(fdv.skipCodegen || !fdthis.skipCodegen))
{
+ // Add fdthis to nestedrefs[] if not already there
+ if (!vd.nestedrefs.contains(fdthis))
+ vd.nestedrefs.push(fdthis);
+
+ // Add this VarDeclaration to fdv.closureVars[] if not already there
if (!fdv.closureVars.contains(vd))
fdv.closureVars.push(vd);
}
import dmd.identifier;
import dmd.location;
import dmd.lexer : CompileEnv;
+import dmd.targetcompiler;
import dmd.utils;
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
-
/// Defines a setting for how compiler warnings and deprecations are handled
enum DiagnosticReporting : ubyte
{
bool betterC; // be a "better C" compiler; no dependency on D runtime
bool addMain; // add a default main() function
bool allInst; // generate code for all template instantiations
- bool bitfields; // support C style bit fields
+ bool bitfields = true; // support C style bit fields
+ bool rewriteNoExceptionToSeq; // Allow finally statements that do not throw an Exception
+ // in try body to rewrite to a sequence.
CppStdRevision cplusplus = CppStdRevision.cpp11; // version of C++ standard to support
uint warnings; /// number of warnings reported so far
uint gag; /// !=0 means gag reporting of errors & warnings
uint gaggedErrors; /// number of errors reported while gagged
- uint gaggedWarnings; /// number of warnings reported while gagged
+ uint gaggedDeprecations; /// number of deprecations reported while gagged
void* console; /// opaque pointer to console for controlling text attributes
extern (C++) uint startGagging() @safe
{
++gag;
- gaggedWarnings = 0;
+ gaggedDeprecations = 0;
return gaggedErrors;
}
errorSinkNull = new ErrorSinkNull;
this.fileManager = new FileManager();
+ compileEnv.vendor = TargetCompiler;
+ compileEnv.switchPrefix = SwitchPrefix;
- version (MARS)
- {
- compileEnv.vendor = "Digital Mars D";
- }
- else version (IN_GCC)
- {
- compileEnv.vendor = "GNU D";
- }
- else version (IN_LLVM)
- {
- compileEnv.vendor = "LDC";
- }
- else
- static assert(0, "unknown vendor");
+ mixin UseAnsiColors;
+ params.v.color = useAnsiColors();
compileEnv.versionNumber = parseVersionNumber(versionString());
semantics = (1 << 3)
};
+enum class Edition : uint16_t
+{
+ v2023 = 2023,
+ v2024,
+ v2025,
+};
+
enum CppStdRevision
{
CppStdRevisionCpp98 = 199711,
d_bool addMain; // add a default main() function
d_bool allInst; // generate code for all template instantiations
d_bool bitfields; // support C style bit fields
+ d_bool rewriteNoExceptionToSeq;
CppStdRevision cplusplus; // version of C++ name mangling to support
Help help;
Verbose v;
- unsigned short edition; // edition year
+ Edition edition; // edition year
void* editionFiles; // Edition corresponding to a filespec
// Options for `-preview=/-revert=`
struct structalign_t
{
- unsigned short value;
- d_bool pack;
-
+private:
+ uint16_t value;
+ uint8_t flags;
+public:
bool isDefault() const;
void setDefault();
bool isUnknown() const;
void set(unsigned value);
unsigned get() const;
bool isPack() const;
- void setPack(bool pack);
+ void setPack();
+ bool fromAlignas() const;
+ void setAlignas();
};
// magic value means "match whatever the underlying C compiler does"
d_bool transitionIn;
d_bool ddocOutput;
d_bool masm;
+ DString switchPrefix;
IdentifierCharLookup cCharLookupTable;
IdentifierCharLookup dCharLookupTable;
};
unsigned warnings; // number of warnings reported so far
unsigned gag; // !=0 means gag reporting of errors & warnings
unsigned gaggedErrors; // number of errors reported while gagged
- unsigned gaggedWarnings; // number of warnings reported while gagged
+ unsigned gaggedDeprecations; // number of deprecations reported while gagged
void* console; // opaque pointer to console for controlling text attributes
import dmd.astenums;
import dmd.attrib;
import dmd.cond;
-import dmd.ctfeexpr;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
import dmd.statement;
import dmd.staticassert;
import dmd.tokens;
-import dmd.typesem;
import dmd.visitor;
struct HdrGenState
void visitInteger(IntegerExp e)
{
- const ulong v = e.toInteger();
+ const ulong v = e.value;
if (e.type)
{
Type t = e.type;
{
foreach (em; *sym.members)
{
- if ((cast(EnumMember)em).value.toInteger == v)
+ if ((cast(EnumMember)em).value.isIntegerExp().value == v)
{
const id = em.ident.toString();
buf.printf("%s.%.*s", sym.toChars(), cast(int)id.length, id.ptr);
if (type)
{
- Type t = type.toBasetype();
+ Type t = type.toBaseTypeNonSemantic();
+
switch (t.ty)
{
case Tfloat32:
default:
break;
}
- if (t.isImaginary())
+ if (t.isImaginaryNonSemantic())
buf.put('i');
}
}
HdrGenState hgs;
hgs.fullQual = qualifyTypes;
- buf.put(ti.name.toChars());
+ buf.put(ti.name == Id.ctor ? "this" : ti.name.toChars());
tiargsToBuffer(cast() ti, buf, hgs);
}
{
Expression ex = (e.op == EXP.cast_ ? (cast(CastExp)e).e1 : e);
ex = ex.optimize(WANTvalue);
- const ulong uval = ex.op == EXP.int64 ? ex.toInteger() : cast(ulong)-1;
+ const ulong uval = ex.op == EXP.int64 ? ex.isIntegerExp().value : cast(ulong)-1;
if (cast(long)uval >= 0)
{
if (uval <= 0xFFFFU)
void visitDArray(TypeDArray t)
{
- Type ut = t.castMod(0);
+ auto basetype = t.next;
if (hgs.declstring)
goto L1;
- if (ut.equals(Type.tstring))
+ if (basetype.ty == Tchar && basetype.isImmutable())
buf.put("string");
- else if (ut.equals(Type.twstring))
+ else if (basetype.ty == Twchar && basetype.isImmutable())
buf.put("wstring");
- else if (ut.equals(Type.tdstring))
+ else if (basetype.ty == Tdchar && basetype.isImmutable())
buf.put("dstring");
else
{
namespace dmd
{
void genhdrfile(Module *m, bool doFuncBodies, OutBuffer &buf);
- void genCppHdrFiles(Modules &ms);
+ void genCppHdrFiles(Modules &ms, ErrorSink *eSink);
void moduleToBuffer(OutBuffer& buf, bool vcg_ast, Module *m);
const char *parametersTypeToChars(ParameterList pl);
import dmd.tokens;
import dmd.statement;
import dmd.statementsem;
+import dmd.typesem;
/***********************************
* Parse and run semantic analysis on a GccAsmStatement.
{
import dmd.dcast : implicitCastTo;
import dmd.dsymbolsem : resolveAliasThis;
- import dmd.mtype : isAggregate, Type;
+ import dmd.mtype : Type;
+ import dmd.typesem : isAggregate;
exp = expressionSemantic(exp, sc);
unittest
{
import dmd.mtype : TypeBasic;
+ import dmd.typesem : merge;
if (!global.errorSink)
global.errorSink = new ErrorSinkCompiler;
const errors = global.startGagging();
scope(exit) global.endGagging(errors);
- // If this check fails, then Type._init() was called before reaching here,
+ // If this check fails, then Type_init() was called before reaching here,
// and the entire chunk of code that follows can be removed.
assert(ASTCodegen.Type.tint32 is null);
// Minimally initialize the cached types in ASTCodegen.Type, as they are
ASTCodegen.Type.tchar = null;
}
scope tint32 = new TypeBasic(ASTCodegen.Tint32);
+ tint32.merge();
ASTCodegen.Type.tint32 = tint32;
scope tchar = new TypeBasic(ASTCodegen.Tchar);
+ tchar.merge();
ASTCodegen.Type.tchar = tchar;
// Imitates asmSemantic if version = IN_GCC.
{ "_d_newitemTTrace" },
{ "_d_newarrayT" },
{ "_d_newarrayTTrace" },
+ { "_d_newarrayU" },
+ { "_d_newarrayUTrace" },
{ "_d_newarraymTX" },
{ "_d_newarraymTXTrace" },
{ "_d_arrayliteralTX" },
isAnonymous_ = isAnonymous;
}
+ bool equals(const Identifier i) const
+ {
+ return this is i;
+ }
+
static Identifier create(const(char)* name)
{
return new Identifier(name);
const char *toHChars2() const;
DYNCAST dyncast() const override;
+ bool equals(const Identifier * const i) { return this == i; };
static Identifier *generateId(const char *prefix, size_t length, size_t suffix);
static Identifier *idPool(const char *s, unsigned len);
"InterpolationFooter": "core.interpolation",
];
cHints = [
+ "va_list": "<stdarg.h>",
+
+ "bool": "<stdbool.h>",
+ "false": "<stdbool.h>",
+ "true": "<stdbool.h>",
+
"NULL": "<stddef.h>",
- "calloc": "<stdlib.h>",
+ "nullptr_t": "<stddef.h>",
+ "offsetof": "<stddef.h>",
+ "ptrdiff_t": "<stddef.h>",
+ "size_t": "<stddef.h>",
+ "wchar_t": "<stddef.h>",
+
+ "INT8_MAX": "<stdint.h>",
+ "INT16_MAX": "<stdint.h>",
+ "INT32_MAX": "<stdint.h>",
+ "INT64_MAX": "<stdint.h>",
+ "INTPTR_MAX": "<stdint.h>",
+ "PTRDIFF_MAX": "<stdint.h>",
+ "PTRDIFF_MIN": "<stdint.h>",
+ "SIZE_MAX": "<stdint.h>",
+ "UINT8_MAX": "<stdint.h>",
+ "UINT16_MAX": "<stdint.h>",
+ "UINT32_MAX": "<stdint.h>",
+ "UINT64_MAX": "<stdint.h>",
+ "UINTPTR_MAX": "<stdint.h>",
+ "WINT_MAX": "<stdint.h>",
+ "WINT_MIN": "<stdint.h>",
+ "int8_t": "<stdint.h>",
+ "int16_t": "<stdint.h>",
+ "int32_t": "<stdint.h>",
+ "int64_t": "<stdint.h>",
+ "intptr_t": "<stdint.h>",
+ "uint8_t": "<stdint.h>",
+ "uint16_t": "<stdint.h>",
+ "uint32_t": "<stdint.h>",
+ "uint64_t": "<stdint.h>",
+ "uintptr_t": "<stdint.h>",
+
+ "EOF": "<stdio.h>",
+ "FILE": "<stdio.h>",
"fopen": "<stdio.h>",
+ "fpos_t": "<stdio.h>",
"fprintf": "<stdio.h>",
+ "getchar": "<stdio.h>",
+ "printf": "<stdio.h>",
+ "snprintf": "<stdio.h>",
+ "sprintf": "<stdio.h>",
+ "stderr": "<stdio.h>",
+ "stdin": "<stdio.h>",
+ "stdout": "<stdio.h>",
+
+ "EXIT_FAILURE": "<stdlib.h>",
+ "EXIT_SUCCESS": "<stdlib.h>",
+ "abort": "<stdlib.h>",
+ "atexit": "<stdlib.h>",
+ "calloc": "<stdlib.h>",
+ "exit": "<stdlib.h>",
"free": "<stdlib.h>",
+ "getenv": "<stdlib.h>",
"malloc": "<stdlib.h>",
+ "realloc": "<stdlib.h>",
+
+ "memchr": "<string.h>",
+ "memcmp": "<string.h>",
"memcpy": "<string.h>",
"memmove": "<string.h>",
"memset": "<string.h>",
- "printf": "<stdio.h>",
- "ptrdiff_t": "<stddef.h>",
- "size_t": "<stddef.h>",
- "stderr": "<stdio.h>",
- "stdin": "<stdio.h>",
- "stdout": "<stdio.h>",
+ "strcat": "<string.h>",
+ "strchr": "<string.h>",
+ "strcmp": "<string.h>",
+ "strcpy": "<string.h>",
+ "strerror": "<string.h>",
+ "strlen": "<string.h>",
+ "strncat": "<string.h>",
+ "strncmp": "<string.h>",
+ "strncpy": "<string.h>",
+ "strrchr": "<string.h>",
+ "strspn": "<string.h>",
+ "strstr": "<string.h>",
+
+ "WCHAR_MAX": "<wchar.h>",
+ "WCHAR_MIN": "<wchar.h>",
];
}
const char *kind() const override;
Visibility visible() override;
Import *syntaxCopy(Dsymbol *s) override; // copy only syntax trees
- bool overloadInsert(Dsymbol *s) override;
void accept(Visitor *v) override { v->visit(this); }
};
dsym._init = new ExpInitializer(dsym.loc, e);
}
-/********************************************
- * Resolve cast/call grammar ambiguity.
- * Params:
- * e = expression that might be a cast, might be a call
- * sc = context
- * Returns:
- * null means leave as is, !=null means rewritten AST
- */
-Expression castCallAmbiguity(Expression e, Scope* sc)
-{
- Expression* pe = &e;
-
- while (1)
- {
- // Walk down the postfix expressions till we find a CallExp or something else
- switch ((*pe).op)
- {
- case EXP.dotIdentifier:
- pe = &(*pe).isDotIdExp().e1;
- continue;
-
- case EXP.plusPlus:
- case EXP.minusMinus:
- pe = &(*pe).isPostExp().e1;
- continue;
-
- case EXP.array:
- pe = &(*pe).isArrayExp().e1;
- continue;
-
- case EXP.call:
- auto ce = (*pe).isCallExp();
- if (ce.e1.parens)
- {
- ce.e1 = expressionSemantic(ce.e1, sc);
- if (ce.e1.op == EXP.type)
- {
- const numArgs = ce.arguments ? ce.arguments.length : 0;
- if (numArgs >= 1)
- {
- ce.e1.parens = false;
- Expression arg;
- foreach (a; (*ce.arguments)[])
- {
- arg = arg ? new CommaExp(a.loc, arg, a) : a;
- }
- auto t = ce.e1.isTypeExp().type;
- *pe = arg;
- return new CastExp(ce.loc, e, t);
- }
- }
- }
- return null;
-
- default:
- return null;
- }
- }
-}
-
/********************************************
* Implement the C11 notion of function equivalence,
* which allows prototyped functions to match K+R functions,
sds.symtab.update(vd); // replace vd2 with the definition
return vd;
}
+ else if (!i1 && !(vd2.storage_class & STC.extern_)) /* incoming has void void definition */
+ {
+ vd.storage_class |= STC.extern_;
+ }
/* BUG: the types should match, which needs semantic() to be run on it
* extern int x;
* INT x; // match
* long x; // collision
* We incorrectly ignore these collisions
+ * when their types are not matching, err on type differences
*/
+
+ if (!cTypeEquivalence(vd.type, vd2.type))
+ {
+ .error(vd.loc, "redefinition of `%s` with different type: `%s` vs `%s`",
+ vd2.ident.toChars(), vd2.type.toChars(), vd.type.toChars());
+ }
return vd2;
}
*/
fd2.overloadInsert(fd);
+ //for the sake of functions declared in function scope.
+ // check for return type equivalence also
+ auto tf1 = fd.type.isTypeFunction();
+ auto tf2 = fd2.type.isTypeFunction();
+ if (sc.func && !cTypeEquivalence(tf1.next, tf2.next) )
+ {
+ .error(fd.loc, "%s `%s` redeclaration with different type", fd.kind, fd.toPrettyChars);
+ }
+
return fd2;
}
import core.stdc.stdio;
import core.checkedint;
-import dmd.aggregate;
-import dmd.aliasthis;
import dmd.arraytypes;
import dmd.astenums;
import dmd.dcast;
// Convert initializer to Expression `ex`
auto tm = fieldType.addMod(t.mod);
auto iz = i.value[j].initializerSemantic(sc, tm, needInterpret);
- auto ex = iz.initializerToExpression(null, sc.inCfile);
+ auto ex = iz.initializerToExpression(fieldType, sc.inCfile);
if (ex.op != EXP.error)
i.value[j] = iz;
return ex;
Type typeb = se.type.toBasetype();
TY tynto = tb.nextOf().ty;
if (!se.committed &&
- typeb.isStaticOrDynamicArray() && tynto.isSomeChar &&
- se.numberOfCodeUnits(tynto) < tb.isTypeSArray().dim.toInteger())
+ typeb.isStaticOrDynamicArray() && tynto.isSomeChar)
{
- i.exp = se.castTo(sc, t);
- goto L1;
+ string s;
+ size_t len = se.numberOfCodeUnits(tynto, s);
+ if (s)
+ error(se.loc, "%.*s", cast(int)s.length, s.ptr);
+ if (len < tb.isTypeSArray().dim.toInteger())
+ {
+ i.exp = se.castTo(sc, t);
+ goto L1;
+ }
}
/* Lop off terminating 0 of initializer for:
static if (0)
if (auto ts = tx.isTypeStruct())
{
- import dmd.common.outbuffer;
OutBuffer buf;
HdrGenState hgs;
toCBuffer(ts.sym, buf, hgs);
*/
t = t.toBasetype();
+ bool isComplexInitilaizer()
+ {
+ switch (t.ty)
+ {
+ case Tcomplex32:
+ case Tcomplex64:
+ case Tcomplex80:
+ return true;
+ default:
+ return false;
+ }
+ }
+
if (auto tv = t.isTypeVector())
t = tv.basetype;
{
DesigInit di = ci.initializerList[index];
Designators* dlist = di.designatorList;
+ VarDeclaration field;
if (dlist)
{
const length = (*dlist).length;
+ auto id = (*dlist)[0].ident;
if (length == 0 || !(*dlist)[0].ident)
{
error(ci.loc, "`.identifier` expected for C struct field initializer `%s`", toChars(ci));
return err();
}
+
if (length > 1)
{
- error(ci.loc, "only 1 designator currently allowed for C struct field initializer `%s`", toChars(ci));
- return err();
+ StructDeclaration nstsd = sd; // use this for member structs we wish to traverse
+ auto subsi = si;
+ /*
+ * run this for each designator in the chain until you hit the last
+ * then perform semantic analysis on the last field in the chain using the previous struct initializer
+ */
+ for (size_t i = 0; i < length; i++)
+ {
+ int found;
+ id = (*dlist)[i].ident;
+ foreach (f; nstsd.fields[])
+ {
+ if (f.ident == id)
+ {
+ field = f;
+ ++found;
+ break;
+ }
+ }
+ if (!found)
+ {
+ error(ci.loc, "`.%s` is not a field of `%s`\n", id.toChars(), nstsd.toChars());
+ return err();
+ }
+
+ auto base = field.type.toBasetype();
+
+ if (i >= length -1)
+ {
+ subsi.addInit(id, di.initializer);
+ ++index;
+ continue Loop1;
+ }
+
+ auto tstr = base.isTypeStruct();
+ auto tarr = base.isTypeSArray();
+
+ if (tstr)
+ {
+ if (!overlaps(field, nstsd.fields[], subsi))
+ {
+ auto innersi = new StructInitializer(ci.loc);
+ subsi.addInit(id, innersi);
+ subsi = innersi;
+ }
+ else {
+ foreach(k, ident; subsi.field[])
+ {
+ if (ident == id && subsi.value[k])
+ subsi = subsi.value[k].isStructInitializer();
+ }
+ }
+ nstsd = tstr.sym;
+ }
+ /*
+ * once we hit an array, check & attach the array initializer to the struct initializer
+ * move to the next initializer id and run initializer semantics on it
+ */
+ else if (tarr)
+ {
+ /*
+ * so tempting to check for null cases for field._init.
+ * but if your object is set to null on decl, you can't use designators anymore
+ * and D does well to default initialize for us
+ */
+ auto ai = field._init.isArrayInitializer();
+
+ if (ai is null)
+ {
+ ai = new ArrayInitializer(ci.loc);
+ subsi.addInit(id, ai);
+ field._init = ai;
+ }
+
+ auto ndx = (*dlist)[i+1].exp;
+ ai.addInit(ndx, di.initializer);
+ ++index;
+ continue Loop1;
+ }
+ else
+ {
+ error(ci.loc, "only 1 designated initializer allowed for C struct field of type `%s`", toChars(base));
+ return err();
+ }
+ }
}
- auto id = (*dlist)[0].ident;
foreach (k, f; sd.fields[]) // linear search for now
{
if (f.ident == id)
continue Loop1;
}
- VarDeclaration field;
while (1) // skip field if it overlaps with previously seen fields
{
field = sd.fields[fieldi];
{
return visitExp(ei);
}
+ else if (isComplexInitilaizer())
+ {
+ /* just convert _Complex = { a, b} to _Complex =. a + b*i */
+ if (ci.initializerList[].length != 2)
+ {
+ error(ci.loc, "only two initializers required for complex type `%s`", t.toChars());
+ return err();
+ }
+ auto rexp = ci.initializerList[0].initializer.initializerToExpression();
+ auto imexp = ci.initializerList[1].initializer.initializerToExpression();
+
+ import dmd.root.ctfloat;
+ auto newExpr = new AddExp(ci.loc, rexp,
+ new MulExp(ci.loc, imexp, new RealExp(ci.loc, CTFloat.one, Type.timaginary64)));
+
+ auto ce = new ExpInitializer(ci.loc, newExpr);
+ return ce.initializerSemantic(sc, t, needInterpret);
+ }
else
{
error(ci.loc, "unrecognized C initializer `%s` for type `%s`", toChars(ci), t.toChars());
{
//printf("ArrayInitializer::toExpression(), dim = %d\n", dim);
//static int i; if (++i == 2) assert(0);
+ if (!itype || itype.toBasetype().isTypeAArray())
+ if (!init.type || init.type.isTypeAArray())
+ if (init.isAssociativeArray())
+ return init.toAssocArrayLiteral();
+
uint edim; // the length of the resulting array literal
const(uint) amax = 0x80000000;
Type t = null; // type of the array literal being initialized
import dmd.astenums : Tdchar;
import dmd.mtype : Type;
import dmd.globals : uinteger_t;
+import dmd.typesem;
private uinteger_t copySign(uinteger_t x, bool sign) @safe
{
import dmd.dmodule;
import dmd.dsymbol;
import dmd.dsymbolsem : include;
+import dmd.templatesem : computeOneMember;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
void jsonProperties(TemplateDeclaration td)
{
jsonProperties(cast(Dsymbol)td);
+ td.computeOneMember();
if (td.onemember && td.onemember.isCtorDeclaration())
property("name", "this"); // __ctor -> this
else
import dmd.dsymbolsem;
import dmd.dtemplate;
import dmd.expression;
-import dmd.expressionsem : getConstInitializer;
+import dmd.expressionsem : getConstInitializer, toInteger;
import dmd.func;
import dmd.hdrgen;
import dmd.mangle;
bool transitionIn; /// `-transition=in` is active, `in` parameters are listed
bool ddocOutput; /// collect embedded documentation comments
bool masm; /// use MASM inline asm syntax
+ const(char)[] switchPrefix;
// these need a default otherwise tests won't work.
IdentifierCharLookup cCharLookupTable; /// C identifier table (set to the lexer by the C parser)
t.blockComment = null;
t.lineComment = null;
- size_t universalCharacterName4, universalCharacterName8;
-
while (1)
{
+ bool startsUCN;
t.ptr = p;
//printf("p = %p, *p = '%c'\n",p,*p);
t.loc = loc();
// Universal Character Name (C) 2 byte
// \uXXXX
// let the main case handling for identifiers process this
-
- // case_indent will always increment, so subtract to prevent branching on the fast path
- p--;
-
+ startsUCN = true;
goto case_ident;
}
else if (p[1] == 'U')
// Universal Character Name (C) 4 byte
// \UXXXXXXXX
// let the main case handling for identifiers process this
-
- // case_indent will always increment, so subtract to prevent branching on the fast path
- p--;
-
+ startsUCN = true;
goto case_ident;
}
}
case '_':
case_ident:
{
- IdentLoop: while (1)
- {
- // If this is changed, change the decrement in C's universal character name code above
- // For syntax \uXXXX and \UXXXXXXXX
- const c = *++p;
-
- // Is this the first character of the identifier
- // For the universal character name this will line up,
- // for the main switch it won't since it wasn't the first,
- // for the default it won't either because a decode increments.
- const isStartCharacter = t.ptr is p;
-
- if (isidchar(c))
- continue;
- if (c & 0x80)
- {
- const s = p;
- const u = decodeUTF();
-
- if (isStartCharacter)
- {
- if (charLookup.isStart(u))
- continue;
- error(t.loc, "character 0x%04x is not allowed as a start character in an identifier", u);
- }
- else
- {
- if (charLookup.isContinue(u))
- continue;
- error(t.loc, "character 0x%04x is not allowed as a continue character in an identifier", u);
- }
-
- p = s;
- }
- else if (Ccompile && c == '\\')
- {
- uint times;
- const s = p;
- p++;
-
- if (*p == 'u')
- {
- // Universal Character Name (C) 2 byte
- // \uXXXX
- p++;
- times = 4;
- }
- else if (*p == 'U')
- {
- // Universal Character Name (C) 4 byte
- // \UXXXXXXXX
- p++;
- times = 8;
- }
- else
- {
- error(t.loc, "char 0x%x is not allowed to follow '\\' expecting a C universal character name in format \\uXXXX or \\UXXXXXXXX with hex digits instead of X with invalid u/U", *p);
- p = s;
- break;
- }
-
- foreach(_; 0 .. times)
- {
- const hc = *p;
- p++;
-
- if ((hc >= '0' && hc <= '9') || (hc >= 'a' && hc <= 'f') || (hc >= 'A' && hc <= 'F'))
- continue;
-
- error(t.loc, "char 0x%x is not allowed to follow '\\' expecting a C universal character name in format \\uXXXX or \\UXXXXXXXX with hex digits instead of X with invalid hex digit", hc);
- p = s;
- break IdentLoop;
- }
-
- continue;
- }
- break;
- }
-
- Identifier id;
-
- if (universalCharacterName4 > 0 || universalCharacterName8 > 0)
- {
- auto priorValidation = t.ptr[0 .. p - t.ptr];
- const(char)* priorVPtr = priorValidation.ptr;
- const possibleLength = (
- priorValidation.length - (
- (universalCharacterName4 * 6) +
- (universalCharacterName8 * 10)
- )) + (
- (universalCharacterName4 * 3) +
- (universalCharacterName8 * 4)
- );
-
- char[64] buffer = void;
- SmallBuffer!char sb = SmallBuffer!char(possibleLength, buffer[]);
-
- char[] storage = sb.extent;
- size_t offset;
-
- while(priorVPtr < &priorValidation[$-1] + 1)
- {
- if (*priorVPtr == '\\')
- {
- dchar tempDchar = 0;
- uint times;
-
- // universal character name (C)
- if (priorVPtr[1] == 'u')
- times = 4;
- else if (priorVPtr[1] == 'U')
- times = 8;
- else
- assert(0, "ICE: Universal character name is 2 or 4 bytes only");
- priorVPtr += 2;
-
- foreach(_; 0 .. times)
- {
- char c = *++priorVPtr;
- if (c >= '0' && c <= '9')
- c -= '0';
- else if (c >= 'a' && c <= 'f')
- c -= 'a' - 10;
- else if (c >= 'A' && c <= 'F')
- c -= 'A' - 10;
-
- tempDchar <<= 4;
- tempDchar |= c;
- }
-
- utf_encodeChar(&storage[offset], tempDchar);
- offset += utf_codeLengthChar(tempDchar);
-
- // Could be an error instead of a warning,
- // but hey it was written specifically so why worry?
- if (priorVPtr is priorValidation.ptr)
- {
- if (!charLookup.isStart(tempDchar))
- warning(t.loc, "char 0x%x is not allowed start character for an identifier", tempDchar);
- }
- else
- {
- if (!charLookup.isContinue(tempDchar))
- warning(t.loc, "char 0x%x is not allowed continue character for an identifier", tempDchar);
- }
- }
- else
- storage[offset++] = *++priorVPtr;
- }
-
- id = Identifier.idPool(storage[0 .. offset], false);
- }
- else
- id = Identifier.idPool((cast(char*)t.ptr)[0 .. p - t.ptr], false);
-
- t.ident = id;
- t.value = cast(TOK)id.getValue();
-
+ if (!lexIdentifier(t, startsUCN))
+ goto default;
anyToken = 1;
/* Different keywords for C and D
t.postfix = 0;
}
- if (id == Id.DATE)
+ if (t.ident == Id.DATE)
toToken(compileEnv.date);
- else if (id == Id.TIME)
+ else if (t.ident == Id.TIME)
toToken(compileEnv.time);
- else if (id == Id.VENDOR)
+ else if (t.ident == Id.VENDOR)
toToken(compileEnv.vendor);
- else if (id == Id.TIMESTAMP)
+ else if (t.ident == Id.TIMESTAMP)
toToken(compileEnv.timestamp);
- else if (id == Id.VERSIONX)
+ else if (t.ident == Id.VERSIONX)
{
t.value = TOK.int64Literal;
t.unsvalue = compileEnv.versionNumber;
}
- else if (id == Id.EOFX)
+ else if (t.ident == Id.EOFX)
{
t.value = TOK.endOfFile;
// Advance scanner to end of file
// Check for start of an identifier
if (charLookup.isStart(c))
+ {
+ p++;
goto case_ident;
-
+ }
if (c == PS || c == LS)
{
endOfLine();
return c;
}
+ /**
+ Lex an identifier
+ Params:
+ t = pointer to the token that starts the identifier, and accepts the result
+ startsUCN = if the identifier s
+ Returns:
+ true if an identifier was set
+ */
+ private bool lexIdentifier(Token* t, bool startsUCN)
+ {
+ Identifier id;
+
+ if (!startsUCN)
+ {
+ while (isidchar(*p))
+ p++;
+ }
+
+ if (!startsUCN && !(*p & 0x80) && (!Ccompile || *p != '\\'))
+ {
+ // Fast path for ascii identifiers
+ id = Identifier.idPool((cast(char*)t.ptr)[0 .. p - t.ptr], false);
+ }
+ else
+ {
+ // Slow path for identifiers with UCNs and UTF8 characters
+ stringbuffer.setsize(0);
+ stringbuffer.writestring(t.ptr[0 .. p - t.ptr]);
+
+ IdentLoop:
+ while (1)
+ {
+ // Is this the first character of the identifier?
+ const isStartCharacter = t.ptr is p;
+ uint u;
+
+ if (isidchar(*p))
+ {
+ const c = *p++;
+ stringbuffer.writeByte(c);
+ continue;
+ }
+ else if (*p & 0x80)
+ {
+ u = decodeUTF();
+ p++;
+ }
+ else if (Ccompile && *p == '\\')
+ {
+ uint times;
+ if (p[1] == 'u')
+ {
+ // Universal Character Name (C) 2 byte
+ // \uXXXX
+ times = 4;
+ }
+ else if (p[1] == 'U')
+ {
+ // Universal Character Name (C) 4 byte
+ // \UXXXXXXXX
+ times = 8;
+ }
+ else
+ break; // end of identifier
+
+ p += 2;
+
+ foreach(i; 0 .. times)
+ {
+ char c = p[i];
+
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'a' && c <= 'f')
+ c -= 'a' - 10;
+ else if (c >= 'A' && c <= 'F')
+ c -= 'A' - 10;
+ else
+ {
+ // Not a valid ucn, if this is the first character, return false now.
+ // Otherwise break to return the identifier lexed so far.
+ p -= 2;
+ if (isStartCharacter)
+ {
+ return false;
+ }
+ break IdentLoop;
+ }
+ u = (u << 4) | c;
+ }
+ if (isBidiControl(u))
+ error(t.loc, "Bidirectional control characters in universal character names are disallowed for security reasons");
+ p += times;
+ }
+ else
+ break; // end of identifier
+
+ if (isStartCharacter && !charLookup.isStart(u))
+ error(t.loc, "character 0x%04x is not allowed as a start character in an identifier", u);
+ else if (!charLookup.isContinue(u))
+ error(t.loc, "character 0x%04x is not allowed as a continue character in an identifier", u);
+ stringbuffer.writeUTF8(u);
+ }
+
+ id = Identifier.idPool(stringbuffer[], false);
+ }
+
+ t.ident = id;
+ t.value = cast(TOK)id.getValue();
+ return true;
+ }
+
/**
Lex a wysiwyg string. `p` must be pointing to the first character before the
contents of the string literal. The character pointed to by `p` will be used as
if (*p == '\'')
{
- error("character constant has multiple characters");
+ const(char)* s = p - 1;
+ while(*s != '\'')
+ {
+ s--;
+ }
+ s++;
+ error("character constant has multiple characters - did you mean \"%.*s\"?", cast(int) (p - s), s);
p++;
}
else
import dmd.attrib;
import dmd.declaration;
import dmd.dsymbol;
-import dmd.dsymbolsem : isGNUABITag, toAlias;
+import dmd.dsymbolsem : isGNUABITag, toAlias, equals;
+import dmd.expressionsem : toStringExp, toInteger, toUInteger;
+import dmd.templatesem : computeOneMember;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
// `&function`
Dsymbol d = isDsymbol(o);
Expression e = isExpression(o);
+
+ if (d && d.isTemplateDeclaration())
+ d.isTemplateDeclaration().computeOneMember();
+
if (d && d.isFuncDeclaration())
{
// X .. E => template parameter is an expression
import dmd.dmodule;
import dmd.dsymbol;
import dmd.dsymbolsem : toAlias;
+import dmd.expressionsem : toInteger, toReal, toImaginary;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
PKGpackage // already determined that's an actual package
};
-enum class Edition : uint16_t
-{
- v2023 = 2023,
- v2024,
- v2025,
-};
-
class Package : public ScopeDsymbol
{
public:
const char *kind() const override;
- bool equals(const RootObject * const o) const override;
-
bool isAncestorPackageOf(const Package * const pkg) const;
void accept(Visitor *v) override { v->visit(this); }
int needModuleInfo();
bool isPackageAccessible(Package *p, Visibility visibility, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all) override;
Dsymbol *symtabInsert(Dsymbol *s) override;
- static void runDeferredSemantic();
- static void runDeferredSemantic2();
- static void runDeferredSemantic3();
int imports(Module *m);
bool isRoot() { return this->importedFrom == this; }
import core.stdc.stdlib;
import core.stdc.string;
-import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
import dmd.ast_node;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
-import dmd.enumsem;
-import dmd.errors;
import dmd.expression;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.location;
-import dmd.root.ctfloat;
import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.rootobject;
import dmd.root.stringtable;
-import dmd.target;
import dmd.tokens;
-import dmd.typesem;
import dmd.visitor;
enum LOGDOTEXP = 0; // log ::dotExp()
private alias StringValueType = StringValue!Type;
}
-private auto X(T, U)(T m, U n)
+private auto X(T, U)(T m, U n) nothrow
{
return (m << 4) | n;
}
+bool isImaginaryNonSemantic(Type _this)
+{
+ assert(_this.isTypeEnum() is null);
+ if (auto tb = _this.isTypeBasic())
+ return (tb.flags & TFlags.imaginary) != 0;
+ return false;
+}
+
+Type toBaseTypeNonSemantic(Type _this)
+{
+ if (auto te = _this.isTypeEnum())
+ {
+ if (!te.sym.members && !te.sym.memtype)
+ return te;
+ return te.sym.memtype.toBaseTypeNonSemantic();
+ }
+ return _this;
+}
+
+/* Helper function for `typeToExpression`. Contains common code
+ * for TypeQualified derived classes.
+ */
+Expression typeToExpressionHelper(TypeQualified t, Expression e, size_t i = 0)
+{
+ //printf("toExpressionHelper(e = %s %s)\n", EXPtoString(e.op).ptr, e.toChars());
+ foreach (id; t.idents[i .. t.idents.length])
+ {
+ //printf("\t[%d] e: '%s', id: '%s'\n", i, e.toChars(), id.toChars());
+
+ final switch (id.dyncast())
+ {
+ // ... '. ident'
+ case DYNCAST.identifier:
+ e = new DotIdExp(e.loc, e, cast(Identifier)id);
+ break;
+
+ // ... '. name!(tiargs)'
+ case DYNCAST.dsymbol:
+ auto ti = (cast(Dsymbol)id).isTemplateInstance();
+ assert(ti);
+ e = new DotTemplateInstanceExp(e.loc, e, ti.name, ti.tiargs);
+ break;
+
+ // ... '[type]'
+ case DYNCAST.type: // https://issues.dlang.org/show_bug.cgi?id=1215
+ e = new ArrayExp(t.loc, e, new TypeExp(t.loc, cast(Type)id));
+ break;
+
+ // ... '[expr]'
+ case DYNCAST.expression: // https://issues.dlang.org/show_bug.cgi?id=1215
+ e = new ArrayExp(t.loc, e, cast(Expression)id);
+ break;
+
+ case DYNCAST.object:
+ case DYNCAST.tuple:
+ case DYNCAST.parameter:
+ case DYNCAST.statement:
+ case DYNCAST.condition:
+ case DYNCAST.templateparameter:
+ case DYNCAST.initializer:
+ assert(0);
+ }
+ }
+ return e;
+}
+
+/******************************************
+ * We've mistakenly parsed `t` as a type.
+ * Redo `t` as an Expression only if there are no type modifiers.
+ * Params:
+ * t = mistaken type
+ * Returns:
+ * t redone as Expression, null if cannot
+ */
+Expression typeToExpression(Type t)
+{
+ static Expression visitSArray(TypeSArray t)
+ {
+ if (auto e = t.next.typeToExpression())
+ return new ArrayExp(t.dim.loc, e, t.dim);
+ return null;
+ }
+
+ static Expression visitAArray(TypeAArray t)
+ {
+ if (auto e = t.next.typeToExpression())
+ {
+ if (auto ei = t.index.typeToExpression())
+ return new ArrayExp(t.loc, e, ei);
+ }
+ return null;
+ }
+
+ static Expression visitIdentifier(TypeIdentifier t)
+ {
+ return typeToExpressionHelper(t, new IdentifierExp(t.loc, t.ident));
+ }
+
+ static Expression visitInstance(TypeInstance t)
+ {
+ return typeToExpressionHelper(t, new ScopeExp(t.loc, t.tempinst));
+ }
+
+ // easy way to enable 'auto v = new int[mixin("exp")];' in 2.088+
+ static Expression visitMixin(TypeMixin t)
+ {
+ return new TypeExp(t.loc, t);
+ }
+
+ if (t.mod)
+ return null;
+ switch (t.ty)
+ {
+ case Tsarray: return visitSArray(t.isTypeSArray());
+ case Taarray: return visitAArray(t.isTypeAArray());
+ case Tident: return visitIdentifier(t.isTypeIdentifier());
+ case Tinstance: return visitInstance(t.isTypeInstance());
+ case Tmixin: return visitMixin(t.isTypeMixin());
+ default: return null;
+ }
+}
+
/***************************
* Return !=0 if modfrom can be implicitly converted to modto
*/
*/
extern (C++) abstract class Type : ASTNode
{
+
TY ty;
MOD mod; // modifiers MODxxxx
char* deco;
return sizeTy;
}();
- final extern (D) this(TY ty) scope @safe
+ final extern (D) this(TY ty) scope @safe nothrow
{
this.ty = ty;
}
assert(0);
}
- override bool equals(const RootObject o) const
+ final bool equals(const Type t) const nothrow
{
- Type t = cast(Type)o;
//printf("Type::equals(%s, %s)\n", toChars(), t.toChars());
+ if (this == t)
+ return true;
+ if (ty == Ttuple)
+ {
+ if (t.ty != Ttuple)
+ return false;
+ auto t1 = this.isTypeTuple();
+ auto t2 = t.isTypeTuple();
+ if (t1.arguments.length != t2.arguments.length)
+ return false;
+ for (size_t i = 0; i < t1.arguments.length; i++)
+ {
+ const Parameter arg1 = (*t1.arguments)[i];
+ const Parameter arg2 = (*t2.arguments)[i];
+ if (!arg1.type.equals(arg2.type))
+ return false;
+ }
+ return true;
+ }
// deco strings are unique
// and semantic() has been run
- if (this == o || ((t && deco == t.deco) && deco !is null))
+ if ((t && deco == t.deco) && deco !is null)
{
//printf("deco = '%s', t.deco = '%s'\n", deco, t.deco);
return true;
}
// kludge for template.isType()
- override final DYNCAST dyncast() const
+ override final DYNCAST dyncast() const nothrow
{
return DYNCAST.type;
}
/// Returns a non-zero unique ID for this Type, or returns 0 if the Type does not (yet) have a unique ID.
/// If `semantic()` has not been run, 0 is returned.
- final size_t getUniqueID() const
+ final size_t getUniqueID() const nothrow
{
return cast(size_t) deco;
}
extern (D)
- final Mcache* getMcache()
+ final Mcache* getMcache() nothrow
{
if (!mcache)
mcache = cast(Mcache*) mem.xcalloc(Mcache.sizeof, 1);
return buf.extractChars();
}
- static void _init()
- {
- stringtable._init(14_000);
-
- // Set basic types
- __gshared TY* basetab =
- [
- Tvoid,
- Tint8,
- Tuns8,
- Tint16,
- Tuns16,
- Tint32,
- Tuns32,
- Tint64,
- Tuns64,
- Tint128,
- Tuns128,
- Tfloat32,
- Tfloat64,
- Tfloat80,
- Timaginary32,
- Timaginary64,
- Timaginary80,
- Tcomplex32,
- Tcomplex64,
- Tcomplex80,
- Tbool,
- Tchar,
- Twchar,
- Tdchar,
- Terror
- ];
-
- static Type merge(Type t)
- {
- import dmd.mangle.basic : tyToDecoBuffer;
-
- OutBuffer buf;
- buf.reserve(3);
-
- if (t.ty == Tnoreturn)
- buf.writestring("Nn");
- else
- tyToDecoBuffer(buf, t.ty);
-
- auto sv = t.stringtable.update(buf[]);
- if (sv.value)
- return sv.value;
- t.deco = cast(char*)sv.toDchars();
- sv.value = t;
- return t;
- }
-
- for (size_t i = 0; basetab[i] != Terror; i++)
- {
- Type t = new TypeBasic(basetab[i]);
- t = merge(t);
- basic[basetab[i]] = t;
- }
- basic[Terror] = new TypeError();
-
- tnoreturn = new TypeNoreturn();
- tnoreturn.deco = merge(tnoreturn).deco;
- basic[Tnoreturn] = tnoreturn;
-
- tvoid = basic[Tvoid];
- tint8 = basic[Tint8];
- tuns8 = basic[Tuns8];
- tint16 = basic[Tint16];
- tuns16 = basic[Tuns16];
- tint32 = basic[Tint32];
- tuns32 = basic[Tuns32];
- tint64 = basic[Tint64];
- tuns64 = basic[Tuns64];
- tint128 = basic[Tint128];
- tuns128 = basic[Tuns128];
- tfloat32 = basic[Tfloat32];
- tfloat64 = basic[Tfloat64];
- tfloat80 = basic[Tfloat80];
-
- timaginary32 = basic[Timaginary32];
- timaginary64 = basic[Timaginary64];
- timaginary80 = basic[Timaginary80];
-
- tcomplex32 = basic[Tcomplex32];
- tcomplex64 = basic[Tcomplex64];
- tcomplex80 = basic[Tcomplex80];
-
- tbool = basic[Tbool];
- tchar = basic[Tchar];
- twchar = basic[Twchar];
- tdchar = basic[Tdchar];
-
- tshiftcnt = tint32;
- terror = basic[Terror];
- tnoreturn = basic[Tnoreturn];
- tnull = new TypeNull();
- tnull.deco = merge(tnull).deco;
-
- tvoidptr = tvoid.pointerTo();
- tstring = tchar.immutableOf().arrayOf();
- twstring = twchar.immutableOf().arrayOf();
- tdstring = tdchar.immutableOf().arrayOf();
-
- const isLP64 = target.isLP64;
-
- tsize_t = basic[isLP64 ? Tuns64 : Tuns32];
- tptrdiff_t = basic[isLP64 ? Tint64 : Tint32];
- thash_t = tsize_t;
-
- static if (__VERSION__ == 2081)
- {
- // Related issue: https://issues.dlang.org/show_bug.cgi?id=19134
- // D 2.081.x regressed initializing class objects at compile time.
- // As a workaround initialize this global at run-time instead.
- TypeTuple.empty = new TypeTuple();
- }
- }
-
/**
* Deinitializes the global state of the compiler.
*
* This can be used to restore the state set by `_init` to its original
* state.
*/
- static void deinitialize()
+ static void deinitialize() nothrow
{
stringtable = stringtable.init;
}
- uint alignsize()
- {
- return cast(uint)size(this, Loc.initial);
- }
-
/*********************************
* Store this type's modifier name into buf.
*/
return buf.extractChars();
}
- bool isIntegral()
- {
- return false;
- }
-
- // real, imaginary, or complex
- bool isFloating()
- {
- return false;
- }
-
- bool isReal()
- {
- return false;
- }
-
- bool isImaginary()
- {
- return false;
- }
-
- bool isComplex()
- {
- return false;
- }
-
- bool isScalar()
- {
- return false;
- }
-
- bool isUnsigned()
- {
- return false;
- }
-
- bool isScopeClass()
- {
- return false;
- }
-
- bool isString()
+ bool isScopeClass() nothrow
{
return false;
}
- /**************************
- * Returns true if T can be converted to boolean value.
- */
- bool isBoolean()
- {
- return isScalar();
- }
-
final bool isConst() const nothrow pure @nogc @safe
{
return (mod & MODFlags.const_) != 0;
return t;
}
- /**********************************
- * For our new type 'this', which is type-constructed from t,
- * fill in the cto, ito, sto, scto, wto shortcuts.
- */
- extern (D) final void fixTo(Type t)
+ inout(ClassDeclaration) isClassHandle() inout
{
- // If fixing this: immutable(T*) by t: immutable(T)*,
- // cache t to this.xto won't break transitivity.
- Type mto = null;
- Type tn = nextOf();
- if (!tn || ty != Tsarray && tn.mod == t.nextOf().mod)
- {
- switch (t.mod)
- {
- case 0:
- mto = t;
- break;
-
- case MODFlags.const_:
- getMcache();
- mcache.cto = t;
- break;
-
- case MODFlags.wild:
- getMcache();
- mcache.wto = t;
- break;
-
- case MODFlags.wildconst:
- getMcache();
- mcache.wcto = t;
- break;
-
- case MODFlags.shared_:
- getMcache();
- mcache.sto = t;
- break;
-
- case MODFlags.shared_ | MODFlags.const_:
- getMcache();
- mcache.scto = t;
- break;
-
- case MODFlags.shared_ | MODFlags.wild:
- getMcache();
- mcache.swto = t;
- break;
-
- case MODFlags.shared_ | MODFlags.wildconst:
- getMcache();
- mcache.swcto = t;
- break;
+ return null;
+ }
- case MODFlags.immutable_:
- getMcache();
- mcache.ito = t;
- break;
+ /***************************************
+ * Return !=0 if the type or any of its subtypes is wild.
+ */
+ int hasWild() const
+ {
+ return mod & MODFlags.wild;
+ }
- default:
- break;
- }
- }
- assert(mod != t.mod);
+ // For eliminating dynamic_cast
+ TypeBasic isTypeBasic() nothrow
+ {
+ return null;
+ }
- if (mod)
+ final pure inout nothrow @nogc
+ {
+ /****************
+ * Is this type a pointer to a function?
+ * Returns:
+ * the function type if it is
+ */
+ inout(TypeFunction) isPtrToFunction()
{
- getMcache();
- t.getMcache();
+ return (ty == Tpointer && (cast(TypePointer)this).next.ty == Tfunction)
+ ? cast(typeof(return))(cast(TypePointer)this).next
+ : null;
}
- switch (mod)
- {
- case 0:
- break;
-
- case MODFlags.const_:
- mcache.cto = mto;
- t.mcache.cto = this;
- break;
- case MODFlags.wild:
- mcache.wto = mto;
- t.mcache.wto = this;
- break;
+ /*****************
+ * Is this type a function, delegate, or pointer to a function?
+ * Returns:
+ * the function type if it is
+ */
+ inout(TypeFunction) isFunction_Delegate_PtrToFunction()
+ {
+ return ty == Tfunction ? cast(typeof(return))this :
- case MODFlags.wildconst:
- mcache.wcto = mto;
- t.mcache.wcto = this;
- break;
+ ty == Tdelegate ? cast(typeof(return))(cast(TypePointer)this).next :
- case MODFlags.shared_:
- mcache.sto = mto;
- t.mcache.sto = this;
- break;
+ ty == Tpointer && (cast(TypePointer)this).next.ty == Tfunction ?
+ cast(typeof(return))(cast(TypePointer)this).next :
- case MODFlags.shared_ | MODFlags.const_:
- mcache.scto = mto;
- t.mcache.scto = this;
- break;
+ null;
+ }
+ }
- case MODFlags.shared_ | MODFlags.wild:
- mcache.swto = mto;
- t.mcache.swto = this;
- break;
+ final pure inout nothrow @nogc @trusted
+ {
+ inout(TypeError) isTypeError() { return ty == Terror ? cast(typeof(return))this : null; }
+ inout(TypeVector) isTypeVector() { return ty == Tvector ? cast(typeof(return))this : null; }
+ inout(TypeSArray) isTypeSArray() { return ty == Tsarray ? cast(typeof(return))this : null; }
+ inout(TypeDArray) isTypeDArray() { return ty == Tarray ? cast(typeof(return))this : null; }
+ inout(TypeAArray) isTypeAArray() { return ty == Taarray ? cast(typeof(return))this : null; }
+ inout(TypePointer) isTypePointer() { return ty == Tpointer ? cast(typeof(return))this : null; }
+ inout(TypeReference) isTypeReference() { return ty == Treference ? cast(typeof(return))this : null; }
+ inout(TypeFunction) isTypeFunction() { return ty == Tfunction ? cast(typeof(return))this : null; }
+ inout(TypeDelegate) isTypeDelegate() { return ty == Tdelegate ? cast(typeof(return))this : null; }
+ inout(TypeIdentifier) isTypeIdentifier() { return ty == Tident ? cast(typeof(return))this : null; }
+ inout(TypeInstance) isTypeInstance() { return ty == Tinstance ? cast(typeof(return))this : null; }
+ inout(TypeTypeof) isTypeTypeof() { return ty == Ttypeof ? cast(typeof(return))this : null; }
+ inout(TypeReturn) isTypeReturn() { return ty == Treturn ? cast(typeof(return))this : null; }
+ inout(TypeStruct) isTypeStruct() { return ty == Tstruct ? cast(typeof(return))this : null; }
+ inout(TypeEnum) isTypeEnum() { return ty == Tenum ? cast(typeof(return))this : null; }
+ inout(TypeClass) isTypeClass() { return ty == Tclass ? cast(typeof(return))this : null; }
+ inout(TypeTuple) isTypeTuple() { return ty == Ttuple ? cast(typeof(return))this : null; }
+ inout(TypeSlice) isTypeSlice() { return ty == Tslice ? cast(typeof(return))this : null; }
+ inout(TypeNull) isTypeNull() { return ty == Tnull ? cast(typeof(return))this : null; }
+ inout(TypeMixin) isTypeMixin() { return ty == Tmixin ? cast(typeof(return))this : null; }
+ inout(TypeTraits) isTypeTraits() { return ty == Ttraits ? cast(typeof(return))this : null; }
+ inout(TypeNoreturn) isTypeNoreturn() { return ty == Tnoreturn ? cast(typeof(return))this : null; }
+ inout(TypeTag) isTypeTag() { return ty == Ttag ? cast(typeof(return))this : null; }
- case MODFlags.shared_ | MODFlags.wildconst:
- mcache.swcto = mto;
- t.mcache.swcto = this;
- break;
+ extern (D) bool isStaticOrDynamicArray() const { return ty == Tarray || ty == Tsarray; }
+ }
- case MODFlags.immutable_:
- t.mcache.ito = this;
- if (t.mcache.cto)
- t.mcache.cto.getMcache().ito = this;
- if (t.mcache.sto)
- t.mcache.sto.getMcache().ito = this;
- if (t.mcache.scto)
- t.mcache.scto.getMcache().ito = this;
- if (t.mcache.wto)
- t.mcache.wto.getMcache().ito = this;
- if (t.mcache.wcto)
- t.mcache.wcto.getMcache().ito = this;
- if (t.mcache.swto)
- t.mcache.swto.getMcache().ito = this;
- if (t.mcache.swcto)
- t.mcache.swcto.getMcache().ito = this;
- break;
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
- default:
+ final TypeFunction toTypeFunction() nothrow
+ {
+ if (ty != Tfunction)
assert(0);
- }
-
- check();
- t.check();
- //printf("fixTo: %s, %s\n", toChars(), t.toChars());
+ return cast(TypeFunction)this;
}
- /***************************
- * Look for bugs in constructing types.
- */
- extern (D) final void check()
+ extern (D) static Types* arraySyntaxCopy(Types* types)
{
- if (mcache)
- with (mcache)
- switch (mod)
+ Types* a = null;
+ if (types)
{
- case 0:
- if (cto)
- assert(cto.mod == MODFlags.const_);
- if (ito)
- assert(ito.mod == MODFlags.immutable_);
- if (sto)
- assert(sto.mod == MODFlags.shared_);
- if (scto)
- assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
- if (wto)
- assert(wto.mod == MODFlags.wild);
- if (wcto)
- assert(wcto.mod == MODFlags.wildconst);
- if (swto)
- assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
- if (swcto)
- assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
- break;
-
- case MODFlags.const_:
- if (cto)
- assert(cto.mod == 0);
- if (ito)
- assert(ito.mod == MODFlags.immutable_);
- if (sto)
- assert(sto.mod == MODFlags.shared_);
- if (scto)
- assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
- if (wto)
- assert(wto.mod == MODFlags.wild);
- if (wcto)
- assert(wcto.mod == MODFlags.wildconst);
- if (swto)
- assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
- if (swcto)
- assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
- break;
+ a = new Types(types.length);
+ foreach (i, t; *types)
+ {
+ (*a)[i] = t ? t.syntaxCopy() : null;
+ }
+ }
+ return a;
+ }
+}
- case MODFlags.wild:
- if (cto)
- assert(cto.mod == MODFlags.const_);
- if (ito)
- assert(ito.mod == MODFlags.immutable_);
- if (sto)
- assert(sto.mod == MODFlags.shared_);
- if (scto)
- assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
- if (wto)
- assert(wto.mod == 0);
- if (wcto)
- assert(wcto.mod == MODFlags.wildconst);
- if (swto)
- assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
- if (swcto)
- assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
- break;
+/***********************************************************
+ */
+extern (C++) final class TypeError : Type
+{
+ extern (D) this() @safe
+ {
+ super(Terror);
+ }
- case MODFlags.wildconst:
- assert(!cto || cto.mod == MODFlags.const_);
- assert(!ito || ito.mod == MODFlags.immutable_);
- assert(!sto || sto.mod == MODFlags.shared_);
- assert(!scto || scto.mod == (MODFlags.shared_ | MODFlags.const_));
- assert(!wto || wto.mod == MODFlags.wild);
- assert(!wcto || wcto.mod == 0);
- assert(!swto || swto.mod == (MODFlags.shared_ | MODFlags.wild));
- assert(!swcto || swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
- break;
+ override const(char)* kind() const nothrow
+ {
+ return "error";
+ }
- case MODFlags.shared_:
- if (cto)
- assert(cto.mod == MODFlags.const_);
- if (ito)
- assert(ito.mod == MODFlags.immutable_);
- if (sto)
- assert(sto.mod == 0);
- if (scto)
- assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
- if (wto)
- assert(wto.mod == MODFlags.wild);
- if (wcto)
- assert(wcto.mod == MODFlags.wildconst);
- if (swto)
- assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
- if (swcto)
- assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
- break;
-
- case MODFlags.shared_ | MODFlags.const_:
- if (cto)
- assert(cto.mod == MODFlags.const_);
- if (ito)
- assert(ito.mod == MODFlags.immutable_);
- if (sto)
- assert(sto.mod == MODFlags.shared_);
- if (scto)
- assert(scto.mod == 0);
- if (wto)
- assert(wto.mod == MODFlags.wild);
- if (wcto)
- assert(wcto.mod == MODFlags.wildconst);
- if (swto)
- assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
- if (swcto)
- assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
- break;
-
- case MODFlags.shared_ | MODFlags.wild:
- if (cto)
- assert(cto.mod == MODFlags.const_);
- if (ito)
- assert(ito.mod == MODFlags.immutable_);
- if (sto)
- assert(sto.mod == MODFlags.shared_);
- if (scto)
- assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
- if (wto)
- assert(wto.mod == MODFlags.wild);
- if (wcto)
- assert(wcto.mod == MODFlags.wildconst);
- if (swto)
- assert(swto.mod == 0);
- if (swcto)
- assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
- break;
-
- case MODFlags.shared_ | MODFlags.wildconst:
- assert(!cto || cto.mod == MODFlags.const_);
- assert(!ito || ito.mod == MODFlags.immutable_);
- assert(!sto || sto.mod == MODFlags.shared_);
- assert(!scto || scto.mod == (MODFlags.shared_ | MODFlags.const_));
- assert(!wto || wto.mod == MODFlags.wild);
- assert(!wcto || wcto.mod == MODFlags.wildconst);
- assert(!swto || swto.mod == (MODFlags.shared_ | MODFlags.wild));
- assert(!swcto || swcto.mod == 0);
- break;
-
- case MODFlags.immutable_:
- if (cto)
- assert(cto.mod == MODFlags.const_);
- if (ito)
- assert(ito.mod == 0);
- if (sto)
- assert(sto.mod == MODFlags.shared_);
- if (scto)
- assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
- if (wto)
- assert(wto.mod == MODFlags.wild);
- if (wcto)
- assert(wcto.mod == MODFlags.wildconst);
- if (swto)
- assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
- if (swcto)
- assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
- break;
-
- default:
- assert(0);
- }
-
- Type tn = nextOf();
- if (tn && ty != Tfunction && tn.ty != Tfunction && ty != Tenum)
- {
- // Verify transitivity
- switch (mod)
- {
- case 0:
- case MODFlags.const_:
- case MODFlags.wild:
- case MODFlags.wildconst:
- case MODFlags.shared_:
- case MODFlags.shared_ | MODFlags.const_:
- case MODFlags.shared_ | MODFlags.wild:
- case MODFlags.shared_ | MODFlags.wildconst:
- case MODFlags.immutable_:
- assert(tn.mod == MODFlags.immutable_ || (tn.mod & mod) == mod);
- break;
-
- default:
- assert(0);
- }
- tn.check();
- }
- }
-
- /*************************************
- * Apply STCxxxx bits to existing type.
- * Use *before* semantic analysis is run.
- */
- extern (D) final Type addSTC(STC stc)
- {
- Type t = this;
- if (t.isImmutable())
- {
- return t;
- }
- else if (stc & STC.immutable_)
- {
- t = t.makeImmutable();
- return t;
- }
-
- 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())
- t = t.makeSharedWildConst();
- else
- t = t.makeSharedConst();
- }
- else
- {
- if (t.isWild())
- t = t.makeWildConst();
- else
- t = t.makeConst();
- }
- }
- if ((stc & STC.wild) && !t.isWild())
- {
- if (t.isShared())
- {
- if (t.isConst())
- t = t.makeSharedWildConst();
- else
- t = t.makeSharedWild();
- }
- else
- {
- if (t.isConst())
- t = t.makeWildConst();
- else
- t = t.makeWild();
- }
- }
-
- return t;
- }
-
- final bool hasDeprecatedAliasThis()
- {
- auto ad = isAggregate(this);
- return ad && ad.aliasthis && (ad.aliasthis.isDeprecated || ad.aliasthis.sym.isDeprecated);
- }
-
- Type makeConst()
- {
- //printf("Type::makeConst() %p, %s\n", this, toChars());
- if (mcache && mcache.cto)
- return mcache.cto;
- Type t = this.nullAttributes();
- t.mod = MODFlags.const_;
- //printf("-Type::makeConst() %p, %s\n", t, toChars());
- return t;
- }
-
- Type makeImmutable()
- {
- if (mcache && mcache.ito)
- return mcache.ito;
- Type t = this.nullAttributes();
- t.mod = MODFlags.immutable_;
- return t;
- }
-
- Type makeShared()
- {
- if (mcache && mcache.sto)
- return mcache.sto;
- Type t = this.nullAttributes();
- t.mod = MODFlags.shared_;
- return t;
- }
-
- Type makeSharedConst()
- {
- if (mcache && mcache.scto)
- return mcache.scto;
- Type t = this.nullAttributes();
- t.mod = MODFlags.shared_ | MODFlags.const_;
- return t;
- }
-
- Type makeWild()
- {
- if (mcache && mcache.wto)
- return mcache.wto;
- Type t = this.nullAttributes();
- t.mod = MODFlags.wild;
- return t;
- }
-
- Type makeWildConst()
- {
- if (mcache && mcache.wcto)
- return mcache.wcto;
- Type t = this.nullAttributes();
- t.mod = MODFlags.wildconst;
- return t;
- }
-
- Type makeSharedWild()
- {
- if (mcache && mcache.swto)
- return mcache.swto;
- Type t = this.nullAttributes();
- t.mod = MODFlags.shared_ | MODFlags.wild;
- return t;
- }
-
- Type makeSharedWildConst()
- {
- if (mcache && mcache.swcto)
- return mcache.swcto;
- Type t = this.nullAttributes();
- t.mod = MODFlags.shared_ | MODFlags.wildconst;
- return t;
- }
-
- Type makeMutable()
- {
- Type t = this.nullAttributes();
- t.mod = mod & MODFlags.shared_;
- return t;
- }
-
- /*******************************
- * If this is a shell around another type,
- * get that other type.
- */
- final Type toBasetype()
- {
- /* This function is used heavily.
- * De-virtualize it so it can be easily inlined.
- */
- TypeEnum te;
- return ((te = isTypeEnum()) !is null) ? te.toBasetype2() : this;
- }
-
- /***************************************
- * Compute MOD bits matching `this` argument type to wild parameter type.
- * Params:
- * t = corresponding parameter type
- * isRef = parameter is `ref` or `out`
- * Returns:
- * MOD bits
- */
- MOD deduceWild(Type t, bool isRef)
- {
- //printf("Type::deduceWild this = '%s', tprm = '%s'\n", toChars(), tprm.toChars());
- if (t.isWild())
- {
- if (isImmutable())
- return MODFlags.immutable_;
- if (isWildConst())
- {
- if (t.isWildConst())
- return MODFlags.wild;
- return MODFlags.wildconst;
- }
- if (isWild())
- return MODFlags.wild;
- if (isConst())
- return MODFlags.const_;
- if (isMutable())
- return MODFlags.mutable;
- assert(0);
- }
- return 0;
- }
-
- inout(ClassDeclaration) isClassHandle() inout
- {
- return null;
- }
-
- /************************************
- * Return alignment to use for this type.
- */
- structalign_t alignment()
- {
- structalign_t s;
- s.setDefault();
- return s;
- }
-
- /***************************************
- * Return !=0 if the type or any of its subtypes is wild.
- */
- int hasWild() const
- {
- return mod & MODFlags.wild;
- }
-
- /*************************************
- * Detect if type has pointer fields that are initialized to void.
- * Local stack variables with such void fields can remain uninitialized,
- * leading to pointer bugs.
- * Returns:
- * true if so
- */
- bool hasVoidInitPointers()
- {
- return false;
- }
-
- /*************************************
- * Detect if this is an unsafe type because of the presence of `@system` members
- * Returns:
- * true if so
- */
- bool hasUnsafeBitpatterns()
- {
- return false;
- }
-
- /***************************************
- * Returns: true if type has any invariants
- */
- bool hasInvariant()
- {
- //printf("Type::hasInvariant() %s, %d\n", toChars(), ty);
- return false;
- }
-
- /*************************************
- * If this is a type of something, return that something.
- */
- Type nextOf()
- {
- return null;
- }
-
- /*************************************
- * If this is a type of static array, return its base element type.
- */
- final Type baseElemOf()
- {
- Type t = toBasetype();
- TypeSArray tsa;
- while ((tsa = t.isTypeSArray()) !is null)
- t = tsa.next.toBasetype();
- return t;
- }
-
- /****************************************
- * Return the mask that an integral type will
- * fit into.
- */
- extern (D) final ulong sizemask()
- {
- ulong m;
- switch (toBasetype().ty)
- {
- case Tbool:
- m = 1;
- break;
- case Tchar:
- case Tint8:
- case Tuns8:
- m = 0xFF;
- break;
- case Twchar:
- case Tint16:
- case Tuns16:
- m = 0xFFFFU;
- break;
- case Tdchar:
- case Tint32:
- case Tuns32:
- m = 0xFFFFFFFFU;
- break;
- case Tint64:
- case Tuns64:
- m = 0xFFFFFFFFFFFFFFFFUL;
- break;
- default:
- assert(0);
- }
- return m;
- }
-
- /********************************
- * true if when type goes out of scope, it needs a destructor applied.
- * Only applies to value types, not ref types.
- */
- bool needsDestruction()
- {
- return false;
- }
-
- /********************************
- * true if when type is copied, it needs a copy constructor or postblit
- * applied. Only applies to value types, not ref types.
- */
- bool needsCopyOrPostblit()
- {
- return false;
- }
-
- /*********************************
- *
- */
- bool needsNested()
- {
- return false;
- }
-
- // For eliminating dynamic_cast
- TypeBasic isTypeBasic()
- {
- return null;
- }
-
- final pure inout nothrow @nogc
- {
- /****************
- * Is this type a pointer to a function?
- * Returns:
- * the function type if it is
- */
- inout(TypeFunction) isPtrToFunction()
- {
- return (ty == Tpointer && (cast(TypePointer)this).next.ty == Tfunction)
- ? cast(typeof(return))(cast(TypePointer)this).next
- : null;
- }
-
- /*****************
- * Is this type a function, delegate, or pointer to a function?
- * Returns:
- * the function type if it is
- */
- inout(TypeFunction) isFunction_Delegate_PtrToFunction()
- {
- return ty == Tfunction ? cast(typeof(return))this :
-
- ty == Tdelegate ? cast(typeof(return))(cast(TypePointer)this).next :
-
- ty == Tpointer && (cast(TypePointer)this).next.ty == Tfunction ?
- cast(typeof(return))(cast(TypePointer)this).next :
-
- null;
- }
- }
-
- final pure inout nothrow @nogc @trusted
- {
- inout(TypeError) isTypeError() { return ty == Terror ? cast(typeof(return))this : null; }
- inout(TypeVector) isTypeVector() { return ty == Tvector ? cast(typeof(return))this : null; }
- inout(TypeSArray) isTypeSArray() { return ty == Tsarray ? cast(typeof(return))this : null; }
- inout(TypeDArray) isTypeDArray() { return ty == Tarray ? cast(typeof(return))this : null; }
- inout(TypeAArray) isTypeAArray() { return ty == Taarray ? cast(typeof(return))this : null; }
- inout(TypePointer) isTypePointer() { return ty == Tpointer ? cast(typeof(return))this : null; }
- inout(TypeReference) isTypeReference() { return ty == Treference ? cast(typeof(return))this : null; }
- inout(TypeFunction) isTypeFunction() { return ty == Tfunction ? cast(typeof(return))this : null; }
- inout(TypeDelegate) isTypeDelegate() { return ty == Tdelegate ? cast(typeof(return))this : null; }
- inout(TypeIdentifier) isTypeIdentifier() { return ty == Tident ? cast(typeof(return))this : null; }
- inout(TypeInstance) isTypeInstance() { return ty == Tinstance ? cast(typeof(return))this : null; }
- inout(TypeTypeof) isTypeTypeof() { return ty == Ttypeof ? cast(typeof(return))this : null; }
- inout(TypeReturn) isTypeReturn() { return ty == Treturn ? cast(typeof(return))this : null; }
- inout(TypeStruct) isTypeStruct() { return ty == Tstruct ? cast(typeof(return))this : null; }
- inout(TypeEnum) isTypeEnum() { return ty == Tenum ? cast(typeof(return))this : null; }
- inout(TypeClass) isTypeClass() { return ty == Tclass ? cast(typeof(return))this : null; }
- inout(TypeTuple) isTypeTuple() { return ty == Ttuple ? cast(typeof(return))this : null; }
- inout(TypeSlice) isTypeSlice() { return ty == Tslice ? cast(typeof(return))this : null; }
- inout(TypeNull) isTypeNull() { return ty == Tnull ? cast(typeof(return))this : null; }
- inout(TypeMixin) isTypeMixin() { return ty == Tmixin ? cast(typeof(return))this : null; }
- inout(TypeTraits) isTypeTraits() { return ty == Ttraits ? cast(typeof(return))this : null; }
- inout(TypeNoreturn) isTypeNoreturn() { return ty == Tnoreturn ? cast(typeof(return))this : null; }
- inout(TypeTag) isTypeTag() { return ty == Ttag ? cast(typeof(return))this : null; }
-
- extern (D) bool isStaticOrDynamicArray() const { return ty == Tarray || ty == Tsarray; }
- }
-
- override void accept(Visitor v)
- {
- v.visit(this);
- }
-
- final TypeFunction toTypeFunction()
- {
- if (ty != Tfunction)
- assert(0);
- return cast(TypeFunction)this;
- }
-
- extern (D) static Types* arraySyntaxCopy(Types* types)
- {
- Types* a = null;
- if (types)
- {
- a = new Types(types.length);
- foreach (i, t; *types)
- {
- (*a)[i] = t ? t.syntaxCopy() : null;
- }
- }
- return a;
- }
-}
-
-/***********************************************************
- */
-extern (C++) final class TypeError : Type
-{
- extern (D) this() @safe
- {
- super(Terror);
- }
-
- override const(char)* kind() const
- {
- return "error";
- }
-
- override TypeError syntaxCopy()
+ override TypeError syntaxCopy() nothrow
{
// No semantic analysis done, no need to copy
return this;
return mod & MODFlags.wild || (next && next.hasWild());
}
- /*******************************
- * For TypeFunction, nextOf() can return NULL if the function return
- * type is meant to be inferred, and semantic() hasn't yet been run
- * on the function. After semantic(), it must no longer be NULL.
- */
- override final Type nextOf() @safe
- {
- return next;
- }
-
- override final Type makeConst()
- {
- //printf("TypeNext::makeConst() %p, %s\n", this, toChars());
- if (mcache && mcache.cto)
- {
- assert(mcache.cto.mod == MODFlags.const_);
- return mcache.cto;
- }
- TypeNext t = cast(TypeNext)Type.makeConst();
- if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
- {
- if (next.isShared())
- {
- if (next.isWild())
- t.next = next.sharedWildConstOf();
- else
- t.next = next.sharedConstOf();
- }
- else
- {
- if (next.isWild())
- t.next = next.wildConstOf();
- else
- t.next = next.constOf();
- }
- }
- //printf("TypeNext::makeConst() returns %p, %s\n", t, t.toChars());
- return t;
- }
-
- override final Type makeImmutable()
- {
- //printf("TypeNext::makeImmutable() %s\n", toChars());
- if (mcache && mcache.ito)
- {
- assert(mcache.ito.isImmutable());
- return mcache.ito;
- }
- TypeNext t = cast(TypeNext)Type.makeImmutable();
- if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
- {
- t.next = next.immutableOf();
- }
- return t;
- }
-
- override final Type makeShared()
- {
- //printf("TypeNext::makeShared() %s\n", toChars());
- if (mcache && mcache.sto)
- {
- assert(mcache.sto.mod == MODFlags.shared_);
- return mcache.sto;
- }
- TypeNext t = cast(TypeNext)Type.makeShared();
- if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
- {
- if (next.isWild())
- {
- if (next.isConst())
- t.next = next.sharedWildConstOf();
- else
- t.next = next.sharedWildOf();
- }
- else
- {
- if (next.isConst())
- t.next = next.sharedConstOf();
- else
- t.next = next.sharedOf();
- }
- }
- //printf("TypeNext::makeShared() returns %p, %s\n", t, t.toChars());
- return t;
- }
-
- override final Type makeSharedConst()
- {
- //printf("TypeNext::makeSharedConst() %s\n", toChars());
- if (mcache && mcache.scto)
- {
- assert(mcache.scto.mod == (MODFlags.shared_ | MODFlags.const_));
- return mcache.scto;
- }
- TypeNext t = cast(TypeNext)Type.makeSharedConst();
- if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
- {
- if (next.isWild())
- t.next = next.sharedWildConstOf();
- else
- t.next = next.sharedConstOf();
- }
- //printf("TypeNext::makeSharedConst() returns %p, %s\n", t, t.toChars());
- return t;
- }
-
- override final Type makeWild()
- {
- //printf("TypeNext::makeWild() %s\n", toChars());
- if (mcache && mcache.wto)
- {
- assert(mcache.wto.mod == MODFlags.wild);
- return mcache.wto;
- }
- TypeNext t = cast(TypeNext)Type.makeWild();
- if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
- {
- if (next.isShared())
- {
- if (next.isConst())
- t.next = next.sharedWildConstOf();
- else
- t.next = next.sharedWildOf();
- }
- else
- {
- if (next.isConst())
- t.next = next.wildConstOf();
- else
- t.next = next.wildOf();
- }
- }
- //printf("TypeNext::makeWild() returns %p, %s\n", t, t.toChars());
- return t;
- }
-
- override final Type makeWildConst()
- {
- //printf("TypeNext::makeWildConst() %s\n", toChars());
- if (mcache && mcache.wcto)
- {
- assert(mcache.wcto.mod == MODFlags.wildconst);
- return mcache.wcto;
- }
- TypeNext t = cast(TypeNext)Type.makeWildConst();
- if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
- {
- if (next.isShared())
- t.next = next.sharedWildConstOf();
- else
- t.next = next.wildConstOf();
- }
- //printf("TypeNext::makeWildConst() returns %p, %s\n", t, t.toChars());
- return t;
- }
-
- override final Type makeSharedWild()
- {
- //printf("TypeNext::makeSharedWild() %s\n", toChars());
- if (mcache && mcache.swto)
- {
- assert(mcache.swto.isSharedWild());
- return mcache.swto;
- }
- TypeNext t = cast(TypeNext)Type.makeSharedWild();
- if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
- {
- if (next.isConst())
- t.next = next.sharedWildConstOf();
- else
- t.next = next.sharedWildOf();
- }
- //printf("TypeNext::makeSharedWild() returns %p, %s\n", t, t.toChars());
- return t;
- }
-
- override final Type makeSharedWildConst()
- {
- //printf("TypeNext::makeSharedWildConst() %s\n", toChars());
- if (mcache && mcache.swcto)
- {
- assert(mcache.swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
- return mcache.swcto;
- }
- TypeNext t = cast(TypeNext)Type.makeSharedWildConst();
- if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
- {
- t.next = next.sharedWildConstOf();
- }
- //printf("TypeNext::makeSharedWildConst() returns %p, %s\n", t, t.toChars());
- return t;
- }
-
- override final Type makeMutable()
- {
- //printf("TypeNext::makeMutable() %p, %s\n", this, toChars());
- TypeNext t = cast(TypeNext)Type.makeMutable();
- if (ty == Tsarray)
- {
- t.next = next.mutableOf();
- }
- //printf("TypeNext::makeMutable() returns %p, %s\n", t, t.toChars());
- return t;
- }
-
- override final MOD deduceWild(Type t, bool isRef)
- {
- if (ty == Tfunction)
- return 0;
-
- ubyte wm;
-
- Type tn = t.nextOf();
- if (!isRef && (ty == Tarray || ty == Tpointer) && tn)
- {
- wm = next.deduceWild(tn, true);
- if (!wm)
- wm = Type.deduceWild(t, true);
- }
- else
- {
- wm = Type.deduceWild(t, isRef);
- if (!wm && tn)
- wm = next.deduceWild(tn, true);
- }
-
- return wm;
- }
-
- final void transitive()
- {
- /* Invoke transitivity of type attributes
- */
- next = next.addMod(mod);
- }
-
override void accept(Visitor v)
{
v.visit(this);
flags |= TFlags.integral | TFlags.unsigned;
break;
- case Tdchar:
- d = Token.toChars(TOK.dchar_);
- flags |= TFlags.integral | TFlags.unsigned;
- break;
-
- default:
- assert(0);
- }
- this.dstring = d;
- this.flags = flags;
- merge(this);
- }
-
- override const(char)* kind() const
- {
- return dstring;
- }
-
- override TypeBasic syntaxCopy()
- {
- // No semantic analysis done on basic types, no need to copy
- return this;
- }
-
- override uint alignsize()
- {
- return target.alignsize(this);
- }
-
- override bool isIntegral()
- {
- //printf("TypeBasic::isIntegral('%s') x%x\n", toChars(), flags);
- return (flags & TFlags.integral) != 0;
- }
-
- override bool isFloating()
- {
- return (flags & TFlags.floating) != 0;
- }
-
- override bool isReal()
- {
- return (flags & TFlags.real_) != 0;
- }
-
- override bool isImaginary()
- {
- return (flags & TFlags.imaginary) != 0;
- }
-
- override bool isComplex()
- {
- return (flags & TFlags.complex) != 0;
- }
+ case Tdchar:
+ d = Token.toChars(TOK.dchar_);
+ flags |= TFlags.integral | TFlags.unsigned;
+ break;
- override bool isScalar()
- {
- return (flags & (TFlags.integral | TFlags.floating)) != 0;
+ default:
+ assert(0);
+ }
+ this.dstring = d;
+ this.flags = flags;
}
- override bool isUnsigned()
+ override const(char)* kind() const
{
- return (flags & TFlags.unsigned) != 0;
+ return dstring;
}
- override bool hasUnsafeBitpatterns()
+ override TypeBasic syntaxCopy()
{
- return ty == Tbool;
+ // No semantic analysis done on basic types, no need to copy
+ return this;
}
// For eliminating dynamic_cast
return new TypeVector(basetype.syntaxCopy());
}
- override uint alignsize()
- {
- return cast(uint)basetype.size();
- }
-
- override bool isIntegral()
- {
- //printf("TypeVector::isIntegral('%s') x%x\n", toChars(), flags);
- return basetype.nextOf().isIntegral();
- }
-
- override bool isFloating()
- {
- return basetype.nextOf().isFloating();
- }
-
- override bool isScalar()
- {
- return basetype.nextOf().isScalar();
- }
-
- override bool isUnsigned()
- {
- return basetype.nextOf().isUnsigned();
- }
-
- override bool isBoolean()
- {
- return false;
- }
-
TypeBasic elementType()
{
assert(basetype.ty == Tsarray);
TypeSArray t = cast(TypeSArray)basetype;
- TypeBasic tb = t.nextOf().isTypeBasic();
+ TypeBasic tb = t.next.isTypeBasic();
assert(tb);
return tb;
}
return dim.isIntegerExp() && dim.isIntegerExp().getInteger() == 0;
}
- override uint alignsize()
- {
- return next.alignsize();
- }
-
- override bool isString()
- {
- TY nty = next.toBasetype().ty;
- return nty.isSomeChar;
- }
-
- override structalign_t alignment()
- {
- return next.alignment();
- }
-
- override bool hasUnsafeBitpatterns()
- {
- return next.hasUnsafeBitpatterns();
- }
-
- override bool hasVoidInitPointers()
- {
- return next.hasVoidInitPointers();
- }
-
- override bool hasInvariant()
- {
- return next.hasInvariant();
- }
-
- override bool needsDestruction()
- {
- return next.needsDestruction();
- }
-
- override bool needsCopyOrPostblit()
- {
- return next.needsCopyOrPostblit();
- }
-
- /*********************************
- *
- */
- override bool needsNested()
- {
- return next.needsNested();
- }
-
override void accept(Visitor v)
{
v.visit(this);
return result;
}
- override uint alignsize()
- {
- // A DArray consists of two ptr-sized values, so align it on pointer size
- // boundary
- return target.ptrsize;
- }
-
- override bool isString()
- {
- TY nty = next.toBasetype().ty;
- return nty.isSomeChar;
- }
-
- override bool isBoolean()
- {
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return result;
}
- override bool isBoolean()
- {
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return result;
}
- override bool isScalar()
- {
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return result;
}
- override uint alignsize()
- {
- return target.ptrsize;
- }
-
- override bool isBoolean()
- {
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return "struct";
}
- override uint alignsize()
- {
- sym.size(Loc.initial); // give error for forward references
- return sym.alignsize;
- }
-
override TypeStruct syntaxCopy()
{
return this;
}
- override structalign_t alignment()
- {
- if (sym.alignment.isUnknown())
- sym.size(sym.loc);
- return sym.alignment;
- }
-
- override bool isBoolean()
- {
- return false;
- }
-
- override bool needsDestruction()
- {
- return sym.dtor !is null;
- }
-
- override bool needsCopyOrPostblit()
- {
- return sym.hasCopyCtor || sym.postblit;
- }
-
- override bool needsNested()
- {
- if (inuse) return false; // circular type, error instead of crashing
-
- inuse = true;
- scope(exit) inuse = false;
-
- if (sym.isNested())
- return true;
-
- for (size_t i = 0; i < sym.fields.length; i++)
- {
- VarDeclaration v = sym.fields[i];
- if (!v.isDataseg() && v.type.needsNested())
- return true;
- }
- return false;
- }
-
- override bool hasVoidInitPointers()
- {
- sym.size(Loc.initial); // give error for forward references
- sym.determineTypeProperties();
- return sym.hasVoidInitPointers;
- }
-
- override bool hasUnsafeBitpatterns()
- {
- sym.size(Loc.initial); // give error for forward references
- sym.determineTypeProperties();
- return sym.hasUnsafeBitpatterns;
- }
-
- override bool hasInvariant()
- {
- sym.size(Loc.initial); // give error for forward references
- sym.determineTypeProperties();
- return sym.hasInvariant() || sym.hasFieldWithInvariant;
- }
-
- override MOD deduceWild(Type t, bool isRef)
- {
- if (ty == t.ty && sym == (cast(TypeStruct)t).sym)
- return Type.deduceWild(t, isRef);
-
- ubyte wm = 0;
-
- if (t.hasWild() && sym.aliasthis && !(att & AliasThisRec.tracing))
- {
- if (auto ato = aliasthisOf(this))
- {
- att = cast(AliasThisRec)(att | AliasThisRec.tracing);
- wm = ato.deduceWild(t, isRef);
- att = cast(AliasThisRec)(att & ~AliasThisRec.tracing);
- }
- }
-
- return wm;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return this;
}
- Type memType()
- {
- return sym.getMemtype(Loc.initial);
- }
-
- override uint alignsize()
- {
- Type t = memType();
- if (t.ty == Terror)
- return 4;
- return t.alignsize();
- }
-
- override bool isIntegral()
- {
- return memType().isIntegral();
- }
-
- override bool isFloating()
- {
- return memType().isFloating();
- }
-
- override bool isReal()
- {
- return memType().isReal();
- }
-
- override bool isImaginary()
- {
- return memType().isImaginary();
- }
-
- override bool isComplex()
- {
- return memType().isComplex();
- }
-
- override bool isScalar()
- {
- return memType().isScalar();
- }
-
- override bool isUnsigned()
- {
- return memType().isUnsigned();
- }
-
- override bool isBoolean()
- {
- return memType().isBoolean();
- }
-
- override bool isString()
- {
- return memType().isString();
- }
-
- override bool needsDestruction()
- {
- return memType().needsDestruction();
- }
-
- override bool needsCopyOrPostblit()
- {
- return memType().needsCopyOrPostblit();
- }
-
- override bool needsNested()
- {
- return memType().needsNested();
- }
-
- extern (D) Type toBasetype2()
- {
- if (!sym.members && !sym.memtype)
- return this;
- auto tb = sym.getMemtype(Loc.initial).toBasetype();
- return tb.castMod(mod); // retain modifier bits from 'this'
- }
-
- override bool hasVoidInitPointers()
- {
- return memType().hasVoidInitPointers();
- }
-
- override bool hasUnsafeBitpatterns()
- {
- return memType().hasUnsafeBitpatterns();
- }
-
- override bool hasInvariant()
- {
- return memType().hasInvariant();
- }
-
- override Type nextOf()
- {
- return memType().nextOf();
- }
-
override void accept(Visitor v)
{
v.visit(this);
return sym;
}
- override MOD deduceWild(Type t, bool isRef)
- {
- ClassDeclaration cd = t.isClassHandle();
- if (cd && (sym == cd || cd.isBaseOf(sym, null)))
- return Type.deduceWild(t, isRef);
-
- ubyte wm = 0;
-
- if (t.hasWild() && sym.aliasthis && !(att & AliasThisRec.tracing))
- {
- if (auto ato = aliasthisOf(this))
- {
- att = cast(AliasThisRec)(att | AliasThisRec.tracing);
- wm = ato.deduceWild(t, isRef);
- att = cast(AliasThisRec)(att & ~AliasThisRec.tracing);
- }
- }
-
- return wm;
- }
-
override bool isScopeClass()
{
return sym.stack;
}
- override bool isBoolean()
- {
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return t;
}
- override bool equals(const RootObject o) const
- {
- Type t = cast(Type)o;
- //printf("TypeTuple::equals(%s, %s)\n", toChars(), t.toChars());
- if (this == t)
- return true;
- if (auto tt = t.isTypeTuple())
- {
- if (arguments.length == tt.arguments.length)
- {
- for (size_t i = 0; i < tt.arguments.length; i++)
- {
- const Parameter arg1 = (*arguments)[i];
- Parameter arg2 = (*tt.arguments)[i];
- if (!arg1.type.equals(arg2.type))
- return false;
- }
- return true;
- }
- }
- return false;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return this;
}
- override bool isBoolean()
- {
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return this;
}
- override bool isBoolean()
- {
- return true; // bottom type can be implicitly converted to any other type
- }
-
- override uint alignsize()
- {
- return 0;
- }
-
override void accept(Visitor v)
{
v.visit(this);
return new Parameter(loc, storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null, userAttribDecl ? userAttribDecl.syntaxCopy(null) : null);
}
- /****************************************************
- * Determine if parameter is a lazy array of delegates.
- * If so, return the return type of those delegates.
- * If not, return NULL.
- *
- * Returns T if the type is one of the following forms:
- * T delegate()[]
- * T delegate()[dim]
- */
- Type isLazyArray()
- {
- Type tb = type.toBasetype();
- if (tb.isStaticOrDynamicArray())
- {
- Type tel = (cast(TypeArray)tb).next.toBasetype();
- if (auto td = tel.isTypeDelegate())
- {
- TypeFunction tf = td.next.toTypeFunction();
- if (tf.parameterList.varargs == VarArg.none && tf.parameterList.length == 0)
- {
- return tf.next; // return type of delegate
- }
- }
- }
- return null;
- }
-
/// Returns: Whether the function parameter is lazy
bool isLazy() const @safe pure nothrow @nogc
{
if (eparam is null)
return 0;
- Type t = eparam.type.toBasetype();
- if (auto tu = t.isTypeTuple())
+ if (auto tu = eparam.type.isTypeTuple())
{
// Check for empty tuples
if (tu.arguments is null)
dg(trustToString(trustAttrib));
}
-/**
- * If the type is a class or struct, returns the symbol for it,
- * else null.
- */
-AggregateDeclaration isAggregate(Type t)
-{
- t = t.toBasetype();
- if (auto tc = t.isTypeClass())
- return tc.sym;
- if (auto ts = t.isTypeStruct())
- return ts.sym;
- return null;
-}
-
-/***************************************************
- * Determine if type t can be indexed or sliced given that it is not an
- * aggregate with operator overloads.
- * Params:
- * t = type to check
- * Returns:
- * true if an expression of type t can be e1 in an array expression
- */
-bool isIndexableNonAggregate(Type t)
-{
- t = t.toBasetype();
- return (t.ty == Tpointer || t.isStaticOrDynamicArray() || t.ty == Taarray ||
- t.ty == Ttuple || t.ty == Tvector);
-}
-
/***************************************
* Computes how a parameter may be returned.
* Shrinking the representation is necessary because STC is so wide
namespace dmd
{
+ void Type_init();
Type *typeSemantic(Type *t, Loc loc, Scope *sc);
Type *merge(Type *type);
Expression *defaultInitLiteral(Type *t, Loc loc);
+ Type *toBasetype(Type *type);
+ Type *nextOf(Type* type);
+ Type *baseElemOf(Type* type);
}
enum class TY : uint8_t
static Type *basic[(int)TY::TMAX];
+ static void _init() { return dmd::Type_init(); }
+
virtual const char *kind();
Type *copy() const;
virtual Type *syntaxCopy();
- bool equals(const RootObject * const o) const override;
+ bool equals(const Type * const t) const;
// kludge for template.isType()
DYNCAST dyncast() const override final { return DYNCAST_TYPE; }
size_t getUniqueID() const;
const char *toChars() const override;
char *toPrettyChars(bool QualifyTypes = false);
- static void _init();
- virtual unsigned alignsize();
void modToBuffer(OutBuffer& buf) const;
char *modToChars() const;
- virtual bool isIntegral();
- virtual bool isFloating(); // real, imaginary, or complex
- virtual bool isReal();
- virtual bool isImaginary();
- virtual bool isComplex();
- virtual bool isScalar();
- virtual bool isUnsigned();
virtual bool isScopeClass();
- virtual bool isString();
- virtual bool isBoolean();
+
bool isConst() const { return (mod & MODconst) != 0; }
bool isImmutable() const { return (mod & MODimmutable) != 0; }
bool isMutable() const { return (mod & (MODconst | MODimmutable | MODwild)) == 0; }
bool isSharedWild() const { return (mod & (MODshared | MODwild)) == (MODshared | MODwild); }
bool isNaked() const { return mod == 0; }
Type *nullAttributes() const;
- bool hasDeprecatedAliasThis();
- virtual Type *makeConst();
- virtual Type *makeImmutable();
- virtual Type *makeShared();
- virtual Type *makeSharedConst();
- virtual Type *makeWild();
- virtual Type *makeWildConst();
- virtual Type *makeSharedWild();
- virtual Type *makeSharedWildConst();
- virtual Type *makeMutable();
- Type *toBasetype();
- virtual unsigned char deduceWild(Type *t, bool isRef);
+
+ Type *toBasetype() { return dmd::toBasetype(this); }
+ Type *nextOf() { return dmd::nextOf(this); }
+ Type *baseElemOf() { return dmd::baseElemOf(this); }
virtual ClassDeclaration *isClassHandle();
- virtual structalign_t alignment();
virtual int hasWild() const;
- virtual bool hasVoidInitPointers();
- virtual bool hasUnsafeBitpatterns();
- virtual bool hasInvariant();
- virtual Type *nextOf();
- Type *baseElemOf();
- virtual bool needsDestruction();
- virtual bool needsCopyOrPostblit();
- virtual bool needsNested();
TypeFunction *toTypeFunction();
Type *next;
int hasWild() const override final;
- Type *nextOf() override final;
- Type *makeConst() override final;
- Type *makeImmutable() override final;
- Type *makeShared() override final;
- Type *makeSharedConst() override final;
- Type *makeWild() override final;
- Type *makeWildConst() override final;
- Type *makeSharedWild() override final;
- Type *makeSharedWildConst() override final;
- Type *makeMutable() override final;
- unsigned char deduceWild(Type *t, bool isRef) override final;
- void transitive();
void accept(Visitor *v) override { v->visit(this); }
};
const char *kind() override;
TypeBasic *syntaxCopy() override;
- unsigned alignsize() override;
- bool isIntegral() override;
- bool isFloating() override;
- bool isReal() override;
- bool isImaginary() override;
- bool isComplex() override;
- bool isScalar() override;
- bool isUnsigned() override;
// For eliminating dynamic_cast
TypeBasic *isTypeBasic() override;
static TypeVector *create(Type *basetype);
const char *kind() override;
TypeVector *syntaxCopy() override;
- unsigned alignsize() override;
- bool isIntegral() override;
- bool isFloating() override;
- bool isScalar() override;
- bool isUnsigned() override;
- bool isBoolean() override;
TypeBasic *elementType();
void accept(Visitor *v) override { v->visit(this); }
const char *kind() override;
TypeSArray *syntaxCopy() override;
bool isIncomplete();
- unsigned alignsize() override;
- bool isString() override;
- structalign_t alignment() override;
- bool hasUnsafeBitpatterns() override;
- bool hasVoidInitPointers() override;
- bool hasInvariant() override;
- bool needsDestruction() override;
- bool needsCopyOrPostblit() override;
- bool needsNested() override;
void accept(Visitor *v) override { v->visit(this); }
};
public:
const char *kind() override;
TypeDArray *syntaxCopy() override;
- unsigned alignsize() override;
- bool isString() override;
- bool isBoolean() override;
void accept(Visitor *v) override { v->visit(this); }
};
static TypeAArray *create(Type *t, Type *index);
const char *kind() override;
TypeAArray *syntaxCopy() override;
- bool isBoolean() override;
void accept(Visitor *v) override { v->visit(this); }
};
static TypePointer *create(Type *t);
const char *kind() override;
TypePointer *syntaxCopy() override;
- bool isScalar() override;
void accept(Visitor *v) override { v->visit(this); }
};
static Parameter *create(Loc loc, StorageClass storageClass, Type *type, Identifier *ident,
Expression *defaultArg, UserAttributeDeclaration *userAttribDecl);
Parameter *syntaxCopy();
- Type *isLazyArray();
bool isLazy() const;
bool isReference() const;
// kludge for template.isType()
static TypeDelegate *create(TypeFunction *t);
const char *kind() override;
TypeDelegate *syntaxCopy() override;
- unsigned alignsize() override;
- bool isBoolean() override;
void accept(Visitor *v) override { v->visit(this); }
};
static TypeStruct *create(StructDeclaration *sym);
const char *kind() override;
- unsigned alignsize() override;
TypeStruct *syntaxCopy() override;
- structalign_t alignment() override;
- bool isBoolean() override;
- bool needsDestruction() override;
- bool needsCopyOrPostblit() override;
- bool needsNested() override;
- bool hasVoidInitPointers() override;
- bool hasUnsafeBitpatterns() override;
- bool hasInvariant() override;
- unsigned char deduceWild(Type *t, bool isRef) override;
void accept(Visitor *v) override { v->visit(this); }
};
const char *kind() override;
TypeEnum *syntaxCopy() override;
- unsigned alignsize() override;
- Type *memType(Loc loc);
- bool isIntegral() override;
- bool isFloating() override;
- bool isReal() override;
- bool isImaginary() override;
- bool isComplex() override;
- bool isScalar() override;
- bool isUnsigned() override;
- bool isBoolean() override;
- bool isString() override;
- bool needsDestruction() override;
- bool needsCopyOrPostblit() override;
- bool needsNested() override;
- bool hasVoidInitPointers() override;
- bool hasUnsafeBitpatterns() override;
- bool hasInvariant() override;
- Type *nextOf() override;
void accept(Visitor *v) override { v->visit(this); }
};
const char *kind() override;
TypeClass *syntaxCopy() override;
ClassDeclaration *isClassHandle() override;
- unsigned char deduceWild(Type *t, bool isRef) override;
bool isScopeClass() override;
- bool isBoolean() override;
void accept(Visitor *v) override { v->visit(this); }
};
static TypeTuple *create(Type *t1, Type *t2);
const char *kind() override;
TypeTuple *syntaxCopy() override;
- bool equals(const RootObject * const o) const override;
void accept(Visitor *v) override { v->visit(this); }
};
const char *kind() override;
TypeNull *syntaxCopy() override;
- bool isBoolean() override;
void accept(Visitor *v) override { v->visit(this); }
};
public:
const char *kind() override;
TypeNoreturn *syntaxCopy() override;
- bool isBoolean() override;
- unsigned alignsize() override;
void accept(Visitor *v) override { v->visit(this); }
};
uinteger_t size(Type *type, Loc loc);
MATCH implicitConvTo(Type* from, Type* to);
MATCH constConv(Type* from, Type* to);
+ bool hasUnsafeBitpatterns(Type* type);
+ bool hasInvariant(Type* type);
+ bool hasVoidInitPointers(Type* type);
+ void transitive(TypeNext* type);
+ structalign_t alignment(Type* type);
+ Type* memType(TypeEnum* type);
+ unsigned alignsize(Type* type);
+ Type *makeConst(Type* type);
+ Type* makeMutable(Type* type);
+ Type* makeImmutable(Type* type);
+ Type* makeShared(Type* type);
+ Type* makeSharedConst(Type* type);
+ Type *makeWild(Type* type);
+ Type *makeWildConst(Type* type);
+ Type *makeSharedWild(Type* type);
+ Type *makeSharedWildConst(Type* type);
+ Type *isLazyArray(Parameter* param);
+ unsigned char deduceWild(Type* type, Type* t, bool isRef);
+ bool isIntegral(Type* type);
+ bool isFloating(Type* type);
+ bool isScalar(Type* type);
+ bool isReal(Type* type);
+ bool isImaginary(Type* type);
+ bool isComplex(Type* type);
+ bool isString(Type* type);
+ bool isBoolean(Type* type);
+ bool isUnsigned(Type* type);
+ bool needsNested(Type* type);
+ bool needsDestruction(Type* type);
+ bool needsCopyOrPostblit(Type* type);
+ bool hasDeprecatedAliasThis(Type* type);
}
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
+import dmd.funcsem : isRootTraitsCompilesScope;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.semantic2;
import dmd.semantic3;
import dmd.tokens;
-import dmd.typesem : unqualify;
+import dmd.typesem : unqualify, toBasetype;
import dmd.visitor;
import dmd.visitor.postorder;
err = true;
return true;
}
- if (f.setGC(e.loc, msg))
+ if (sc.setGC(f, e.loc, msg))
{
error(e.loc, "%s causes a GC allocation in `@nogc` %s `%s`", msg, f.kind(), f.toChars());
err = true;
{
if (e.placement)
return; // placement new doesn't use the GC
- if (e.member && !e.member.isNogc() && f.setGC(e.loc, null))
+ if (e.member && e.member !is f && !e.member.isNogc() && sc.setGC(f, e.loc, null))
{
// @nogc-ness is already checked in NewExp::semantic
return;
* so mark it as not nogc (not no-how).
*
* Params:
+ * sc = scope that the GC action is in
* fd = function
* loc = location of GC action
* fmt = format string for error message. Must include "%s `%s`" for the function kind and name.
* Returns:
* true if function is marked as @nogc, meaning a user error occurred
*/
-extern (D) bool setGC(FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject[] args...)
+extern (D) bool setGC(Scope* sc, FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject[] args...)
{
//printf("setGC() %s\n", toChars());
if (fd.nogcInprocess && fd.semanticRun < PASS.semantic3 && fd._scope)
fd.semantic3(fd._scope);
}
+ if (sc && isRootTraitsCompilesScope(sc)) // __traits(compiles, x)
+ {
+ if (sc.func.isNogcBypassingInference())
+ {
+ // Message wil be gagged, but still call error() to update global.errors and for
+ // -verrors=spec
+ string action = AttributeViolation(loc, fmt, args).action;
+ .error(loc, "%.*s is not allowed in a `@nogc` function", action.fTuple.expand);
+ return true;
+ }
+ return false;
+ }
+
if (fd.nogcInprocess)
{
fd.nogcInprocess = false;
fd.type.toTypeFunction().isNogc = false;
if (fd.fes)
- fd.fes.func.setGC(Loc.init, null, null);
+ sc.setGC(fd.fes.func, Loc.init, null, null);
}
else if (fd.isNogc())
return true;
/**************************************
* The function calls non-`@nogc` function f, mark it as not nogc.
* Params:
- * fd = function doin the call
+ * sc = scope that the GC action is in
+ * fd = function doing the call
* f = function being called
* Returns:
* true if function is marked as @nogc, meaning a user error occurred
*/
-extern (D) bool setGCCall(FuncDeclaration fd, FuncDeclaration f)
+extern (D) bool setGCCall(Scope* sc, FuncDeclaration fd, FuncDeclaration f)
{
- return fd.setGC(fd.loc, null, f);
+ return sc.setGC(fd, fd.loc, null, f);
}
- bool isNogc(FuncDeclaration fd)
+bool isNogc(FuncDeclaration fd)
{
//printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess));
if (fd.nogcInprocess)
- fd.setGC(fd.loc, null);
+ setGC(null, fd, fd.loc, null);
return fd.type.toTypeFunction().isNogc;
}
import dmd.arraytypes;
import dmd.astenums;
import dmd.attrib;
-import dmd.attribsem;
+import dmd.attribsem : foreachUda;
import dmd.cond;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
-import dmd.dmodule;
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
/* The explicit cast is necessary for interfaces
* https://issues.dlang.org/show_bug.cgi?id=4088
*/
- Type to = ClassDeclaration.object.getType();
+ Type to = dmd.dsymbolsem.getType(ClassDeclaration.object);
if (cd1.isInterfaceDeclaration())
e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf());
if (cd2.isInterfaceDeclaration())
{
if (!ve.var.isReference() && !ve.var.isImportedSymbol())
{
- ret = new SymOffExp(e.loc, ve.var, 0, ve.hasOverloads);
+ bool hasOverloads = ve.hasOverloads;
+ if (auto v = ve.var.isVarDeclaration())
+ {
+ if (v.needThis())
+ {
+ auto t = v.isThis();
+ assert(t);
+ .error(e.loc, "taking the address of non-static variable `%s` requires an instance of `%s`", v.toChars(), t.toChars());
+ ret = ErrorExp.get();
+ return;
+ }
+ hasOverloads = false;
+ }
+ ret = new SymOffExp(e.loc, ve.var, 0, hasOverloads);
ret.type = e.type;
return;
}
}
else
{
- version (IN_GCC)
- error("attribute `scope` cannot be applied with `in`, use `-fpreview=in` instead");
- else
- error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead");
+ error("attribute `scope` cannot be applied with `in`, use `-%spreview=in` instead", compileEnv.switchPrefix.ptr);
}
return orig;
}
}
else
{
- version (IN_GCC)
- error("attribute `in` cannot be added after `scope`: remove `scope` and use `-fpreview=in`");
- else
- error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`");
+ error("attribute `in` cannot be added after `scope`: remove `scope` and use `-%spreview=in`", compileEnv.switchPrefix.ptr);
}
return orig;
}
AST.Expression constraint = tpl ? parseConstraint() : null;
AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // ReturnType -> auto
- tf = tf.addSTC(stc);
+ tf = AST.addSTC(tf, stc);
auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf);
AST.Dsymbol s = parseContracts(f, !!tpl);
auto parameters = new AST.Parameters();
VarArg varargs = VarArg.none;
STC varargsStc;
+ bool hasAutoRefParam = false;
// Attributes allowed for ...
enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope | STC.returnRef;
}
L2:
storageClass = appendStorageClass(storageClass, stc);
+ hasAutoRefParam |= (storageClass & (STC.auto_ | STC.ref_)) == (STC.auto_ | STC.ref_);
continue;
default:
}
else
{
- at = parseType(&ai, null, &loc);
+ if (tpl && !*tpl && hasAutoRefParam)
+ *tpl = new AST.TemplateParameters();
+ at = parseType(&ai, &loc);
}
ae = null;
if (token.value == TOK.assign) // = defaultArg
{
Ltype:
// Type identifier
- type = parseType(&ident, null);
+ type = parseType(&ident);
if (type == AST.Type.terror)
{
type = null;
/* Parse a type and optional identifier
* Params:
- * pident = set to Identifier if there is one, null if not
- * ptpl = if !null, then set to TemplateParameterList
- * pdeclLoc = if !null, then set to location of the declarator
+ * pident = set to Identifier if there is one, null if not
+ * pdeclLoc = if !null, then set to location of the declarator
+ * stc = Storage Class
*/
- AST.Type parseType(Identifier* pident = null, AST.TemplateParameters** ptpl = null, Loc* pdeclLoc = null)
+ AST.Type parseType(STC stc)
+ {
+ return parseType(null, null, stc);
+ }
+
+ ///Ditto
+ AST.Type parseType(Identifier* pident = null, Loc* pdeclLoc = null,
+ STC stc = STC.none)
{
/* Take care of the storage class prefixes that
* serve as type attributes:
* shared inout type
* shared inout const type
*/
- STC stc = STC.none;
while (1)
{
switch (token.value)
if (pdeclLoc)
*pdeclLoc = token.loc;
int alt = 0;
- t = parseDeclarator(t, alt, pident, ptpl);
+ t = parseDeclarator(t, alt, pident, null);
checkCstyleTypeSyntax(typeLoc, t, alt, pident ? *pident : null);
- t = t.addSTC(stc);
+ t = AST.addSTC(t, stc);
return t;
}
// const(type)
nextToken();
check(TOK.leftParenthesis);
- t = parseType().addSTC(STC.const_);
+ t = parseType(STC.const_);
check(TOK.rightParenthesis);
break;
// immutable(type)
nextToken();
check(TOK.leftParenthesis);
- t = parseType().addSTC(STC.immutable_);
+ t = parseType(STC.immutable_);
check(TOK.rightParenthesis);
break;
// shared(type)
nextToken();
check(TOK.leftParenthesis);
- t = parseType().addSTC(STC.shared_);
+ t = parseType(STC.shared_);
check(TOK.rightParenthesis);
break;
// wild(type)
nextToken();
check(TOK.leftParenthesis);
- t = parseType().addSTC(STC.wild);
+ t = parseType(STC.wild);
check(TOK.rightParenthesis);
break;
if (save == TOK.function_)
error("`const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions");
else
- tf = cast(AST.TypeFunction)tf.addSTC(stc);
+ tf = cast(AST.TypeFunction)AST.addSTC(tf, stc);
}
t = save == TOK.delegate_ ? new AST.TypeDelegate(tf) : new AST.TypePointer(tf); // pointer to function
continue;
STC stc = parsePostfix(storageClass, pudas);
AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, stc);
- tf = tf.addSTC(stc);
+ tf = AST.addSTC(tf, stc);
if (pdisable)
*pdisable = stc & STC.disable ? true : false;
}
auto tf = new AST.TypeFunction(parameterList, tret, linkage, stc);
- tf = cast(AST.TypeFunction)tf.addSTC(stc);
+ tf = cast(AST.TypeFunction)AST.addSTC(tf, stc);
auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save, null, null, stc & STC.auto_);
if (token.value == TOK.goesTo)
error("missing `do { ... }` after `in` or `out`");
const returnloc = token.loc;
nextToken();
- if (f.isCtorDeclaration)
- f.fbody = new AST.ExpStatement(returnloc, parseExpression());
- else
- f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
+ f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
f.endloc = token.loc;
check(TOK.semicolon);
break;
{
if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.isValid)
{
- eSink.warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
+ eSink.error(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
}
}
error("expression expected, not `%s`", token.toChars());
Lerr:
// Anything for e, as long as it's not NULL
- e = AST.ErrorExp.get();
+ e = new AST.ErrorExp();
nextToken();
break;
}
else
{
AST.Type t = parseType(); // cast( type )
- t = t.addSTC(AST.ModToStc(m)); // cast( const type )
+ t = AST.addSTC(t, AST.ModToStc(m)); // cast( const type )
check(TOK.rightParenthesis);
e = parseUnaryExp();
e = new AST.CastExp(loc, e, t);
STC stc = parseTypeCtor();
AST.Type t = parseBasicType();
- t = t.addSTC(stc);
+ t = AST.addSTC(t, stc);
if (stc == 0 && token.value == TOK.dot)
{
{
error("identifier expected following `%s.`, not `%s`",
t.toChars(), token.toChars());
- return AST.ErrorExp.get();
+ return new AST.ErrorExp();
}
e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
nextToken();
{
error("identifier or new keyword expected following `(...)`.");
nextToken();
- return AST.ErrorExp.get();
+ return new AST.ErrorExp();
}
e = new AST.TypeExp(loc, t);
e.parens = true;
const stc = parseTypeCtor();
auto t = parseBasicType(true);
t = parseTypeSuffixes(t);
- t = t.addSTC(stc);
+ t = AST.addSTC(t, stc);
if (t.ty == Taarray)
{
AST.TypeAArray taa = cast(AST.TypeAArray)t;
module dmd.pragmasem;
import core.stdc.stdio;
+import core.stdc.string;
import dmd.astenums;
import dmd.arraytypes;
*/
void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc)
{
- import dmd.aggregate;
import dmd.common.outbuffer;
import dmd.dmodule;
import dmd.dsymbolsem;
import dmd.identifier;
- import dmd.mangle : isValidMangling;
import dmd.root.rmem;
- import dmd.root.utf;
import dmd.target;
import dmd.utils;
- StringExp verifyMangleString(ref Expression e)
- {
- auto se = semanticString(sc, e, "mangled name");
- if (!se)
- return null;
- e = se;
- if (!se.len)
- {
- .error(pd.loc, "%s `%s` - zero-length string not allowed for mangled name", pd.kind, pd.toPrettyChars);
- return null;
- }
- if (se.sz != 1)
- {
- .error(pd.loc, "%s `%s` - mangled name characters can only be of type `char`", pd.kind, pd.toPrettyChars);
- return null;
- }
- version (all)
- {
- import dmd.common.charactertables;
-
- /* Note: D language specification should not have any assumption about backend
- * implementation. Ideally pragma(mangle) can accept a string of any content.
- *
- * Therefore, this validation is compiler implementation specific.
- */
- auto slice = se.peekString();
- for (size_t i = 0; i < se.len;)
- {
- dchar c = slice[i];
- if (c < 0x80)
- {
- if (c.isValidMangling)
- {
- ++i;
- continue;
- }
- else
- {
- .error(pd.loc, "%s `%s` char 0x%02x not allowed in mangled name", pd.kind, pd.toPrettyChars, c);
- break;
- }
- }
- if (const msg = utf_decodeChar(slice, i, c))
- {
- .error(pd.loc, "%s `%s` %.*s", pd.kind, pd.toPrettyChars, cast(int)msg.length, msg.ptr);
- break;
- }
- if (!isAnyIdentifierCharacter(c))
- {
- .error(pd.loc, "%s `%s` char `0x%04x` not allowed in mangled name", pd.kind, pd.toPrettyChars, c);
- break;
- }
- }
- }
- return se;
- }
void declarations()
{
if (!pd.decl)
}
s.dsymbolSemantic(sc2);
- if (pd.ident != Id.mangle)
- continue;
- assert(pd.args);
- if (auto ad = s.isAggregateDeclaration())
- {
- Expression e = (*pd.args)[0];
- sc2 = sc2.startCTFE();
- e = e.expressionSemantic(sc);
- e = resolveProperties(sc2, e);
- sc2 = sc2.endCTFE();
- AggregateDeclaration agg;
- if (auto tc = e.type.isTypeClass())
- agg = tc.sym;
- else if (auto ts = e.type.isTypeStruct())
- agg = ts.sym;
- ad.pMangleOverride = new MangleOverride;
- void setString(ref Expression e)
- {
- if (auto se = verifyMangleString(e))
- {
- const name = (cast(const(char)[])se.peekData()).xarraydup;
- ad.pMangleOverride.id = Identifier.idPool(name);
- e = se;
- }
- else
- error(e.loc, "must be a string");
- }
- if (agg)
- {
- ad.pMangleOverride.agg = agg;
- if (pd.args.length == 2)
- {
- setString((*pd.args)[1]);
- }
- else
- ad.pMangleOverride.id = agg.ident;
- }
- else
- setString((*pd.args)[0]);
- }
- else if (auto td = s.isTemplateDeclaration())
- {
- .error(pd.loc, "%s `%s` cannot apply to a template declaration", pd.kind, pd.toPrettyChars);
- errorSupplemental(pd.loc, "use `template Class(Args...){ pragma(mangle, \"other_name\") class Class {} }`");
- }
- else if (auto se = verifyMangleString((*pd.args)[0]))
- {
- const name = (cast(const(char)[])se.peekData()).xarraydup;
- uint cnt = setMangleOverride(s, name);
- if (cnt > 1)
- .error(pd.loc, "%s `%s` can only apply to a single declaration", pd.kind, pd.toPrettyChars);
- }
}
}
}
else if (pd.ident == Id.mangle)
{
- if (!pd.args)
- pd.args = new Expressions();
- if (pd.args.length == 0 || pd.args.length > 2)
- {
- .error(pd.loc, pd.args.length == 0 ? "%s `%s` - string expected for mangled name"
- : "%s `%s` expected 1 or 2 arguments", pd.kind, pd.toPrettyChars);
- pd.args.setDim(1);
- (*pd.args)[0] = ErrorExp.get(); // error recovery
- }
- return declarations();
+ Scope* sc2 = pd.newScope(sc);
+ pragmaMangleSemantic(pd.loc, sc2, pd.args, pd.decl);
+ if (sc2 != sc)
+ sc2.pop();
+ return;
}
else if (pd.ident == Id.crt_constructor || pd.ident == Id.crt_destructor)
{
{
auto es = ps._body ? ps._body.isExpStatement() : null;
auto de = es ? es.exp.isDeclarationExp() : null;
- if (!de)
- {
- error(ps.loc, "`pragma(mangle)` must be attached to a declaration");
+ Dsymbols decls = de ? Dsymbols(de.declaration) : Dsymbols();
+ if (!pragmaMangleSemantic(ps.loc, sc, ps.args, decls.length ? &decls : null))
return false;
- }
- const se = ps.args && (*ps.args).length == 1 ? semanticString(sc, (*ps.args)[0], "pragma mangle argument") : null;
- if (!se)
- {
- error(ps.loc, "`pragma(mangle)` takes a single argument that must be a string literal");
- return false;
- }
- const cnt = setMangleOverride(de.declaration, cast(const(char)[])se.peekData());
- if (cnt != 1)
- assert(0);
}
else if (!global.params.ignoreUnsupportedPragmas)
{
return PINLINE.never;
}
-/**
- * Apply pragma mangle to FuncDeclarations and VarDeclarations
- * under `s`, poking through attribute declarations such as
- * `extern(C)` but not through aggregates or function bodies.
- *
- * Params:
- * s = symbol to apply
- * sym = overriding symbol name
- */
-private uint setMangleOverride(Dsymbol s, const(char)[] sym)
-{
- if (s.isFuncDeclaration() || s.isVarDeclaration())
- {
- s.isDeclaration().mangleOverride = sym;
- return 1;
- }
-
- if (auto ad = s.isAttribDeclaration())
- {
- uint nestedCount = 0;
-
- ad.include(null).foreachDsymbol( (s) { nestedCount += setMangleOverride(s, sym); } );
-
- return nestedCount;
- }
- return 0;
-}
-
/***********************************************************
* Evaluate and print a `pragma(msg, args)`
*
}
return true;
}
+
+/***********************************************************
+ * Evaluate `pragma(mangle)` and store the mangled string in `decls`
+ * This accepts any of the following variants.
+ *
+ * pragma(mangle, StringExp) AggregateDeclaration
+ * pragma(mangle, StringExp) VarDeclaration
+ * pragma(mangle, StringExp) FuncDeclaration
+ * pragma(mangle, TypeExp) AggregateDeclaration
+ * pragma(mangle, TypeExp, StringExp) AggregateDeclaration
+ * pragma(mangle, StringExp, TypeExp) AggregateDeclaration
+ *
+ * Params:
+ * loc = location for error messages
+ * sc = scope for argument interpretation
+ * args = pragma arguments
+ * decls = declarations to set mangled string to
+ * Returns:
+ * `true` on success
+ */
+private bool pragmaMangleSemantic(Loc loc, Scope* sc, Expressions* args, Dsymbols* decls)
+{
+ import dmd.root.rmem;
+
+ StringExp verifyMangleString(ref Expression e)
+ {
+ import dmd.mangle : isValidMangling;
+ import dmd.root.utf : utf_decodeChar;
+ auto se = semanticString(sc, e, "pragma mangle argument");
+ if (!se)
+ return null;
+ e = se;
+ if (!se.len)
+ {
+ error(loc, "`pragma(mangle)` zero-length string not allowed for mangled name");
+ return null;
+ }
+ if (se.sz != 1)
+ {
+ error(loc, "`pragma(mangle)` mangled name characters can only be of type `char`");
+ return null;
+ }
+ auto slice = se.toStringz();
+ if (strlen(slice.ptr) != se.len)
+ .error(loc, "pragma `mangle` null character not allowed in mangled name");
+ mem.xfree(cast(void*)slice.ptr);
+ return se;
+ }
+
+ bool applyPragmaMangle(Dsymbols* decls, ref uint count, ref bool ignored)
+ {
+ if (decls is null)
+ return true;
+
+ foreach (s; (*decls)[])
+ {
+ import dmd.aggregate;
+ import dmd.common.outbuffer;
+ import dmd.dsymbolsem : dsymbolSemantic;
+ import dmd.hdrgen : arrayObjectsToBuffer;
+ import dmd.identifier : Identifier;
+
+ s.dsymbolSemantic(sc);
+
+ if (auto ad = s.isAggregateDeclaration())
+ {
+ /* pragma(mangle) AggregateDeclaration;
+ For aggregates there may be one or two AssignExpressions:
+ - one of which must evaluate at compile time to a string literal
+ - one which must evaluate to a symbol
+ The spec does not specify which order these should be in, so we
+ allow any of:
+ pragma(mangle, "name") struct { ... }
+ pragma(mangle, T) struct { ... }
+ pragma(mangle, "name", T) struct { ... }
+ pragma(mangle, T, "name") struct { ... }
+ */
+ AggregateDeclaration symbol;
+ StringExp literal;
+
+ foreach (ref Expression e; (*args)[])
+ {
+ sc = sc.startCTFE();
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+
+ bool expectedString()
+ {
+ error(e.loc, "`string` expected for pragma mangle argument, not `%s` of type `%s`",
+ e.toChars(), e.type.toChars());
+ return false;
+ }
+
+ bool expectedType()
+ {
+ error(e.loc, "`class` or `struct` type expected for pragma mangle argument, not `%s` of type `%s`",
+ e.toChars(), e.type.toChars());
+ return false;
+ }
+
+ // Validate arguments to pragma(mangle)
+ if (e.isTypeExp())
+ {
+ /* Check for and reject:
+ pragma(mangle, TypeExp, TypeExp)
+ where the first TypeExp already resolved to a symbol. */
+ if (symbol)
+ return expectedString();
+
+ /* Type must be a class or struct symbol. */
+ if (auto tc = e.type.isTypeClass())
+ symbol = tc.sym;
+ else if (auto ts = e.type.isTypeStruct())
+ symbol = ts.sym;
+ else
+ return expectedType();
+ }
+ else
+ {
+ /* Check for and reject:
+ pragma(mangle, StringExp, AssignExpression)
+ where AssignExpression did not resolve to a symbol. */
+ if (literal)
+ return expectedType();
+
+ /* Must evaluate to a compile time string literal. */
+ auto se = verifyMangleString(e);
+ if (se is null)
+ return false;
+ literal = se;
+ }
+ }
+
+ ad.pMangleOverride = new MangleOverride;
+
+ if (symbol)
+ {
+ ad.pMangleOverride.agg = symbol;
+ /* The identifier of the symbol is used when no string is supplied. */
+ if (literal is null)
+ {
+ ad.pMangleOverride.id = symbol.ident;
+ count += 1;
+ continue;
+ }
+ }
+
+ assert(literal);
+ const name = literal.peekString().xarraydup;
+ ad.pMangleOverride.id = Identifier.idPool(name);
+ count += 1;
+ }
+ else if (auto td = s.isTemplateDeclaration())
+ {
+ /* pragma(mangle) TemplateDeclaration
+ Give an informative error message to avoid pragma(mangle)
+ silently ignoring the template symbol. */
+ error(loc, "`pragma(mangle)` cannot apply to a template declaration");
+ OutBuffer buf;
+ buf.arrayObjectsToBuffer(cast(Objects*)args);
+ errorSupplemental(loc, "use `template %s(Args...) { pragma(mangle, %s) ... }`", td.ident.toChars(), buf.peekChars());
+ return false;
+ }
+ else if (auto ad = s.isAttribDeclaration())
+ {
+ /* pragma(mangle) AttribDeclaration
+ Poke through the attribute to get to the underlying declaration. */
+ if (!applyPragmaMangle(ad.include(null), count, ignored))
+ return false;
+ }
+ else if (s.isFuncDeclaration() || s.isVarDeclaration())
+ {
+ /* pragma(mangle) Declaration;
+ For all other symbols, there must be one AssignExpression and it
+ must evaluate at compile time to a string literal. */
+ if (args.length != 1)
+ {
+ error(loc, "`pragma(mangle)` takes a single argument that must be a string literal");
+ return false;
+ }
+ auto se = verifyMangleString((*args)[0]);
+ if (!se)
+ return false;
+
+ const name = se.peekString().xarraydup;
+ s.isDeclaration().mangleOverride = name;
+ count += 1;
+ }
+ else
+ {
+ /* pragma(mangle) only applies to function and variable
+ symbols. Other symbols are ignored. */
+ ignored = true;
+ }
+ }
+ return true;
+ }
+
+ if (args is null)
+ {
+ error(loc, "`pragma(mangle)` expects string literal argument for mangled name");
+ return false;
+ }
+ if (args.length > 2)
+ {
+ error(loc, "`pragma(mangle)` expects 1 or 2 arguments");
+ return false;
+ }
+
+ uint count = 0;
+ bool ignored = false;
+ if (!applyPragmaMangle(decls, count, ignored))
+ return false;
+
+ if (count == 0 && !ignored)
+ {
+ error(loc, "`pragma(mangle)` must be attached to a declaration");
+ return false;
+ }
+ if (count > 1)
+ {
+ error(loc, "`pragma(mangle)` can only apply to a single declaration");
+ return false;
+ }
+ return true;
+}
import core.stdc.stdio;
import dmd.expression;
+import dmd.expressionsem : toInteger;
import dmd.ctfeexpr;
import dmd.tokens;
import dmd.visitor;
import core.stdc.string;
import dmd.root.rmem;
-import dmd.root.string;
+import dmd.root.string : toDString;
// `qsort` is only `nothrow` since 2.081.0
private extern(C) void qsort(scope void* base, size_t nmemb, size_t size, _compare_fp_t compar) nothrow @nogc;
else
{
- static if (hasLength!Range1 && hasLength!Range2 && is(typeof(r1.length == r2.length)))
+ static if (hasLength!Range1 && hasLength!Range2 && is(typeof(range1.length == range2.length)))
{
if (range1.length != range2.length)
return false;
import dmd.root.rmem;
+nothrow:
+
struct BitArray
{
import core.stdc.errno;
import core.stdc.stdio;
import core.stdc.stdlib;
-import core.stdc.string : strerror;
import core.sys.posix.fcntl;
import core.sys.posix.unistd;
import core.sys.windows.winbase;
extern (C) char* canonicalize_file_name(const char*) nothrow;
}
-alias Strings = Array!(const(char)*);
+nothrow:
+alias Strings = Array!(const(char)*);
// Check whether character is a directory separator
bool isDirSeparator(char c) pure nothrow @nogc @safe
module dmd.root.hash;
+nothrow:
+@safe:
+
// MurmurHash2 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
// https://github.com/aappleby/smhasher/
import dmd.root.rmem;
import dmd.root.array;
+nothrow:
+
/*****
* Simple region storage allocator.
*/
import core.memory : GC;
+nothrow:
+
extern (C++) struct Mem
{
static char* xstrdup(const(char)* s) nothrow
import core.stdc.string;
import dmd.root.rmem;
+nothrow:
+
/// Slices a `\0`-terminated C-string, excluding the terminator
inout(char)[] toDString (inout(char)* s) pure nothrow @nogc
{
import core.stdc.string;
import dmd.root.rmem, dmd.root.hash;
+nothrow:
+
private enum POOL_BITS = 12;
private enum POOL_SIZE = (1U << POOL_BITS);
{
}
- bool equals(const RootObject o) const
- {
- return o is this;
- }
-
const(char)* toChars() const
{
assert(0);
public:
RootObject() { }
- virtual bool equals(const RootObject * const o) const;
-
/**
* Pretty-print an Object. Useful for debugging the old-fashioned way.
*/
import dmd.rootobject;
import dmd.root.string : fTuple;
import dmd.target;
+import dmd.targetcompiler;
import dmd.tokens;
-import dmd.typesem : hasPointers, arrayOf, size;
+import dmd.typesem;
/*************************************************************
* Check for unsafe access in @safe code:
// Implicit conversions are always safe
if (tfrom.implicitConvTo(tto))
return true;
-
if (!tto.hasPointers())
+ {
+ // casting to bool is safe as it's a special op
+ // casting to struct with non-pointer @system field is not @safe
+ if (tto.ty != Tbool && tto.hasUnsafeBitpatterns())
+ {
+ msg = "Target element type has unsafe bit patterns";
+ return false;
+ }
return true;
-
+ }
auto tfromb = tfrom.toBasetype();
auto ttob = tto.toBasetype();
* Call when `fd` was just inferred to be @system OR
* `fd` was @safe and an tried something unsafe.
* Params:
- * fd = function we're gonna rat on
+ * fd = function we are gonna rat on
* gag = suppress error message (used in escape.d)
* loc = location of error
* format = printf-style format string
*/
extern (D) void reportSafeError(FuncDeclaration fd, bool gag, Loc loc,
const(char)* format, RootObject[] args...)
+{
+ reportSafeError(fd, gag, loc, null, format, args);
+}
+
+/// Overload that also stores the variable whose scope status caused the violation
+extern (D) void reportSafeError(FuncDeclaration fd, bool gag, Loc loc,
+ VarDeclaration scopeVar, const(char)* format, RootObject[] args...)
{
if (fd.type.toTypeFunction().trust == TRUST.system) // function was just inferred to be @system
{
if (format)
{
- fd.safetyViolation = new AttributeViolation(loc, format, args);
+ if (scopeVar)
+ fd.safetyViolation = new AttributeViolation(loc, format, scopeVar, args);
+ else
+ fd.safetyViolation = new AttributeViolation(loc, format, args);
}
else if (args.length > 0)
{
buf.writestring(" is not allowed in a `@safe` function");
else
{
- version (IN_GCC)
- buf.writestring(" is not allowed in a function with default safety with `-fpreview=safer`");
- else
- buf.writestring(" is not allowed in a function with default safety with `-preview=safer`");
+ buf.printf(" is not allowed in a function with default safety with `-%spreview=safer`", SwitchPrefix.ptr);
}
.error(loc, "%s", buf.extractChars());
}
* Returns: whether there is a safe error
*/
bool setUnsafe(Scope* sc, bool gag, Loc loc, const(char)* format, RootObject[] args...)
+{
+ return sc.setUnsafe(gag, loc, null, format, args);
+}
+
+/// Overload that also stores the variable whose scope status caused the violation
+bool setUnsafe(Scope* sc, bool gag, Loc loc, VarDeclaration scopeVar,
+ const(char)* format, RootObject[] args...)
{
if (sc.intypeof)
return false; // typeof(cast(int*)0) is safe
{
if (format || args.length > 0)
{
- reportSafeError(sc.func, gag, loc, format, args);
+ reportSafeError(sc.func, gag, loc, scopeVar, format, args);
}
return sc.func.isSafe(); // it is only an error if in an @safe function
}
* Returns: whether an actual safe error (not deprecation) occured
*/
bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* format, RootObject[] args...)
+{
+ return setUnsafePreview(sc, fs, gag, loc, null, format, args);
+}
+
+/// Overload for scope violations that also stores the variable whose scope status caused the issue
+bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, VarDeclaration scopeVar,
+ const(char)* format, RootObject[] args...)
{
//printf("setUnsafePreview() fs:%d %s\n", fs, fmt);
assert(format);
return false;
case enabled:
- return sc.setUnsafe(gag, loc, format, args);
+ return sc.setUnsafe(gag, loc, scopeVar, format, args);
case default_:
if (!sc.func)
else if (!sc.func.safetyViolation)
{
import dmd.func : AttributeViolation;
- sc.func.safetyViolation = new AttributeViolation(loc, format, args);
+ if (scopeVar)
+ sc.func.safetyViolation = new AttributeViolation(loc, format, scopeVar, args);
+ else
+ sc.func.safetyViolation = new AttributeViolation(loc, format, args);
}
return false;
}
bool fullinst(bool v);
bool ctfeBlock() const;
bool ctfeBlock(bool v);
+ bool knownACompileTimeOnlyContext() const;
+ bool knownACompileTimeOnlyContext(bool v);
UserAttributeDeclaration *userAttribDecl; // user defined attributes
// Note that modules get their own scope, from scratch.
// This is so regardless of where in the syntax a module
// gets imported, it is unaffected by context.
- Scope* sc = Scope.createGlobal(mod, global.errorSink); // create root scope
+ Scope* sc = scopeCreateGlobal(mod, global.errorSink); // create root scope
//printf("Module = %p\n", sc.scopesym);
if (mod.members)
{
// `const` (and nor is `StringExp`, by extension).
static int predicate(const scope Expression* e1, const scope Expression* e2)
{
- return (cast(Expression*)e1).toStringExp().compare((cast(Expression*)e2).toStringExp());
+ Expression e11 = cast(Expression) *e1;
+ Expression e22 = cast(Expression) *e2;
+ return e11.toStringExp().compare(e22.toStringExp());
}
ale.elements.sort!predicate;
}
+/****************
+ * Find virtual function matching identifier and type.
+ * Used to build virtual function tables for interface implementations.
+ * Params:
+ * _this = ClassDeclaration's vtbl to search
+ * ident = function's identifier
+ * tf = function's type
+ * Returns:
+ * function symbol if found, null if not
+ * Errors:
+ * prints error message if more than one match
+ */
+FuncDeclaration findFunc(ClassDeclaration _this, Identifier ident, TypeFunction tf)
+{
+ //printf("ClassDeclaration.findFunc(%s, %s) %s\n", ident.toChars(), tf.toChars(), toChars());
+ FuncDeclaration fdmatch = null;
+ FuncDeclaration fdambig = null;
+
+ void updateBestMatch(FuncDeclaration fd)
+ {
+ fdmatch = fd;
+ fdambig = null;
+ //printf("Lfd fdmatch = %s %s [%s]\n", fdmatch.toChars(), fdmatch.type.toChars(), fdmatch.loc.toChars());
+ }
+
+ void searchVtbl(ref Dsymbols vtbl)
+ {
+ bool seenInterfaceVirtual;
+ foreach (s; vtbl)
+ {
+ auto fd = s.isFuncDeclaration();
+ if (!fd)
+ continue;
+
+ // the first entry might be a ClassInfo
+ //printf("\t[%d] = %s\n", i, fd.toChars());
+ if (ident != fd.ident || fd.type.covariant(tf) != Covariant.yes)
+ {
+ //printf("\t\t%d\n", fd.type.covariant(tf));
+ continue;
+ }
+
+ //printf("fd.parent.isClassDeclaration() = %p\n", fd.parent.isClassDeclaration());
+ if (!fdmatch)
+ {
+ updateBestMatch(fd);
+ continue;
+ }
+ if (fd == fdmatch)
+ continue;
+
+ /* Functions overriding interface functions for extern(C++) with VC++
+ * are not in the normal vtbl, but in vtblFinal. If the implementation
+ * is again overridden in a child class, both would be found here.
+ * The function in the child class should override the function
+ * in the base class, which is done here, because searchVtbl is first
+ * called for the child class. Checking seenInterfaceVirtual makes
+ * sure, that the compared functions are not in the same vtbl.
+ */
+ if (fd.interfaceVirtual &&
+ fd.interfaceVirtual is fdmatch.interfaceVirtual &&
+ !seenInterfaceVirtual &&
+ fdmatch.type.covariant(fd.type) == Covariant.yes)
+ {
+ seenInterfaceVirtual = true;
+ continue;
+ }
+
+ {
+ // Function type matching: exact > covariant
+ MATCH m1 = tf.equals(fd.type) ? MATCH.exact : MATCH.nomatch;
+ MATCH m2 = tf.equals(fdmatch.type) ? MATCH.exact : MATCH.nomatch;
+ if (m1 > m2)
+ {
+ updateBestMatch(fd);
+ continue;
+ }
+ else if (m1 < m2)
+ continue;
+ }
+ {
+ MATCH m1 = (tf.mod == fd.type.mod) ? MATCH.exact : MATCH.nomatch;
+ MATCH m2 = (tf.mod == fdmatch.type.mod) ? MATCH.exact : MATCH.nomatch;
+ if (m1 > m2)
+ {
+ updateBestMatch(fd);
+ continue;
+ }
+ else if (m1 < m2)
+ continue;
+ }
+ {
+ // The way of definition: non-mixin > mixin
+ MATCH m1 = fd.parent.isClassDeclaration() ? MATCH.exact : MATCH.nomatch;
+ MATCH m2 = fdmatch.parent.isClassDeclaration() ? MATCH.exact : MATCH.nomatch;
+ if (m1 > m2)
+ {
+ updateBestMatch(fd);
+ continue;
+ }
+ else if (m1 < m2)
+ continue;
+ }
+
+ fdambig = fd;
+ //printf("Lambig fdambig = %s %s [%s]\n", fdambig.toChars(), fdambig.type.toChars(), fdambig.loc.toChars());
+ }
+ }
+
+ searchVtbl(_this.vtbl);
+ for (auto cd = _this; cd; cd = cd.baseClass)
+ {
+ searchVtbl(cd.vtblFinal);
+ }
+
+ if (fdambig)
+ _this.classError("%s `%s` ambiguous virtual function `%s`", fdambig.toChars());
+
+ return fdmatch;
+}
+
+
/**
* Try lower a variable's Associative Array initializer to a newaa struct
* so it can be put in static data.
import dmd.optimize;
import dmd.parse;
import dmd.root.filename;
+import dmd.root.array;
import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.rootobject;
import dmd.semantic2;
import dmd.statement;
import dmd.target;
+import dmd.targetcompiler;
import dmd.templateparamsem;
import dmd.typesem;
import dmd.visitor;
// Note that modules get their own scope, from scratch.
// This is so regardless of where in the syntax a module
// gets imported, it is unaffected by context.
- Scope* sc = Scope.createGlobal(mod, global.errorSink); // create root scope
+ Scope* sc = scopeCreateGlobal(mod, global.errorSink); // create root scope
//printf("Module = %p\n", sc.scopesym);
if (mod.members)
{
//printf("Module %s: %s.semantic3()\n", toChars(), s.toChars());
s.semantic3(sc);
- mod.runDeferredSemantic2();
+ runDeferredSemantic2();
}
}
if (mod.userAttribDecl)
timeTraceBeginEvent(TimeTraceEventType.sema3);
scope (exit) timeTraceEndEvent(TimeTraceEventType.sema3, funcdecl);
- /* Determine if function should add `return 0;`
- */
- bool addReturn0()
- {
- //printf("addReturn0()\n");
- auto f = funcdecl.type.isTypeFunction();
-
- // C11 5.1.2.2.3
- if (sc.inCfile && funcdecl.isCMain() && f.next.ty == Tint32)
- return true;
-
- return f.next.ty == Tvoid && (funcdecl.isMain() || funcdecl.isCMain());
- }
-
VarDeclaration _arguments = null;
if (!funcdecl.parent)
if (!global.params.useTypeInfo || !Type.dtypeinfo || !Type.typeinfotypelist)
{
if (!global.params.useTypeInfo)
- {
- version (IN_GCC)
- .error(funcdecl.loc, "%s `%s` D-style variadic functions cannot be used with `-fno-rtti`", funcdecl.kind, funcdecl.toPrettyChars);
- else
- .error(funcdecl.loc, "%s `%s` D-style variadic functions cannot be used with -betterC", funcdecl.kind, funcdecl.toPrettyChars);
- }
+ .error(funcdecl.loc, "%s `%s` D-style variadic functions cannot be used with `-%s`", funcdecl.kind, funcdecl.toPrettyChars, SwitchVariadic.ptr);
else if (!Type.typeinfotypelist)
.error(funcdecl.loc, "%s `%s` `object.TypeInfo_Tuple` could not be found, but is implicitly used in D-style variadic functions", funcdecl.kind, funcdecl.toPrettyChars);
else
Expression exp = (*funcdecl.returns)[i].exp;
if (exp.op == EXP.variable && (cast(VarExp)exp).var == funcdecl.vresult)
{
- if (addReturn0())
+ if (fds.addReturn0())
exp.type = Type.tint32;
else
exp.type = f.next;
if (funcdecl.returns)
{
- bool implicit0 = addReturn0();
+ bool implicit0 = fds.addReturn0();
Type tret = implicit0 ? Type.tint32 : f.next;
assert(tret.ty != Tvoid);
if (funcdecl.vresult || funcdecl.returnLabel)
if (funcdecl.vresult)
{
// Create: return vresult = exp;
- exp = new BlitExp(rs.loc, funcdecl.vresult, exp);
+ if (canElideCopy(exp, funcdecl.vresult.type, false))
+ exp = new ConstructExp(rs.loc, funcdecl.vresult, exp);
+ else
+ exp = new BlitExp(rs.loc, funcdecl.vresult, exp);
+
exp.type = funcdecl.vresult.type;
if (rs.caseDim)
a.push(s);
}
}
- if (addReturn0())
+ if (fds.addReturn0())
{
// Add a return 0; statement
Statement s = new ReturnStatement(Loc.initial, IntegerExp.literal!0);
return false;
}
- if (isCppNonMappableType(f.next.toBasetype()))
+ if (isCppNonMappableType(f.next.toBasetype()) && !funcdecl.skipCodegen)
{
.error(funcdecl.loc, "%s `%s` cannot return type `%s` because its linkage is `extern(C++)`", funcdecl.kind, funcdecl.toErrMsg(), f.next.toChars());
if (f.next.isTypeDArray())
if (ctor.semanticRun >= PASS.semantic3)
return;
+ if (!ctor.fbody)
+ return visit(cast(FuncDeclaration)ctor);
+
+
/* If any of the fields of the aggregate have a destructor, add
* scope (failure) { this.fieldDtor(); }
* as the first statement of the constructor (unless the constructor
* https://issues.dlang.org/show_bug.cgi?id=14246
*/
AggregateDeclaration ad = ctor.isMemberDecl();
- if (!ctor.fbody || !ad || !ad.fieldDtor ||
+ if (!ad || !ad.fieldDtor ||
global.params.dtorFields == FeatureState.disabled || !global.params.useExceptions || ctor.type.toTypeFunction.isNothrow)
return visit(cast(FuncDeclaration)ctor);
visit(cast(FuncDeclaration)ctor);
}
-
override void visit(Nspace ns)
{
if (ns.semanticRun >= PASS.semantic3)
sc2.pop();
- // Instantiate RTInfo!S to provide a pointer bitmap for the GC
- // Don't do it in -betterC or on unused deprecated / error types
- if (!ad.getRTInfo && global.params.useTypeInfo && Type.rtinfo &&
- (!ad.isDeprecated() || global.params.useDeprecated != DiagnosticReporting.error) &&
- (ad.type && ad.type.ty != Terror))
- {
- // Evaluate: RTinfo!type
- auto tiargs = new Objects(ad.type);
- auto ti = new TemplateInstance(ad.loc, Type.rtinfo, tiargs);
-
- Scope* sc3 = ti.tempdecl._scope.startCTFE();
- sc3.tinst = sc.tinst;
- sc3.minst = sc.minst;
- if (ad.isDeprecated())
- sc3.stc |= STC.deprecated_;
-
- ti.dsymbolSemantic(sc3);
- ti.semantic2(sc3);
- ti.semantic3(sc3);
- auto e = symbolToExp(ti.toAlias(), Loc.initial, sc3, false);
-
- sc3.endCTFE();
-
- e = e.ctfeInterpret();
- ad.getRTInfo = e;
- }
if (sd)
sd.semanticTypeInfoMembers();
+ else
+ ad.semanticRTInfo();
ad.semanticRun = PASS.semantic3done;
}
}
}
}
+
+ /* Determine if function should add `return 0;`
+ */
+ bool addReturn0()
+ {
+ //printf("addReturn0()\n");
+ auto f = funcdecl.type.isTypeFunction();
+ if (!f) return false;
+
+ // C11 5.1.2.2.3
+ if (sc.inCfile && funcdecl.isCMain() && f.next.ty == Tint32)
+ return true;
+
+ return f.next.ty == Tvoid && (funcdecl.isMain() || funcdecl.isCMain());
+ }
}
/***************************************
*/
void semanticTypeInfoMembers(StructDeclaration sd)
{
- if (sd.xeq &&
- sd.xeq._scope &&
- sd.xeq.semanticRun < PASS.semantic3done)
+ void runSemantic(ref FuncDeclaration fd, ref FuncDeclaration errFd)
{
- const errors = global.startGagging();
- sd.xeq.semantic3(sd.xeq._scope);
- if (global.endGagging(errors))
- sd.xeq = sd.xerreq;
+ if (fd && fd._scope && fd.semanticRun < PASS.semantic3done)
+ {
+ const errors = global.startGagging();
+ fd.semantic3(fd._scope);
+ if (global.endGagging(errors))
+ fd = errFd;
+ }
}
- if (sd.xcmp &&
- sd.xcmp._scope &&
- sd.xcmp.semanticRun < PASS.semantic3done)
- {
- const errors = global.startGagging();
- sd.xcmp.semantic3(sd.xcmp._scope);
- if (global.endGagging(errors))
- sd.xcmp = sd.xerrcmp;
- }
+ runSemantic(sd.xeq, sd.xerreq);
+ runSemantic(sd.xcmp, sd.xerrcmp);
+
FuncDeclaration ftostr = search_toString(sd);
if (ftostr &&
{
sd.dtor.semantic3(sd.dtor._scope);
}
+ sd.semanticRTInfo();
+}
+
+void semanticRTInfo(AggregateDeclaration ad)
+{
+ // Instantiate RTInfo!S to provide a pointer bitmap for the GC
+ // Don't do it in -betterC or on error types
+ if (ad.getRTInfo || !global.params.useTypeInfo || !Type.rtinfo)
+ return;
+ if (!ad.rtInfoScope || !ad.type || ad.type.ty == Terror)
+ return;
+
+ // Evaluate: RTinfo!type
+ auto tiargs = new Objects(ad.type);
+ auto ti = new TemplateInstance(ad.loc, Type.rtinfo, tiargs);
+
+ auto sc = ad.rtInfoScope;
+ Scope* sc3 = ti.tempdecl._scope.startCTFE();
+ sc3.tinst = sc.tinst;
+ sc3.minst = sc.minst;
+ if (ad.isDeprecated())
+ sc3.stc |= STC.deprecated_;
+
+ ti.dsymbolSemantic(sc3);
+ ti.semantic2(sc3);
+ ti.semantic3(sc3);
+ auto e = symbolToExp(ti.toAlias(), Loc.initial, sc3, false);
+
+ sc3.endCTFE();
+
+ e = e.ctfeInterpret();
+ ad.getRTInfo = e;
}
/**
if (!fd.needsClosure())
return false;
- if (fd.setGC(fd.loc, "allocating a closure for `%s()`", fd))
+ if (setGC(null, fd, fd.loc, "allocating a closure for `%s()`", fd))
{
.error(fd.loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", fd.kind, fd.toPrettyChars(), fd.toChars());
if (global.gag) // need not report supplemental errors
auto fx = s.isFuncDeclaration();
if (!fx)
continue;
- if (fx.isThis() ||
- fx.tookAddressOf ||
- checkEscapingSiblings(fx, fd))
+ if (fx.isThis() || fx.tookAddressOf || checkEscapingSiblings(fx, fd))
{
- foreach (f2; a)
+ if (!a.contains(f))
{
- if (f2 == f)
- break LcheckAncestorsOfANestedRef;
+ a.push(f);
+ .errorSupplemental(f.loc, "%s `%s` closes over variable `%s`",
+ f.kind, f.toErrMsg(), v.toChars());
+ if (v.ident != Id.This)
+ .errorSupplemental(v.loc, "`%s` declared here", v.toChars());
}
- a.push(f);
- .errorSupplemental(f.loc, "%s `%s` closes over variable `%s`",
- f.kind, f.toErrMsg(), v.toChars());
- if (v.ident != Id.This)
- .errorSupplemental(v.loc, "`%s` declared here", v.toChars());
-
break LcheckAncestorsOfANestedRef;
}
}
import dmd.sideeffect;
import dmd.statement;
import dmd.target;
+import dmd.targetcompiler;
import dmd.tokens;
import dmd.typesem;
import dmd.visitor;
version = CallbackAPI;
}
+/*****************************************
+ * Returns:
+ * `true` iff ready to call `dmd.statementsem.makeTupleForeach`.
+ */
+bool ready(StaticForeach _this)
+{
+ return _this.aggrfe && _this.aggrfe.aggr && _this.aggrfe.aggr.type && _this.aggrfe.aggr.type.toBasetype().ty == Ttuple;
+}
+
/*****************************************
* CTFE requires FuncDeclaration::labtab for the interpretation.
* So fixing the label name inside in/out contracts is necessary
Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
- /* Check for inference errors and apply mutability checks inline */
+ /* Check for inference errors and apply modifier checks inline */
if (!inferApplyArgTypes(fs, sc, sapply))
{
bool foundMismatch = false;
size_t foreachParamCount = 0;
+
if (sapplyOld)
{
if (FuncDeclaration fd = sapplyOld.isFuncDeclaration())
{
auto fparameters = fd.getParameterList();
- if (fparameters.length == 1)
+ // ignore overloads, can't determine which non-matching is closest
+ if (!fd.overnext && fparameters.length == 1)
{
// first param should be the callback function
Parameter fparam = fparameters[0];
foreachParamCount = tf.parameterList.length;
foundMismatch = true;
- // Mutability check
- if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst())
+ if (fd.isThis() &&
+ !MODmethodConv(fs.aggr.type.mod, fd.type.mod))
{
- // First error: The call site
- error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object",
- fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars());
-
- // Second error: Suggest how to fix
- errorSupplemental(fd.loc, "Consider adding `const` or `inout` here");
-
+ error(fs.aggr.loc, "%s method `%s` is not callable using a `%s` foreach aggregate",
+ !fd.type.mod ? "mutable" : fd.type.modToChars(),
+ fd.toPrettyChars(),
+ fs.aggr.type.toChars());
+ errorSupplemental(fd.loc, "Consider adding a method type qualifier here");
return setError();
}
}
return setError();
}
- // If inference succeeds, proceed with post-checks
- if (sapply && sapply.isFuncDeclaration())
- {
- FuncDeclaration fd = sapply.isFuncDeclaration();
-
- if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst())
- {
- // First error: The call site
- error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object",
- fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars());
-
- // Second error: Suggest how to fix
- errorSupplemental(fd.loc, "Consider adding `const` or `inout` here");
-
- return setError();
- }
- }
-
Type tab = fs.aggr.type.toBasetype();
if (tab.ty == Ttuple) // don't generate new scope for tuple loops
if (fd.isCtorDeclaration())
{
- if (rs.exp)
+ // Constructors implicitly do:
+ // return this;
+ auto ctorReturn = new ThisExp(Loc.initial);
+ ctorReturn.type = tret;
+
+ bool isConstructorCall(Expression e)
{
- error(rs.loc, "cannot return expression from constructor");
- errors = true;
+ auto ce = e.isCallExp();
+ if (!ce)
+ return false;
+
+ auto dve = ce.e1.isDotVarExp();
+ if (!dve)
+ return false;
+
+ return dve.var.isThis !is null;
}
- // Constructors implicitly do:
- // return this;
- rs.exp = new ThisExp(Loc.initial);
- rs.exp.type = tret;
+ if (rs.exp)
+ {
+ rs.exp = rs.exp.expressionSemantic(sc);
+
+ if (rs.exp.type.ty != Tvoid && !isConstructorCall(rs.exp))
+ {
+
+ error(rs.loc, "can only return void expression, `this` call or `super` call from constructor");
+ errors = true;
+ rs.exp = ErrorExp.get();
+ }
+ else
+ {
+ rs.exp = new CommaExp(rs.loc, rs.exp, ctorReturn).expressionSemantic(sc);
+ }
+ }
+ else
+ {
+ rs.exp = ctorReturn;
+ }
}
else if (rs.exp)
{
rs.exp = resolveAliasThis(sc, rs.exp);
rs.exp = resolveProperties(sc, rs.exp);
- if (rs.exp.checkType())
+ if (!rs.exp.hasValidType())
rs.exp = ErrorExp.get();
if (auto f = isFuncAddress(rs.exp))
{
if (checkNonAssignmentArrayOp(rs.exp))
rs.exp = ErrorExp.get();
+ if (fd.ident == Id.apply && rs.exp.isConst() && rs.exp.toInteger() != 0)
+ {
+ // @@@DEPRECATED_2.121
+ // uncomment ErrorExp and call `error`
+ deprecation(rs.exp.loc, "cannot return non-zero compile-time value from `opApply`");
+ deprecationSupplemental(rs.exp.loc, "Any non-zero value must be the result of calling its delegate");
+ //rs.exp = ErrorExp.get();
+ }
+
// Extract side-effect part
rs.exp = Expression.extractLast(rs.exp, e0);
if (rs.exp.isCallExp())
auto args = new Parameters(new Parameter(Loc.initial, STC.none, ClassDeclaration.object.type,
null, null, null));
- FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter);
+ FuncDeclaration fdenter = genCfunc(args, Type.tvoid, Id.monitorenter);
Expression e = new CallExp(ss.loc, fdenter, new VarExp(ss.loc, tmp));
e.type = Type.tvoid; // do not run semantic on e
cs.push(new ExpStatement(ss.loc, e));
- FuncDeclaration fdexit = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorexit);
+ FuncDeclaration fdexit = genCfunc(args, Type.tvoid, Id.monitorexit);
e = new CallExp(ss.loc, fdexit, new VarExp(ss.loc, tmp));
e.type = Type.tvoid; // do not run semantic on e
Statement s = new ExpStatement(ss.loc, e);
auto enterArgs = new Parameters(new Parameter(Loc.initial, STC.none, t.pointerTo(), null, null, null));
- FuncDeclaration fdenter = FuncDeclaration.genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_);
+ FuncDeclaration fdenter = genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_);
Expression e = new AddrExp(ss.loc, tmpExp);
e = e.expressionSemantic(sc);
e = new CallExp(ss.loc, fdenter, e);
auto exitArgs = new Parameters(new Parameter(Loc.initial, STC.none, t, null, null, null));
- FuncDeclaration fdexit = FuncDeclaration.genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_);
+ FuncDeclaration fdexit = genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_);
e = new CallExp(ss.loc, fdexit, tmpExp);
e.type = Type.tvoid; // do not run semantic on e
Statement s = new ExpStatement(ss.loc, e);
if (!global.params.useExceptions)
{
- error(tcs.loc, "cannot use try-catch statements with %s", global.params.betterC ? "-betterC".ptr : "-nothrow".ptr);
+ error(tcs.loc, "cannot use try-catch statements with `%s`", global.params.betterC ? "-betterC".ptr : "-nothrow".ptr);
return setError();
}
blockexit &= ~BE.throw_; // don't worry about paths that otherwise may throw
// Don't care about paths that halt, either
- if ((blockexit & ~BE.halt) == BE.fallthru)
+ // Only rewrite if it was requested.
+ // This has side effects where Error will not run destructors, unsafe.
+ if (global.params.rewriteNoExceptionToSeq && (blockexit & ~BE.halt) == BE.fallthru)
{
result = new CompoundStatement(tfs.loc, tfs._body, tfs.finalbody);
return;
}
+
tfs.bodyFallsThru = (blockexit & BE.fallthru) != 0;
result = tfs;
}
// https://issues.dlang.org/show_bug.cgi?id=23159
if (!global.params.useExceptions)
{
- version (IN_GCC)
- error(oss.loc, "`%s` cannot be used with `-fno-exceptions`", Token.toChars(oss.tok));
- else
- error(oss.loc, "`%s` cannot be used with -betterC", Token.toChars(oss.tok));
+ error(oss.loc, "`%s` cannot be used with `-%s`", Token.toChars(oss.tok), SwitchScopeGuard);
return setError();
}
assert(sc.func);
if (!(cas.stc & STC.pure_) && sc.func.setImpure(cas.loc, "executing an `asm` statement without `pure` annotation"))
error(cas.loc, "`asm` statement is assumed to be impure - mark it with `pure` if it is not");
- if (!(cas.stc & STC.nogc) && sc.func.setGC(cas.loc, "executing an `asm` statement without `@nogc` annotation"))
+ if (!(cas.stc & STC.nogc) && sc.setGC(sc.func, cas.loc, "executing an `asm` statement without `@nogc` annotation"))
error(cas.loc, "`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not");
// @@@DEPRECATED_2.114@@@
// change deprecation() to error(), add `else` and remove `| STC.safe`
// for the current scope.
if (s.mod !is null)
{
- Module.addDeferredSemantic2(s); // https://issues.dlang.org/show_bug.cgi?id=14666
+ addDeferredSemantic2(s); // https://issues.dlang.org/show_bug.cgi?id=14666
sc.insert(s);
foreach (aliasdecl; s.aliasdecls)
{
if (!global.params.useExceptions)
{
- version (IN_GCC)
- loc.error("cannot use `throw` statements with `-fno-exceptions`");
- else
- loc.error("cannot use `throw` statements with %s", global.params.betterC ? "-betterC".ptr : "-nothrow".ptr);
+ const(char)* s = SwitchExceptions ? SwitchExceptions : global.params.betterC ? "betterC".ptr : "nothrow".ptr;
+ loc.error("cannot use `throw` statements with `-%s`", s);
return false;
}
dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
params.push(new Parameter(Loc.initial, STC.none, dgty, null, null, null));
- fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);
+ fdapply = genCfunc(params, Type.tint32, fdname.ptr);
if (tab.isTypeSArray())
fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf());
{
Statement s = p.parseStatement(ParseStatementFlags.curlyScope);
if (!s || global.errors != errors)
+ {
+ errorSupplemental(s.loc, "while parsing string mixin statement");
return errorStatements();
+ }
a.push(s);
}
return a;
import dmd.expression;
import dmd.location;
import dmd.id;
-import dmd.identifier;
import dmd.visitor;
/***********************************************************
--- /dev/null
+/**
+ * Encapsulates the vagaries of which of the three D compilers (gdc, ldc or dmd) is being built
+ *
+ * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mars.d, _targetcompiler.d)
+ * Documentation: https://dlang.org/phobos/dmd_targetcompiler.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/targetcompiler.d
+ */
+
+module dmd.targetcompiler;
+
+version (IN_GCC) {} // compiler is being built with gdc
+else version (IN_LLVM) {} // compiler is being built with ldc
+else version = MARS; // default means compiler is built with Digital Mars compiler (DMD)
+
+/***************************
+ */
+
+version (IN_GCC)
+{
+ enum TargetCompiler = "GNU D"; // compiler being built
+ enum SwitchPrefix = "f"; // prefix to switch
+ enum SwitchVariadic = "fno-rtti"; // disables D-style variadic functions
+ enum const(char)* SwitchExceptions = "fno-exceptions"; // switch that disables exceptions
+ enum const(char)* SwitchScopeGuard = "fno-exceptions"; // switch that disables exceptions
+}
+else version (IN_LLVM)
+{
+ enum TargetCompiler = "LDC";
+ enum SwitchPrefix = "";
+ enum SwitchVariadic = "betterC";
+ enum const(char)* SwitchExceptions = null;
+ enum const(char)* SwitchScopeGuard = "betterC";
+}
+else version (MARS)
+{
+ enum TargetCompiler = "Digital Mars D";
+ enum SwitchPrefix = "";
+ enum SwitchVariadic = "betterC";
+ enum const(char)* SwitchExceptions = null;
+ enum const(char)* SwitchScopeGuard = "betterC";
+}
+else
+ static assert(0, "unknown compiler being built");
+
+
+/**********************************
+ * Extra Fields for VarDeclaration
+ */
+version (IN_GCC)
+{
+ mixin template VarDeclarationExtra() { }
+}
+else version (IN_LLVM)
+{
+ mixin template VarDeclarationExtra() { }
+}
+else version (MARS)
+{
+ mixin template VarDeclarationExtra()
+ {
+ bool inClosure; /// is inserted into a GC allocated closure
+ bool inAlignSection; /// is inserted into an aligned section on stack
+ }
+}
+else
+ static assert(0, "unknown compiler being built");
+
+/**********************************
+ * Extra Fields for FuncDeclaration
+ */
+version (IN_GCC)
+{
+ mixin template FuncDeclarationExtra() { }
+}
+else version (IN_LLVM)
+{
+ mixin template FuncDeclarationExtra() { }
+}
+else version (MARS)
+{
+ mixin template FuncDeclarationExtra()
+ {
+ VarDeclarations* alignSectionVars; /// local variables with alignment needs larger than stackAlign
+ import dmd.backend.cc : Symbol;
+ Symbol* salignSection; /// pointer to aligned section, if any
+ }
+}
+else
+ static assert(0, "unknown compiler being built");
+
+/**********************************
+ * Extra Fields for FuncDeclaration
+ */
+version (IN_GCC)
+{
+ mixin template alignSectionVarsExtra() { void doAlign() { } }
+}
+else version (IN_LLVM)
+{
+ mixin template alignSectionVarsExtra() { void doAlign() { } }
+}
+else version (MARS)
+{
+ /* If the alignment of a stack local is greater than the stack alignment,
+ * note it in the enclosing function's alignSectionVars
+ */
+ mixin template alignSectionVarsExtra()
+ {
+ void doAlign()
+ {
+ if (!dsym.alignment.isDefault() && sc.func &&
+ dsym.alignment.get() > target.stackAlign() &&
+ sc.func && !dsym.isDataseg() && !dsym.isParameter() && !dsym.isField())
+ {
+ auto fd = sc.func;
+ if (!fd.alignSectionVars)
+ fd.alignSectionVars = new VarDeclarations();
+ fd.alignSectionVars.push(dsym);
+ }
+ }
+ }
+}
+else
+ static assert(0, "unknown compiler being built");
+
+
+/* Returns: true if v is in an align section
+ */
+mixin template alignSectionVarsContains()
+{
+ version (IN_GCC)
+ {
+ bool isAlignSectionVar(VarDeclaration v) { return false; }
+ }
+ else version (IN_LLVM)
+ {
+ bool isAlignSectionVar(VarDeclaration v) { return false; }
+ }
+ else version (MARS)
+ {
+ bool isAlignSectionVar(VarDeclaration v)
+ {
+ return fd.alignSectionVars && (*fd.alignSectionVars).contains(v);
+ }
+ }
+ else
+ static assert(0, "unknown compiler being built");
+}
+
+/* Returns: true if ansi color is to be used
+ */
+
+mixin template UseAnsiColors()
+{
+ bool useAnsiColors()
+ {
+ version (IN_GCC)
+ {
+ return false;
+ }
+ else version (IN_LLVM)
+ {
+ import dmd.console : detectTerminal;
+ return detectTerminal();
+ }
+ else version (MARS)
+ {
+ // -color=auto is the default value
+ import dmd.console : detectTerminal, detectColorPreference;
+ return detectTerminal() && detectColorPreference();
+ }
+ else
+ static assert(0, "unknown compiler being built");
+ }
+}
+
+/******************************************
+ * Let user know object.d cannot be found.
+ * Parameters:
+ * loc = location of error
+ * id = name of symbol that cannot be found
+ * configFile = configuration file name
+ * eSink = where to send the error messages
+ */
+
+mixin template HostObjectNotFound()
+{
+ void hostObjectNotFound(Loc loc, const(char)* id, const(char)[] configFile, ErrorSink eSink)
+ {
+ eSink.error(loc, "`%s` not found. object.d may be incorrectly installed or corrupt.", id);
+ version (IN_GCC)
+ {
+ }
+ else version (IN_LLVM)
+ {
+ eSink.errorSupplemental(loc, "ldc2 might not be correctly installed.");
+ eSink.errorSupplemental(loc, "Please check your ldc2.conf configuration file.");
+ eSink.errorSupplemental(loc, "Installation instructions can be found at http://wiki.dlang.org/LDC.");
+ }
+ else version (MARS)
+ {
+ eSink.errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions.");
+ eSink.errorSupplemental(loc, "config file: %.*s", configFile.length, configFile.ptr);
+ }
+ else
+ static assert(0, "unknown compiler being built");
+ }
+}
d_bool isTrivialAlias; // matches pattern `template Alias(T) { alias Alias = qualifiers(T); }`
d_bool deprecated_; // this template declaration is deprecated
d_bool isCmacro; // Whether this template is a translation of a C macro
+ d_bool haveComputedOneMember; // Whether computeOneMeber has been called
Visibility visibility;
TemplatePrevious *previous; // threaded list of previous instantiation attempts on stack
TemplateDeclaration *syntaxCopy(Dsymbol *) override;
- bool overloadInsert(Dsymbol *s) override;
const char *kind() const override;
Visibility visible() override;
virtual TemplateTupleParameter *isTemplateTupleParameter();
virtual TemplateParameter *syntaxCopy() = 0;
- virtual bool declareParameter(Scope *sc) = 0;
virtual void print(RootObject *oarg, RootObject *oded) = 0;
virtual RootObject *specialization() = 0;
virtual bool hasDefaultArg() = 0;
TemplateTypeParameter *isTemplateTypeParameter() override final;
TemplateTypeParameter *syntaxCopy() override;
- bool declareParameter(Scope *sc) override final;
void print(RootObject *oarg, RootObject *oded) override final;
RootObject *specialization() override final;
bool hasDefaultArg() override final;
TemplateValueParameter *isTemplateValueParameter() override;
TemplateValueParameter *syntaxCopy() override;
- bool declareParameter(Scope *sc) override;
void print(RootObject *oarg, RootObject *oded) override;
RootObject *specialization() override;
bool hasDefaultArg() override;
TemplateAliasParameter *isTemplateAliasParameter() override;
TemplateAliasParameter *syntaxCopy() override;
- bool declareParameter(Scope *sc) override;
void print(RootObject *oarg, RootObject *oded) override;
RootObject *specialization() override;
bool hasDefaultArg() override;
public:
TemplateTupleParameter *isTemplateTupleParameter() override;
TemplateTupleParameter *syntaxCopy() override;
- bool declareParameter(Scope *sc) override;
void print(RootObject *oarg, RootObject *oded) override;
RootObject *specialization() override;
bool hasDefaultArg() override;
Dsymbol *aliasdecl; // !=NULL if instance is an alias for its sole member
TemplateInstance *inst; // refer to existing instance
ScopeDsymbol *argsym; // argument symbol table
- hash_t hash; // cached result of toHash()
Expressions *fargs; // for function template, these are the function arguments
Identifiers *fnames; // for function template, argument names
bool isError(const RootObject *const o);
void printTemplateStats(bool listInstances, ErrorSink* eSink);
void printInstantiationTrace(TemplateInstance *ti);
+ bool declareParameter(TemplateParameter *tp, Scope *sc);
}
import dmd.location;
import dmd.expression;
import dmd.expressionsem;
+import dmd.templatesem;
import dmd.rootobject;
import dmd.mtype;
import dmd.typesem;
import dmd.dinterpret;
import dmd.dmodule;
import dmd.dscope;
+import dmd.dstruct;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.dtemplate;
import dmd.semantic2;
import dmd.semantic3;
import dmd.templateparamsem;
+import dmd.timetrace;
import dmd.tokens;
import dmd.typesem;
import dmd.visitor;
enum LOG = false;
+/***********************************************************
+ * Check whether the type t representation relies on one or more the template parameters.
+ * Params:
+ * t = Tested type, if null, returns false.
+ * tparams = Template parameters.
+ * iStart = Start index of tparams to limit the tested parameters. If it's
+ * nonzero, tparams[0..iStart] will be excluded from the test target.
+ */
+bool reliesOnTident(Type t, TemplateParameters* tparams, size_t iStart = 0)
+{
+ return reliesOnTemplateParameters(t, (*tparams)[0 .. tparams.length]);
+}
+
+/***********************************************************
+ * Check whether the type t representation relies on one or more the template parameters.
+ * Params:
+ * t = Tested type, if null, returns false.
+ * tparams = Template parameters.
+ */
+bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams)
+{
+ bool visitVector(TypeVector t)
+ {
+ return t.basetype.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitAArray(TypeAArray t)
+ {
+ return t.next.reliesOnTemplateParameters(tparams) ||
+ t.index.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitFunction(TypeFunction t)
+ {
+ foreach (i, fparam; t.parameterList)
+ {
+ if (fparam.type.reliesOnTemplateParameters(tparams))
+ return true;
+ }
+ return t.next.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitIdentifier(TypeIdentifier t)
+ {
+ foreach (tp; tparams)
+ {
+ if (tp.ident.equals(t.ident))
+ return true;
+ }
+ return false;
+ }
+
+ bool visitInstance(TypeInstance t)
+ {
+ foreach (tp; tparams)
+ {
+ if (t.tempinst.name == tp.ident)
+ return true;
+ }
+
+ if (t.tempinst.tiargs)
+ foreach (arg; *t.tempinst.tiargs)
+ {
+ if (Type ta = isType(arg))
+ {
+ if (ta.reliesOnTemplateParameters(tparams))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool visitTypeof(TypeTypeof t)
+ {
+ //printf("TypeTypeof.reliesOnTemplateParameters('%s')\n", t.toChars());
+ return t.exp.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitTuple(TypeTuple t)
+ {
+ if (t.arguments)
+ foreach (arg; *t.arguments)
+ {
+ if (arg.type.reliesOnTemplateParameters(tparams))
+ return true;
+ }
+
+ return false;
+ }
+
+ if (!t)
+ return false;
+
+ Type tb = t.toBasetype();
+ switch (tb.ty)
+ {
+ case Tvector: return visitVector(tb.isTypeVector());
+ case Taarray: return visitAArray(tb.isTypeAArray());
+ case Tfunction: return visitFunction(tb.isTypeFunction());
+ case Tident: return visitIdentifier(tb.isTypeIdentifier());
+ case Tinstance: return visitInstance(tb.isTypeInstance());
+ case Ttypeof: return visitTypeof(tb.isTypeTypeof());
+ case Ttuple: return visitTuple(tb.isTypeTuple());
+ case Tenum: return false;
+ default: return tb.nextOf().reliesOnTemplateParameters(tparams);
+ }
+}
+
+/***********************************************************
+ * Check whether the expression representation relies on one or more the template parameters.
+ * Params:
+ * e = expression to test
+ * tparams = Template parameters.
+ * Returns:
+ * true if it does
+ */
+private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparams)
+{
+ extern (C++) final class ReliesOnTemplateParameters : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ TemplateParameter[] tparams;
+ bool result;
+
+ extern (D) this(TemplateParameter[] tparams) @safe
+ {
+ this.tparams = tparams;
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("Expression.reliesOnTemplateParameters('%s')\n", e.toChars());
+ }
+
+ override void visit(IdentifierExp e)
+ {
+ //printf("IdentifierExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ foreach (tp; tparams)
+ {
+ if (e.ident == tp.ident)
+ {
+ result = true;
+ return;
+ }
+ }
+ }
+
+ override void visit(TupleExp e)
+ {
+ //printf("TupleExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.exps)
+ {
+ foreach (ea; *e.exps)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ //printf("ArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.elements)
+ {
+ foreach (el; *e.elements)
+ {
+ el.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ //printf("AssocArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ foreach (ek; *e.keys)
+ {
+ ek.accept(this);
+ if (result)
+ return;
+ }
+ foreach (ev; *e.values)
+ {
+ ev.accept(this);
+ if (result)
+ return;
+ }
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ //printf("StructLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.elements)
+ {
+ foreach (ea; *e.elements)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(TypeExp e)
+ {
+ //printf("TypeExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = e.type.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(NewExp e)
+ {
+ //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.placement)
+ e.placement.accept(this);
+ if (e.thisexp)
+ e.thisexp.accept(this);
+ result = e.newtype.reliesOnTemplateParameters(tparams);
+ if (!result && e.arguments)
+ {
+ foreach (ea; *e.arguments)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(NewAnonClassExp e)
+ {
+ //printf("NewAnonClassExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = true;
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("FuncExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = true;
+ }
+
+ override void visit(TypeidExp e)
+ {
+ //printf("TypeidExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (auto ea = isExpression(e.obj))
+ ea.accept(this);
+ else if (auto ta = isType(e.obj))
+ result = ta.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(TraitsExp e)
+ {
+ //printf("TraitsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.args)
+ {
+ foreach (oa; *e.args)
+ {
+ if (auto ea = isExpression(oa))
+ ea.accept(this);
+ else if (auto ta = isType(oa))
+ result = ta.reliesOnTemplateParameters(tparams);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(IsExp e)
+ {
+ //printf("IsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = e.targ.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(UnaExp e)
+ {
+ //printf("UnaExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.e1.accept(this);
+ }
+
+ override void visit(DotTemplateInstanceExp e)
+ {
+ //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(e.isUnaExp());
+ if (!result && e.ti.tiargs)
+ {
+ foreach (oa; *e.ti.tiargs)
+ {
+ if (auto ea = isExpression(oa))
+ ea.accept(this);
+ else if (auto ta = isType(oa))
+ result = ta.reliesOnTemplateParameters(tparams);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(CallExp e)
+ {
+ //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(e.isUnaExp());
+ if (!result && e.arguments)
+ {
+ foreach (ea; *e.arguments)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(CastExp e)
+ {
+ //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(e.isUnaExp());
+ // e.to can be null for cast() with no type
+ if (!result && e.to)
+ result = e.to.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(SliceExp e)
+ {
+ //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(e.isUnaExp());
+ if (!result && e.lwr)
+ e.lwr.accept(this);
+ if (!result && e.upr)
+ e.upr.accept(this);
+ }
+
+ override void visit(IntervalExp e)
+ {
+ //printf("IntervalExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.lwr.accept(this);
+ if (!result)
+ e.upr.accept(this);
+ }
+
+ override void visit(ArrayExp e)
+ {
+ //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(e.isUnaExp());
+ if (!result && e.arguments)
+ {
+ foreach (ea; *e.arguments)
+ ea.accept(this);
+ }
+ }
+
+ override void visit(BinExp e)
+ {
+ //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.e1.accept(this);
+ if (!result)
+ e.e2.accept(this);
+ }
+
+ override void visit(CondExp e)
+ {
+ //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.econd.accept(this);
+ if (!result)
+ visit(e.isBinExp());
+ }
+ }
+
+ scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams);
+ e.accept(v);
+ return v.result;
+}
+
+void computeOneMember(TemplateDeclaration td)
+{
+ if (td is null || td.haveComputedOneMember)
+ return;
+
+ if (td && td.members && td.ident)
+ {
+ Dsymbol s;
+ if (oneMembers(td.members, s, td.ident) && s)
+ {
+ td.onemember = s;
+ s.parent = td;
+ td.computeIsTrivialAlias(s);
+ }
+ td.haveComputedOneMember = true;
+ }
+}
+
+bool declareParameter(TemplateParameter _this, Scope* sc)
+{
+ static bool typeDeclareParameter(TemplateTypeParameter _this, Scope* sc)
+ {
+ //printf("TemplateTypeParameter.declareParameter('%s')\n", ident.toChars());
+ auto ti = new TypeIdentifier(_this.loc, _this.ident);
+ Declaration ad = new AliasDeclaration(_this.loc, _this.ident, ti);
+ return sc.insert(ad) !is null;
+ }
+
+ static bool valueDeclareParameter(TemplateValueParameter _this, Scope* sc)
+ {
+ /*
+ Do type semantic earlier.
+
+ This means for certain erroneous value parameters
+ their "type" can be known earlier and thus a better
+ error message given.
+
+ For example:
+ `template test(x* x) {}`
+ now yields "undefined identifier" rather than the opaque
+ "variable `x` is used as a type".
+ */
+ if (_this.valType)
+ _this.valType = _this.valType.typeSemantic(_this.loc, sc);
+ auto v = new VarDeclaration(_this.loc, _this.valType, _this.ident, null);
+ v.storage_class = STC.templateparameter;
+ return sc.insert(v) !is null;
+ }
+
+ static bool aliasDeclareParameter(TemplateAliasParameter _this, Scope* sc)
+ {
+ auto ti = new TypeIdentifier(_this.loc, _this.ident);
+ Declaration ad = new AliasDeclaration(_this.loc, _this.ident, ti);
+ return sc.insert(ad) !is null;
+ }
+
+ static bool tupleDeclareParameter(TemplateTupleParameter _this, Scope* sc)
+ {
+ auto ti = new TypeIdentifier(_this.loc, _this.ident);
+ Declaration ad = new AliasDeclaration(_this.loc, _this.ident, ti);
+ return sc.insert(ad) !is null;
+ }
+
+ if (auto tp = _this.isTemplateTypeParameter())
+ return typeDeclareParameter(tp, sc);
+ else if (auto vp = _this.isTemplateValueParameter())
+ return valueDeclareParameter(vp, sc);
+ else if (auto ap = _this.isTemplateAliasParameter())
+ return aliasDeclareParameter(ap, sc);
+ else if (auto tup = _this.isTemplateTupleParameter())
+ return tupleDeclareParameter(tup, sc);
+
+ assert(0); // unreachable
+}
+
/************************************
- * Perform semantic analysis on template.
+ * Return hash of Objects.
+ */
+private size_t arrayObjectHash(ref Objects oa1)
+{
+ import dmd.root.hash : mixHash;
+
+ size_t hash = 0;
+ foreach (o1; oa1)
+ {
+ /* Must follow the logic of match()
+ */
+ if (auto t1 = isType(o1))
+ hash = mixHash(hash, cast(size_t)t1.deco);
+ else if (auto e1 = getExpression(o1))
+ hash = mixHash(hash, expressionHash(e1));
+ else if (auto s1 = isDsymbol(o1))
+ {
+ if (auto fa1 = s1.isFuncAliasDeclaration())
+ s1 = fa1.toAliasFunc();
+ hash = mixHash(hash, mixHash(cast(size_t)cast(void*)s1.getIdent(), cast(size_t)cast(void*)s1.parent));
+ }
+ else if (auto u1 = isTuple(o1))
+ hash = mixHash(hash, arrayObjectHash(u1.objects));
+ }
+ return hash;
+}
+
+/************************************
+ * Computes hash of expression.
+ * Handles all Expression classes and MUST match their equals method,
+ * i.e. e1.equals(e2) implies expressionHash(e1) == expressionHash(e2).
+ */
+private size_t expressionHash(Expression e)
+{
+ import dmd.root.ctfloat : CTFloat;
+ import dmd.root.hash : calcHash, mixHash;
+
+ switch (e.op)
+ {
+ case EXP.int64:
+ return cast(size_t) e.isIntegerExp().getInteger();
+
+ case EXP.float64:
+ return CTFloat.hash(e.isRealExp().value);
+
+ case EXP.complex80:
+ auto ce = e.isComplexExp();
+ return mixHash(CTFloat.hash(ce.toReal), CTFloat.hash(ce.toImaginary));
+
+ case EXP.identifier:
+ return cast(size_t)cast(void*) e.isIdentifierExp().ident;
+
+ case EXP.null_:
+ return cast(size_t)cast(void*) e.isNullExp().type;
+
+ case EXP.string_:
+ return calcHash(e.isStringExp.peekData());
+
+ case EXP.tuple:
+ {
+ auto te = e.isTupleExp();
+ size_t hash = 0;
+ hash += te.e0 ? expressionHash(te.e0) : 0;
+ foreach (elem; *te.exps)
+ hash = mixHash(hash, expressionHash(elem));
+ return hash;
+ }
+
+ case EXP.arrayLiteral:
+ {
+ auto ae = e.isArrayLiteralExp();
+ size_t hash;
+ foreach (i; 0 .. ae.elements.length)
+ hash = mixHash(hash, expressionHash(ae[i]));
+ return hash;
+ }
+
+ case EXP.assocArrayLiteral:
+ {
+ auto ae = e.isAssocArrayLiteralExp();
+ size_t hash;
+ foreach (i; 0 .. ae.keys.length)
+ // reduction needs associative op as keys are unsorted (use XOR)
+ hash ^= mixHash(expressionHash((*ae.keys)[i]), expressionHash((*ae.values)[i]));
+ return hash;
+ }
+
+ case EXP.structLiteral:
+ {
+ auto se = e.isStructLiteralExp();
+ size_t hash;
+ foreach (elem; *se.elements)
+ hash = mixHash(hash, elem ? expressionHash(elem) : 0);
+ return hash;
+ }
+
+ case EXP.variable:
+ return cast(size_t)cast(void*) e.isVarExp().var;
+
+ case EXP.function_:
+ return cast(size_t)cast(void*) e.isFuncExp().fd;
+
+ default:
+ // no custom equals for this expression
+ //assert((&e.equals).funcptr is &Expression.equals);
+ // equals based on identity
+ return cast(size_t)cast(void*) e;
+ }
+}
+
+/************************************
+ * This struct is needed for TemplateInstance to be the key in an associative array.
+ * Fixing https://issues.dlang.org/show_bug.cgi?id=15813 would make it unnecessary.
+ */
+struct TemplateInstanceBox
+{
+ TemplateInstance ti;
+ size_t hash; // cached result of toHash()
+
+ this(TemplateInstance ti)
+ {
+ this.ti = ti;
+ hash = cast(size_t)cast(void*)this.ti.enclosing;
+ hash += arrayObjectHash(this.ti.tdtypes);
+ hash += hash == 0;
+ }
+
+ size_t toHash() const @safe pure nothrow
+ {
+ assert(hash);
+ return hash;
+ }
+
+ bool opEquals(ref const TemplateInstanceBox s) @trusted const
+ {
+ bool res = void;
+ if (ti.inst && s.ti.inst)
+ {
+ /* This clause is only used when an instance with errors
+ * is replaced with a correct instance.
+ */
+ res = ti is s.ti;
+ }
+ else
+ {
+ /* Used when a proposed instance is used to see if there's
+ * an existing instance.
+ */
+ static if (__VERSION__ < 2099) // https://issues.dlang.org/show_bug.cgi?id=22717
+ res = (cast()s.ti).equalsx(cast()ti);
+ else
+ res = (cast()ti).equalsx(cast()s.ti);
+ }
+
+ debug (FindExistingInstance) ++(res ? nHits : nCollisions);
+ return res;
+ }
+
+ debug (FindExistingInstance)
+ {
+ __gshared uint nHits, nCollisions;
+
+ shared static ~this()
+ {
+ printf("debug (FindExistingInstance) TemplateInstanceBox.equals hits: %u collisions: %u\n",
+ nHits, nCollisions);
+ }
+ }
+}
+
+/************************************
+ * Perform semantic analysis on template.
+ * Params:
+ * sc = context
+ * tempdecl = template declaration
+ */
+void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl)
+{
+ enum log = false;
+ static if (log)
+ {
+ printf("TemplateDeclaration.dsymbolSemantic(this = %p, id = '%s')\n", this, tempdecl.ident.toChars());
+ printf("sc.stc = %llx\n", sc.stc);
+ printf("sc.module = %s\n", sc._module.toChars());
+ }
+ if (tempdecl.semanticRun != PASS.initial)
+ return; // semantic() already run
+
+ if (tempdecl._scope)
+ {
+ sc = tempdecl._scope;
+ tempdecl._scope = null;
+ }
+ if (!sc)
+ return;
+
+ import dmd.timetrace;
+ timeTraceBeginEvent(TimeTraceEventType.sema1TemplateDecl);
+ scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1TemplateDecl, tempdecl);
+ // Remember templates defined in module object that we need to know about
+ if (sc._module && sc._module.ident == Id.object)
+ {
+ if (tempdecl.ident == Id.RTInfo)
+ Type.rtinfo = tempdecl;
+ }
+
+ /* Remember Scope for later instantiations, but make
+ * a copy since attributes can change.
+ */
+ if (!tempdecl._scope)
+ {
+ tempdecl._scope = sc.copy();
+ tempdecl._scope.setNoFree();
+ }
+
+ tempdecl.semanticRun = PASS.semantic;
+
+ tempdecl.parent = sc.parent;
+ tempdecl.visibility = sc.visibility;
+ tempdecl.userAttribDecl = sc.userAttribDecl;
+ tempdecl.cppnamespace = sc.namespace;
+ tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_);
+ tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_);
+
+ checkGNUABITag(tempdecl, sc.linkage);
+
+ if (!tempdecl.isstatic)
+ {
+ if (auto ad = tempdecl.parent.pastMixin().isAggregateDeclaration())
+ ad.makeNested();
+ }
+
+ // Set up scope for parameters
+ auto paramsym = new ScopeDsymbol();
+ paramsym.parent = tempdecl.parent;
+ Scope* paramscope = sc.push(paramsym);
+ paramscope.stc = STC.none;
+
+ if (global.params.ddoc.doOutput)
+ {
+ tempdecl.origParameters = new TemplateParameters(tempdecl.parameters.length);
+ for (size_t i = 0; i < tempdecl.parameters.length; i++)
+ {
+ TemplateParameter tp = (*tempdecl.parameters)[i];
+ (*tempdecl.origParameters)[i] = tp.syntaxCopy();
+ }
+ }
+ for (size_t i = 0; i < tempdecl.parameters.length; i++)
+ {
+ TemplateParameter tp = (*tempdecl.parameters)[i];
+ if (!tp.declareParameter(paramscope))
+ {
+ error(tp.loc, "parameter `%s` multiply defined", tp.ident.toChars());
+ tempdecl.errors = true;
+ }
+ if (!tp.tpsemantic(paramscope, tempdecl.parameters))
+ {
+ tempdecl.errors = true;
+ }
+ if (i + 1 != tempdecl.parameters.length && tp.isTemplateTupleParameter())
+ {
+ tempdecl.computeOneMember(); // for .kind
+ .error(tempdecl.loc, "%s `%s` template sequence parameter must be the last one", tempdecl.kind, tempdecl.toPrettyChars);
+ tempdecl.errors = true;
+ }
+ }
+
+ /* Calculate TemplateParameter.dependent
+ */
+ auto tparams = TemplateParameters(1);
+ for (size_t i = 0; i < tempdecl.parameters.length; i++)
+ {
+ TemplateParameter tp = (*tempdecl.parameters)[i];
+ tparams[0] = tp;
+
+ for (size_t j = 0; j < tempdecl.parameters.length; j++)
+ {
+ // Skip cases like: X(T : T)
+ if (i == j)
+ continue;
+
+ if (TemplateTypeParameter ttp = (*tempdecl.parameters)[j].isTemplateTypeParameter())
+ {
+ if (reliesOnTident(ttp.specType, &tparams))
+ tp.dependent = true;
+ }
+ else if (TemplateAliasParameter tap = (*tempdecl.parameters)[j].isTemplateAliasParameter())
+ {
+ if (reliesOnTident(tap.specType, &tparams) ||
+ reliesOnTident(isType(tap.specAlias), &tparams))
+ {
+ tp.dependent = true;
+ }
+ }
+ }
+ }
+
+ paramscope.pop();
+
+ // Compute again
+ tempdecl.onemember = null;
+ tempdecl.computeOneMember();
+ /* BUG: should check:
+ * 1. template functions must not introduce virtual functions, as they
+ * cannot be accomodated in the vtbl[]
+ * 2. templates cannot introduce non-static data members (i.e. fields)
+ * as they would change the instance size of the aggregate.
+ */
+
+ tempdecl.semanticRun = PASS.semanticdone;
+}
+
+void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList argumentList)
+{
+ //printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc);
+ version (none)
+ {
+ for (Dsymbol s = tempinst; s; s = s.parent)
+ {
+ printf("\t%s\n", s.toChars());
+ }
+ printf("Scope\n");
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ printf("\t%s parent %s\n", scx._module ? scx._module.toChars() : "null", scx.parent ? scx.parent.toChars() : "null");
+ }
+ }
+
+ static if (LOG)
+ {
+ printf("\n+TemplateInstance.dsymbolSemantic('%s', this=%p)\n", tempinst.toChars(), tempinst);
+ }
+ if (tempinst.inst) // if semantic() was already run
+ {
+ static if (LOG)
+ {
+ printf("-TemplateInstance.dsymbolSemantic('%s', this=%p) already run\n",
+ tempinst.inst.toChars(), tempinst.inst);
+ }
+ return;
+ }
+ if (tempinst.semanticRun != PASS.initial)
+ {
+ static if (LOG)
+ {
+ printf("Recursive template expansion\n");
+ }
+ auto ungag = Ungag(global.gag);
+ if (!tempinst.gagged)
+ global.gag = 0;
+ .error(tempinst.loc, "%s `%s` recursive template expansion", tempinst.kind, tempinst.toPrettyChars);
+ if (tempinst.gagged)
+ tempinst.semanticRun = PASS.initial;
+ else
+ tempinst.inst = tempinst;
+ tempinst.errors = true;
+ return;
+ }
+
+ timeTraceBeginEvent(TimeTraceEventType.sema1TemplateInstance);
+ scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1TemplateInstance, tempinst);
+
+ // Get the enclosing template instance from the scope tinst
+ tempinst.tinst = sc.tinst;
+
+ // Get the instantiating module from the scope minst
+ tempinst.minst = sc.minst;
+ // https://issues.dlang.org/show_bug.cgi?id=10920
+ // If the enclosing function is non-root symbol,
+ // this instance should be speculative.
+ if (!tempinst.tinst && sc.func && sc.func.inNonRoot())
+ {
+ tempinst.minst = null;
+ }
+
+ tempinst.gagged = (global.gag > 0);
+
+ tempinst.semanticRun = PASS.semantic;
+
+ static if (LOG)
+ {
+ printf("\tdo semantic\n");
+ }
+ /* Find template declaration first,
+ * then run semantic on each argument (place results in tiargs[]),
+ * last find most specialized template from overload list/set.
+ */
+ if (!tempinst.findTempDecl(sc, null) || !tempinst.semanticTiargs(sc) || !tempinst.findBestMatch(sc, argumentList))
+ {
+ Lerror:
+ if (tempinst.gagged)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=13220
+ // Roll back status for later semantic re-running
+ tempinst.semanticRun = PASS.initial;
+ }
+ else
+ tempinst.inst = tempinst;
+ tempinst.errors = true;
+ return;
+ }
+ TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ if (global.params.v.templates)
+ TemplateStats.incInstance(tempdecl, tempinst, global.params.v.templatesListInstances);
+
+ tempdecl.checkDeprecated(tempinst.loc, sc);
+
+ // If tempdecl is a mixin, disallow it
+ if (tempdecl.ismixin)
+ {
+ .error(tempinst.loc, "%s `%s` mixin templates are not regular templates", tempinst.kind, tempinst.toPrettyChars);
+ goto Lerror;
+ }
+
+ tempinst.hasNestedArgs(tempinst.tiargs, tempdecl.isstatic);
+ if (tempinst.errors)
+ goto Lerror;
+
+ // Copy the tempdecl namespace (not the scope one)
+ tempinst.cppnamespace = tempdecl.cppnamespace;
+ if (tempinst.cppnamespace)
+ tempinst.cppnamespace.dsymbolSemantic(sc);
+
+ /* Greatly simplified semantic processing for AliasSeq templates
+ */
+ if (tempdecl.isTrivialAliasSeq)
+ {
+ tempinst.inst = tempinst;
+ return aliasSeqInstanceSemantic(tempinst, sc, tempdecl);
+ }
+
+ /* Greatly simplified semantic processing for Alias templates
+ */
+ else if (tempdecl.isTrivialAlias)
+ {
+ tempinst.inst = tempinst;
+ return aliasInstanceSemantic(tempinst, sc, tempdecl);
+ }
+
+
+ /* See if there is an existing TemplateInstantiation that already
+ * implements the typeargs. If so, just refer to that one instead.
+ */
+ tempinst.inst = tempdecl.findExistingInstance(tempinst, argumentList);
+ TemplateInstance errinst = null;
+ if (!tempinst.inst)
+ {
+ // So, we need to implement 'this' instance.
+ }
+ else if (tempinst.inst.gagged && !tempinst.gagged && tempinst.inst.errors)
+ {
+ // If the first instantiation had failed, re-run semantic,
+ // so that error messages are shown.
+ errinst = tempinst.inst;
+ }
+ else
+ {
+ // It's a match
+ tempinst.parent = tempinst.inst.parent;
+ tempinst.errors = tempinst.inst.errors;
+
+ // If both this and the previous instantiation were gagged,
+ // use the number of errors that happened last time.
+ global.errors += tempinst.errors;
+ global.gaggedErrors += tempinst.errors;
+
+ // If the first instantiation was gagged, but this is not:
+ if (tempinst.inst.gagged)
+ {
+ // It had succeeded, mark it is a non-gagged instantiation,
+ // and reuse it.
+ tempinst.inst.gagged = tempinst.gagged;
+ }
+
+ tempinst.tnext = tempinst.inst.tnext;
+ tempinst.inst.tnext = tempinst;
+
+ /* A module can have explicit template instance and its alias
+ * in module scope (e,g, `alias Base64 = Base64Impl!('+', '/');`).
+ * If the first instantiation 'inst' had happened in non-root module,
+ * compiler can assume that its instantiated code would be included
+ * in the separately compiled obj/lib file (e.g. phobos.lib).
+ *
+ * However, if 'this' second instantiation happened in root module,
+ * compiler might need to invoke its codegen
+ * (https://issues.dlang.org/show_bug.cgi?id=2500 & https://issues.dlang.org/show_bug.cgi?id=2644).
+ * But whole import graph is not determined until all semantic pass finished,
+ * so 'inst' should conservatively finish the semantic3 pass for the codegen.
+ */
+ if (tempinst.minst && tempinst.minst.isRoot() && !(tempinst.inst.minst && tempinst.inst.minst.isRoot()))
+ {
+ /* Swap the position of 'inst' and 'this' in the instantiation graph.
+ * Then, the primary instance `inst` will be changed to a root instance,
+ * along with all members of `inst` having their scopes updated.
+ *
+ * Before:
+ * non-root -> A!() -> B!()[inst] -> C!() { members[non-root] }
+ * |
+ * root -> D!() -> B!()[this]
+ *
+ * After:
+ * non-root -> A!() -> B!()[this]
+ * |
+ * root -> D!() -> B!()[inst] -> C!() { members[root] }
+ */
+ Module mi = tempinst.minst;
+ TemplateInstance ti = tempinst.tinst;
+ tempinst.minst = tempinst.inst.minst;
+ tempinst.tinst = tempinst.inst.tinst;
+ tempinst.inst.minst = mi;
+ tempinst.inst.tinst = ti;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=21299
+ `minst` has been updated on the primary instance `inst` so it is
+ now coming from a root module, however all Dsymbol `inst.members`
+ of the instance still have their `_scope.minst` pointing at the
+ original non-root module. We must now propagate `minst` to all
+ members so that forward referenced dependencies that get
+ instantiated will also be appended to the root module, otherwise
+ there will be undefined references at link-time. */
+ extern (C++) final class InstMemberWalker : Visitor
+ {
+ alias visit = Visitor.visit;
+ TemplateInstance inst;
+
+ extern (D) this(TemplateInstance inst) scope @safe
+ {
+ this.inst = inst;
+ }
+
+ override void visit(Dsymbol d)
+ {
+ if (d._scope)
+ d._scope.minst = inst.minst;
+ }
+
+ override void visit(ScopeDsymbol sds)
+ {
+ sds.members.foreachDsymbol( s => s.accept(this) );
+ 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) );
+ visit(cast(Dsymbol)ad);
+ }
+
+ override void visit(ConditionalDeclaration cd)
+ {
+ if (cd.condition.inc)
+ visit(cast(AttribDeclaration)cd);
+ else
+ visit(cast(Dsymbol)cd);
+ }
+ }
+ scope v = new InstMemberWalker(tempinst.inst);
+ tempinst.inst.accept(v);
+
+ if (!global.params.allInst &&
+ tempinst.minst) // if inst was not speculative...
+ {
+ assert(!tempinst.minst.isRoot()); // ... it was previously appended to a non-root module
+ // Append again to the root module members[], so that the instance will
+ // get codegen chances (depending on `tempinst.inst.needsCodegen()`).
+ tempinst.inst.appendToModuleMember();
+ }
+
+ assert(tempinst.inst.memberOf && tempinst.inst.memberOf.isRoot(), "no codegen chances");
+ }
+
+ // modules imported by an existing instance should be added to the module
+ // that instantiates the instance.
+ if (tempinst.minst)
+ foreach(imp; tempinst.inst.importedModules)
+ if (!tempinst.minst.aimports.contains(imp))
+ tempinst.minst.aimports.push(imp);
+
+ static if (LOG)
+ {
+ printf("\tit's a match with instance %p, %d\n", tempinst.inst, tempinst.inst.semanticRun);
+ }
+ return;
+ }
+ static if (LOG)
+ {
+ printf("\timplement template instance %s '%s'\n", tempdecl.parent.toChars(), tempinst.toChars());
+ printf("\ttempdecl %s\n", tempdecl.toChars());
+ }
+ const errorsave = global.errors;
+
+ tempinst.inst = tempinst;
+ tempinst.parent = tempinst.enclosing ? tempinst.enclosing : tempdecl.parent;
+ //printf("parent = '%s'\n", parent.kind());
+
+ if (global.params.v.templates)
+ TemplateStats.incUnique(tempdecl, tempinst);
+
+ TemplateInstance tempdecl_instance_idx = tempdecl.addInstance(tempinst);
+
+ //getIdent();
+
+ // Store the place we added it to in target_symbol_list(_idx) so we can
+ // remove it later if we encounter an error.
+ Dsymbols* target_symbol_list = tempinst.appendToModuleMember();
+ size_t target_symbol_list_idx = target_symbol_list ? target_symbol_list.length - 1 : 0;
+
+ // Copy the syntax trees from the TemplateDeclaration
+ tempinst.members = Dsymbol.arraySyntaxCopy(tempdecl.members);
+
+ // resolve TemplateThisParameter
+ for (size_t i = 0; i < tempdecl.parameters.length; i++)
+ {
+ if ((*tempdecl.parameters)[i].isTemplateThisParameter() is null)
+ continue;
+ Type t = isType((*tempinst.tiargs)[i]);
+ assert(t);
+ if (STC stc = ModToStc(t.mod))
+ {
+ //printf("t = %s, stc = x%llx\n", t.toChars(), stc);
+ auto s = new Dsymbols(new StorageClassDeclaration(stc, tempinst.members));
+ tempinst.members = s;
+ }
+ break;
+ }
+
+ // Create our own scope for the template parameters
+ Scope* _scope = tempdecl._scope;
+ if (tempdecl.semanticRun == PASS.initial)
+ {
+ .error(tempinst.loc, "%s `%s` template instantiation `%s` forward references template declaration `%s`",
+ tempinst.kind, tempinst.toPrettyChars, tempinst.toChars(), tempdecl.toChars());
+ return;
+ }
+
+ static if (LOG)
+ {
+ printf("\tcreate scope for template parameters '%s'\n", tempinst.toChars());
+ }
+ tempinst.argsym = new ScopeDsymbol();
+ tempinst.argsym.parent = _scope.parent;
+ _scope = _scope.push(tempinst.argsym);
+ _scope.tinst = tempinst;
+ _scope.minst = tempinst.minst;
+ //scope.stc = STC.none;
+
+ if (sc.isKnownToHaveACompileTimeContext)
+ _scope.knownACompileTimeOnlyContext = true;
+
+ // Declare each template parameter as an alias for the argument type
+ Scope* paramscope = _scope.push();
+ paramscope.stc = STC.none;
+ paramscope.visibility = Visibility(Visibility.Kind.public_); // https://issues.dlang.org/show_bug.cgi?id=14169
+ // template parameters should be public
+ tempinst.declareParameters(paramscope);
+ paramscope.pop();
+
+ // Add members of template instance to template instance symbol table
+ //parent = scope.scopesym;
+ tempinst.symtab = new DsymbolTable();
+
+ tempinst.members.foreachDsymbol( (s)
+ {
+ static if (LOG)
+ {
+ printf("\t adding member '%s' %p kind %s to '%s'\n", s.toChars(), s, s.kind(), tempinst.toChars());
+ }
+ s.addMember(_scope, tempinst);
+ });
+
+ static if (LOG)
+ {
+ printf("adding members done\n");
+ }
+
+ /* See if there is only one member of template instance, and that
+ * member has the same name as the template instance.
+ * If so, this template instance becomes an alias for that member.
+ */
+ //printf("members.length = %d\n", tempinst.members.length);
+ if (tempinst.members.length)
+ {
+ Dsymbol s;
+ if (oneMembers(tempinst.members, s, tempdecl.ident) && s)
+ {
+ //printf("tempdecl.ident = %s, s = `%s %s`\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
+ //printf("setting aliasdecl\n");
+ tempinst.aliasdecl = s;
+ }
+ }
+
+ /* If function template declaration
+ */
+ if (argumentList.length > 0 && tempinst.aliasdecl)
+ {
+ if (auto fd = tempinst.aliasdecl.isFuncDeclaration())
+ {
+ /* Transmit fargs to type so that TypeFunction.dsymbolSemantic() can
+ * resolve any "auto ref" storage classes.
+ */
+ if (fd.type)
+ if (auto tf = fd.type.isTypeFunction())
+ tf.inferenceArguments = argumentList;
+ }
+ }
+
+ // Do semantic() analysis on template instance members
+ static if (LOG)
+ {
+ printf("\tdo semantic() on template instance members '%s'\n", tempinst.toChars());
+ }
+ Scope* sc2;
+ sc2 = _scope.push(tempinst);
+ //printf("enclosing = %d, sc.parent = %s\n", tempinst.enclosing, sc.parent.toChars());
+ sc2.parent = tempinst;
+ sc2.tinst = tempinst;
+ sc2.minst = tempinst.minst;
+ sc2.stc &= ~STC.deprecated_;
+ tempinst.tryExpandMembers(sc2);
+
+ tempinst.semanticRun = PASS.semanticdone;
+
+ /* ConditionalDeclaration may introduce eponymous declaration,
+ * so we should find it once again after semantic.
+ */
+ if (tempinst.members.length)
+ {
+ Dsymbol s;
+ if (oneMembers(tempinst.members, s, tempdecl.ident) && s)
+ {
+ if (!tempinst.aliasdecl || tempinst.aliasdecl != s)
+ {
+ //printf("tempdecl.ident = %s, s = `%s %s`\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
+ //printf("setting aliasdecl 2\n");
+ tempinst.aliasdecl = s;
+ }
+ }
+ }
+
+ if (global.errors != errorsave)
+ goto Laftersemantic;
+
+ /* If any of the instantiation members didn't get semantic() run
+ * on them due to forward references, we cannot run semantic2()
+ * or semantic3() yet.
+ */
+ {
+ bool found_deferred_ad = false;
+ for (size_t i = 0; i < Module.deferred.length; i++)
+ {
+ Dsymbol sd = Module.deferred[i];
+ AggregateDeclaration ad = sd.isAggregateDeclaration();
+ if (ad && ad.parent && ad.parent.isTemplateInstance())
+ {
+ //printf("deferred template aggregate: %s %s\n",
+ // sd.parent.toChars(), sd.toChars());
+ found_deferred_ad = true;
+ if (ad.parent == tempinst)
+ {
+ ad.deferred = tempinst;
+ break;
+ }
+ }
+ }
+ if (found_deferred_ad || Module.deferred.length)
+ goto Laftersemantic;
+ }
+
+ /* The problem is when to parse the initializer for a variable.
+ * Perhaps VarDeclaration.dsymbolSemantic() should do it like it does
+ * for initializers inside a function.
+ */
+ //if (sc.parent.isFuncDeclaration())
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=782
+ * this has problems if the classes this depends on
+ * are forward referenced. Find a way to defer semantic()
+ * on this template.
+ */
+ tempinst.semantic2(sc2);
+ }
+ if (global.errors != errorsave)
+ goto Laftersemantic;
+
+ if ((sc.func || sc.fullinst) && !tempinst.tinst)
+ {
+ /* If a template is instantiated inside function, the whole instantiation
+ * should be done at that position. But, immediate running semantic3 of
+ * dependent templates may cause unresolved forward reference.
+ * https://issues.dlang.org/show_bug.cgi?id=9050
+ * To avoid the issue, don't run semantic3 until semantic and semantic2 done.
+ */
+ TemplateInstances deferred;
+ tempinst.deferred = &deferred;
+
+ //printf("Run semantic3 on %s\n", toChars());
+
+ /* https://issues.dlang.org/show_bug.cgi?id=23965
+ * DRuntime hooks are not deprecated, but may be used for deprecated
+ * types. Deprecations are disabled while analysing hooks to avoid
+ * spurious error messages.
+ */
+ auto saveUseDeprecated = global.params.useDeprecated;
+ if (sc.isDeprecated() && isDRuntimeHook(tempinst.name))
+ global.params.useDeprecated = DiagnosticReporting.off;
+
+ tempinst.trySemantic3(sc2);
+
+ global.params.useDeprecated = saveUseDeprecated;
+
+ for (size_t i = 0; i < deferred.length; i++)
+ {
+ //printf("+ run deferred semantic3 on %s\n", deferred[i].toChars());
+ deferred[i].semantic3(null);
+ }
+
+ tempinst.deferred = null;
+ }
+ else if (tempinst.tinst)
+ {
+ bool doSemantic3 = false;
+ FuncDeclaration fd;
+ if (tempinst.aliasdecl)
+ fd = tempinst.aliasdecl.toAlias2().isFuncDeclaration();
+
+ if (fd)
+ {
+ /* Template function instantiation should run semantic3 immediately
+ * for attribute inference.
+ */
+ scope fld = fd.isFuncLiteralDeclaration();
+ if (fld && fld.tok == TOK.reserved)
+ doSemantic3 = true;
+ else if (sc.func)
+ doSemantic3 = true;
+ }
+ else if (sc.func)
+ {
+ /* A lambda function in template arguments might capture the
+ * instantiated scope context. For the correct context inference,
+ * all instantiated functions should run the semantic3 immediately.
+ * See also compilable/test14973.d
+ */
+ foreach (oarg; tempinst.tdtypes)
+ {
+ auto s = getDsymbol(oarg);
+ if (!s)
+ continue;
+
+ if (auto td = s.isTemplateDeclaration())
+ {
+ if (!td.literal)
+ continue;
+ assert(td.members && td.members.length == 1);
+ s = (*td.members)[0];
+ }
+ if (auto fld = s.isFuncLiteralDeclaration())
+ {
+ if (fld.tok == TOK.reserved)
+ {
+ doSemantic3 = true;
+ break;
+ }
+ }
+ }
+ //printf("[%s] %s doSemantic3 = %d\n", tempinst.tinst.loc.toChars(), tempinst.tinst.toChars(), doSemantic3);
+ }
+ if (doSemantic3)
+ tempinst.trySemantic3(sc2);
+
+ TemplateInstance ti = tempinst.tinst;
+ int nest = 0;
+ while (ti && !ti.deferred && ti.tinst)
+ {
+ ti = ti.tinst;
+ if (++nest > global.recursionLimit)
+ {
+ global.gag = 0; // ensure error message gets printed
+ .error(tempinst.loc, "%s `%s` recursive expansion", tempinst.kind, tempinst.toPrettyChars);
+ fatal();
+ }
+ }
+ if (ti && ti.deferred)
+ {
+ //printf("deferred semantic3 of %p %s, ti = %s, ti.deferred = %p\n", this, toChars(), ti.toChars());
+ for (size_t i = 0;; i++)
+ {
+ if (i == ti.deferred.length)
+ {
+ ti.deferred.push(tempinst);
+ break;
+ }
+ if ((*ti.deferred)[i] == tempinst)
+ break;
+ }
+ }
+ }
+
+ if (tempinst.aliasdecl)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=13816
+ * AliasDeclaration tries to resolve forward reference
+ * twice (See inuse check in AliasDeclaration.toAlias()). It's
+ * necessary to resolve mutual references of instantiated symbols, but
+ * it will left a true recursive alias in tuple declaration - an
+ * AliasDeclaration A refers TupleDeclaration B, and B contains A
+ * in its elements. To correctly make it an error, we strictly need to
+ * resolve the alias of eponymous member.
+ */
+ tempinst.aliasdecl = tempinst.aliasdecl.toAlias2();
+
+ // stop AliasAssign tuple building
+ if (auto td = tempinst.aliasdecl.isTupleDeclaration())
+ td.building = false;
+ }
+
+Laftersemantic:
+ sc2.pop();
+ _scope.pop();
+
+ // Give additional context info if error occurred during instantiation
+ if (global.errors != errorsave)
+ {
+ if (!tempinst.errors)
+ {
+ if (!tempdecl.literal)
+ .error(tempinst.loc, "%s `%s` error instantiating", tempinst.kind, tempinst.toPrettyChars);
+ if (tempinst.tinst)
+ tempinst.tinst.printInstantiationTrace();
+ }
+ tempinst.errors = true;
+ if (tempinst.gagged)
+ {
+ // Errors are gagged, so remove the template instance from the
+ // instance/symbol lists we added it to and reset our state to
+ // finish clean and so we can try to instantiate it again later
+ // (see https://issues.dlang.org/show_bug.cgi?id=4302 and https://issues.dlang.org/show_bug.cgi?id=6602).
+ tempdecl.removeInstance(tempdecl_instance_idx);
+ if (target_symbol_list)
+ {
+ // Because we added 'this' in the last position above, we
+ // should be able to remove it without messing other indices up.
+ assert((*target_symbol_list)[target_symbol_list_idx] == tempinst);
+ target_symbol_list.remove(target_symbol_list_idx);
+ tempinst.memberOf = null; // no longer a member
+ }
+ tempinst.semanticRun = PASS.initial;
+ tempinst.inst = null;
+ tempinst.symtab = null;
+ }
+ }
+ else if (errinst)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=14541
+ * If the previous gagged instance had failed by
+ * circular references, currrent "error reproduction instantiation"
+ * might succeed, because of the difference of instantiated context.
+ * On such case, the cached error instance needs to be overridden by the
+ * succeeded instance.
+ */
+ //printf("replaceInstance()\n");
+ assert(errinst.errors);
+ auto ti1 = TemplateInstanceBox(errinst);
+ (cast(TemplateInstance[TemplateInstanceBox])tempdecl.instances).remove(ti1);
+
+ auto ti2 = TemplateInstanceBox(tempinst);
+ (*(cast(TemplateInstance[TemplateInstanceBox]*) &tempdecl.instances))[ti2] = tempinst;
+ }
+
+ static if (LOG)
+ {
+ printf("-TemplateInstance.dsymbolSemantic('%s', this=%p)\n", tempinst.toChars(), tempinst);
+ }
+}
+
+/*****************************************
+ * Determines if a TemplateInstance will need a nested
+ * generation of the TemplateDeclaration.
+ * Sets enclosing property if so, and returns != 0;
+ */
+private bool hasNestedArgs(TemplateInstance _this, Objects* args, bool isstatic)
+{
+ int nested = 0;
+ //printf("TemplateInstance.hasNestedArgs('%s')\n", tempdecl.ident.toChars());
+
+ // arguments from parent instances are also accessible
+ if (!_this.enclosing)
+ {
+ if (TemplateInstance ti = _this.tempdecl.toParent().isTemplateInstance())
+ _this.enclosing = ti.enclosing;
+ }
+
+ /* Search for the most deeply nested of `dparent` and `enclosing` assigning
+ * `dparent` to `enclosing` if `dparent` is more nested than `enclosing`.
+ *
+ * Returns:
+ * `true` if an error should be reported
+ */
+ static bool search(Dsymbol dparent, ref Dsymbol enclosing)
+ {
+ if (!dparent || dparent.isModule)
+ return false;
+ if (!enclosing)
+ {
+ enclosing = dparent;
+ return false;
+ }
+ if (enclosing == dparent)
+ return false;
+
+ /* Select the more deeply nested of the two.
+ * Error if one is not nested inside the other.
+ */
+ for (Dsymbol p = enclosing; p; p = p.parent)
+ {
+ if (p == dparent)
+ return false; // enclosing is most nested
+ }
+ for (Dsymbol p = dparent; p; p = p.parent)
+ {
+ if (p == enclosing)
+ {
+ enclosing = dparent;
+ return false; // dparent is most nested
+ }
+ }
+ //https://issues.dlang.org/show_bug.cgi?id=17870
+ auto pc = dparent.isClassDeclaration();
+ auto ec = enclosing.isClassDeclaration();
+ if (pc && ec)
+ {
+ if (pc.isBaseOf(ec, null))
+ return false;
+ else if (ec.isBaseOf(pc, null))
+ {
+ enclosing = dparent;
+ return false;
+ }
+ }
+ return true;
+ }
+ int search2(Dsymbol sa)
+ {
+ Dsymbol dparent = sa.toParent2();
+ if (search(dparent, _this.enclosing))
+ {
+ .error(_this.loc, "%s `%s` `%s` is nested in both `%s` and `%s`",
+ _this.kind, _this.toPrettyChars(), _this.toChars(),
+ _this.enclosing.toChars(), dparent.toChars());
+ _this.errors = true;
+ }
+ //printf("\tnested inside %s as it references %s\n", enclosing.toChars(), sa.toChars());
+ return 1;
+ }
+ int dsym(Dsymbol sa)
+ {
+ sa = sa.toAlias();
+ TemplateDeclaration td = sa.isTemplateDeclaration();
+ if (td)
+ {
+ TemplateInstance ti = sa.toParent().isTemplateInstance();
+ if (ti && ti.enclosing)
+ sa = ti;
+ }
+ TemplateInstance ti = sa.isTemplateInstance();
+ Declaration d = sa.isDeclaration();
+ if (td && td.literal)
+ return search2(sa);
+ if (ti && ti.enclosing)
+ return search2(sa);
+ if (d && !d.isDataseg()
+ && !(d.storage_class & STC.manifest)
+ && (!d.isFuncDeclaration() || d.isFuncDeclaration().isNested())
+ && !_this.isTemplateMixin())
+ {
+ return search2(sa);
+ }
+ return 0;
+ }
+ /* A nested instance happens when an argument references a local
+ * symbol that is on the stack.
+ */
+ foreach (o; *args)
+ {
+ if (Dsymbol sa = isDsymbol(o))
+ {
+ nested |= dsym(sa);
+ continue;
+ }
+ else if (Tuple va = isTuple(o))
+ {
+ nested |= cast(int)_this.hasNestedArgs(&va.objects, isstatic);
+ continue;
+ }
+ Expression ea = isExpression(o);
+ if (!ea)
+ continue;
+
+ if (auto ve = ea.isVarExp())
+ {
+ nested |= dsym(ve.var);
+ continue;
+ }
+ if (auto te = ea.isThisExp())
+ {
+ nested |= dsym(te.var);
+ continue;
+ }
+ if (auto fe = ea.isFuncExp())
+ {
+ nested |= dsym(fe.td? fe.td : fe.fd);
+ continue;
+ }
+ // Emulate Expression.toMangleBuffer call that had exist in TemplateInstance.genIdent.
+ if (ea.op != EXP.int64 && ea.op != EXP.float64 && ea.op != EXP.complex80 && ea.op != EXP.null_ && ea.op != EXP.string_ && ea.op != EXP.arrayLiteral && ea.op != EXP.assocArrayLiteral && ea.op != EXP.structLiteral)
+ {
+ if (!ea.type.isTypeError())
+ .error(ea.loc, "%s `%s` expression `%s` is not a valid template value argument", _this.kind, _this.toPrettyChars, ea.toChars());
+ _this.errors = true;
+ }
+ }
+ //printf("-TemplateInstance.hasNestedArgs('%s') = %d\n", tempdecl.ident.toChars(), nested);
+ return nested != 0;
+}
+
+/// Pair of MATCHes
+private struct MATCHpair
+{
+ MATCH mta; /// match template parameters by initial template arguments
+ MATCH mfa; /// match template parameters by inferred template arguments
+
+ debug this(MATCH mta, MATCH mfa)
+ {
+ assert(MATCH.min <= mta && mta <= MATCH.max);
+ assert(MATCH.min <= mfa && mfa <= MATCH.max);
+ this.mta = mta;
+ this.mfa = mfa;
+ }
+}
+
+/*****************************************
+ * Append `ti` to the specific module `ti.members[]`
+ */
+private Dsymbols* appendToModuleMember(TemplateInstance ti)
+{
+ Module mi = ti.minst; // instantiated . inserted module
+
+ //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n",
+ // toPrettyChars(),
+ // enclosing ? enclosing.toPrettyChars() : null,
+ // mi ? mi.toPrettyChars() : null);
+ if (global.params.allInst || !mi || mi.isRoot())
+ {
+ /* If the instantiated module is speculative or root, insert to the
+ * member of a root module. Then:
+ * - semantic3 pass will get called on the instance members.
+ * - codegen pass will get a selection chance to do/skip it (needsCodegen()).
+ */
+ static Dsymbol getStrictEnclosing(TemplateInstance ti)
+ {
+ do
+ {
+ if (ti.enclosing)
+ return ti.enclosing;
+ ti = ti.tempdecl.isInstantiated();
+ } while (ti);
+ return null;
+ }
+
+ Dsymbol enc = getStrictEnclosing(ti);
+ // insert target is made stable by using the module
+ // where tempdecl is declared.
+ mi = (enc ? enc : ti.tempdecl).getModule();
+ if (!mi.isRoot())
+ {
+ if (mi.importedFrom)
+ {
+ mi = mi.importedFrom;
+ assert(mi.isRoot());
+ }
+ else
+ {
+ // This can happen when using the frontend as a library.
+ // Append it to the non-root module.
+ }
+ }
+ }
+ else
+ {
+ /* If the instantiated module is non-root, insert to the member of the
+ * non-root module. Then:
+ * - semantic3 pass won't be called on the instance.
+ * - codegen pass won't reach to the instance.
+ * Unless it is re-appended to a root module later (with changed minst).
+ */
+ }
+ //printf("\t-. mi = %s\n", mi.toPrettyChars());
+
+ if (ti.memberOf) // already appended to some module
+ {
+ assert(mi.isRoot(), "can only re-append to a root module");
+ if (ti.memberOf.isRoot())
+ return null; // no need to move to another root module
+ }
+
+ Dsymbols* a = mi.members;
+ a.push(ti);
+ ti.memberOf = mi;
+ if (mi.semanticRun >= PASS.semantic2done && mi.isRoot())
+ addDeferredSemantic2(ti);
+ if (mi.semanticRun >= PASS.semantic3done && mi.isRoot())
+ addDeferredSemantic3(ti);
+ return a;
+}
+
+private void expandMembers(TemplateInstance ti,Scope* sc2)
+{
+ ti.members.foreachDsymbol( (s) { s.setScope (sc2); } );
+
+ ti.members.foreachDsymbol( (s) { s.importAll(sc2); } );
+
+ if (!ti.aliasdecl)
+ {
+ /* static if's are crucial to evaluating aliasdecl correctly. But
+ * evaluating the if/else bodies may require aliasdecl.
+ * So, evaluate the condition for static if's, but not their if/else bodies.
+ * Then try to set aliasdecl.
+ * Later do the if/else bodies.
+ * https://issues.dlang.org/show_bug.cgi?id=23598
+ * It might be better to do this by attaching a lambda to the StaticIfDeclaration
+ * to do the oneMembers call after the sid.include(sc2) is run as part of dsymbolSemantic().
+ */
+ bool done;
+ void staticIfDg(Dsymbol s)
+ {
+ if (done || ti.aliasdecl)
+ return;
+ //printf("\t staticIfDg on '%s %s' in '%s'\n", s.kind(), s.toChars(), this.toChars());
+ if (!s.isStaticIfDeclaration())
+ {
+ //s.dsymbolSemantic(sc2);
+ done = true;
+ return;
+ }
+ auto sid = s.isStaticIfDeclaration();
+ sid.include(sc2);
+ if (ti.members.length)
+ {
+ Dsymbol sa;
+ if (oneMembers(ti.members, sa, ti.tempdecl.ident) && sa)
+ ti.aliasdecl = sa;
+ }
+ done = true;
+ }
+
+ ti.members.foreachDsymbol(&staticIfDg);
+ }
+
+ void symbolDg(Dsymbol s)
+ {
+ //printf("\t semantic on '%s' %p kind %s in '%s'\n", s.toChars(), s, s.kind(), this.toChars());
+ //printf("test: enclosing = %d, sc2.parent = %s\n", enclosing, sc2.parent.toChars());
+ //if (enclosing)
+ // s.parent = sc.parent;
+ //printf("test3: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
+ s.dsymbolSemantic(sc2);
+ //printf("test4: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
+ runDeferredSemantic();
+ }
+
+ ti.members.foreachDsymbol(&symbolDg);
+}
+
+private void tryExpandMembers(TemplateInstance ti, Scope* sc2)
+{
+ __gshared int nest;
+ // extracted to a function to allow windows SEH to work without destructors in the same function
+ //printf("%d\n", nest);
+ if (++nest > global.recursionLimit)
+ {
+ global.gag = 0; // ensure error message gets printed
+ .error(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
+ fatal();
+ }
+
+ ti.expandMembers(sc2);
+
+ nest--;
+}
+
+private void trySemantic3(TemplateInstance ti, Scope* sc2)
+{
+ // extracted to a function to allow windows SEH to work without destructors in the same function
+ __gshared int nest;
+ //printf("%d\n", nest);
+ if (++nest > global.recursionLimit)
+ {
+ global.gag = 0; // ensure error message gets printed
+ .error(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
+ fatal();
+ }
+
+ semantic3(ti, sc2);
+
+ --nest;
+}
+
+debug (FindExistingInstance)
+{
+ private __gshared uint nFound, nNotFound, nAdded, nRemoved;
+
+ shared static ~this()
+ {
+ printf("debug (FindExistingInstance) nFound %u, nNotFound: %u, nAdded: %u, nRemoved: %u\n",
+ nFound, nNotFound, nAdded, nRemoved);
+ }
+}
+
+/******************************************************
+ * Do template instance semantic for isAlias templates.
+ * This is a greatly simplified version of templateInstanceSemantic().
+ */
+private
+void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclaration tempdecl)
+{
+ //printf("[%s] aliasInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars());
+ Scope* paramscope = sc.push();
+ paramscope.stc = STC.none;
+ paramscope.visibility = Visibility(Visibility.Kind.public_);
+
+ TemplateTypeParameter ttp = (*tempdecl.parameters)[0].isTemplateTypeParameter();
+ Type ta = tempinst.tdtypes[0].isType();
+ tempdecl.computeOneMember();
+ auto ad = tempdecl.onemember.isAliasDeclaration();
+
+ // Note: qualifiers can be in both 'ad.type.mod' and 'ad.storage_class'
+ Declaration d = new AliasDeclaration(tempinst.loc, ttp.ident, ta.addMod(ad.type.mod));
+ d.storage_class |= STC.templateparameter | ad.storage_class;
+ d.dsymbolSemantic(sc);
+
+ paramscope.pop();
+
+ tempinst.aliasdecl = d;
+
+ tempinst.semanticRun = PASS.semanticdone;
+}
+
+/****************************************************
+ * Given a new instance `tithis` of this TemplateDeclaration,
+ * see if there already exists an instance.
+ *
+ * Params:
+ * td = template declaration
+ * tithis = template instance to check
+ * argumentList = For function templates, needed because different
+ * `auto ref` resolutions create different instances,
+ * even when template parameters are identical
+ *
+ * Returns: that existing instance, or `null` when it doesn't exist
+ */
+private TemplateInstance findExistingInstance(TemplateDeclaration td, TemplateInstance tithis,
+ ArgumentList argumentList)
+{
+ //printf("findExistingInstance() %s\n", tithis.toChars());
+ tithis.fargs = argumentList.arguments;
+ tithis.fnames = argumentList.names;
+ auto tibox = TemplateInstanceBox(tithis);
+ auto p = tibox in cast(TemplateInstance[TemplateInstanceBox]) td.instances;
+ debug (FindExistingInstance) ++(p ? nFound : nNotFound);
+ //if (p) printf("\tfound %p\n", *p); else printf("\tnot found\n");
+ return p ? *p : null;
+}
+
+/********************************************
+ * Add instance ti to TemplateDeclaration's table of instances.
+ * Return a handle we can use to later remove it if it fails instantiation.
+ */
+private TemplateInstance addInstance(TemplateDeclaration td, TemplateInstance ti)
+{
+ //printf("addInstance() %p %s\n", instances, ti.toChars());
+ auto tibox = TemplateInstanceBox(ti);
+ (*(cast(TemplateInstance[TemplateInstanceBox]*) &td.instances))[tibox] = ti;
+ debug (FindExistingInstance) ++nAdded;
+ return ti;
+}
+
+/*******************************************
+ * Remove TemplateInstance from table of instances.
+ * Input:
+ * handle returned by addInstance()
+ */
+private void removeInstance(TemplateDeclaration td, TemplateInstance ti)
+{
+ //printf("removeInstance() %s\n", ti.toChars());
+ auto tibox = TemplateInstanceBox(ti);
+ debug (FindExistingInstance) ++nRemoved;
+ (cast(TemplateInstance[TemplateInstanceBox])td.instances).remove(tibox);
+}
+
+/******************************************************
+ * Do template instance semantic for isAliasSeq templates.
+ * This is a greatly simplified version of templateInstanceSemantic().
+ */
+private
+void aliasSeqInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclaration tempdecl)
+{
+ //printf("[%s] aliasSeqInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars());
+ Scope* paramscope = sc.push();
+ paramscope.stc = STC.none;
+ paramscope.visibility = Visibility(Visibility.Kind.public_);
+
+ TemplateTupleParameter ttp = (*tempdecl.parameters)[0].isTemplateTupleParameter();
+ Tuple va = tempinst.tdtypes[0].isTuple();
+ Declaration d = new TupleDeclaration(tempinst.loc, ttp.ident, &va.objects);
+ d.storage_class |= STC.templateparameter;
+ d.dsymbolSemantic(sc);
+
+ paramscope.pop();
+
+ tempinst.aliasdecl = d;
+
+ tempinst.semanticRun = PASS.semanticdone;
+}
+
+/**********************************************
+ * Find template declaration corresponding to template instance.
+ *
+ * Returns:
+ * false if finding fails.
+ * Note:
+ * This function is reentrant against error occurrence. If returns false,
+ * any members of this object won't be modified, and repetition call will
+ * reproduce same error.
+ */
+bool findTempDecl(TemplateInstance ti, Scope* sc, WithScopeSymbol* pwithsym)
+{
+ if (pwithsym)
+ *pwithsym = null;
+
+ if (ti.havetempdecl)
+ return true;
+
+ //printf("TemplateInstance.findTempDecl() %s\n", toChars());
+ if (!ti.tempdecl)
+ {
+ /* Given:
+ * foo!( ... )
+ * figure out which TemplateDeclaration foo refers to.
+ */
+ Identifier id = ti.name;
+ Dsymbol scopesym;
+ Dsymbol s = sc.search(ti.loc, id, scopesym);
+ if (!s)
+ {
+ s = sc.search_correct(id);
+ if (s)
+ .error(ti.loc, "%s `%s` template `%s` is not defined, did you mean %s?", ti.kind, ti.toPrettyChars(), id.toChars(), s.toChars());
+ else
+ .error(ti.loc, "%s `%s` template `%s` is not defined", ti.kind, ti.toPrettyChars(), id.toChars());
+ return false;
+ }
+ static if (LOG)
+ {
+ printf("It's an instance of '%s' kind '%s'\n", s.toChars(), s.kind());
+ if (s.parent)
+ printf("s.parent = '%s'\n", s.parent.toChars());
+ }
+ if (pwithsym)
+ *pwithsym = scopesym.isWithScopeSymbol();
+
+ /* We might have found an alias within a template when
+ * we really want the template.
+ */
+ TemplateInstance ti2;
+ if (s.parent && (ti2 = s.parent.isTemplateInstance()) !is null)
+ {
+ if (ti2.tempdecl && ti2.tempdecl.ident == id)
+ {
+ /* This is so that one can refer to the enclosing
+ * template, even if it has the same name as a member
+ * of the template, if it has a !(arguments)
+ */
+ TemplateDeclaration td = ti2.tempdecl.isTemplateDeclaration();
+ assert(td);
+ if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
+ td = td.overroot; // then get the start
+ s = td;
+ }
+ }
+
+ // The template might originate from a selective import which implies that
+ // s is a lowered AliasDeclaration of the actual TemplateDeclaration.
+ // This is the last place where we see the deprecated alias because it is
+ // stripped below, so check if the selective import was deprecated.
+ // See https://issues.dlang.org/show_bug.cgi?id=20840.
+ if (s.isAliasDeclaration())
+ s.checkDeprecated(ti.loc, sc);
+
+ if (!ti.updateTempDecl(sc, s))
+ {
+ return false;
+ }
+ }
+ assert(ti.tempdecl);
+
+ // Look for forward references
+ 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 (td.semanticRun == PASS.initial)
+ {
+ if (td._scope)
+ {
+ // Try to fix forward reference. Ungag errors while doing so.
+ auto 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;
+ }
+ }
+ return 0;
+ });
+ if (r)
+ return false;
+ }
+ return true;
+}
+
+bool findMixinTempDecl(TemplateMixin tm, Scope* sc)
+{
+ // Follow qualifications to find the TemplateDeclaration
+ if (!tm.tempdecl)
+ {
+ Expression e;
+ Type t;
+ Dsymbol s;
+ tm.tqual.resolve(tm.loc, sc, e, t, s);
+ if (!s)
+ {
+ .error(tm.loc, "%s `%s` is not defined", tm.kind, tm.toPrettyChars);
+ return false;
+ }
+ s = s.toAlias();
+ tm.tempdecl = s.isTemplateDeclaration();
+ OverloadSet os = s.isOverloadSet();
+
+ /* If an OverloadSet, look for a unique member that is a template declaration
+ */
+ if (os)
+ {
+ Dsymbol ds = null;
+ foreach (i, sym; os.a)
+ {
+ Dsymbol s2 = sym.isTemplateDeclaration();
+ if (s2)
+ {
+ if (ds)
+ {
+ tm.tempdecl = os;
+ break;
+ }
+ ds = s2;
+ }
+ }
+ }
+ if (!tm.tempdecl)
+ {
+ .error(tm.loc, "%s `%s` - `%s` is a %s, not a template", tm.kind,
+ tm.toPrettyChars, s.toChars(), s.kind());
+ return false;
+ }
+ }
+ assert(tm.tempdecl);
+
+ // Look for forward references
+ auto tovers = tm.tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
+ {
+ Dsymbol dstart = tovers ? tovers.a[oi] : tm.tempdecl;
+ int r = overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+
+ if (td.semanticRun == PASS.initial)
+ {
+ if (td._scope)
+ td.dsymbolSemantic(td._scope);
+ else
+ {
+ tm.semanticRun = PASS.initial;
+ return 1;
+ }
+ }
+ return 0;
+ });
+ if (r)
+ return false;
+ }
+ return true;
+}
+
+/******************************************************
+ * Verifies if the given Identifier is a DRuntime hook. It uses the hooks
+ * defined in `id.d`.
+ *
* Params:
- * sc = context
- * tempdecl = template declaration
+ * id = Identifier to verify
+ * Returns:
+ * true if `id` is a DRuntime hook
+ * false otherwise
*/
-void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl)
+private bool isDRuntimeHook(Identifier id)
+{
+ return id == Id._d_HookTraceImpl ||
+ id == Id._d_newclassT || id == Id._d_newclassTTrace ||
+ id == Id._d_arraycatnTX || id == Id._d_arraycatnTXTrace ||
+ id == Id._d_newThrowable || id == Id._d_delThrowable ||
+ id == Id._d_arrayassign_l || id == Id._d_arrayassign_r ||
+ id == Id._d_arraysetassign || id == Id._d_arraysetctor ||
+ id == Id._d_arrayctor ||
+ id == Id._d_arraysetlengthT ||
+ id == Id._d_arraysetlengthTTrace ||
+ id == Id._d_arrayappendT || id == Id._d_arrayappendTTrace ||
+ id == Id._d_arrayappendcTX;
+}
+
+/******************************
+ * See if two objects match
+ * Params:
+ * o1 = first object
+ * o2 = second object
+ * Returns: true if they match
+ */
+private bool match(RootObject o1, RootObject o2)
{
enum log = false;
+
static if (log)
{
- printf("TemplateDeclaration.dsymbolSemantic(this = %p, id = '%s')\n", this, tempdecl.ident.toChars());
- printf("sc.stc = %llx\n", sc.stc);
- printf("sc.module = %s\n", sc._module.toChars());
+ printf("match() o1 = %p %s (%d), o2 = %p %s (%d)\n",
+ o1, o1.toChars(), o1.dyncast(), o2, o2.toChars(), o2.dyncast());
}
- if (tempdecl.semanticRun != PASS.initial)
- return; // semantic() already run
- if (tempdecl._scope)
+ bool yes()
{
- sc = tempdecl._scope;
- tempdecl._scope = null;
+ static if (log)
+ printf("\t. match\n");
+ return true;
}
- if (!sc)
- return;
-
- // Remember templates defined in module object that we need to know about
- if (sc._module && sc._module.ident == Id.object)
+ bool no()
{
- if (tempdecl.ident == Id.RTInfo)
- Type.rtinfo = tempdecl;
+ static if (log)
+ printf("\t. nomatch\n");
+ return false;
}
-
- /* Remember Scope for later instantiations, but make
- * a copy since attributes can change.
+ /* A proper implementation of the various equals() overrides
+ * should make it possible to just do o1.equals(o2), but
+ * we'll do that another day.
*/
- if (!tempdecl._scope)
+ /* Manifest constants should be compared by their values,
+ * at least in template arguments.
+ */
+
+ if (auto t1 = isType(o1))
{
- tempdecl._scope = sc.copy();
- tempdecl._scope.setNoFree();
- }
+ auto t2 = isType(o2);
+ if (!t2)
+ return no();
- tempdecl.semanticRun = PASS.semantic;
+ static if (log)
+ {
+ printf("\tt1 = %s\n", t1.toChars());
+ printf("\tt2 = %s\n", t2.toChars());
+ }
+ if (!t1.equals(t2))
+ return no();
- tempdecl.parent = sc.parent;
- tempdecl.visibility = sc.visibility;
- tempdecl.userAttribDecl = sc.userAttribDecl;
- tempdecl.cppnamespace = sc.namespace;
- tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_);
- tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_);
+ return yes();
+ }
+ if (auto e1 = getExpression(o1))
+ {
+ auto e2 = getExpression(o2);
+ if (!e2)
+ return no();
- checkGNUABITag(tempdecl, sc.linkage);
+ static if (log)
+ {
+ printf("\te1 = %s '%s' %s\n", e1.type ? e1.type.toChars() : "null", EXPtoString(e1.op).ptr, e1.toChars());
+ printf("\te2 = %s '%s' %s\n", e2.type ? e2.type.toChars() : "null", EXPtoString(e2.op).ptr, e2.toChars());
+ }
- if (!tempdecl.isstatic)
- {
- if (auto ad = tempdecl.parent.pastMixin().isAggregateDeclaration())
- ad.makeNested();
+ // two expressions can be equal although they do not have the same
+ // type; that happens when they have the same value. So check type
+ // as well as expression equality to ensure templates are properly
+ // matched.
+ if (!(e1.type && e2.type && e1.type.equals(e2.type)) || !e1.equals(e2))
+ return no();
+
+ return yes();
}
+ if (auto s1 = isDsymbol(o1))
+ {
+ auto s2 = isDsymbol(o2);
+ if (!s2)
+ return no();
- // Set up scope for parameters
- auto paramsym = new ScopeDsymbol();
- paramsym.parent = tempdecl.parent;
- Scope* paramscope = sc.push(paramsym);
- paramscope.stc = STC.none;
+ static if (log)
+ {
+ printf("\ts1 = %s \n", s1.kind(), s1.toChars());
+ printf("\ts2 = %s \n", s2.kind(), s2.toChars());
+ }
+ if (!s1.equals(s2))
+ return no();
+ if (s1.parent != s2.parent && !s1.isFuncDeclaration() && !s2.isFuncDeclaration())
+ return no();
- if (global.params.ddoc.doOutput)
+ return yes();
+ }
+ if (auto u1 = isTuple(o1))
{
- tempdecl.origParameters = new TemplateParameters(tempdecl.parameters.length);
- for (size_t i = 0; i < tempdecl.parameters.length; i++)
+ auto u2 = isTuple(o2);
+ if (!u2)
+ return no();
+
+ static if (log)
{
- TemplateParameter tp = (*tempdecl.parameters)[i];
- (*tempdecl.origParameters)[i] = tp.syntaxCopy();
+ printf("\tu1 = %s\n", u1.toChars());
+ printf("\tu2 = %s\n", u2.toChars());
}
+ if (!arrayObjectMatch(u1.objects, u2.objects))
+ return no();
+
+ return yes();
}
+ return yes();
+}
- for (size_t i = 0; i < tempdecl.parameters.length; i++)
+/************************************
+ * Match an array of them.
+ */
+private bool arrayObjectMatch(ref Objects oa1, ref Objects oa2)
+{
+ if (&oa1 == &oa2)
+ return true;
+ if (oa1.length != oa2.length)
+ return false;
+ immutable oa1dim = oa1.length;
+ auto oa1d = oa1[].ptr;
+ auto oa2d = oa2[].ptr;
+ foreach (j; 0 .. oa1dim)
{
- TemplateParameter tp = (*tempdecl.parameters)[i];
- if (!tp.declareParameter(paramscope))
- {
- error(tp.loc, "parameter `%s` multiply defined", tp.ident.toChars());
- tempdecl.errors = true;
- }
- if (!tp.tpsemantic(paramscope, tempdecl.parameters))
- {
- tempdecl.errors = true;
- }
- if (i + 1 != tempdecl.parameters.length && tp.isTemplateTupleParameter())
+ RootObject o1 = oa1d[j];
+ RootObject o2 = oa2d[j];
+ if (!match(o1, o2))
{
- .error(tempdecl.loc, "%s `%s` template sequence parameter must be the last one", tempdecl.kind, tempdecl.toPrettyChars);
- tempdecl.errors = true;
+ return false;
}
}
+ return true;
+}
- /* Calculate TemplateParameter.dependent
- */
- auto tparams = TemplateParameters(1);
- for (size_t i = 0; i < tempdecl.parameters.length; i++)
- {
- TemplateParameter tp = (*tempdecl.parameters)[i];
- tparams[0] = tp;
- for (size_t j = 0; j < tempdecl.parameters.length; j++)
- {
- // Skip cases like: X(T : T)
- if (i == j)
- continue;
+/*************************************
+ * Compare proposed template instantiation with existing template instantiation.
+ * Note that this is not commutative because of the auto ref check.
+ * Params:
+ * ti1 = proposed template instantiation
+ * ti2 = existing template instantiation
+ * Returns:
+ * true for match
+ */
+private bool equalsx(TemplateInstance ti1, TemplateInstance ti2)
+{
+ //printf("this = %p, ti2 = %p\n", this, ti2);
+ assert(ti1.tdtypes.length == ti2.tdtypes.length);
- if (TemplateTypeParameter ttp = (*tempdecl.parameters)[j].isTemplateTypeParameter())
- {
- if (reliesOnTident(ttp.specType, &tparams))
- tp.dependent = true;
- }
- else if (TemplateAliasParameter tap = (*tempdecl.parameters)[j].isTemplateAliasParameter())
- {
- if (reliesOnTident(tap.specType, &tparams) ||
- reliesOnTident(isType(tap.specAlias), &tparams))
- {
- tp.dependent = true;
- }
- }
- }
+ // Nesting must match
+ if (ti1.enclosing != ti2.enclosing)
+ {
+ //printf("test2 enclosing %s ti2.enclosing %s\n",
+ // enclosing ? enclosing.toChars() : "", ti2.enclosing ? ti.enclosing.toChars() : "");
+ return false;
}
+ //printf("parent = %s, ti2.parent = %s\n", parent.toPrettyChars(), ti2.parent.toPrettyChars());
- paramscope.pop();
+ if (!arrayObjectMatch(ti1.tdtypes, ti2.tdtypes))
+ return false;
- // Compute again
- tempdecl.onemember = null;
- if (tempdecl.members)
+ /* Template functions may have different instantiations based on
+ * "auto ref" parameters.
+ */
+ auto fd = ti2.toAlias().isFuncDeclaration();
+ if (!fd)
+ return true;
+ if (fd.errors)
+ return true;
+
+ auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs(
+ ArgumentList(ti1.fargs, ti1.fnames), null);
+
+ // 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 (ti1.fargs && !resolvedArgs)
+ return true;
+
+ Expression[] args = resolvedArgs ? (*resolvedArgs)[] : [];
+
+ auto fparameters = fd.getParameterList();
+ size_t nfparams = fparameters.length; // Num function parameters
+ for (size_t j = 0; j < nfparams; j++)
{
- Dsymbol s;
- if (oneMembers(tempdecl.members, s, tempdecl.ident) && s)
+ 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())
{
- tempdecl.onemember = s;
- s.parent = tempdecl;
+ 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
}
}
-
- /* BUG: should check:
- * 1. template functions must not introduce virtual functions, as they
- * cannot be accomodated in the vtbl[]
- * 2. templates cannot introduce non-static data members (i.e. fields)
- * as they would change the instance size of the aggregate.
- */
-
- tempdecl.semanticRun = PASS.semanticdone;
+ return true;
}
+
/*******************************************
* Match to a particular TemplateParameter.
* Input:
return matchArgParameter();
}
-MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam)
+private MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam)
{
MATCH matchArgNoMatch()
{
if (!s.parent && global.errors)
return false;
- if (!s.parent && s.getType())
+ if (!s.parent && dmd.dsymbolsem.getType(s))
{
- Dsymbol s2 = s.getType().toDsymbol(sc);
+ Dsymbol s2 = dmd.dsymbolsem.getType(s).toDsymbol(sc);
if (!s2)
{
- .error(ti.loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", ti.toChars(), id.toChars(), id.toChars(), s.getType.kind());
+ .error(ti.loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", ti.toChars(), id.toChars(), id.toChars(), dmd.dsymbolsem.getType(s).kind());
return false;
}
// because s can be the alias created for a TemplateParameter
ti.parent = td.parent;
// Similar to doHeaderInstantiation
+ td.computeOneMember();
FuncDeclaration fd = td.onemember ? td.onemember.isFuncDeclaration() : null;
if (fd)
{
/****************************
* Check to see if constraint is satisfied.
*/
-bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd)
+private bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd)
{
/* Detect recursive attempts to instantiate this template declaration,
* https://issues.dlang.org/show_bug.cgi?id=4072
const(char)* getConstraintEvalError(TemplateDeclaration td, ref const(char)* tip)
{
import dmd.staticcond;
-
// there will be a full tree view in verbose mode, and more compact list in the usual
const full = global.params.v.verbose;
uint count;
else
{
auto tdecl = ti.tempdecl.isTemplateDeclaration();
+ tdecl.computeOneMember();
if (errs != global.errors)
errorSupplemental(ti.loc, "while looking for match for `%s`", ti.toChars());
* Returns:
* new scope for the parameters of ti
*/
-Scope* createScopeForTemplateParameters(TemplateDeclaration td, TemplateInstance ti, Scope* sc)
+private Scope* createScopeForTemplateParameters(TemplateDeclaration td, TemplateInstance ti, Scope* sc)
{
ScopeDsymbol paramsym = new ScopeDsymbol();
paramsym.parent = td._scope.parent;
return MATCH.nomatch;
}
-RootObject defaultArg(TemplateParameter tp, Loc instLoc, Scope* sc)
+private RootObject defaultArg(TemplateParameter tp, Loc instLoc, Scope* sc)
{
if (tp.isTemplateTupleParameter())
return null;
* Returns:
* match pair of initial and inferred template arguments
*/
-extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, TemplateInstance ti, Scope* sc, ref FuncDeclaration fd, Type tthis, ArgumentList argumentList)
+private MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, TemplateInstance ti, Scope* sc, ref FuncDeclaration fd, Type tthis, ArgumentList argumentList)
{
version (none)
{
return nomatch();
if (m2 < matchTiargs)
matchTiargs = m2; // pick worst match
- if (!(*dedtypes)[i].equals(oded))
+ if (!rootObjectsEqual((*dedtypes)[i], oded))
.error(td.loc, "%s `%s` specialization not allowed for deduced parameter `%s`",
td.kind, td.toPrettyChars, td.kind, td.toPrettyChars, tparam.ident.toChars());
}
return nomatch();
if (m2 < matchTiargs)
matchTiargs = m2; // pick worst match
- if (!(*dedtypes)[i].equals(oded))
+ if (!rootObjectsEqual((*dedtypes)[i],oded))
.error(td.loc, "%s `%s` specialization not allowed for deduced parameter `%s`", td.kind, td.toPrettyChars, tparam.ident.toChars());
}
else
return nomatch();
if (m2 < matchTiargs)
matchTiargs = m2; // pick worst match
- if (!(*dedtypes)[i].equals(oded))
+ if (!rootObjectsEqual((*dedtypes)[i], oded))
.error(td.loc, "%s `%s` specialization not allowed for deduced parameter `%s`", td.kind, td.toPrettyChars, tparam.ident.toChars());
}
}
return fd;
}
+/****************************************************
+ * Declare parameters of template instance, initialize them with the
+ * template instance arguments.
+ */
+void declareParameters(TemplateInstance ti, Scope* sc)
+{
+ TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ //printf("TemplateInstance.declareParameters()\n");
+ foreach (i, o; ti.tdtypes) // initializer for tp
+ {
+ TemplateParameter tp = (*tempdecl.parameters)[i];
+ //printf("\ttdtypes[%d] = %p\n", i, o);
+ declareParameter(tempdecl, sc, tp, o);
+ }
+}
/**************************************************
* Declare template parameter tp with value o, and install it in the scope sc.
*/
-extern (D) RootObject declareParameter(TemplateDeclaration td, Scope* sc, TemplateParameter tp, RootObject o)
+private RootObject declareParameter(TemplateDeclaration td, Scope* sc, TemplateParameter tp, RootObject o)
{
//printf("TemplateDeclaration.declareParameter('%s', o = %p)\n", tp.ident.toChars(), o);
Type ta = isType(o);
return o;
}
+/**********************************
+ * Run semantic on the elements of `ti.tiargs`.
+ * Input:
+ * ti = template instance whose `tiargs` should have semantic done
+ * sc = scope
+ * Returns:
+ * false if one or more arguments have errors.
+ * Note:
+ * This function is reentrant against error occurrence. If returns false,
+ * all elements of tiargs won't be modified.
+ */
+bool semanticTiargs(TemplateInstance ti, Scope* sc)
+{
+ //printf("+TemplateInstance.semanticTiargs() %s\n", toChars());
+ if (ti.semantictiargsdone)
+ return true;
+ if (TemplateInstance_semanticTiargs(ti.loc, sc, ti.tiargs, 0))
+ {
+ // cache the result iff semantic analysis succeeded entirely
+ ti.semantictiargsdone = 1;
+ return true;
+ }
+ return false;
+}
+
/**********************************
* Run semantic of tiargs as arguments of template.
* Input:
return 1;
}
//printf("td = %s\n", td.toChars());
-
+ td.computeOneMember();
auto f = td.onemember ? td.onemember.isFuncDeclaration() : null;
if (!f)
{
if (td_best && ti_best && m.count == 1)
{
// Matches to template function
+ td_best.computeOneMember();
assert(td_best.onemember && td_best.onemember.isFuncDeclaration());
/* The best match is td_best with arguments tdargs.
* Now instantiate the template.
}
}
-__gshared Expression emptyArrayElement = null;
+private __gshared Expression emptyArrayElement = null;
/*
* Returns `true` if `t` is a reference type, or an array of reference types.
private MATCH deduceParentInstance(Scope* sc, Dsymbol sym, TypeInstance tpi,
ref TemplateParameters parameters, ref Objects dedtypes, uint* wm)
{
- if (tpi.idents.length)
- {
- RootObject id = tpi.idents[tpi.idents.length - 1];
- if (id.dyncast() == DYNCAST.identifier && sym.ident.equals(cast(Identifier)id))
- {
- Type tparent = sym.parent.getType();
- if (tparent)
- {
- tpi.idents.length--;
- auto m = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
- tpi.idents.length++;
- return m;
- }
- }
- }
- return MATCH.nomatch;
+ if (!tpi.idents.length)
+ return MATCH.nomatch;
+
+ RootObject id = tpi.idents[tpi.idents.length - 1];
+ if (id.dyncast() != DYNCAST.identifier || !sym.ident.equals(cast(Identifier)id))
+ return MATCH.nomatch;
+
+ Type tparent = dmd.dsymbolsem.getType(sym.parent);
+ if (!tparent)
+ return MATCH.nomatch;
+
+ tpi.idents.length--;
+ auto m = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
+ tpi.idents.length++;
+ return m;
}
private MATCH matchAll(TypeDeduced td, Type tt)
//printf("[%d] s = %s %s, s2 = %s %s\n", j, s.kind(), s.toChars(), s2.kind(), s2.toChars());
if (s != s2)
{
- if (Type tx = s2.getType())
+ if (Type tx = dmd.dsymbolsem.getType(s2))
{
if (s != tx.toDsymbol(sc))
goto Lnomatch;
//printf("[e] s = %s\n", s?s.toChars():"(null)");
if (tp.isTemplateTypeParameter())
{
- Type tt = s.getType();
+ Type tt = dmd.dsymbolsem.getType(s);
if (!tt)
goto Lnomatch;
Type at = cast(Type)dedtypes[i];
{
RootObject id1 = t.idents[i];
RootObject id2 = tp.idents[i];
- if (!id1.equals(id2))
+ if (!rootObjectsEqual(id1, id2))
{
result = MATCH.nomatch;
return;
}
}
+private bool rootObjectsEqual(RootObject o1, RootObject o2)
+{
+ auto d = o1.dyncast();
+ if (d != o2.dyncast())
+ return false;
+ bool check(T)(RootObject id1, RootObject id2)
+ {
+ return (cast(T)id1).equals(cast(T)id2);
+ }
+ with (DYNCAST) final switch(d)
+ {
+ case expression: return check!Expression(o1, o2);
+ case dsymbol: return check!Dsymbol (o1, o2);
+ case type: return check!Type (o1, o2);
+ case identifier: //return check!Identifier(o1, o2); // Identifier.equals checks `o1 is o2`
+ case object:
+ case tuple:
+ case parameter:
+ case statement:
+ case condition:
+ case templateparameter:
+ case initializer:
+ return o1 is o2;
+ }
+}
/*
* Handle tuple matching for function parameters.
* If the last parameter of `tp` is a template tuple parameter,
ref TemplateParameters parameters, ref Objects dedtypes,
size_t nfargs, ref size_t nfparams)
{
- if (nfparams > 0 && nfargs >= nfparams - 1)
+ if (nfparams == 0 || nfargs < nfparams - 1)
+ return nfargs == nfparams;
+
+ Parameter fparam = tp.parameterList[nfparams - 1];
+ assert(fparam && fparam.type);
+ if (fparam.type.ty != Tident)
+ return nfargs == nfparams;
+
+ TypeIdentifier tid = fparam.type.isTypeIdentifier();
+ if (tid.idents.length != 0)
+ return nfargs == nfparams;
+
+ size_t tupi = 0;
+ for (; tupi < parameters.length; ++tupi)
{
- Parameter fparam = tp.parameterList[nfparams - 1];
- assert(fparam && fparam.type);
- if (fparam.type.ty == Tident)
- {
- TypeIdentifier tid = fparam.type.isTypeIdentifier();
- if (tid.idents.length == 0)
- {
- size_t tupi = 0;
- for (; tupi < parameters.length; ++tupi)
- {
- TemplateParameter tx = parameters[tupi];
- TemplateTupleParameter tup = tx.isTemplateTupleParameter();
- if (tup && tup.ident.equals(tid.ident))
- break;
- }
- if (tupi == parameters.length)
- return nfargs == nfparams;
+ TemplateParameter tx = parameters[tupi];
+ TemplateTupleParameter tup = tx.isTemplateTupleParameter();
+ if (tup && tup.ident.equals(tid.ident))
+ break;
+ }
+ if (tupi == parameters.length)
+ return nfargs == nfparams;
- size_t tuple_dim = nfargs - (nfparams - 1);
+ size_t tuple_dim = nfargs - (nfparams - 1);
- RootObject o = dedtypes[tupi];
- if (o)
- {
- Tuple tup = isTuple(o);
- if (!tup || tup.objects.length != tuple_dim)
- return false;
- for (size_t i = 0; i < tuple_dim; ++i)
- {
- if (!t.parameterList[nfparams - 1 + i].type.equals(tup.objects[i]))
- return false;
- }
- }
- else
- {
- auto tup = new Tuple(tuple_dim);
- for (size_t i = 0; i < tuple_dim; ++i)
- tup.objects[i] = t.parameterList[nfparams - 1 + i].type;
- dedtypes[tupi] = tup;
- }
- --nfparams; // ignore tuple parameter for further deduction
- return true;
- }
+ RootObject o = dedtypes[tupi];
+ if (o)
+ {
+ Tuple tup = isTuple(o);
+ if (!tup || tup.objects.length != tuple_dim)
+ return false;
+ for (size_t i = 0; i < tuple_dim; ++i)
+ {
+ if (!rootObjectsEqual(t.parameterList[nfparams - 1 + i].type,
+ tup.objects[i]))
+ return false;
}
}
- return nfargs == nfparams;
+ else
+ {
+ auto tup = new Tuple(tuple_dim);
+ for (size_t i = 0; i < tuple_dim; ++i)
+ tup.objects[i] = t.parameterList[nfparams - 1 + i].type;
+ dedtypes[tupi] = tup;
+ }
+ --nfparams; // ignore tuple parameter for further deduction
+ return true;
}
/********************
return e;
}
-Expression getExpression(RootObject o)
+private Expression getExpression(RootObject o)
{
auto s = isDsymbol(o);
return s ? .getValue(s) : .getValue(isExpression(o));
semaGeneral,
sema1Import,
sema1Module,
+ sema1TemplateDecl,
+ sema1TemplateInstance,
+ sema1Function,
sema2,
sema3,
+ dfa,
ctfe,
ctfeCall,
codegenGlobal,
// C only extended keywords
_assert,
_import,
+ _module,
__cdecl,
__declspec,
__stdcall,
// C only extended keywords
TOK._assert,
TOK._import,
+ TOK._module,
TOK.__cdecl,
TOK.__declspec,
TOK.__stdcall,
union_, unsigned, void_, volatile, while_, asm_, typeof_,
_Alignas, _Alignof, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn,
_Static_assert, _Thread_local,
- _import, __cdecl, __declspec, __stdcall, __thread, __pragma, __int128, __attribute__,
+ _import, _module, __cdecl, __declspec, __stdcall, __thread, __pragma, __int128, __attribute__,
_assert ];
foreach (kw; Ckwds)
// C only extended keywords
TOK._assert : "__check",
TOK._import : "__import",
+ TOK._module : "__module",
TOK.__cdecl : "__cdecl",
TOK.__declspec : "__declspec",
TOK.__stdcall : "__stdcall",
// C only extended keywords
_assert,
_import,
+ _module,
cdecl_,
declspec,
stdcall,
import dmd.aggregate;
import dmd.arraytypes;
-import dmd.astcodegen;
import dmd.astenums;
import dmd.attrib;
import dmd.attribsem;
import dmd.canthrow;
import dmd.dclass;
import dmd.declaration;
-import dmd.dimport;
import dmd.dinterpret;
import dmd.dmodule;
import dmd.dscope;
import dmd.mtype;
import dmd.nogc;
import dmd.optimize;
-import dmd.parse;
import dmd.root.array;
import dmd.root.speller;
import dmd.root.stringtable;
import dmd.target;
-import dmd.templatesem : TemplateInstance_semanticTiargs;
+import dmd.templatesem : TemplateInstance_semanticTiargs, TemplateInstanceBox;
import dmd.tokens;
import dmd.typesem;
-import dmd.visitor;
import dmd.rootobject;
-import dmd.common.outbuffer;
import dmd.root.string;
enum LOGSEMANTIC = false;
ulong nextsize = t.next.size();
if (nextsize == SIZE_INVALID)
error = true;
- ulong dim = t.dim.toInteger();
- for (ulong i = 0; i < dim; i++)
+ if (t.hasPointers)
{
- offset = arrayoff + i * nextsize;
- visit(t.next);
+ ulong dim = t.dim.toInteger();
+ for (ulong i = 0; i < dim; i++)
+ {
+ offset = arrayoff + i * nextsize;
+ visit(t.next);
+ }
}
offset = arrayoff;
}
auto fd = s.isFuncDeclaration();
if (!fd)
{
- if (includeTemplates)
+ if (!includeTemplates)
+ return 0;
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+ // if td is part of an overload set we must take a copy
+ // which shares the same `instances` cache but without
+ // `overroot` and `overnext` set to avoid overload
+ // behaviour in the result.
+ if (td.overnext !is null)
{
- if (auto td = s.isTemplateDeclaration())
+ if (td.instances is null)
{
- // if td is part of an overload set we must take a copy
- // which shares the same `instances` cache but without
- // `overroot` and `overnext` set to avoid overload
- // behaviour in the result.
- if (td.overnext !is null)
- {
- if (td.instances is null)
- {
- // create an empty AA just to copy it
- scope ti = new TemplateInstance(Loc.initial, Id.empty, null);
- auto tib = TemplateInstanceBox(ti);
- td.instances[tib] = null;
- td.instances.clear();
- }
- td = td.syntaxCopy(null);
- import core.stdc.string : memcpy;
- memcpy(cast(void*) td, cast(void*) s,
- __traits(classInstanceSize, TemplateDeclaration));
- td.overroot = null;
- td.overnext = null;
- }
-
- auto e = ex ? new DotTemplateExp(Loc.initial, ex, td)
- : new DsymbolExp(Loc.initial, td);
- exps.push(e);
+ // create an empty AA just to copy it
+ scope ti = new TemplateInstance(Loc.initial, Id.empty, null);
+ auto tib = TemplateInstanceBox(ti);
+ (*(cast(TemplateInstance[TemplateInstanceBox]*) &td.instances))[tib] = null;
+ (cast(TemplateInstance[TemplateInstanceBox])td.instances).clear();
}
+ td = td.syntaxCopy(null);
+ import core.stdc.string : memcpy;
+ memcpy(cast(void*) td, cast(void*) s,
+ __traits(classInstanceSize, TemplateDeclaration));
+ td.overroot = null;
+ td.overnext = null;
}
+
+ auto e = ex ? new DotTemplateExp(Loc.initial, ex, td)
+ : new DsymbolExp(Loc.initial, td);
+ exps.push(e);
return 0;
}
if (e.ident == Id.getVirtualFunctions && !fd.isVirtual())
import dmd.access;
import dmd.aggregate;
-import dmd.aliasthis;
import dmd.arrayop;
import dmd.arraytypes;
import dmd.astcodegen;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
-import dmd.dimport;
import dmd.dinterpret;
import dmd.dmodule;
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dsymbolsem;
+import dmd.templatesem : computeOneMember;
import dmd.dtemplate;
import dmd.enumsem;
import dmd.errors;
-import dmd.errorsink;
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
import dmd.init;
import dmd.initsem;
import dmd.location;
-import dmd.visitor;
import dmd.mtype;
import dmd.mangle;
import dmd.nogc;
import dmd.common.outbuffer;
import dmd.rootobject;
import dmd.root.string;
-import dmd.root.stringtable;
import dmd.safe;
import dmd.semantic3;
import dmd.sideeffect;
import dmd.target;
import dmd.tokens;
+bool hasDeprecatedAliasThis(Type _this)
+{
+ auto ad = isAggregate(_this);
+ return ad && ad.aliasthis && (ad.aliasthis.isDeprecated || ad.aliasthis.sym.isDeprecated);
+}
+
+/*************************************
+ * Apply STCxxxx bits to existing type.
+ * Use *before* semantic analysis is run.
+ */
+Type addSTC(Type _this, STC stc)
+{
+ Type t = _this;
+ if (t.isImmutable())
+ {
+ return t;
+ }
+ else if (stc & STC.immutable_)
+ {
+ t = t.makeImmutable();
+ return t;
+ }
+
+ 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())
+ t = t.makeSharedWildConst();
+ else
+ t = t.makeSharedConst();
+ }
+ else
+ {
+ if (t.isWild())
+ t = t.makeWildConst();
+ else
+ t = t.makeConst();
+ }
+ }
+ if ((stc & STC.wild) && !t.isWild())
+ {
+ if (t.isShared())
+ {
+ if (t.isConst())
+ t = t.makeSharedWildConst();
+ else
+ t = t.makeSharedWild();
+ }
+ else
+ {
+ if (t.isConst())
+ t = t.makeWildConst();
+ else
+ t = t.makeWild();
+ }
+ }
+
+ return t;
+}
+
+/***************************************************
+ * Determine if type t can be indexed or sliced given that it is not an
+ * aggregate with operator overloads.
+ * Params:
+ * t = type to check
+ * Returns:
+ * true if an expression of type t can be e1 in an array expression
+ */
+bool isIndexableNonAggregate(Type t)
+{
+ t = t.toBasetype();
+ return (t.ty == Tpointer || t.isStaticOrDynamicArray() || t.ty == Taarray ||
+ t.ty == Ttuple || t.ty == Tvector);
+}
+
+/**
+ * If the type is a class or struct, returns the symbol for it,
+ * else null.
+ */
+AggregateDeclaration isAggregate(Type t)
+{
+ t = t.toBasetype();
+ if (auto tc = t.isTypeClass())
+ return tc.sym;
+ if (auto ts = t.isTypeStruct())
+ return ts.sym;
+ return null;
+}
+
+/****************************************************
+ * Determine if parameter is a lazy array of delegates.
+ * If so, return the return type of those delegates.
+ * If not, return NULL.
+ *
+ * Returns T if the type is one of the following forms:
+ * T delegate()[]
+ * T delegate()[dim]
+ */
+Type isLazyArray(Parameter _this)
+{
+ Type tb = _this.type.toBasetype();
+ if (tb.isStaticOrDynamicArray())
+ {
+ Type tel = (cast(TypeArray)tb).next.toBasetype();
+ if (auto td = tel.isTypeDelegate())
+ {
+ TypeFunction tf = td.next.toTypeFunction();
+ if (tf.parameterList.varargs == VarArg.none && tf.parameterList.length == 0)
+ {
+ return tf.next; // return type of delegate
+ }
+ }
+ }
+ return null;
+}
+
+/****************************************
+ * Return the mask that an integral type will
+ * fit into.
+ */
+ulong sizemask(Type _this)
+{
+ ulong m;
+ switch (_this.toBasetype().ty)
+ {
+ case Tbool:
+ m = 1;
+ break;
+ case Tchar:
+ case Tint8:
+ case Tuns8:
+ m = 0xFF;
+ break;
+ case Twchar:
+ case Tint16:
+ case Tuns16:
+ m = 0xFFFFU;
+ break;
+ case Tdchar:
+ case Tint32:
+ case Tuns32:
+ m = 0xFFFFFFFFU;
+ break;
+ case Tint64:
+ case Tuns64:
+ m = 0xFFFFFFFFFFFFFFFFUL;
+ break;
+ default:
+ assert(0);
+ }
+ return m;
+}
+
+/*************************************
+ * If _this is a type of static array, return its base element type.
+ */
+Type baseElemOf(Type _this)
+{
+ Type t = _this.toBasetype();
+ TypeSArray tsa;
+ while ((tsa = t.isTypeSArray()) !is null)
+ t = tsa.next.toBasetype();
+ return t;
+}
+
+/*************************************
+ * If this is a type of something, return that something.
+ */
+Type nextOf(Type _this)
+{
+ /*******************************
+ * For TypeFunction, nextOf() can return NULL if the function return
+ * type is meant to be inferred, and semantic() hasn't yet been run
+ * on the function. After semantic(), it must no longer be NULL.
+ */
+ if (auto tn = _this.isTypeNext())
+ return tn.next;
+ else if (auto te = _this.isTypeEnum())
+ return te.memType().nextOf();
+ return null;
+}
+
+/***************************
+ * Look for bugs in constructing types.
+ */
+void check(Type _this)
+{
+ if (_this.mcache)
+ with (_this.mcache)
+ switch (_this.mod)
+ {
+ case 0:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.const_:
+ if (cto)
+ assert(cto.mod == 0);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.wild:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == 0);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.wildconst:
+ assert(!cto || cto.mod == MODFlags.const_);
+ assert(!ito || ito.mod == MODFlags.immutable_);
+ assert(!sto || sto.mod == MODFlags.shared_);
+ assert(!scto || scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ assert(!wto || wto.mod == MODFlags.wild);
+ assert(!wcto || wcto.mod == 0);
+ assert(!swto || swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ assert(!swcto || swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.shared_:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == 0);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.shared_ | MODFlags.const_:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == 0);
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.shared_ | MODFlags.wild:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == 0);
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.shared_ | MODFlags.wildconst:
+ assert(!cto || cto.mod == MODFlags.const_);
+ assert(!ito || ito.mod == MODFlags.immutable_);
+ assert(!sto || sto.mod == MODFlags.shared_);
+ assert(!scto || scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ assert(!wto || wto.mod == MODFlags.wild);
+ assert(!wcto || wcto.mod == MODFlags.wildconst);
+ assert(!swto || swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ assert(!swcto || swcto.mod == 0);
+ break;
+
+ case MODFlags.immutable_:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == 0);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ default:
+ assert(0);
+ }
+
+ Type tn = _this.nextOf();
+ if (tn && _this.ty != Tfunction && tn.ty != Tfunction && _this.ty != Tenum)
+ {
+ // Verify transitivity
+ switch (_this.mod)
+ {
+ case 0:
+ case MODFlags.const_:
+ case MODFlags.wild:
+ case MODFlags.wildconst:
+ case MODFlags.shared_:
+ case MODFlags.shared_ | MODFlags.const_:
+ case MODFlags.shared_ | MODFlags.wild:
+ case MODFlags.shared_ | MODFlags.wildconst:
+ case MODFlags.immutable_:
+ assert(tn.mod == MODFlags.immutable_ || (tn.mod & _this.mod) == _this.mod);
+ break;
+
+ default:
+ assert(0);
+ }
+ tn.check();
+ }
+}
+
+/**********************************
+ * For our new type '_this', which is type-constructed from t,
+ * fill in the cto, ito, sto, scto, wto shortcuts.
+ */
+void fixTo(Type _this, Type t)
+{
+ // If fixing this: immutable(T*) by t: immutable(T)*,
+ // cache t to this.xto won't break transitivity.
+ Type mto = null;
+ Type tn = _this.nextOf();
+ if (!tn || _this.ty != Tsarray && tn.mod == t.nextOf().mod)
+ {
+ switch (t.mod)
+ {
+ case 0:
+ mto = t;
+ break;
+
+ case MODFlags.const_:
+ _this.getMcache();
+ _this.mcache.cto = t;
+ break;
+
+ case MODFlags.wild:
+ _this.getMcache();
+ _this.mcache.wto = t;
+ break;
+
+ case MODFlags.wildconst:
+ _this.getMcache();
+ _this.mcache.wcto = t;
+ break;
+
+ case MODFlags.shared_:
+ _this.getMcache();
+ _this.mcache.sto = t;
+ break;
+
+ case MODFlags.shared_ | MODFlags.const_:
+ _this.getMcache();
+ _this.mcache.scto = t;
+ break;
+
+ case MODFlags.shared_ | MODFlags.wild:
+ _this.getMcache();
+ _this.mcache.swto = t;
+ break;
+
+ case MODFlags.shared_ | MODFlags.wildconst:
+ _this.getMcache();
+ _this.mcache.swcto = t;
+ break;
+
+ case MODFlags.immutable_:
+ _this.getMcache();
+ _this.mcache.ito = t;
+ break;
+
+ default:
+ break;
+ }
+ }
+ assert(_this.mod != t.mod);
+
+ if (_this.mod)
+ {
+ _this.getMcache();
+ t.getMcache();
+ }
+ switch (_this.mod)
+ {
+ case 0:
+ break;
+
+ case MODFlags.const_:
+ _this.mcache.cto = mto;
+ t.mcache.cto = _this;
+ break;
+
+ case MODFlags.wild:
+ _this.mcache.wto = mto;
+ t.mcache.wto = _this;
+ break;
+
+ case MODFlags.wildconst:
+ _this.mcache.wcto = mto;
+ t.mcache.wcto = _this;
+ break;
+
+ case MODFlags.shared_:
+ _this.mcache.sto = mto;
+ t.mcache.sto = _this;
+ break;
+
+ case MODFlags.shared_ | MODFlags.const_:
+ _this.mcache.scto = mto;
+ t.mcache.scto = _this;
+ break;
+
+ case MODFlags.shared_ | MODFlags.wild:
+ _this.mcache.swto = mto;
+ t.mcache.swto = _this;
+ break;
+
+ case MODFlags.shared_ | MODFlags.wildconst:
+ _this.mcache.swcto = mto;
+ t.mcache.swcto = _this;
+ break;
+
+ case MODFlags.immutable_:
+ t.mcache.ito = _this;
+ if (t.mcache.cto)
+ t.mcache.cto.getMcache().ito = _this;
+ if (t.mcache.sto)
+ t.mcache.sto.getMcache().ito = _this;
+ if (t.mcache.scto)
+ t.mcache.scto.getMcache().ito = _this;
+ if (t.mcache.wto)
+ t.mcache.wto.getMcache().ito = _this;
+ if (t.mcache.wcto)
+ t.mcache.wcto.getMcache().ito = _this;
+ if (t.mcache.swto)
+ t.mcache.swto.getMcache().ito = _this;
+ if (t.mcache.swcto)
+ t.mcache.swcto.getMcache().ito = _this;
+ break;
+
+ default:
+ assert(0);
+ }
+ _this.check();
+ t.check();
+ //printf("fixTo: %s, %s\n", toChars(), t.toChars());
+}
+
+void transitive(TypeNext _this)
+{
+ // Invoke transitivity of type attributes
+ _this.next = _this.next.addMod(_this.mod);
+}
+
+private inout(TypeNext) isTypeNext(inout Type _this)
+{
+ switch(_this.ty)
+ {
+ case Tpointer, Treference, Tfunction, Tdelegate, Tslice, Tarray, Taarray, Tsarray:
+ return cast(typeof(return)) _this;
+ default: return null;
+ }
+}
+
+/********************************
+ * true if when type is copied, it needs a copy constructor or postblit
+ * applied. Only applies to value types, not ref types.
+ */
+bool needsCopyOrPostblit(Type _this)
+{
+ if (auto tsa = _this.isTypeSArray())
+ return tsa.next.needsCopyOrPostblit();
+ else if (auto ts = _this.isTypeStruct())
+ return ts.sym.hasCopyCtor || ts.sym.postblit;
+ else if (auto te = _this.isTypeEnum())
+ return te.memType().needsCopyOrPostblit();
+ return false;
+}
+
+/********************************
+ * true if when type goes out of scope, it needs a destructor applied.
+ * Only applies to value types, not ref types.
+ */
+bool needsDestruction(Type _this)
+{
+ if (auto tsa = _this.isTypeSArray())
+ return tsa.next.needsDestruction();
+ else if (auto ts = _this.isTypeStruct())
+ return ts.sym.dtor !is null;
+ else if (auto te = _this.isTypeEnum())
+ return te.memType().needsDestruction();
+ return false;
+}
+
+bool needsNested(Type _this)
+{
+ static bool typeStructNeedsNested(TypeStruct _this)
+ {
+ if (_this.inuse) return false; // circular type, error instead of crashing
+
+ _this.inuse = true;
+ scope(exit) _this.inuse = false;
+
+ if (_this.sym.isNested())
+ return true;
+
+ for (size_t i = 0; i < _this.sym.fields.length; i++)
+ {
+ VarDeclaration v = _this.sym.fields[i];
+ if (!v.isDataseg() && v.type.needsNested())
+ return true;
+ }
+ return false;
+ }
+
+ if (auto tsa = _this.isTypeSArray())
+ return tsa.next.needsNested();
+ else if (auto ts = _this.isTypeStruct())
+ return typeStructNeedsNested(ts);
+ else if (auto te = _this.isTypeEnum())
+ return te.memType().needsNested();
+ return false;
+}
+
+bool isScalar(Type _this)
+{
+ if (auto tb = _this.isTypeBasic())
+ return (tb.flags & (TFlags.integral | TFlags.floating)) != 0;
+ else if (auto tv = _this.isTypeVector())
+ return tv.basetype.nextOf().isScalar();
+ else if (auto te = _this.isTypeEnum())
+ return te.memType().isScalar();
+ else if (_this.isTypePointer())
+ return true;
+ return false;
+}
+
+bool isUnsigned(Type _this)
+{
+ if (auto tb = _this.isTypeBasic())
+ return (tb.flags & TFlags.unsigned) != 0;
+ else if (auto tv = _this.isTypeVector())
+ return tv.basetype.nextOf().isUnsigned();
+ else if (auto te = _this.isTypeEnum())
+ return te.memType().isUnsigned();
+ return false;
+}
+
+bool isImaginary(Type _this)
+{
+ if (auto te = _this.isTypeEnum())
+ return te.memType().isImaginary();
+ return _this.isImaginaryNonSemantic();
+}
+
+bool isComplex(Type _this)
+{
+ if (auto tb = _this.isTypeBasic())
+ return (tb.flags & TFlags.complex) != 0;
+ else if (auto te = _this.isTypeEnum())
+ return te.memType().isComplex();
+ return false;
+}
+
+// Exposed as it is used in `expressionsem`
+MOD typeDeduceWild(Type _this, Type t, bool isRef)
+{
+ //printf("Type::deduceWild this = '%s', tprm = '%s'\n", toChars(), tprm.toChars());
+ if (t.isWild())
+ {
+ if (_this.isImmutable())
+ return MODFlags.immutable_;
+ if (_this.isWildConst())
+ {
+ if (t.isWildConst())
+ return MODFlags.wild;
+ return MODFlags.wildconst;
+ }
+ if (_this.isWild())
+ return MODFlags.wild;
+ if (_this.isConst())
+ return MODFlags.const_;
+ if (_this.isMutable())
+ return MODFlags.mutable;
+ assert(0);
+ }
+ return 0;
+}
+
+/***************************************
+ * Compute MOD bits matching `this` argument type to wild parameter type.
+ * Params:
+ * _this = base parameter type
+ * t = corresponding parameter type
+ * isRef = parameter is `ref` or `out`
+ * Returns:
+ * MOD bits
+ */
+MOD deduceWild(Type _this, Type t, bool isRef)
+{
+ static MOD typeNextDeduceWild(TypeNext _this, Type t, bool isRef)
+ {
+ if (_this.ty == Tfunction)
+ return 0;
+
+ ubyte wm;
+
+ Type tn = t.nextOf();
+ if (!isRef && (_this.ty == Tarray || _this.ty == Tpointer) && tn)
+ {
+ wm = _this.next.deduceWild(tn, true);
+ if (!wm)
+ wm = typeDeduceWild(cast(Type)_this, t, true);
+ }
+ else
+ {
+ wm = typeDeduceWild(cast(Type)_this, t, isRef);
+ if (!wm && tn)
+ wm = _this.next.deduceWild(tn, true);
+ }
+ return wm;
+ }
+
+ static MOD typeStructDeduceWild(TypeStruct _this, Type t, bool isRef)
+ {
+ if (_this.ty == t.ty && _this.sym == (cast(TypeStruct)t).sym)
+ return typeDeduceWild(cast(Type)_this, t, isRef);
+
+ ubyte wm = 0;
+
+ if (t.hasWild() && _this.sym.aliasthis && !(_this.att & AliasThisRec.tracing))
+ {
+ if (auto ato = aliasthisOf(_this))
+ {
+ _this.att = cast(AliasThisRec)(_this.att | AliasThisRec.tracing);
+ wm = ato.deduceWild(t, isRef);
+ _this.att = cast(AliasThisRec)(_this.att & ~AliasThisRec.tracing);
+ }
+ }
+
+ return wm;
+ }
+
+ static MOD typeClassDeduceWild(TypeClass _this, Type t, bool isRef)
+ {
+ ClassDeclaration cd = t.isClassHandle();
+ if (cd && (_this.sym == cd || cd.isBaseOf(_this.sym, null)))
+ return typeDeduceWild(cast(Type)_this, t, isRef);
+
+ ubyte wm = 0;
+
+ if (t.hasWild() && _this.sym.aliasthis && !(_this.att & AliasThisRec.tracing))
+ {
+ if (auto ato = aliasthisOf(_this))
+ {
+ _this.att = cast(AliasThisRec)(_this.att | AliasThisRec.tracing);
+ wm = ato.deduceWild(t, isRef);
+ _this.att = cast(AliasThisRec)(_this.att & ~AliasThisRec.tracing);
+ }
+ }
+
+ return wm;
+ }
+
+ if (auto tn = _this.isTypeNext())
+ return typeNextDeduceWild(tn, t, isRef);
+ else if (auto ts = _this.isTypeStruct())
+ return typeStructDeduceWild(ts, t, isRef);
+ else if (auto tc = _this.isTypeClass())
+ return typeClassDeduceWild(tc, t, isRef);
+ return typeDeduceWild(_this, t, isRef);
+}
+
+bool isString(Type _this)
+{
+ if (auto tsa = _this.isTypeSArray())
+ {
+ TY nty = tsa.next.toBasetype().ty;
+ return nty.isSomeChar();
+ }
+ else if (auto tda = _this.isTypeDArray())
+ {
+ TY nty = tda.next.toBasetype().ty;
+ return nty.isSomeChar();
+ }
+ else if (auto te = _this.isTypeEnum())
+ return te.memType().isString();
+ return false;
+}
+
+/**************************
+ * Returns true if T can be converted to boolean value.
+ */
+bool isBoolean(Type _this)
+{
+ switch(_this.ty)
+ {
+ case Tvector, Tstruct: return false;
+ case Tarray, Taarray, Tdelegate, Tclass, Tnull: return true;
+ case Tenum: return _this.isTypeEnum().memType().isBoolean();
+ // bottom type can be implicitly converted to any other type
+ case Tnoreturn: return true;
+ default: return _this.isScalar();
+ }
+}
+
+bool isReal(Type _this)
+{
+ if (auto tb = _this.isTypeBasic())
+ return (tb.flags & TFlags.real_) != 0;
+ else if (auto te = _this.isTypeEnum())
+ return te.memType().isReal();
+ return false;
+}
+
+// real, imaginary, or complex
+bool isFloating(Type _this)
+{
+ if (auto tb = _this.isTypeBasic())
+ return (tb.flags & TFlags.floating) != 0;
+ else if (auto tv = _this.isTypeVector())
+ return tv.basetype.nextOf().isFloating();
+ else if (auto te = _this.isTypeEnum())
+ return te.memType().isFloating();
+ return false;
+}
+
+bool isIntegral(Type _this)
+{
+ if (auto tb = _this.isTypeBasic())
+ return (tb.flags & TFlags.integral) != 0;
+ else if (auto tv = _this.isTypeVector())
+ return tv.basetype.nextOf().isIntegral();
+ else if (auto te = _this.isTypeEnum())
+ return te.memType().isIntegral();
+ return false;
+}
+
+Type makeSharedWildConst(Type _this)
+{
+ if (_this.mcache && _this.mcache.swcto)
+ return _this.mcache.swcto;
+ Type t = _this.nullAttributes();
+ t.mod = MODFlags.shared_ | MODFlags.wildconst;
+
+ if (auto tn = _this.isTypeNext())
+ {
+ //printf("TypeNext::makeSharedWildConst() %s\n", toChars());
+ TypeNext _t = cast(TypeNext) t;
+ if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+ {
+ _t.next = tn.next.sharedWildConstOf();
+ }
+ //printf("TypeNext::makeSharedWildConst() returns %p, %s\n", t, t.toChars());
+ return _t;
+ }
+ return t;
+}
+
+Type makeSharedWild(Type _this)
+{
+ if (_this.mcache && _this.mcache.swto)
+ return _this.mcache.swto;
+ Type t = _this.nullAttributes();
+ t.mod = MODFlags.shared_ | MODFlags.wild;
+
+ if (auto tn = _this.isTypeNext())
+ {
+ //printf("TypeNext::makeSharedWild() %s\n", toChars());
+ TypeNext _t = cast(TypeNext) t;
+ if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+ {
+ if (tn.next.isConst())
+ _t.next = tn.next.sharedWildConstOf();
+ else
+ _t.next = tn.next.sharedWildOf();
+ }
+ //printf("TypeNext::makeSharedWild() returns %p, %s\n", t, t.toChars());
+ return _t;
+ }
+ return t;
+}
+
+Type makeWildConst(Type _this)
+{
+ if (_this.mcache && _this.mcache.wcto)
+ return _this.mcache.wcto;
+ Type t = _this.nullAttributes();
+ t.mod = MODFlags.wildconst;
+
+ if (auto tn = _this.isTypeNext())
+ {
+ //printf("TypeNext::makeWildConst() %s\n", toChars());
+ TypeNext _t = cast(TypeNext) t;
+ if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+ {
+ if (tn.next.isShared())
+ _t.next = tn.next.sharedWildConstOf();
+ else
+ _t.next = tn.next.wildConstOf();
+ }
+ //printf("TypeNext::makeWildConst() returns %p, %s\n", t, t.toChars());
+ return _t;
+ }
+ return t;
+}
+
+Type makeWild(Type _this)
+{
+ if (_this.mcache && _this.mcache.wto)
+ return _this.mcache.wto;
+ Type t = _this.nullAttributes();
+ t.mod = MODFlags.wild;
+
+ if (auto tn = _this.isTypeNext())
+ {
+ //printf("TypeNext::makeWild() %s\n", toChars());
+ TypeNext _t = cast(TypeNext) t;
+ if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+ {
+ if (tn.next.isShared())
+ {
+ if (tn.next.isConst())
+ _t.next = tn.next.sharedWildConstOf();
+ else
+ _t.next = tn.next.sharedWildOf();
+ }
+ else
+ {
+ if (tn.next.isConst())
+ _t.next = tn.next.wildConstOf();
+ else
+ _t.next = tn.next.wildOf();
+ }
+ }
+ //printf("TypeNext::makeWild() returns %p, %s\n", t, t.toChars());
+ return _t;
+ }
+ return t;
+}
+
+Type makeSharedConst(Type _this)
+{
+ if (_this.mcache && _this.mcache.scto)
+ return _this.mcache.scto;
+ Type t = _this.nullAttributes();
+ t.mod = MODFlags.shared_ | MODFlags.const_;
+
+ if (auto tn = _this.isTypeNext())
+ {
+ //printf("TypeNext::makeSharedConst() %s\n", toChars());
+ TypeNext _t = cast(TypeNext) t;
+ if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+ {
+ if (tn.next.isWild())
+ _t.next = tn.next.sharedWildConstOf();
+ else
+ _t.next = tn.next.sharedConstOf();
+ }
+ //printf("TypeNext::makeSharedConst() returns %p, %s\n", t, t.toChars());
+ return _t;
+ }
+ return t;
+}
+
+Type makeShared(Type _this)
+{
+ if (_this.mcache && _this.mcache.sto)
+ return _this.mcache.sto;
+ Type t = _this.nullAttributes();
+ t.mod = MODFlags.shared_;
+
+ if (auto tn = _this.isTypeNext())
+ {
+ //printf("TypeNext::makeShared() %s\n", toChars());
+ TypeNext _t = cast(TypeNext) t;
+ if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+ {
+ if (tn.next.isWild())
+ {
+ if (tn.next.isConst())
+ _t.next = tn.next.sharedWildConstOf();
+ else
+ _t.next = tn.next.sharedWildOf();
+ }
+ else
+ {
+ if (tn.next.isConst())
+ _t.next = tn.next.sharedConstOf();
+ else
+ _t.next = tn.next.sharedOf();
+ }
+ }
+ //printf("TypeNext::makeShared() returns %p, %s\n", t, t.toChars());
+ return _t;
+ }
+ return t;
+}
+
+Type makeImmutable(Type _this)
+{
+ if (_this.mcache && _this.mcache.ito)
+ return _this.mcache.ito;
+ Type t = _this.nullAttributes();
+ t.mod = MODFlags.immutable_;
+
+ if (auto tn = _this.isTypeNext())
+ {
+ //printf("TypeNext::makeImmutable() %s\n", toChars());
+ TypeNext _t = cast(TypeNext) t;
+ if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+ {
+ _t.next = tn.next.immutableOf();
+ }
+ return _t;
+ }
+ return t;
+}
+
+Type makeMutable(Type _this)
+{
+ Type t = _this.nullAttributes();
+ t.mod = _this.mod & MODFlags.shared_;
+
+ if (auto tn = _this.isTypeNext())
+ {
+ //printf("TypeNext::makeMutable() %p, %s\n", this, toChars());
+ TypeNext _t = cast(TypeNext)t;
+ if (tn.ty == Tsarray)
+ {
+ _t.next = tn.next.mutableOf();
+ }
+ //printf("TypeNext::makeMutable() returns %p, %s\n", t, t.toChars());
+ return _t;
+ }
+ return t;
+}
+
+Type makeConst(Type _this)
+{
+ if (_this.mcache && _this.mcache.cto)
+ {
+ assert(_this.mcache.cto.mod == MODFlags.const_);
+ return _this.mcache.cto;
+ }
+
+ static Type defaultMakeConst(Type _this)
+ {
+ //printf("Type::makeConst() %p, %s\n", this, toChars());
+ Type t = _this.nullAttributes();
+ t.mod = MODFlags.const_;
+ //printf("-Type::makeConst() %p, %s\n", t, toChars());
+ return t;
+ }
+
+ static Type typeNextMakeConst(TypeNext _this)
+ {
+ //printf("TypeNext::makeConst() %p, %s\n", this, toChars());
+ TypeNext t = cast(TypeNext)defaultMakeConst(cast(Type)_this);
+ if (_this.ty != Tfunction && _this.next.ty != Tfunction && !_this.next.isImmutable())
+ {
+ if (_this.next.isShared())
+ {
+ if (_this.next.isWild())
+ t.next = _this.next.sharedWildConstOf();
+ else
+ t.next = _this.next.sharedConstOf();
+ }
+ else
+ {
+ if (_this.next.isWild())
+ t.next = _this.next.wildConstOf();
+ else
+ t.next = _this.next.constOf();
+ }
+ }
+ //printf("TypeNext::makeConst() returns %p, %s\n", t, t.toChars());
+ return t;
+ }
+
+ if (auto tn = _this.isTypeNext())
+ return typeNextMakeConst(tn);
+
+ return defaultMakeConst(_this);
+}
+
+/*******************************
+ * If _this is a shell around another type,
+ * get that other type.
+ */
+Type toBasetype(Type _this)
+{
+ /* This function is used heavily.
+ * De-virtualize it so it can be easily inlined.
+ */
+ if (auto te = _this.isTypeEnum())
+ return te.toBasetype2();
+ return _this;
+}
+
+Type toBasetype2(TypeEnum _this)
+{
+ if (!_this.sym.members && !_this.sym.memtype)
+ return _this;
+ auto tb = _this.sym.getMemtype(Loc.initial).toBasetype();
+ return tb.castMod(_this.mod); // retain modifier bits from '_this'
+}
+
+Type memType(TypeEnum _this)
+{
+ return _this.sym.getMemtype(Loc.initial);
+}
+
+uint alignsize(Type _this)
+{
+ static uint structAlignsize(TypeStruct _this)
+ {
+ import dmd.dsymbolsem : size;
+ _this.sym.size(Loc.initial); // give error for forward references
+ return _this.sym.alignsize;
+ }
+
+ static uint enumAlignsize(TypeEnum _this)
+ {
+ Type t = _this.memType();
+ if (t.ty == Terror)
+ return 4;
+ return t.alignsize();
+ }
+
+ if (auto tb = _this.isTypeBasic())
+ return target.alignsize(tb);
+
+ switch(_this.ty)
+ {
+ case Tvector: return cast(uint)_this.isTypeVector().basetype.size();
+ case Tsarray: return _this.isTypeSArray().next.alignsize();
+ // A DArray consists of two ptr-sized values, so align it on pointer size boundary
+ case Tarray, Tdelegate: return target.ptrsize;
+ case Tstruct: return structAlignsize(_this.isTypeStruct());
+ case Tenum: return enumAlignsize(_this.isTypeEnum());
+ case Tnoreturn: return 0;
+ default: return cast(uint)size(_this, Loc.initial);
+ }
+}
+
+/*************************************
+ * Detect if type has pointer fields that are initialized to void.
+ * Local stack variables with such void fields can remain uninitialized,
+ * leading to pointer bugs.
+ * Returns:
+ * true if so
+ */
+
+bool hasVoidInitPointers(Type _this)
+{
+ if (auto tsa = _this.isTypeSArray())
+ {
+ return tsa.next.hasVoidInitPointers();
+ }
+ else if (auto ts = _this.isTypeStruct())
+ {
+ import dmd.dsymbolsem : size;
+ ts.sym.size(Loc.initial); // give error for forward references
+ ts.sym.determineTypeProperties();
+ return ts.sym.hasVoidInitPointers;
+ }
+ else if (auto te = _this.isTypeEnum())
+ {
+ return te.memType().hasVoidInitPointers();
+ }
+ return false;
+}
+
+void Type_init()
+{
+ Type.stringtable._init(14_000);
+
+ // Set basic types
+ __gshared TY* basetab =
+ [
+ Tvoid,
+ Tint8,
+ Tuns8,
+ Tint16,
+ Tuns16,
+ Tint32,
+ Tuns32,
+ Tint64,
+ Tuns64,
+ Tint128,
+ Tuns128,
+ Tfloat32,
+ Tfloat64,
+ Tfloat80,
+ Timaginary32,
+ Timaginary64,
+ Timaginary80,
+ Tcomplex32,
+ Tcomplex64,
+ Tcomplex80,
+ Tbool,
+ Tchar,
+ Twchar,
+ Tdchar,
+ Terror
+ ];
+
+ static Type merge(Type t)
+ {
+ import dmd.mangle.basic : tyToDecoBuffer;
+
+ OutBuffer buf;
+ buf.reserve(3);
+
+ if (t.ty == Tnoreturn)
+ buf.writestring("Nn");
+ else
+ tyToDecoBuffer(buf, t.ty);
+
+ auto sv = t.stringtable.update(buf[]);
+ if (sv.value)
+ return sv.value;
+ t.deco = cast(char*)sv.toDchars();
+ sv.value = t;
+ return t;
+ }
+
+ for (size_t i = 0; basetab[i] != Terror; i++)
+ {
+ Type t = new TypeBasic(basetab[i]);
+ .merge(t);
+ t = merge(t);
+ Type.basic[basetab[i]] = t;
+ }
+ Type.basic[Terror] = new TypeError();
+
+ Type.tnoreturn = new TypeNoreturn();
+ Type.tnoreturn.deco = merge(Type.tnoreturn).deco;
+ Type.basic[Tnoreturn] = Type.tnoreturn;
+
+ Type.tvoid = Type.basic[Tvoid];
+ Type.tint8 = Type.basic[Tint8];
+ Type.tuns8 = Type.basic[Tuns8];
+ Type.tint16 = Type.basic[Tint16];
+ Type.tuns16 = Type.basic[Tuns16];
+ Type.tint32 = Type.basic[Tint32];
+ Type.tuns32 = Type.basic[Tuns32];
+ Type.tint64 = Type.basic[Tint64];
+ Type.tuns64 = Type.basic[Tuns64];
+ Type.tint128 = Type.basic[Tint128];
+ Type.tuns128 = Type.basic[Tuns128];
+ Type.tfloat32 = Type.basic[Tfloat32];
+ Type.tfloat64 = Type.basic[Tfloat64];
+ Type.tfloat80 = Type.basic[Tfloat80];
+
+ Type.timaginary32 = Type.basic[Timaginary32];
+ Type.timaginary64 = Type.basic[Timaginary64];
+ Type.timaginary80 = Type.basic[Timaginary80];
+
+ Type.tcomplex32 = Type.basic[Tcomplex32];
+ Type.tcomplex64 = Type.basic[Tcomplex64];
+ Type.tcomplex80 = Type.basic[Tcomplex80];
+
+ Type.tbool = Type.basic[Tbool];
+ Type.tchar = Type.basic[Tchar];
+ Type.twchar = Type.basic[Twchar];
+ Type.tdchar = Type.basic[Tdchar];
+
+ Type.tshiftcnt = Type.tint32;
+ Type.terror = Type.basic[Terror];
+ Type.tnoreturn = Type.basic[Tnoreturn];
+ Type.tnull = new TypeNull();
+ Type.tnull.deco = merge(Type.tnull).deco;
+
+ Type.tvoidptr = Type.tvoid.pointerTo();
+ Type.tstring = Type.tchar.immutableOf().arrayOf();
+ Type.twstring = Type.twchar.immutableOf().arrayOf();
+ Type.tdstring = Type.tdchar.immutableOf().arrayOf();
+
+ const isLP64 = target.isLP64;
+
+ Type.tsize_t = Type.basic[isLP64 ? Tuns64 : Tuns32];
+ Type.tptrdiff_t = Type.basic[isLP64 ? Tint64 : Tint32];
+ Type.thash_t = Type.tsize_t;
+
+ static if (__VERSION__ == 2081)
+ {
+ // Related issue: https://issues.dlang.org/show_bug.cgi?id=19134
+ // D 2.081.x regressed initializing class objects at compile time.
+ // As a workaround initialize this global at run-time instead.
+ TypeTuple.empty = new TypeTuple();
+ }
+}
+
+/************************************
+ * Return alignment to use for this type.
+ */
+structalign_t alignment(Type _this)
+{
+ if (auto tsa = _this.isTypeSArray())
+ {
+ return tsa.next.alignment();
+ }
+ else if (auto ts = _this.isTypeStruct())
+ {
+ import dmd.dsymbolsem : size;
+ if (ts.sym.alignment.isUnknown())
+ ts.sym.size(ts.sym.loc);
+ return ts.sym.alignment;
+ }
+ structalign_t s;
+ s.setDefault();
+ return s;
+}
+
+/***************************************
+ * Returns: true if type has any invariants
+ */
+bool hasInvariant(Type _this)
+{
+ if (auto tsa = _this.isTypeSArray())
+ {
+ return tsa.next.hasInvariant();
+ }
+ else if (auto ts = _this.isTypeStruct())
+ {
+ import dmd.dsymbolsem : size;
+ ts.sym.size(Loc.initial); // give error for forward references
+ ts.sym.determineTypeProperties();
+ return ts.sym.hasInvariant() || ts.sym.hasFieldWithInvariant;
+ }
+ else if (auto te = _this.isTypeEnum())
+ {
+ return te.memType().hasInvariant();
+ }
+ return false;
+}
+
+/*************************************
+ * Detect if this is an unsafe type because of the presence of `@system` members
+ * Returns:
+ * true if so
+ */
+bool hasUnsafeBitpatterns(Type _this)
+{
+ static bool tstructImpl(TypeStruct _this)
+ {
+ import dmd.dsymbolsem : size;
+ _this.sym.size(Loc.initial); // give error for forward references
+ _this.sym.determineTypeProperties();
+ return _this.sym.hasUnsafeBitpatterns;
+ }
+
+ if(auto tb = _this.isTypeBasic())
+ return tb.ty == Tbool;
+
+ switch(_this.ty)
+ {
+ case Tenum: return _this.isTypeEnum().memType().hasUnsafeBitpatterns();
+ case Tstruct: return tstructImpl(_this.isTypeStruct());
+ case Tsarray: return _this.isTypeSArray().next.hasUnsafeBitpatterns();
+ default: return false;
+ }
+}
+
/*************************************
* Resolve a tuple index, `s[oindex]`, by figuring out what `s[oindex]` represents.
* Setting one of pe/pt/ps.
error(loc, "`%s` is not defined, perhaps `import %.*s;` ?", p, cast(int)n.length, n.ptr);
else if (auto s2 = sc.search_correct(id))
error(loc, "undefined identifier `%s`, did you mean %s `%s`?", p, s2.kind(), s2.toChars());
- else if (const q = Scope.search_correct_C(id))
+ else if (const q = search_correct_C(id))
error(loc, "undefined identifier `%s`, did you mean `%s`?", p, q);
else if ((id == Id.This && sc.getStructClassScope()) ||
(id == Id._super && sc.getClassScope()))
break;
}
- Type t = s.getType(); // type symbol, type alias, or type tuple?
+ Type t = dmd.dsymbolsem.getType(s); // type symbol, type alias, or type tuple?
const errorsave = global.errors;
SearchOptFlags flags = t is null ? SearchOpt.localsOnly : SearchOpt.ignorePrivateImports;
Type t;
while (1)
{
- t = s.getType();
+ t = dmd.dsymbolsem.getType(s);
if (t)
break;
ps = s;
tf.purity = typeFunction.purity;
}
-/******************************************
- * We've mistakenly parsed `t` as a type.
- * Redo `t` as an Expression only if there are no type modifiers.
- * Params:
- * t = mistaken type
- * Returns:
- * t redone as Expression, null if cannot
- */
-Expression typeToExpression(Type t)
-{
- static Expression visitSArray(TypeSArray t)
- {
- if (auto e = t.next.typeToExpression())
- return new ArrayExp(t.dim.loc, e, t.dim);
- return null;
- }
-
- static Expression visitAArray(TypeAArray t)
- {
- if (auto e = t.next.typeToExpression())
- {
- if (auto ei = t.index.typeToExpression())
- return new ArrayExp(t.loc, e, ei);
- }
- return null;
- }
-
- static Expression visitIdentifier(TypeIdentifier t)
- {
- return typeToExpressionHelper(t, new IdentifierExp(t.loc, t.ident));
- }
-
- static Expression visitInstance(TypeInstance t)
- {
- return typeToExpressionHelper(t, new ScopeExp(t.loc, t.tempinst));
- }
-
- // easy way to enable 'auto v = new int[mixin("exp")];' in 2.088+
- static Expression visitMixin(TypeMixin t)
- {
- return new TypeExp(t.loc, t);
- }
-
- if (t.mod)
- return null;
- switch (t.ty)
- {
- case Tsarray: return visitSArray(t.isTypeSArray());
- case Taarray: return visitAArray(t.isTypeAArray());
- case Tident: return visitIdentifier(t.isTypeIdentifier());
- case Tinstance: return visitInstance(t.isTypeInstance());
- case Tmixin: return visitMixin(t.isTypeMixin());
- default: return null;
- }
-}
-
/*************************************
* https://issues.dlang.org/show_bug.cgi?id=14488
* Check if the inner most base type is complex or imaginary.
bool bpure = !f.isPure && sc.func.setImpure(arg.loc, null);
bool bsafe = !f.isSafe() && !f.isTrusted() && sc.setUnsafe(false, arg.loc, null);
- bool bnogc = !f.isNogc && sc.func.setGC(arg.loc, null);
+ bool bnogc = !f.isNogc && sc.setGC(sc.func, arg.loc, null);
if (bpure | bsafe | bnogc)
{
const nullptr = "".ptr;
}
// check if the copy constructor may be called to copy the argument
- if (arg.isLvalue() && !isRef && argStruct && argStruct == prmStruct && argStruct.hasCopyCtor)
+ if (arg.isLvalue() && !isRef && argStruct && argStruct == prmStruct && argStruct.hasCopyCtor &&
+ !isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage))
{
- if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage))
- return MATCH.nomatch;
- m = MATCH.exact;
+ return MATCH.nomatch;
}
else
{
default:
// We can have things as `foo(int[int] wat...)` but they only match
// with an associative array proper.
- if (pMessage && trailingArgs.length) *pMessage = tf.getParamError(trailingArgs[0], p);
+ if (!pMessage) {}
+ else if (!trailingArgs.length)
+ {
+ OutBuffer buf;
+ getMatchError(buf, "expected an argument for parameter `%s`",
+ parameterToChars(p, tf, false));
+ *pMessage = buf.extractChars();
+ }
+ else
+ *pMessage = tf.getParamError(trailingArgs[0], p);
+
return MATCH.nomatch;
}
}
case Tinstance:
case Ttypeof:
case Treturn: return visitTypeQualified(cast(TypeQualified)t);
- case Tstruct: return t.isTypeStruct().sym.size(loc);
+ case Tstruct:
+ {
+ import dmd.dsymbolsem: size;
+ return t.isTypeStruct().sym.size(loc);
+ }
case Tenum: return t.isTypeEnum().sym.getMemtype(loc).size(loc);
case Tnull: return t.tvoidptr.size(loc);
case Tnoreturn: return 0;
Type visitComplex(TypeBasic t)
{
- if (!sc.inCfile)
- return visitType(t);
-
- auto tc = getComplexLibraryType(loc, sc, t.ty);
- if (tc.ty == Terror)
- return tc;
- return tc.addMod(t.mod).merge();
+ return visitType(t);
}
Type visitVector(TypeVector mtype)
if (s)
{
auto td = s.isTemplateDeclaration;
+ td.computeOneMember();
if (td && td.onemember && td.onemember.isAggregateDeclaration)
.error(loc, "template %s `%s` is used as a type without instantiation"
~ "; to instantiate it use `%s!(arguments)`",
Type t;
Dsymbol s;
mtype.resolve(loc, sc, e, t, s);
- if (s && (t = s.getType()) !is null)
+ if (s && (t = dmd.dsymbolsem.getType(s)) !is null)
t = t.addMod(mtype.mod);
if (!t)
{
Type t;
Dsymbol s;
mtype.resolve(loc, sc, e, t, s);
- if (s && (t = s.getType()) !is null)
+ if (s && (t = dmd.dsymbolsem.getType(s)) !is null)
t = t.addMod(mtype.mod);
if (!t)
{
// If `typeSemantic` succeeded, there may have been deprecations that
// were gagged due the `startGagging` above. Run again to display
// those deprecations. https://issues.dlang.org/show_bug.cgi?id=19107
- if (global.gaggedWarnings > 0)
+ if (global.gaggedDeprecations > 0)
typeSemantic(tcopy, loc, sc);
}
//printf("-trySemantic(%s) %d\n", toChars(), global.errors);
{
printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars());
}
- ts.sym.size(loc);
+ {
+ import dmd.dsymbolsem: size;
+ ts.sym.size(loc);
+ }
if (ts.sym.sizeok != Sizeok.done)
return ErrorExp.get();
!mt.exp.type.isTypeTuple())
{
if (!sc.inCfile && // in (extended) C typeof may be used on types as with sizeof
- mt.exp.checkType())
+ !mt.exp.hasValidType())
goto Lerr;
/* Today, 'typeof(func)' returns void if func is a
{
auto ad = v.isMember();
objc.checkOffsetof(e, ad);
- ad.size(e.loc);
+ {
+ import dmd.dsymbolsem: size;
+ ad.size(e.loc);
+ }
if (ad.sizeok != Sizeok.done)
return ErrorExp.get();
uint value;
}
}
- if (auto t = s.getType())
+ if (auto t = dmd.dsymbolsem.getType(s))
{
return (new TypeExp(e.loc, t)).expressionSemantic(sc);
}
if (TupleDeclaration tup = d.isTupleDeclaration())
{
e = new TupleExp(e.loc, tup);
+ fillTupleExpExps(e.isTupleExp(), tup);
return e.expressionSemantic(sc);
}
if (d.needThis() && sc.intypeof != 1)
*/
e = e.expressionSemantic(sc); // do this before turning on noAccessCheck
- mt.sym.size(e.loc); // do semantic of type
+ {
+ import dmd.dsymbolsem: size;
+ mt.sym.size(e.loc); // do semantic of type
+ }
Expression e0;
Expression ev = e.op == EXP.type ? null : e;
}
}
- if (auto t = s.getType())
+ if (auto t = dmd.dsymbolsem.getType(s))
{
return (new TypeExp(e.loc, t)).expressionSemantic(sc);
}
if (TupleDeclaration tup = d.isTupleDeclaration())
{
e = new TupleExp(e.loc, tup);
+ fillTupleExpExps(e.isTupleExp(), tup);
e = e.expressionSemantic(sc);
return e;
}
&& d.isFuncDeclaration().objc.selector)
{
auto classRef = new ObjcClassReferenceExp(e.loc, mt.sym);
- classRef.type = objc.getRuntimeMetaclass(mt.sym).getType();
+ classRef.type = dmd.dsymbolsem.getType((objc.getRuntimeMetaclass(mt.sym)));
return new DotVarExp(e.loc, classRef, d).expressionSemantic(sc);
}
else if (d.needThis() && sc.intypeof != 1)
return *pt;
}
s = s.toAlias();
- if (auto t = s.getType())
+ if (auto t = dmd.dsymbolsem.getType(s))
{
if (auto ts = t.toBasetype().isTypeStruct())
{
private:
-/* Helper function for `typeToExpression`. Contains common code
- * for TypeQualified derived classes.
- */
-Expression typeToExpressionHelper(TypeQualified t, Expression e, size_t i = 0)
-{
- //printf("toExpressionHelper(e = %s %s)\n", EXPtoString(e.op).ptr, e.toChars());
- foreach (id; t.idents[i .. t.idents.length])
- {
- //printf("\t[%d] e: '%s', id: '%s'\n", i, e.toChars(), id.toChars());
-
- final switch (id.dyncast())
- {
- // ... '. ident'
- case DYNCAST.identifier:
- e = new DotIdExp(e.loc, e, cast(Identifier)id);
- break;
-
- // ... '. name!(tiargs)'
- case DYNCAST.dsymbol:
- auto ti = (cast(Dsymbol)id).isTemplateInstance();
- assert(ti);
- e = new DotTemplateInstanceExp(e.loc, e, ti.name, ti.tiargs);
- break;
-
- // ... '[type]'
- case DYNCAST.type: // https://issues.dlang.org/show_bug.cgi?id=1215
- e = new ArrayExp(t.loc, e, new TypeExp(t.loc, cast(Type)id));
- break;
-
- // ... '[expr]'
- case DYNCAST.expression: // https://issues.dlang.org/show_bug.cgi?id=1215
- e = new ArrayExp(t.loc, e, cast(Expression)id);
- break;
-
- case DYNCAST.object:
- case DYNCAST.tuple:
- case DYNCAST.parameter:
- case DYNCAST.statement:
- case DYNCAST.condition:
- case DYNCAST.templateparameter:
- case DYNCAST.initializer:
- assert(0);
- }
- }
- return e;
-}
-
/**************************
* This evaluates exp while setting length to be the number
* of elements in the tuple t.
import dmd.mtype;
import dmd.templatesem;
import dmd.typesem;
+import dmd.dsymbolsem : addDeferredSemantic3;
import core.stdc.stdio;
/****************************************************
if (e)
.error(loc, "expression `%s` uses the GC and cannot be used with switch `-betterC`", e.toChars());
else
- .error(loc, "`TypeInfo` cannot be used with -betterC");
+ .error(loc, "`TypeInfo` cannot be used with `-betterC`");
if (sc && sc.tinst)
sc.tinst.printInstantiationTrace(Classification.error, uint.max);
t.vtinfo = ti; // assign it early to avoid recursion in expressionSemantic
ti._scope = sc;
sc.setNoFree();
- Module.addDeferredSemantic3(ti);
+ addDeferredSemantic3(ti);
return ti;
}
#include "d-tree.h"
-/* Determine if type T is a struct that has a postblit. */
-
-static bool
-needs_postblit (Type *t)
-{
- t = t->baseElemOf ();
-
- if (TypeStruct *ts = t->isTypeStruct ())
- {
- if (ts->sym->postblit)
- return true;
- }
-
- return false;
-}
-
-/* Determine if type T is a struct that has a destructor. */
-
-static bool
-needs_dtor (Type *t)
-{
- t = t->baseElemOf ();
-
- if (TypeStruct *ts = t->isTypeStruct ())
- {
- if (ts->sym->dtor)
- return true;
- }
-
- return false;
-}
-
-/* Determine if expression E is a suitable lvalue. */
-
-static bool
-lvalue_p (Expression *e)
-{
- SliceExp *se = e->isSliceExp ();
- if (se != NULL && se->e1->isLvalue ())
- return true;
-
- CastExp *ce = e->isCastExp ();
- if (ce != NULL && ce->e1->isLvalue ())
- return true;
-
- return (e->op != EXP::slice && e->isLvalue ());
-}
-
/* Build an expression of code CODE, data type TYPE, and operands ARG0 and
ARG1. Perform relevant conversions needed for correct code operations. */
this->result_ = d_convert (build_ctype (e->type),
build_boolop (code, t1, t2));
}
- else if (tb1->isFloating () && tb1->ty != TY::Tvector)
+ else if (dmd::isFloating (tb1) && tb1->ty != TY::Tvector)
{
/* For floating-point values, identity is defined as the bits in the
operands being identical. */
tree t1 = d_save_expr (build_expr (e->e1));
tree t2 = d_save_expr (build_expr (e->e2));
- if (!tb1->isComplex ())
+ if (!dmd::isComplex (tb1))
this->result_ = build_float_identity (code, t1, t2);
else
{
{
case EXP::add:
case EXP::min:
- if ((e->e1->type->isReal () && e->e2->type->isImaginary ())
- || (e->e1->type->isImaginary () && e->e2->type->isReal ()))
+ if ((dmd::isReal (e->e1->type) && dmd::isImaginary (e->e2->type))
+ || (dmd::isImaginary (e->e1->type) && dmd::isReal (e->e2->type)))
{
/* If the result is complex, then we can shortcut binary_op.
Frontend should have already validated types and sizes. */
if (e->op == EXP::min)
t2 = build1 (NEGATE_EXPR, TREE_TYPE (t2), t2);
- if (e->e1->type->isReal ())
+ if (dmd::isReal (e->e1->type))
this->result_ = complex_expr (build_ctype (e->type), t1, t2);
else
this->result_ = complex_expr (build_ctype (e->type), t2, t1);
}
}
- code = e->e1->type->isIntegral ()
+ code = dmd::isIntegral (e->e1->type)
? TRUNC_DIV_EXPR : RDIV_EXPR;
break;
case EXP::mod:
- code = e->e1->type->isFloating ()
+ code = dmd::isFloating (e->e1->type)
? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR;
break;
break;
case EXP::divAssign:
- code = e->e1->type->isIntegral ()
+ code = dmd::isIntegral (e->e1->type)
? TRUNC_DIV_EXPR : RDIV_EXPR;
break;
case EXP::modAssign:
- code = e->e1->type->isFloating ()
+ code = dmd::isFloating (e->e1->type)
? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR;
break;
}
}
+ /* Lower a construction expression, or forward to assignment expression. */
+
+ void visit (ConstructExp *e) final override
+ {
+ if (!e->lowering)
+ return ExprVisitor::visit ((AssignExp *) e);
+
+ this->result_ = build_expr (e->lowering);
+ }
+
/* Build an assignment expression. The right operand is implicitly
converted to the type of the left operand, and assigned to it. */
Type *stype = se->e1->type->toBasetype ();
Type *etype = stype->nextOf ()->toBasetype ();
- /* Determine if we need to run postblit or dtor. */
- bool postblit = needs_postblit (etype) && lvalue_p (e->e2);
- bool destructor = needs_dtor (etype);
-
if (e->memset == MemorySet::blockAssign)
{
/* Set a range of elements to one value. */
tree init = stabilize_expr (&t1);
t1 = d_save_expr (t1);
- if ((postblit || destructor) && e->op != EXP::blit)
- {
- /* This case should have been rewritten to `_d_arraysetassign`
- in the semantic phase. */
- gcc_unreachable ();
- }
-
if (integer_zerop (t2))
{
tree size = size_mult_expr (d_array_length (t1),
/* Perform a memcpy operation. */
gcc_assert (e->e2->type->ty != TY::Tpointer);
- if (!postblit && !destructor)
- {
- tree t1 = d_save_expr (d_array_convert (e->e1));
- tree t2 = d_save_expr (d_array_convert (e->e2));
+ tree t1 = d_save_expr (d_array_convert (e->e1));
+ tree t2 = d_save_expr (d_array_convert (e->e2));
- /* References to array data. */
- tree t1ptr = d_array_ptr (t1);
- tree t1len = d_array_length (t1);
- tree t2ptr = d_array_ptr (t2);
+ /* References to array data. */
+ tree t1ptr = d_array_ptr (t1);
+ tree t1len = d_array_length (t1);
+ tree t2ptr = d_array_ptr (t2);
- /* Generate: memcpy(to, from, size) */
- tree size =
- size_mult_expr (t1len, size_int (dmd::size (etype)));
- tree result = build_memcpy_call (t1ptr, t2ptr, size);
+ /* Generate: memcpy(to, from, size) */
+ tree size = size_mult_expr (t1len, size_int (dmd::size (etype)));
+ tree result = build_memcpy_call (t1ptr, t2ptr, size);
- /* Insert check that array lengths match and do not overlap. */
- if (array_bounds_check ())
- {
- /* tlencmp = (t1len == t2len) */
- tree t2len = d_array_length (t2);
- tree tlencmp = build_boolop (EQ_EXPR, t1len, t2len);
-
- /* toverlap = (t1ptr + size <= t2ptr
- || t2ptr + size <= t1ptr) */
- tree t1ptrcmp = build_boolop (LE_EXPR,
- build_offset (t1ptr, size),
- t2ptr);
- tree t2ptrcmp = build_boolop (LE_EXPR,
- build_offset (t2ptr, size),
- t1ptr);
- tree toverlap = build_boolop (TRUTH_ORIF_EXPR, t1ptrcmp,
- t2ptrcmp);
-
- /* (tlencmp && toverlap) ? memcpy() : _d_arraybounds() */
- tree tassert = build_array_bounds_call (e->loc);
- tree tboundscheck = build_boolop (TRUTH_ANDIF_EXPR,
- tlencmp, toverlap);
-
- result = build_condition (void_type_node, tboundscheck,
- result, tassert);
- }
-
- this->result_ = compound_expr (result, t1);
- }
- else if ((postblit || destructor)
- && e->op != EXP::blit && e->op != EXP::construct)
- {
- /* Assigning to a non-trivially copyable array has already been
- handled by the front-end. */
- gcc_unreachable ();
- }
- else
+ /* Insert check that array lengths match and do not overlap. */
+ if (array_bounds_check ())
{
- /* Generate: _d_arraycopy() */
- this->result_ = build_libcall (LIBCALL_ARRAYCOPY, e->type, 3,
- size_int (dmd::size (etype)),
- d_array_convert (e->e2),
- d_array_convert (e->e1));
+ /* tlencmp = (t1len == t2len) */
+ tree t2len = d_array_length (t2);
+ tree tlencmp = build_boolop (EQ_EXPR, t1len, t2len);
+
+ /* toverlap = (t1ptr + size <= t2ptr
+ || t2ptr + size <= t1ptr) */
+ tree t1ptrcmp = build_boolop (LE_EXPR,
+ build_offset (t1ptr, size),
+ t2ptr);
+ tree t2ptrcmp = build_boolop (LE_EXPR,
+ build_offset (t2ptr, size),
+ t1ptr);
+ tree toverlap = build_boolop (TRUTH_ORIF_EXPR, t1ptrcmp,
+ t2ptrcmp);
+
+ /* (tlencmp && toverlap) ? memcpy() : _d_arraybounds() */
+ tree tassert = build_array_bounds_call (e->loc);
+ tree tboundscheck = build_boolop (TRUTH_ANDIF_EXPR,
+ tlencmp, toverlap);
+
+ result = build_condition (void_type_node, tboundscheck,
+ result, tassert);
}
+
+ this->result_ = compound_expr (result, t1);
}
return;
this->result_ = build_memset_call (build_expr (e->e1));
return;
}
-
- Type *etype = tb1->nextOf ();
- gcc_assert (e->e2->type->toBasetype ()->ty == TY::Tsarray);
-
- /* Determine if we need to run postblit. */
- const bool postblit = needs_postblit (etype);
- const bool destructor = needs_dtor (etype);
- const bool lvalue = lvalue_p (e->e2);
-
- /* Optimize static array assignment with array literal. Even if the
- elements in rhs are all rvalues and don't have to call postblits,
- this assignment should call dtors on old assigned elements. */
- if ((!postblit && !destructor)
- || (e->op == EXP::construct && e->e2->op == EXP::arrayLiteral)
- || (e->op == EXP::construct && e->e2->op == EXP::call)
- || (e->op == EXP::construct && !lvalue && postblit)
- || (e->op == EXP::blit || dmd::size (e->e1->type) == 0))
+ else
{
+ /* Optimize static array assignment with array literal. Even if the
+ elements in rhs are all rvalues and don't have to call postblits,
+ this assignment should call dtors on old assigned elements. */
+ gcc_assert (e->e2->type->toBasetype ()->ty == TY::Tsarray);
tree t1 = build_expr (e->e1);
tree t2 = convert_for_assignment (e->e2, e->e1->type);
this->result_ = build_assign (modifycode, t1, t2);
return;
}
-
- /* All other kinds of lvalue or rvalue static array assignment.
- Array construction has already been handled by the front-end. */
- gcc_assert (e->op != EXP::construct);
- gcc_unreachable ();
}
/* Simple assignment. */
{
AddExp *ae = e->e1->isAddExp ();
if (ae->e1->op == EXP::address
- && ae->e2->isConst () && ae->e2->type->isIntegral ())
+ && ae->e2->isConst () && dmd::isIntegral (ae->e2->type))
{
Expression *ex = ae->e1->isAddrExp ()->e1;
tnext = ex->type->toBasetype ();
result = build_expr (ex);
- offset = ae->e2->toUInteger ();
+ offset = dmd::toUInteger (ae->e2);
}
}
else if (e->e1->op == EXP::symbolOffset)
tree new_call;
/* Cannot new an opaque struct. */
- if (sd->size (e->loc) == 0)
+ if (dmd::size (sd, e->loc) == 0)
{
this->result_ = d_convert (build_ctype (e->type),
integer_zero_node);
/* Implicitly convert void[n] to ubyte[n]. */
if (tb->ty == TY::Tsarray && tb->nextOf ()->toBasetype ()->ty == TY::Tvoid)
{
- dinteger_t ndim = tb->isTypeSArray ()->dim->toUInteger ();
+ dinteger_t ndim = dmd::toUInteger (tb->isTypeSArray ()->dim);
tb = dmd::sarrayOf (Type::tuns8, ndim);
}
void visit (VarDeclaration *d) final override
{
/* Not all kinds of manifest constants create a CONST_DECL. */
- if (!d->canTakeAddressOf () && !d->type->isScalar ())
+ if (!d->canTakeAddressOf () && !dmd::isScalar (d->type))
return;
visit ((Declaration *) d);
if (dsym == d)
{
- Type *type = d->getType ();
+ Type *type = dmd::getType (d);
/* Type imports should really be part of their own visit method. */
if (type != NULL)
/* core.stdc.stdarg intrinsics. */
-DEF_D_BUILTIN (INTRINSIC_VA_ARG, BUILT_IN_NONE, "va_arg", "core.stdc.stdarg",
- "FK@7va_listK@1TZv")
-DEF_D_BUILTIN (INTRINSIC_C_VA_ARG, BUILT_IN_NONE, "va_arg", "core.stdc.stdarg",
- "FK@7va_listZ@1T")
-DEF_D_BUILTIN (INTRINSIC_VASTART, BUILT_IN_NONE, "va_start", "core.stdc.stdarg",
- "FJ@7va_listK@1TZv")
+DEF_D_BUILTIN (INTRINSIC_VA_ARG, BUILT_IN_NONE, "va_arg",
+ "core.internal.vararg.gnu", "FK@7va_listK@1TZv")
+DEF_D_BUILTIN (INTRINSIC_C_VA_ARG, BUILT_IN_NONE, "va_arg",
+ "core.internal.vararg.gnu", "FK@7va_listZ@1T")
+DEF_D_BUILTIN (INTRINSIC_VASTART, BUILT_IN_NONE, "va_start",
+ "core.internal.vararg.gnu", "FJ@7va_listK@1TZv")
/* gcc.simd intrinsics. */
Driver
Link the standard D library dynamically in the compilation.
+std=d2024
+D
+Conform to the 2024 D language edition.
+
+std=d202y
+D
+Conform to the latest 202Y D language edition (experimental).
+
v
D
; Documented in C
/* A switch statement on a string gets turned into a library call.
It is not lowered during codegen. */
- if (!condtype->isScalar ())
+ if (!dmd::isScalar (condtype))
{
error ("cannot handle switch condition of type %s",
condtype->toChars ());
else
{
tree casevalue;
- if (s->exp->type->isScalar ())
+ if (dmd::isScalar (s->exp->type))
casevalue = build_expr (s->exp);
else
casevalue = build_integer_cst (s->index, build_ctype (Type::tint32));
void visit (GccAsmStatement *s) final override
{
- StringExp *insn = s->insn->toStringExp ();
+ StringExp *insn = dmd::toStringExp (s->insn);
tree outputs = NULL_TREE;
tree inputs = NULL_TREE;
tree clobbers = NULL_TREE;
const char *sname = name ? name->toChars () : NULL;
tree id = name ? build_string (strlen (sname), sname) : NULL_TREE;
- StringExp *constr = (*s->constraints)[i]->toStringExp ();
+ StringExp *constr = dmd::toStringExp ((*s->constraints)[i]);
const char *cstring = (const char *)(constr->len
? constr->string : "");
tree str = build_string (constr->len, cstring);
{
for (size_t i = 0; i < s->clobbers->length; i++)
{
- StringExp *clobber = (*s->clobbers)[i]->toStringExp ();
+ StringExp *clobber = dmd::toStringExp ((*s->clobbers)[i]);
const char *cstring = (const char *)(clobber->len
? clobber->string : "");
/* Fill in the vtbl[]. */
if (!cd->isInterfaceDeclaration ())
- b->fillVtbl (cd, &b->vtbl, 1);
+ dmd::fillVtbl (b, cd, &b->vtbl, 1);
/* ClassInfo for the interface. */
value = build_address (get_classinfo_decl (id));
return;
/* Fill bvtbl with the functions we want to put out. */
- if (cd != bcd && !bs->fillVtbl (cd, &bvtbl, 0))
+ if (cd != bcd && !dmd::fillVtbl (bs, cd, &bvtbl, 0))
return;
/* First entry is struct Interface reference. */
this->layout_field (build_typeinfo (d->loc, ti->next));
/* Static array length. */
- this->layout_field (size_int (ti->dim->toInteger ()));
+ this->layout_field (size_int (dmd::toInteger (ti->dim)));
}
/* Layout of TypeInfo_AssociativeArray is:
if (!sd->members)
return;
+ if (sd->semanticRun () < PASS::semantic3done)
+ dmd::semanticTypeInfoMembers (sd);
+
/* Mangled name of the struct declaration. */
this->layout_string (ti->deco);
this->layout_field (null_pointer_node);
/* uint m_align; */
- this->layout_field (build_integer_cst (ti->alignsize (), d_uint_type));
+ this->layout_field (build_integer_cst (dmd::alignsize (ti), d_uint_type));
/* immutable(void)* xgetRTInfo; */
if (sd->getRTInfo)
for (size_t k = 0; k < cd2->vtblInterfaces->length; k++)
{
BaseClass *bs = (*cd2->vtblInterfaces)[k];
- if (bs->fillVtbl (cd, NULL, 0))
+ if (dmd::fillVtbl (bs, cd, NULL, 0))
{
if (bc == bs)
return csymoffset;
void visit (TypeSArray *t) final override
{
- if (t->dim->isConst () && t->dim->type->isIntegral ())
+ if (t->dim->isConst () && dmd::isIntegral (t->dim->type))
{
- uinteger_t size = t->dim->toUInteger ();
+ uinteger_t size = dmd::toUInteger (t->dim);
t->ctype = make_array_type (t->next, size);
}
else
void visit (TypeVector *t) final override
{
- int nunits = t->basetype->isTypeSArray ()->dim->toUInteger ();
+ int nunits = dmd::toUInteger (t->basetype->isTypeSArray ()->dim);
tree inner = build_ctype (t->elementType ());
/* Same rationale as void static arrays. */
tree ident = get_identifier (member->ident->toChars ());
Expression *evalue = member->value ();
- tree value = build_integer_cst (evalue->toInteger (),
+ tree value = build_integer_cst (dmd::toInteger (evalue),
basetype);
/* Build an identifier for the enumeration constant. */
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90601
// { dg-do compile }
+// { dg-options "-Wno-deprecated" }
int postincr(int a)
{
--- /dev/null
+alias f = (auto ref int x) {};
-/* REQUIRED_ARGS: -preview=bitfields
+/*
*/
/***************************************************/
--- /dev/null
+// REQUIRED_ARGS: -inline
+
+struct Foo
+{
+ int x;
+ int foo()
+ {
+ ref get() { return x; }
+ return get();
+ }
+}
--- /dev/null
+struct Vars {
+ int x²;
+ int \u2160;
+ int \u2182;
+ int \u00C0;
+ int \u00C1;
+ int \U000000C2;
+ int wh\u00ff;
+ int a\u00c4b\u0441\U000003b4e;
+ int b\u3021𗘰\u3023e;
+};
--- /dev/null
+// https://github.com/dlang/dmd/issues/21894
+
+inout(int[]) test1(inout(int[]) a, int b)
+{
+ return a ~ b;
+}
+inout(int[]) test2(inout(int[]) a, inout(int[]) b)
+{
+ return a ~ b;
+}
+void main()
+{
+ test1([1], 2);
+ test2([1], [2]);
+}
--- /dev/null
+// REQUIRED_ARGS: -preview=dip1000
+
+void assertEq(scope int[][3] x) @safe
+{
+ bool b = x == x;
+}
class Tree {
int opApply(int delegate(size_t, Tree) dg) {
- if (dg(0, this)) return 1;
+ if (auto r = dg(0, this)) return r;
return 0;
}
}
--- /dev/null
+struct NameAttribute
+{
+ int delegate() foo;
+}
+
+static NameAttribute getNamedAttribute(alias S)()
+{
+ return __traits(getAttributes, S)[0];
+}
+
+struct TestStruct
+{
+ @NameAttribute({ return 42; }) int a;
+}
+
+void test()
+{
+ TestStruct m;
+ enum nameAttr = getNamedAttribute!(m.a);
+}
--- /dev/null
+extern(C++) struct S
+{
+ static void link(alias MO)(){}
+ __gshared int i;
+}
+
+void main()
+{
+ S.link!(S.i);
+}
--- /dev/null
+//spaces
+__gshared pragma(mangle, "test 9") ubyte test9_1;
+__gshared extern pragma(mangle, "test 9") ubyte test9_1_e;
+
+//\n chars
+__gshared pragma(mangle, "test\\\n9") ubyte test9_2;
+__gshared extern pragma(mangle, "test\\\n9") ubyte test9_2_e;
+
+//\a chars
+__gshared pragma(mangle, "test\a9") ubyte test9_3;
+__gshared extern pragma(mangle, "test\a9") ubyte test9_3_e;
+
+//\x01 chars
+__gshared pragma(mangle, "test\x019") ubyte test9_4;
+__gshared extern pragma(mangle, "test\x019") ubyte test9_4_e;
+
+//\xff chars
+__gshared pragma(mangle, "test\xff9") ubyte test9_6;
+__gshared extern pragma(mangle, "test\xff9") ubyte test9_6_e;
+
+//unicode
+__gshared pragma(mangle, "😀ÀÁÂÃÄÅßàáâãäaåbæçèéêcëìígîïð7ñ9__òóô4õöÆ3ÇÈÉÊËabcÌÍÎÏÐÑÒÓÔÕÖ😄😅🤣😂_ÿ")
+ubyte test9_7;
+__gshared extern pragma(mangle, "😀ÀÁÂÃÄÅßàáâãäaåbæçèéêcëìígîïð7ñ9__òóô4õöÆ3ÇÈÉÊËabcÌÍÎÏÐÑÒÓÔÕÖ😄😅🤣😂_ÿ")
+ubyte test9_7_e;
--- /dev/null
+// Tests for pragma mangle
+module pragmamangle;
+
+version (Posix): // Itanium C++ ABI only
+
+string ctfe(string s) { return "mangle_" ~ "ctfe_" ~ s; }
+immutable string const_function = "mangle_const_function";
+immutable string const_variable = "mangle_const_variable";
+
+// This mangle string doesn't propagate to tests that use type_symbol.
+pragma(mangle, "Q_should_mangle_string_propagate")
+struct type_symbol { }
+
+class template_symbol(T) { }
+
+/* 1. Overrides the default mangling for a symbol.
+ */
+pragma(mangle, "mangle_function") void test_fun1();
+static assert(test_fun1.mangleof == "mangle_function");
+
+pragma(mangle, "mangle_attribute") extern(C) { nothrow { void test_pokeattr1(); } }
+static assert(test_pokeattr1.mangleof == "mangle_attribute");
+
+/* 2a. For variables and functions there must be one AssignExpression and it
+ * must evaluate at compile time to a string literal
+ */
+pragma(mangle, ctfe("function")) void test_fun2a1();
+static assert(test_fun2a1.mangleof == "mangle_ctfe_function");
+
+pragma(mangle, const_function) void test_fun2a2();
+static assert(test_fun2a2.mangleof == "mangle_const_function");
+
+pragma(mangle, ctfe("variable")) int test_var2a1;
+static assert(test_var2a1.mangleof == "mangle_ctfe_variable");
+
+pragma(mangle, const_variable) int test_var2a2;
+static assert(test_var2a2.mangleof == "mangle_const_variable");
+
+/* 2b. For aggregates there may be one or two AssignExpressions, one of which
+ * must evaluate at compile time to a string literal and one which must
+ * evaluate to a symbol.
+ * [UNDOCUMENTED] The pragma(mangle) attribute only gets applied to the
+ * encoded parameters types of extern(C++) functions.
+ */
+pragma(mangle, "mangle_struct") extern(C++) { struct S1 { } }
+extern(C++) void test_struct2b1(S1);
+extern(D) void externD_struct2b1(S1);
+static assert(test_struct2b1.mangleof == "_Z14test_struct2b113mangle_struct");
+static assert(externD_struct2b1.mangleof == "_D12pragmamangle17externD_struct2b1FSQBj2S1Zv");
+
+pragma(mangle, type_symbol, "mangle_struct") struct S2 { }
+extern(C++) void test_struct2b3(S2);
+extern(D) void externD_struct2b3(S2);
+static assert(test_struct2b3.mangleof == "_Z14test_struct2b313mangle_struct");
+static assert(externD_struct2b3.mangleof == "_D12pragmamangle17externD_struct2b3FSQBj2S2Zv");
+
+pragma(mangle, "mangle_struct", type_symbol) struct S3 { }
+extern(C++) void test_struct2b4(S3);
+extern(D) void externD_struct2b4(S3);
+static assert(test_struct2b4.mangleof == "_Z14test_struct2b413mangle_struct");
+static assert(externD_struct2b4.mangleof == "_D12pragmamangle17externD_struct2b4FSQBj2S3Zv");
+
+// Repeat struct tests on classes.
+
+pragma(mangle, "mangle_class") extern(C++) { class C1 { } }
+extern(C++) void test_class2b1(C1);
+extern(D) void externD_class2b1(C1);
+static assert(test_class2b1.mangleof == "_Z13test_class2b1P12mangle_class");
+static assert(externD_class2b1.mangleof == "_D12pragmamangle16externD_class2b1FCQBi2C1Zv");
+
+pragma(mangle, type_symbol, "mangle_class") class C2 { }
+extern(C++) void test_class2b3(C2);
+extern(D) void externD_class2b3(C2);
+static assert(test_class2b3.mangleof == "_Z13test_class2b3P12mangle_class");
+static assert(externD_class2b3.mangleof == "_D12pragmamangle16externD_class2b3FCQBi2C2Zv");
+
+pragma(mangle, "mangle_class", type_symbol) class C3 { }
+extern(C++) void test_class2b4(C3);
+extern(D) void externD_class2b4(C3);
+static assert(test_class2b4.mangleof == "_Z13test_class2b4P12mangle_class");
+static assert(externD_class2b4.mangleof == "_D12pragmamangle16externD_class2b4FCQBi2C3Zv");
+
+/* 2c. If that symbol is a TemplateInstance, the aggregate is treated as a
+ * template that has the signature and arguments of the TemplateInstance.
+ */
+template T1(C)
+{
+ pragma(mangle, C, "mangle_template") struct T1 { }
+}
+extern(C++) void test_template2c3(T1!(template_symbol!int));
+extern(C++) void test_template2c4(T1!(template_symbol!float));
+extern(C++) void test_template2c5(T1!(type_symbol));
+static assert(test_template2c3.mangleof == "_Z16test_template2c315mangle_templateIiE");
+static assert(test_template2c4.mangleof == "_Z16test_template2c415mangle_templateIfE");
+static assert(test_template2c5.mangleof == "_Z16test_template2c515mangle_template");
+
+template T2(C)
+{
+ pragma(mangle, "mangle_template", C) struct T2 { }
+}
+extern(C++) void test_template2c6(T2!(template_symbol!int));
+extern(C++) void test_template2c7(T2!(template_symbol!float));
+extern(C++) void test_template2c8(T2!(type_symbol));
+static assert(test_template2c6.mangleof == "_Z16test_template2c615mangle_templateIiE");
+static assert(test_template2c7.mangleof == "_Z16test_template2c715mangle_templateIfE");
+static assert(test_template2c8.mangleof == "_Z16test_template2c815mangle_template");
+
+/* 2d. The identifier of the symbol is used when no string is supplied.
+ */
+pragma(mangle, type_symbol) struct I1 { }
+extern(C++) void test_struct2d1(I1);
+static assert(test_struct2d1.mangleof == "_Z14test_struct2d111type_symbol");
+
+pragma(mangle, type_symbol) class I2 { }
+extern(C++) void test_class2d1(I2);
+static assert(test_class2d1.mangleof == "_Z13test_class2d1P11type_symbol");
+
+template I3(C)
+{
+ pragma(mangle, C) struct I3 { }
+}
+extern(C++) void test_template2d1(I3!(template_symbol!int));
+extern(C++) void test_template2d2(I3!(template_symbol!float));
+extern(C++) void test_template2d3(I3!(type_symbol));
+static assert(test_template2d1.mangleof == "_Z16test_template2d115template_symbolIiE");
+static assert(test_template2d2.mangleof == "_Z16test_template2d215template_symbolIfE");
+static assert(test_template2d3.mangleof == "_Z16test_template2d311type_symbol");
+
+// ??? No template arguments encoded.
+
+pragma(mangle, template_symbol!float) struct I4 { }
+extern(C++) void test_template2d4(I4);
+static assert(test_template2d4.mangleof == "_Z16test_template2d415template_symbol");
+
+/* 3. It only applies to function and variable symbols. Other symbols are
+ * ignored.
+ */
+pragma(mangle, "mangle_alias")
+alias ignored_alias = int;
+
+pragma(mangle, "mangle_enum")
+enum ignored_enum { a = 1 }
+
+pragma(mangle, "mangle_mixed_ignored")
+{
+ enum test_ignored1 { b }
+ void test_not_ignored();
+ alias test_ignored2 = int delegate(int);
+}
+static assert(test_not_ignored.mangleof == "mangle_mixed_ignored");
--- /dev/null
+// Tests for pragma mangle
+module pragmamangle;
+
+version (Posix): // Itanium C++ ABI only
+
+string ctfe(string s) { return "mangle_" ~ "ctfe_" ~ s; }
+immutable string const_function = "mangle_const_function";
+immutable string const_variable = "mangle_const_variable";
+
+// This mangle string doesn't propagate to tests that use type_symbol.
+pragma(mangle, "Q_should_mangle_string_propagate")
+struct type_symbol { }
+
+class template_symbol(T) { }
+
+void pragma_statement_test()
+{
+ /* 1. Overrides the default mangling for a symbol.
+ */
+ pragma(mangle, "mangle_function") void test_fun1();
+ static assert(test_fun1.mangleof == "mangle_function");
+
+ pragma(mangle, "mangle_attribute") extern(C) nothrow void test_pokeattr1();
+ static assert(test_pokeattr1.mangleof == "mangle_attribute");
+
+ /* 2a. For variables and functions there must be one AssignExpression and it
+ * must evaluate at compile time to a string literal
+ */
+ pragma(mangle, ctfe("function")) void test_fun2a1();
+ static assert(test_fun2a1.mangleof == "mangle_ctfe_function");
+
+ pragma(mangle, const_function) void test_fun2a2();
+ static assert(test_fun2a2.mangleof == "mangle_const_function");
+
+ pragma(mangle, ctfe("variable")) int test_var2a1;
+ static assert(test_var2a1.mangleof == "mangle_ctfe_variable");
+
+ pragma(mangle, const_variable) int test_var2a2;
+ static assert(test_var2a2.mangleof == "mangle_const_variable");
+
+ /* 2b. For aggregates there may be one or two AssignExpressions, one of which
+ * must evaluate at compile time to a string literal and one which must
+ * evaluate to a symbol.
+ * [UNDOCUMENTED] The pragma(mangle) attribute only gets applied to the
+ * encoded parameters types of extern(C++) functions.
+ */
+ pragma(mangle, "mangle_struct") extern(C++) struct S1 { }
+ extern(C++) void test_struct2b1(S1);
+ extern(D) void externD_struct2b1(S1);
+ static assert(test_struct2b1.mangleof == "_ZN21pragma_statement_test14test_struct2b1ENS_13mangle_structE");
+ static assert(externD_struct2b1.mangleof == "_D12pragmamangle21pragma_statement_testFZ17externD_struct2b1MFSQCjQByFZ2S1Zv");
+
+ pragma(mangle, type_symbol, "mangle_struct") struct S2 { }
+ extern(C++) void test_struct2b3(S2);
+ extern(D) void externD_struct2b3(S2);
+ static assert(test_struct2b3.mangleof == "_ZN21pragma_statement_test14test_struct2b3ENS_13mangle_structE");
+ static assert(externD_struct2b3.mangleof == "_D12pragmamangle21pragma_statement_testFZ17externD_struct2b3MFSQCjQByFZ2S2Zv");
+
+ pragma(mangle, "mangle_struct", type_symbol) struct S3 { }
+ extern(C++) void test_struct2b4(S3);
+ extern(D) void externD_struct2b4(S3);
+ static assert(test_struct2b4.mangleof == "_ZN21pragma_statement_test14test_struct2b4ENS_13mangle_structE");
+ static assert(externD_struct2b4.mangleof == "_D12pragmamangle21pragma_statement_testFZ17externD_struct2b4MFSQCjQByFZ2S3Zv");
+
+ // Repeat struct tests on classes.
+
+ pragma(mangle, "mangle_class") extern(C++) class C1 { }
+ extern(C++) void test_class2b1(C1);
+ extern(D) void externD_class2b1(C1);
+ static assert(test_class2b1.mangleof == "_ZN21pragma_statement_test13test_class2b1EPNS_12mangle_classE");
+ static assert(externD_class2b1.mangleof == "_D12pragmamangle21pragma_statement_testFZ16externD_class2b1MFCQCiQBxFZ2C1Zv");
+
+ pragma(mangle, type_symbol, "mangle_class") class C2 { }
+ extern(C++) void test_class2b3(C2);
+ extern(D) void externD_class2b3(C2);
+ static assert(test_class2b3.mangleof == "_ZN21pragma_statement_test13test_class2b3EPNS_12mangle_classE");
+ static assert(externD_class2b3.mangleof == "_D12pragmamangle21pragma_statement_testFZ16externD_class2b3MFCQCiQBxFZ2C2Zv");
+
+ pragma(mangle, "mangle_class", type_symbol) class C3 { }
+ extern(C++) void test_class2b4(C3);
+ extern(D) void externD_class2b4(C3);
+ static assert(test_class2b4.mangleof == "_ZN21pragma_statement_test13test_class2b4EPNS_12mangle_classE");
+ static assert(externD_class2b4.mangleof == "_D12pragmamangle21pragma_statement_testFZ16externD_class2b4MFCQCiQBxFZ2C3Zv");
+
+ /* 2c. If that symbol is a TemplateInstance, the aggregate is treated as a
+ * template that has the signature and arguments of the TemplateInstance.
+ */
+ template T1(C)
+ {
+ pragma(mangle, C, "mangle_template") struct T1 { }
+ }
+ extern(C++) void test_template2c3(T1!(template_symbol!int));
+ extern(C++) void test_template2c4(T1!(template_symbol!float));
+ extern(C++) void test_template2c5(T1!(type_symbol));
+ static assert(test_template2c3.mangleof == "_ZN21pragma_statement_test16test_template2c3ENS_15mangle_templateIiEE");
+ static assert(test_template2c4.mangleof == "_ZN21pragma_statement_test16test_template2c4ENS_15mangle_templateIfEE");
+ static assert(test_template2c5.mangleof == "_ZN21pragma_statement_test16test_template2c5ENS_15mangle_templateE");
+
+ template T2(C)
+ {
+ pragma(mangle, "mangle_template", C) struct T2 { }
+ }
+ extern(C++) void test_template2c6(T2!(template_symbol!int));
+ extern(C++) void test_template2c7(T2!(template_symbol!float));
+ extern(C++) void test_template2c8(T2!(type_symbol));
+ static assert(test_template2c6.mangleof == "_ZN21pragma_statement_test16test_template2c6ENS_15mangle_templateIiEE");
+ static assert(test_template2c7.mangleof == "_ZN21pragma_statement_test16test_template2c7ENS_15mangle_templateIfEE");
+ static assert(test_template2c8.mangleof == "_ZN21pragma_statement_test16test_template2c8ENS_15mangle_templateE");
+
+ /* 2d. The identifier of the symbol is used when no string is supplied.
+ */
+ pragma(mangle, type_symbol) struct I1 { }
+ extern(C++) void test_struct2d1(I1);
+ static assert(test_struct2d1.mangleof == "_ZN21pragma_statement_test14test_struct2d1ENS_11type_symbolE");
+
+ pragma(mangle, type_symbol) class I2 { }
+ extern(C++) void test_class2d1(I2);
+ static assert(test_class2d1.mangleof == "_ZN21pragma_statement_test13test_class2d1EPNS_11type_symbolE");
+
+ template I3(C)
+ {
+ pragma(mangle, C) struct I3 { }
+ }
+ extern(C++) void test_template2d1(I3!(template_symbol!int));
+ extern(C++) void test_template2d2(I3!(template_symbol!float));
+ extern(C++) void test_template2d3(I3!(type_symbol));
+ static assert(test_template2d1.mangleof == "_ZN21pragma_statement_test16test_template2d1ENS_15template_symbolIiEE");
+ static assert(test_template2d2.mangleof == "_ZN21pragma_statement_test16test_template2d2ENS_15template_symbolIfEE");
+ static assert(test_template2d3.mangleof == "_ZN21pragma_statement_test16test_template2d3ENS_11type_symbolE");
+
+ // ??? No template arguments encoded.
+
+ pragma(mangle, template_symbol!float) struct I4 { }
+ extern(C++) void test_template2d4(I4);
+ static assert(test_template2d4.mangleof == "_ZN21pragma_statement_test16test_template2d4ENS_15template_symbolE");
+
+ /* 3. It only applies to function and variable symbols. Other symbols are
+ * ignored.
+ */
+ pragma(mangle, "mangle_alias")
+ alias ignored_alias = int;
+
+ pragma(mangle, "mangle_enum")
+ enum ignored_enum { a = 1 }
+}
--- /dev/null
+// REQUIRED_ARGS: -profile=gc
+struct T(string s) {}
+alias TypeWithQuotes = T!q"EOS
+`"'}])>
+EOS";
+
+void foo() {
+ TypeWithQuotes[] arr;
+ arr ~= TypeWithQuotes();
+}
class B : A{
// short syntax also overrides the same as long syntax
override bool isNull() => this !is null;
+
+ this(float y) => super(y);
}
static assert((new A).x == 34);
void inc() {}
this(this) => inc();
+ // https://github.com/dlang/dmd/issues/21576
+ this(int) => inc();
+
+ this(byte) { return inc(); }
+
void free() {}
~this() => free();
}
{
void removeTerminationHook(void delegate() hook)
{
- moveToEnd([], hook);
+ void delegate()[] array = [() {}];
+ moveToEnd(array, hook);
}
}
--- /dev/null
+// https://github.com/dlang/dmd/issues/21495
+extern (C++)
+{
+ enum string[string] meta = [
+ "hellos": "worlds"
+ ];
+
+ struct metas
+ {
+ static foreach (key, value; meta)
+ {
+ mixin("enum string " ~ key ~ " = `" ~ value ~ "`;");
+ }
+ }
+}
--- /dev/null
+// https://github.com/dlang/dmd/issues/21504
+
+// Test that:
+// 1. __traits(compiles) tests fail when violating an explicitly set attribute.
+// 2. __traits(compiles) tests pass when attribute is inferred (see functions
+// isPureBypassingInference, isSafeBypassingInference, isNogcBypassingInference).
+// 3. __traits(compiles) tests do not affect the result of attribute inference,
+// as the they are only evaluated at compile-time, not run-time.
+
+__gshared int testPure;
+
+auto testPure1() pure
+{
+ return __traits(compiles, testPure = 1);
+}
+auto testPure2()
+{
+ return __traits(compiles, testPure = 1);
+}
+
+static assert(!testPure1() && testPure2());
+static assert(__traits(getFunctionAttributes, testPure1) == __traits(getFunctionAttributes, testPure2));
+
+////////////////////////////
+
+__gshared Exception testNothrow;
+
+auto testNothrow1() nothrow
+{
+ return __traits(compiles, throw testNothrow);
+}
+auto testNothrow2()
+{
+ return __traits(compiles, throw testNothrow);
+}
+
+static assert(!testNothrow1() && testNothrow2());
+static assert(__traits(getFunctionAttributes, testNothrow1) == __traits(getFunctionAttributes, testNothrow2));
+
+////////////////////////////
+
+__gshared int* testSafe;
+
+auto testSafe1() @safe
+{
+ return __traits(compiles, testSafe + 1);
+}
+auto testSafe2()
+{
+ return __traits(compiles, testSafe + 1);
+}
+
+static assert(!testSafe1() && testSafe2());
+static assert(__traits(getFunctionAttributes, testSafe1) == __traits(getFunctionAttributes, testSafe2));
+
+////////////////////////////
+
+auto testNogc1() @nogc
+{
+ return __traits(compiles, [1, 2]);
+}
+auto testNogc2()
+{
+ return __traits(compiles, [1, 2]);
+}
+
+static assert(!testNogc1() && testNogc2());
+static assert(__traits(getFunctionAttributes, testNogc1) == __traits(getFunctionAttributes, testNogc2));
--- /dev/null
+// https://github.com/dlang/dmd/issues/21504
+
+// Test case where `@safe` triggers attribute inference of all nested
+// declarations, and the NOGCVisitor for `new` expressions encounters a self
+// referencing GC action.
+
+void test21504() @nogc @safe
+{
+ static struct Nest
+ {
+ this(int i)
+ {
+ static assert(__traits(compiles, new Nest(i)));
+ }
+ }
+ auto s = Nest(1);
+}
--- /dev/null
+// https://github.com/dlang/dmd/issues/21832
+// resize array of implicitly non-copyable items
+
+void bugGH21832()
+{
+ static struct S { @disable this(this); }
+ static struct Item { S s; }
+
+ Item[] arr;
+ arr.length++;
+}
void test23862()
{
- scope char** _errors;
+ char* source;
+ scope char** _errors = &source;
g = front_p(_errors); // should pass
g = front_r(_errors); // should pass
}
X!(integral, boolean, all)();
X!(integral, integral, all)();
- X!(integral, floating, arith)();
X!(floating, boolean, arith)();
X!(floating, integral, arith)();
--- /dev/null
+/+
+REQUIRED_ARGS: -Icompilable/extra-files
+EXTRA_FILES: extra-files/ucn_vars.i
++/
+
+import ucn_vars;
+
+alias M = __traits(allMembers, Vars);
+enum expected = ["x²", "Ⅰ", "ↂ", "À", "Á", "Â", "whÿ", "aÄbсδe", "b〡𗘰〣e"];
+
+static foreach(i; 0 .. M.length) {
+ static assert(M[i] == expected[i]);
+}
{
int opApply(immutable int delegate(const ref int) pure nothrow dg) pure nothrow
{
- return 1;
+ return 0;
}
}
bool overflow;
assert(mulu(cast(long)3, cast(uint)4, overflow) == 12);
+ assert(mulu(cast(ulong)3, cast(uint)4, overflow) == 12);
assert(testCheckedUnsigned!uint(3,4) == 12);
assert(testCheckedUnsigned!ulong(3,4) == 12);
-// REQUIRED_ARGS: -w -o-
+// REQUIRED_ARGS: -de
/*
TEST_OUTPUT:
---
-fail_compilation/b3841.d-mixin-32(32): Warning: `char += float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `int += float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `long += double` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `char -= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `int -= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `long -= double` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `char *= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `int *= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `long *= double` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `char /= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `int /= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `long /= double` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `char %= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `int %= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `long %= double` is performing truncating conversion
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `char += float` is performing truncating conversion
+fail_compilation/b3841.d(69): Error: template instance `b3841.f!("+=", char, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `int += float` is performing truncating conversion
+fail_compilation/b3841.d(70): Error: template instance `b3841.f!("+=", int, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `long += double` is performing truncating conversion
+fail_compilation/b3841.d(71): Error: template instance `b3841.f!("+=", long, double)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `char -= float` is performing truncating conversion
+fail_compilation/b3841.d(69): Error: template instance `b3841.f!("-=", char, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `int -= float` is performing truncating conversion
+fail_compilation/b3841.d(70): Error: template instance `b3841.f!("-=", int, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `long -= double` is performing truncating conversion
+fail_compilation/b3841.d(71): Error: template instance `b3841.f!("-=", long, double)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `char *= float` is performing truncating conversion
+fail_compilation/b3841.d(69): Error: template instance `b3841.f!("*=", char, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `int *= float` is performing truncating conversion
+fail_compilation/b3841.d(70): Error: template instance `b3841.f!("*=", int, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `long *= double` is performing truncating conversion
+fail_compilation/b3841.d(71): Error: template instance `b3841.f!("*=", long, double)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `char /= float` is performing truncating conversion
+fail_compilation/b3841.d(69): Error: template instance `b3841.f!("/=", char, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `int /= float` is performing truncating conversion
+fail_compilation/b3841.d(70): Error: template instance `b3841.f!("/=", int, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `long /= double` is performing truncating conversion
+fail_compilation/b3841.d(71): Error: template instance `b3841.f!("/=", long, double)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `char %= float` is performing truncating conversion
+fail_compilation/b3841.d(69): Error: template instance `b3841.f!("%=", char, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `int %= float` is performing truncating conversion
+fail_compilation/b3841.d(70): Error: template instance `b3841.f!("%=", int, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `long %= double` is performing truncating conversion
+fail_compilation/b3841.d(71): Error: template instance `b3841.f!("%=", long, double)` error instantiating
---
*/
/* REQUIRED_ARGS: -betterC
* TEST_OUTPUT:
---
-fail_compilation/betterc.d(12): Error: cannot use `throw` statements with -betterC
-fail_compilation/betterc.d(17): Error: cannot use try-catch statements with -betterC
-fail_compilation/betterc.d(29): Error: `TypeInfo` cannot be used with -betterC
+fail_compilation/betterc.d(12): Error: cannot use `throw` statements with `-betterC`
+fail_compilation/betterc.d(17): Error: cannot use try-catch statements with `-betterC`
+fail_compilation/betterc.d(29): Error: `TypeInfo` cannot be used with `-betterC`
---
*/
-/* REQUIRED_ARGS: -preview=bitfields
+/*
* TEST_OUTPUT:
---
fail_compilation/biterrors.d(103): Error: initializer not allowed for bitfield declaration
-/* REQUIRED_ARGS: -preview=bitfields
+/*
* TEST_OUTPUT:
---
fail_compilation/biterrors2.d(100): Error: variable `biterrors2.a` - bitfield must be member of struct, union, or class
-/* REQUIRED_ARGS: -preview=bitfields
+/*
* TEST_OUTPUT:
---
fail_compilation/biterrors3.d(103): Error: storage class not allowed for bitfield declaration
-/* REQUIRED_ARGS: -preview=bitfields
+/*
* TEST_OUTPUT:
---
fail_compilation/biterrors4.d(109): Error: cannot take address of bitfield `s.a`
-/* REQUIRED_ARGS: -preview=bitfields
+/*
* TEST_OUTPUT:
---
fail_compilation/biterrors5.d(25): Error: bitfield symbol expected not struct `biterrors5.S`
-/* REQUIRED_ARGS: -preview=bitfields
+/*
*/
struct S
/*
TEST_OUTPUT:
---
-fail_compilation/bug15613.d(18): Error: function `f` is not callable using argument types `(typeof(null))`
-fail_compilation/bug15613.d(18): cannot pass argument `null` of type `typeof(null)` to parameter `int...`
-fail_compilation/bug15613.d(13): `bug15613.f(int...)` declared here
-fail_compilation/bug15613.d(19): Error: function `g` is not callable using argument types `(int)`
-fail_compilation/bug15613.d(19): cannot pass argument `8` of type `int` to parameter `Object`
-fail_compilation/bug15613.d(14): `bug15613.g(Object, ...)` declared here
+fail_compilation/bug15613.d(21): Error: function `f` is not callable using argument types `(typeof(null))`
+fail_compilation/bug15613.d(21): cannot pass argument `null` of type `typeof(null)` to parameter `int...`
+fail_compilation/bug15613.d(16): `bug15613.f(int...)` declared here
+fail_compilation/bug15613.d(22): Error: function `f` is not callable using argument types `()`
+fail_compilation/bug15613.d(22): expected an argument for parameter `int...`
+fail_compilation/bug15613.d(16): `bug15613.f(int...)` declared here
+fail_compilation/bug15613.d(23): Error: function `g` is not callable using argument types `(int)`
+fail_compilation/bug15613.d(23): cannot pass argument `8` of type `int` to parameter `Object`
+fail_compilation/bug15613.d(17): `bug15613.g(Object, ...)` declared here
---
*/
void main()
{
f(null);
+ f();
g(8);
}
+#line 22
/*
TEST_OUTPUT:
---
TEST_OUTPUT:
---
fail_compilation/bug8150b.d(15): Error: `object.Exception` is thrown but not caught
-fail_compilation/bug8150b.d(13): Error: constructor `bug8150b.Foo.__ctor!().this` may throw but is marked as `nothrow`
-fail_compilation/bug8150b.d(20): Error: template instance `bug8150b.Foo.__ctor!()` error instantiating
+fail_compilation/bug8150b.d(13): Error: constructor `bug8150b.Foo.this!().this` may throw but is marked as `nothrow`
+fail_compilation/bug8150b.d(20): Error: template instance `bug8150b.Foo.this!()` error instantiating
---
*/
--- /dev/null
+/*
+REQUIRED_ARGS: -preview=systemVariables
+TEST_OUTPUT:
+---
+fail_compilation/cast_system.d(31): Error: cast from `int[1]` to `UniqueInt` is not allowed in a `@safe` function
+fail_compilation/cast_system.d(31): Target element type has unsafe bit patterns
+---
+*/
+
+struct UniqueInt
+{
+ @system int n;
+
+ this(int n) @safe
+ {
+ this.n = n;
+ }
+
+ @disable this(ref inout UniqueInt) inout;
+
+ ~this() @trusted
+ {
+ this.n = 0;
+ }
+}
+
+void main() @safe
+{
+ auto s = cast(UniqueInt) 7; // OK, calls ctor
+ int[1] a;
+ s = cast(UniqueInt) a; // doesn't call ctor
+}
+
+void ok() @safe
+{
+ auto b = cast(bool) 7; // OK
+}
TEST_OUTPUT:
---
-fail_compilation/covariant_override.d(23): Error: function `@safe void covariant_override.CI.f(void delegate() @safe dg)` does not override any function, did you mean to override `@safe void covariant_override.I.f(void delegate() @system dg)`?
-fail_compilation/covariant_override.d(34): Error: function `@safe void covariant_override.CA.f(void delegate() @safe dg)` does not override any function, did you mean to override `@safe void covariant_override.A.f(void delegate() @system dg)`?
-fail_compilation/covariant_override.d(20): Error: class `covariant_override.CI` interface function `void f(void delegate() @system dg) @safe` is not implemented
+fail_compilation/covariant_override.d(25): Error: function `@safe void covariant_override.CI.f(void delegate() @safe dg)` does not override any function
+fail_compilation/covariant_override.d(19): did you mean to override `@safe void covariant_override.I.f(void delegate() @system dg)`?
+fail_compilation/covariant_override.d(36): Error: function `@safe void covariant_override.CA.f(void delegate() @safe dg)` does not override any function
+fail_compilation/covariant_override.d(30): did you mean to override `@safe void covariant_override.A.f(void delegate() @system dg)`?
+fail_compilation/covariant_override.d(22): Error: class `covariant_override.CI` interface function `void f(void delegate() @system dg) @safe` is not implemented
---
++/
-/* REQUIRED_ARGS: -preview=bitfields
+/*
* TEST_OUTPUT:
---
fail_compilation/dbitfields.d(118): Error: reinterpretation through overlapped field `e` is not allowed in CTFE
TEST_OUTPUT:
---
fail_compilation/diag23295.d(21): Error: assigning scope variable `x` to non-scope parameter `y` calling `foo` is not allowed in a `@safe` function
-fail_compilation/diag23295.d(32): which is assigned to non-scope parameter `z`
-fail_compilation/diag23295.d(34): which is not `scope` because of `f = & z`
+fail_compilation/diag23295.d(32): `y` is assigned to non-scope parameter `z`
+fail_compilation/diag23295.d(34): `z` is not `scope` because of `f = & z`
fail_compilation/diag23295.d(24): Error: assigning scope variable `ex` to non-scope parameter `e` calling `thro` is not allowed in a `@safe` function
-fail_compilation/diag23295.d(39): which is not `scope` because of `throw e`
+fail_compilation/diag23295.d(39): `e` is not `scope` because of `throw e`
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/diag9191.d(16): Error: function `void diag9191.C1.aaa()` does not override any function, did you mean to override `void diag9191.B1.aa()`?
-fail_compilation/diag9191.d(22): Error: function `diag9191.C2.aaa` does not override any function
-fail_compilation/diag9191.d(33): Error: function `void diag9191.C3.foo()` does not override any function, did you mean to override `void diag9191.B2._foo()`?
-fail_compilation/diag9191.d(38): Error: function `void diag9191.C4.toStringa()` does not override any function, did you mean to override `string object.Object.toString()`?
+fail_compilation/diag9191.d(19): Error: function `void diag9191.C1.aaa()` does not override any function
+fail_compilation/diag9191.d(15): did you mean to override `void diag9191.B1.aa()`?
+fail_compilation/diag9191.d(25): Error: function `diag9191.C2.aaa` does not override any function
+fail_compilation/diag9191.d(36): Error: function `void diag9191.C3.foo()` does not override any function
+fail_compilation/diag9191.d(31): did you mean to override `void diag9191.B2._foo()`?
+fail_compilation/diag9191.d(41): Error: function `void diag9191.C4.toStringa()` does not override any function
+$p:druntime/import/object.d$($n$): did you mean to override `string object.Object.toString()`?
---
*/
--- /dev/null
+/*
+REQUIRED_ARGS: -edition=2024
+TEST_OUTPUT:
+---
+fail_compilation/edition_switch.d(10): Error: usage of identifer `body` as a keyword is obsolete. Use `do` instead.
+---
+*/
+
+void test()
+in { } body { }
+++ /dev/null
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail12901.d(11): Error: constructor `fail12901.S.this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
----
-*/
-
-struct S
-{
- int a;
- this(int n)
- in { a = n; }
- // no body
-}
/* TEST_OUTPUT:
---
-fail_compilation/fail18093.d(19): Error: function `void fail18093.GenericTransitiveVisitor!(ASTCodegen).GenericTransitiveVisitor.ParseVisitMethods!(ASTCodegen).visit()` does not override any function, did you mean to override `extern (C++) void fail18093.ParseTimeVisitor!(ASTCodegen).ParseTimeVisitor.visit()`?
-fail_compilation/fail18093.d(24): Error: mixin `fail18093.GenericTransitiveVisitor!(ASTCodegen).GenericTransitiveVisitor.ParseVisitMethods!(ASTCodegen)` error instantiating
-fail_compilation/fail18093.d(27): Error: template instance `fail18093.GenericTransitiveVisitor!(ASTCodegen)` error instantiating
+fail_compilation/fail18093.d(20): Error: function `void fail18093.GenericTransitiveVisitor!(ASTCodegen).GenericTransitiveVisitor.ParseVisitMethods!(ASTCodegen).visit()` does not override any function
+fail_compilation/fail18093.d(16): did you mean to override `extern (C++) void fail18093.ParseTimeVisitor!(ASTCodegen).ParseTimeVisitor.visit()`?
+fail_compilation/fail18093.d(25): Error: mixin `fail18093.GenericTransitiveVisitor!(ASTCodegen).GenericTransitiveVisitor.ParseVisitMethods!(ASTCodegen)` error instantiating
+fail_compilation/fail18093.d(28): Error: template instance `fail18093.GenericTransitiveVisitor!(ASTCodegen)` error instantiating
---
* https://issues.dlang.org/show_bug.cgi?id=18093
*/
REQUIRED_ARGS: -betterC
TEST_OUTPUT:
---
-fail_compilation/fail19911a.d(9): Error: function `fail19911a.fun` D-style variadic functions cannot be used with -betterC
+fail_compilation/fail19911a.d(9): Error: function `fail19911a.fun` D-style variadic functions cannot be used with `-betterC`
---
*/
TEST_OUTPUT:
---
fail_compilation/fail222.d(11): Error: template `fail222.getMixin(TArg..., int i = 0)()` template sequence parameter must be the last one
-fail_compilation/fail222.d(18): Error: template instance `getMixin!()` does not match template declaration `getMixin(TArg..., int i = 0)()`
+fail_compilation/fail222.d(18): Error: template instance `getMixin!()` does not match template declaration `getMixin(TArg..., int i = 0)`
fail_compilation/fail222.d(21): Error: template instance `fail222.Thing!()` error instantiating
fail_compilation/fail222.d(23): Error: template `fail222.fooBar(A..., B...)()` template sequence parameter must be the last one
---
REQUIRED_ARGS: -de
TEST_OUTPUT:
---
-fail_compilation/fail22351.d(18): Deprecation: overriding `extern(C++)` function `fail22351.C22351.func(int*)` with `const` qualified function `fail22351.Fail22351.func(const(int*))` is deprecated
-fail_compilation/fail22351.d(18): Either remove `override`, or adjust the `const` qualifiers of the overriding function parameters
-fail_compilation/fail22351.d(19): Error: function `extern (C++) void fail22351.Fail22351.func(const(int*)**)` does not override any function, did you mean to override `extern (C++) void fail22351.C22351.func(int*)`?
+fail_compilation/fail22351.d(19): Deprecation: overriding `extern(C++)` function `fail22351.C22351.func(int*)` with `const` qualified function `fail22351.Fail22351.func(const(int*))` is deprecated
+fail_compilation/fail22351.d(19): Either remove `override`, or adjust the `const` qualifiers of the overriding function parameters
+fail_compilation/fail22351.d(20): Error: function `extern (C++) void fail22351.Fail22351.func(const(int*)**)` does not override any function
+fail_compilation/fail22351.d(13): did you mean to override `extern (C++) void fail22351.C22351.func(int*)`?
---
*/
extern(C++) class C22351
TEST_OUTPUT:
---
fail_compilation/fail24208.d(19): Error: assigning reference to local variable `n` to non-scope parameter `p` calling `escape` is not allowed in a `@safe` function
-fail_compilation/fail24208.d(15): which is not `scope` because of `escaped = p`
+fail_compilation/fail24208.d(15): `p` is not `scope` because of `escaped = p`
---
+/
void test() @safe
/*
TEST_OUTPUT:
---
-fail_compilation/fail246.d-mixin-11(11): Error: identifier expected, not `End of File`
-fail_compilation/fail246.d-mixin-11(11): Error: `;` expected after `mixin`
+fail_compilation/fail246.d-mixin-12(12): Error: identifier expected, not `End of File`
+fail_compilation/fail246.d-mixin-12(12): Error: `;` expected after `mixin`
+fail_compilation/fail246.d-mixin-12(12): while parsing string mixin statement
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/fail262.d(23): Error: function `const void fail262.B.f()` does not override any function, did you mean to override `shared const void fail262.A.f()`?
+fail_compilation/fail262.d(24): Error: function `const void fail262.B.f()` does not override any function
+fail_compilation/fail262.d(16): did you mean to override `shared const void fail262.A.f()`?
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/fail265.d-mixin-10(10): Error: found `End of File` instead of statement
+fail_compilation/fail265.d-mixin-11(11): Error: found `End of File` instead of statement
+fail_compilation/fail265.d-mixin-11(11): while parsing string mixin statement
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375a.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375a.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375a.d(13): Error: else is dangling, add { } after condition at fail_compilation/fail4375a.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375b.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375b.d(14)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375b.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375b.d(11)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375c.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375c.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375c.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375c.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375d.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375d.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375d.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375d.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375e.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375e.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375e.d(13): Error: else is dangling, add { } after condition at fail_compilation/fail4375e.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375f.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375f.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375f.d(13): Error: else is dangling, add { } after condition at fail_compilation/fail4375f.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375g.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375g.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375g.d(13): Error: else is dangling, add { } after condition at fail_compilation/fail4375g.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375h.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375h.d(15)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375h.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375h.d(12)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375i.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375i.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375i.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375i.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375j.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375j.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375j.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375j.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375k.d-mixin-13(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375k.d-mixin-13(14)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375k.d-mixin-11(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375k.d-mixin-11(12)
+fail_compilation/fail4375k.d-mixin-11(12): while parsing string mixin statement
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375l.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375l.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375l.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375l.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375m.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375m.d(14)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375m.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375m.d(11)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375o.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375o.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375o.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375o.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375p.d(19): Warning: else is dangling, add { } after condition at fail_compilation/fail4375p.d(12)
-fail_compilation/fail4375p.d(16): Error: undefined identifier `x`
+fail_compilation/fail4375p.d(17): Error: else is dangling, add { } after condition at fail_compilation/fail4375p.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375q.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375q.d(13)
-fail_compilation/fail4375q.d(14): Error: `with` expression types must be enums or aggregates or pointers to them, not `int`
+fail_compilation/fail4375q.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375q.d(11)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375r.d(19): Warning: else is dangling, add { } after condition at fail_compilation/fail4375r.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375r.d(16): Error: else is dangling, add { } after condition at fail_compilation/fail4375r.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375s.d(19): Warning: else is dangling, add { } after condition at fail_compilation/fail4375s.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375s.d(16): Error: else is dangling, add { } after condition at fail_compilation/fail4375s.d(10)
---
*/
-// REQUIRED_ARGS: -w -unittest
+// REQUIRED_ARGS: -unittest
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375t.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375t.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375t.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375t.d(11)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375u.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375u.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375u.d(12): Error: else is dangling, add { } after condition at fail_compilation/fail4375u.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375v.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375v.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375v.d(12): Error: else is dangling, add { } after condition at fail_compilation/fail4375v.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375w.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375w.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375w.d(12): Error: else is dangling, add { } after condition at fail_compilation/fail4375w.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375x.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375x.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375x.d(13): Error: else is dangling, add { } after condition at fail_compilation/fail4375x.d(10)
---
*/
-// REQUIRED_ARGS: -w
// https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375y.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375y.d(13)
-Error: warnings are treated as errors
- Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375y.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375y.d(10)
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/fail4544.d(15): Error: character constant has multiple characters
+fail_compilation/fail4544.d(15): Error: character constant has multiple characters - did you mean "asd"?
fail_compilation/fail4544.d(16): Error: `0x` isn't a valid integer literal, use `0x0` instead
fail_compilation/fail4544.d(16): Error: variable name expected after type `int`, not `0`
fail_compilation/fail4544.d(17): Error: unterminated character constant
-fail_compilation/fail4544.d(18): Error: character constant has multiple characters
+fail_compilation/fail4544.d(18): Error: character constant has multiple characters - did you mean "asdasdsa"?
---
*/
TEST_OUTPUT:
---
fail_compilation/fail50.d(12): Error: taking the address of non-static variable `a` requires an instance of `Marko`
-fail_compilation/fail50.d(12): Error: variable `a` cannot be read at compile time
---
*/
+
struct Marko
{
int a;
/*
TEST_OUTPUT:
---
-fail_compilation/fail6497.d(13): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
-fail_compilation/fail6497.d(13): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
-fail_compilation/fail6497.d(19): Error: taking the address of local variable `i` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(14): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(14): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(20): Error: taking the address of local variable `i` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(28): Error: taking the address of local variable `i` is not allowed in a `@safe` function
---
*/
ref i = *new int;
auto b = &i;
}
+
+const(int)* ptr;
+
+int g() @safe
+out (i)
+{
+ ptr = &i;
+}
+do { return 0; }
/*
TEST_OUTPUT:
---
-fail_compilation/fail8631.d(14): Error: function `shared const int fail8631.D.foo()` does not override any function, did you mean to override `immutable int fail8631.B.foo()`?
+fail_compilation/fail8631.d(15): Error: function `shared const int fail8631.D.foo()` does not override any function
+fail_compilation/fail8631.d(10): did you mean to override `immutable int fail8631.B.foo()`?
---
*/
--- /dev/null
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail_contracts5.d(13): Error: constructor `fail_contracts5.S.this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
+---
+*/
+
+// bugzilla 12901
+struct S
+{
+ int a;
+ this(int n)
+ in { a = n; }
+ // no body
+}
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail_contracts5.d(33): Error: function `fail_contracts5.C2.foo` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
+fail_compilation/fail_contracts5.d(32): Error: constructor `fail_contracts5.C2.this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
+---
+*/
+// https://github.com/dlang/dmd/issues/21403
+abstract class C1 {}
+
+class C2 : C1
+{
+ bool member;
+
+ this() out (member) {}
+ final void foo() out (member) {}
+}
REQUIRED_ARGS: -verrors=context
TEST_OUTPUT:
---
-fail_compilation/fail_pretty_errors.d(29): Error: undefined identifier `a`
+fail_compilation/fail_pretty_errors.d(31): Error: undefined identifier `a`
a = 1;
^
-fail_compilation/fail_pretty_errors.d-mixin-34(34): Error: undefined identifier `b`
+fail_compilation/fail_pretty_errors.d-mixin-36(36): Error: undefined identifier `b`
b = 1;
^
-fail_compilation/fail_pretty_errors.d(39): Error: cannot implicitly convert expression `5` of type `int` to `string`
+fail_compilation/fail_pretty_errors.d(41): Error: cannot implicitly convert expression `5` of type `int` to `string`
string x = 5;
^
-fail_compilation/fail_pretty_errors.d(44): Error: mixin `fail_pretty_errors.testMixin2.mixinTemplate!()` error instantiating
+fail_compilation/fail_pretty_errors.d(46): Error: mixin `fail_pretty_errors.testMixin2.mixinTemplate!()` error instantiating
mixin mixinTemplate;
^
-fail_compilation/fail_pretty_errors.d(50): Error: invalid array operation `"" + ""` (possible missing [])
+fail_compilation/fail_pretty_errors.d(52): Error: invalid array operation `"" + ""` (possible missing [])
auto x = ""+"";
^
-fail_compilation/fail_pretty_errors.d(50): did you mean to concatenate (`"" ~ ""`) instead ?
-fail_compilation/fail_pretty_errors.d(53): Error: cannot implicitly convert expression `1111` of type `int` to `byte`
+fail_compilation/fail_pretty_errors.d(52): did you mean to concatenate (`"" ~ ""`) instead ?
+ auto x = ""+"";
+ ^
+fail_compilation/fail_pretty_errors.d(55): Error: cannot implicitly convert expression `1111` of type `int` to `byte`
byte ɑ = 1111;
^
---
--- /dev/null
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong %= real` is performing truncating conversion
+fail_compilation/failexpression1.d(148): Error: template instance `failexpression1.X!(integral, floating, arith)` error instantiating
+fail_compilation/failexpression1.d(153): instantiated from here: `OpAssignCases!(TestOpAssign)`
+---
+*/
+template TT(T...) { alias T TT; }
+
+void TestOpAssign(Tx, Ux, ops)()
+{
+ foreach(T; Tx.x)
+ foreach(U; Ux.x)
+ foreach(op; ops.x)
+ {
+ T a = cast(T)1;
+ mixin("a " ~ op ~ " cast(U)1;");
+ }
+}
+
+struct integral { alias TT!(byte, ubyte, short, ushort, int, uint, long, ulong) x; }
+struct floating { alias TT!(float, double, real) x; }
+struct arith { alias TT!("+=", "-=", "*=", "/=", "%=") x; }
+
+void OpAssignCases(alias X)()
+{
+ X!(integral, floating, arith)();
+}
+
+void main()
+{
+ OpAssignCases!TestOpAssign();
+}
--- /dev/null
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong %= real` is performing truncating conversion
+fail_compilation/failexpression2.d(150): Error: template instance `failexpression2.X!(integral, floating, arith)` error instantiating
+fail_compilation/failexpression2.d(155): instantiated from here: `OpAssignCases!(TestOpAssignAssign)`
+---
+*/
+template TT(T...) { alias T TT; }
+
+void TestOpAssignAssign(Tx, Ux, ops)()
+{
+ foreach(T; Tx.x)
+ foreach(U; Ux.x)
+ foreach(op; ops.x)
+ {
+ T a = cast(T)1;
+ U b = cast(U)1;
+ T r;
+ mixin("r = a " ~ op ~ " cast(U)1;");
+ }
+}
+
+struct integral { alias TT!(byte, ubyte, short, ushort, int, uint, long, ulong) x; }
+struct floating { alias TT!(float, double, real) x; }
+struct arith { alias TT!("+=", "-=", "*=", "/=", "%=") x; }
+
+void OpAssignCases(alias X)()
+{
+ X!(integral, floating, arith)();
+}
+
+void main()
+{
+ OpAssignCases!TestOpAssignAssign(); // was once disabled due to bug 7436
+}
--- /dev/null
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong %= real` is performing truncating conversion
+fail_compilation/failexpression3.d(149): Error: template instance `failexpression3.X!(integral, floating, arith)` error instantiating
+fail_compilation/failexpression3.d(154): instantiated from here: `OpAssignCases!(TestOpAssignAuto)`
+---
+*/
+template TT(T...) { alias T TT; }
+
+void TestOpAssignAuto(Tx, Ux, ops)()
+{
+ foreach(T; Tx.x)
+ foreach(U; Ux.x)
+ foreach(op; ops.x)
+ {
+ T a = cast(T)1;
+ U b = cast(U)1;
+ mixin("auto r = a " ~ op ~ " cast(U)1;");
+ }
+}
+
+struct integral { alias TT!(byte, ubyte, short, ushort, int, uint, long, ulong) x; }
+struct floating { alias TT!(float, double, real) x; }
+struct arith { alias TT!("+=", "-=", "*=", "/=", "%=") x; }
+
+void OpAssignCases(alias X)()
+{
+ X!(integral, floating, arith)();
+}
+
+void main()
+{
+ OpAssignCases!TestOpAssignAuto(); // https://issues.dlang.org/show_bug.cgi?id=5181
+}
--- /dev/null
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a + 1.0F` of type `float` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a - 1.0F` of type `float` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a * 1.0F` of type `float` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a / 1.0F` of type `float` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a % 1.0F` of type `float` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a + 1.0` of type `double` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a - 1.0` of type `double` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a * 1.0` of type `double` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a / 1.0` of type `double` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a % 1.0` of type `double` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a + 1.0L` of type `real` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a - 1.0L` of type `real` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a * 1.0L` of type `real` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a / 1.0L` of type `real` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a % 1.0L` of type `real` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a + 1.0F` of type `float` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a - 1.0F` of type `float` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a * 1.0F` of type `float` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a / 1.0F` of type `float` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a % 1.0F` of type `float` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a + 1.0` of type `double` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a - 1.0` of type `double` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a * 1.0` of type `double` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a / 1.0` of type `double` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a % 1.0` of type `double` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a + 1.0L` of type `real` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a - 1.0L` of type `real` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a * 1.0L` of type `real` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a / 1.0L` of type `real` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a % 1.0L` of type `real` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a + 1.0F` of type `float` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a - 1.0F` of type `float` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a * 1.0F` of type `float` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a / 1.0F` of type `float` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a % 1.0F` of type `float` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a + 1.0` of type `double` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a - 1.0` of type `double` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a * 1.0` of type `double` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a / 1.0` of type `double` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a % 1.0` of type `double` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a + 1.0L` of type `real` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a - 1.0L` of type `real` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a * 1.0L` of type `real` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a / 1.0L` of type `real` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a % 1.0L` of type `real` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a + 1.0F` of type `float` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a - 1.0F` of type `float` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a * 1.0F` of type `float` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a / 1.0F` of type `float` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a % 1.0F` of type `float` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a + 1.0` of type `double` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a - 1.0` of type `double` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a * 1.0` of type `double` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a / 1.0` of type `double` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a % 1.0` of type `double` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a + 1.0L` of type `real` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a - 1.0L` of type `real` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a * 1.0L` of type `real` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a / 1.0L` of type `real` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a % 1.0L` of type `real` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a + 1.0F` of type `float` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a - 1.0F` of type `float` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a * 1.0F` of type `float` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a / 1.0F` of type `float` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a % 1.0F` of type `float` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a + 1.0` of type `double` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a - 1.0` of type `double` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a * 1.0` of type `double` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a / 1.0` of type `double` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a % 1.0` of type `double` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a + 1.0L` of type `real` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a - 1.0L` of type `real` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a * 1.0L` of type `real` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a / 1.0L` of type `real` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a % 1.0L` of type `real` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a + 1.0F` of type `float` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a - 1.0F` of type `float` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a * 1.0F` of type `float` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a / 1.0F` of type `float` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a % 1.0F` of type `float` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a + 1.0` of type `double` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a - 1.0` of type `double` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a * 1.0` of type `double` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a / 1.0` of type `double` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a % 1.0` of type `double` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a + 1.0L` of type `real` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a - 1.0L` of type `real` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a * 1.0L` of type `real` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a / 1.0L` of type `real` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a % 1.0L` of type `real` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a + 1.0F` of type `float` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a - 1.0F` of type `float` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a * 1.0F` of type `float` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a / 1.0F` of type `float` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a % 1.0F` of type `float` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a + 1.0` of type `double` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a - 1.0` of type `double` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a * 1.0` of type `double` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a / 1.0` of type `double` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a % 1.0` of type `double` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a + 1.0L` of type `real` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a - 1.0L` of type `real` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a * 1.0L` of type `real` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a / 1.0L` of type `real` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a % 1.0L` of type `real` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a + 1.0F` of type `float` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a - 1.0F` of type `float` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a * 1.0F` of type `float` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a / 1.0F` of type `float` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a % 1.0F` of type `float` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a + 1.0` of type `double` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a - 1.0` of type `double` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a * 1.0` of type `double` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a / 1.0` of type `double` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a % 1.0` of type `double` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a + 1.0L` of type `real` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a - 1.0L` of type `real` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a * 1.0L` of type `real` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a / 1.0L` of type `real` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a % 1.0L` of type `real` to `ulong`
+fail_compilation/failexpression4.d(149): Error: template instance `failexpression4.X!(integral, floating, arith)` error instantiating
+fail_compilation/failexpression4.d(154): instantiated from here: `OpReAssignCases!(TestOpAndAssign)`
+---
+*/
+template TT(T...) { alias T TT; }
+
+void TestOpAndAssign(Tx, Ux, ops)()
+{
+ foreach(T; Tx.x)
+ foreach(U; Ux.x)
+ foreach(op; ops.x)
+ {
+ T a = cast(T)1;
+ U b = cast(U)1;
+ mixin("a = a " ~ op[0..$-1] ~ " cast(U)1;");
+ }
+}
+
+struct integral { alias TT!(byte, ubyte, short, ushort, int, uint, long, ulong) x; }
+struct floating { alias TT!(float, double, real) x; }
+struct arith { alias TT!("+=", "-=", "*=", "/=", "%=") x; }
+
+void OpReAssignCases(alias X)()
+{
+ X!(integral, floating, arith)();
+}
+
+void main()
+{
+ OpReAssignCases!TestOpAndAssign();
+}
/*
TEST_OUTPUT:
---
-fail_compilation/ice13788.d(11): Error: pragma `mangle` - string expected for mangled name
-fail_compilation/ice13788.d(12): Error: `string` expected for mangled name, not `(1)` of type `int`
-fail_compilation/ice13788.d(13): Error: pragma `mangle` - zero-length string not allowed for mangled name
-fail_compilation/ice13788.d(14): Error: pragma `mangle` - mangled name characters can only be of type `char`
+fail_compilation/ice13788.d(11): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/ice13788.d(12): Error: `string` expected for pragma mangle argument, not `(1)` of type `int`
+fail_compilation/ice13788.d(13): Error: `pragma(mangle)` zero-length string not allowed for mangled name
+fail_compilation/ice13788.d(14): Error: `pragma(mangle)` mangled name characters can only be of type `char`
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/ice21095.d(14): Error: constructor `ice21095.Mutex.__ctor!().this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
-fail_compilation/ice21095.d(12): Error: template instance `ice21095.Mutex.__ctor!()` error instantiating
+fail_compilation/ice21095.d(14): Error: constructor `ice21095.Mutex.this!().this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
+fail_compilation/ice21095.d(12): Error: template instance `ice21095.Mutex.this!()` error instantiating
---
*/
class Mutex
/*
TEST_OUTPUT:
---
-fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` when expecting `(`
-fail_compilation/ice8795.d-mixin-14(14): Error: expression expected, not `End of File`
-fail_compilation/ice8795.d-mixin-14(14): Error: missing closing `)` after `switch (__error`
-fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` instead of statement
-fail_compilation/ice8795.d-mixin-15(15): Error: { } expected following `interface` declaration
-fail_compilation/ice8795.d-mixin-15(15): Error: anonymous interfaces not allowed
+fail_compilation/ice8795.d-mixin-16(16): Error: found `End of File` when expecting `(`
+fail_compilation/ice8795.d-mixin-16(16): Error: expression expected, not `End of File`
+fail_compilation/ice8795.d-mixin-16(16): Error: missing closing `)` after `switch (__error`
+fail_compilation/ice8795.d-mixin-16(16): Error: found `End of File` instead of statement
+fail_compilation/ice8795.d-mixin-16(16): while parsing string mixin statement
+fail_compilation/ice8795.d-mixin-17(17): Error: { } expected following `interface` declaration
+fail_compilation/ice8795.d-mixin-17(17): Error: anonymous interfaces not allowed
+fail_compilation/ice8795.d-mixin-17(17): while parsing string mixin statement
---
*/
void main()
/*
TEST_OUTPUT:
---
-fail_compilation/ice9273a.d(19): Error: constructor `ice9273a.C.__ctor!().this` no match for implicit `super()` call in constructor
-fail_compilation/ice9273a.d(23): Error: template instance `ice9273a.C.__ctor!()` error instantiating
+fail_compilation/ice9273a.d(19): Error: constructor `ice9273a.C.this!().this` no match for implicit `super()` call in constructor
+fail_compilation/ice9273a.d(23): Error: template instance `ice9273a.C.this!()` error instantiating
---
*/
---
fail_compilation/ice9284.d(14): Error: template `__ctor` is not callable using argument types `!()(int)`
fail_compilation/ice9284.d(12): Candidate is: `this()(string)`
-fail_compilation/ice9284.d(20): Error: template instance `ice9284.C.__ctor!()` error instantiating
+fail_compilation/ice9284.d(20): Error: template instance `ice9284.C.this!()` error instantiating
---
*/
REQUIRED_ARGS: -de
TEST_OUTPUT:
---
-fail_compilation/issue21203.d(12): Error: pragma `mangle` cannot apply to a template declaration
-fail_compilation/issue21203.d(12): use `template Class(Args...){ pragma(mangle, "other_name") class Class {} }`
+fail_compilation/issue21203.d(12): Error: `pragma(mangle)` cannot apply to a template declaration
+fail_compilation/issue21203.d(12): use `template F(Args...) { pragma(mangle, "gdkfjgh") ... }`
---
*/
/* TEST_OUTPUT:
---
-fail_compilation/issue22682.d(14): Error: `pragma(mangle)` must be attached to a declaration
-fail_compilation/issue22682.d(15): Error: `pragma(mangle)` takes a single argument that must be a string literal
-fail_compilation/issue22682.d(16): Error: `string` expected for pragma mangle argument, not `(0)` of type `int`
-fail_compilation/issue22682.d(16): Error: `pragma(mangle)` takes a single argument that must be a string literal
-fail_compilation/issue22682.d(17): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/issue22682.d(13): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/issue22682.d(14): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/issue22682.d(15): Error: `string` expected for pragma mangle argument, not `(0)` of type `int`
+fail_compilation/issue22682.d(16): Error: `pragma(mangle)` expects string literal argument for mangled name
---
*/
module issue22682;
/*
TEST_OUTPUT:
---
-fail_compilation/lexer23465.d(22): Error: character 0x1f37a is not allowed as a continue character in an identifier
-fail_compilation/lexer23465.d(22): Error: character 0x1f37a is not a valid token
-fail_compilation/lexer23465.d(23): Error: character '\' is not a valid token
-fail_compilation/lexer23465.d(24): Error: octal digit expected, not `9`
-fail_compilation/lexer23465.d(24): Error: octal literals larger than 7 are no longer supported
-fail_compilation/lexer23465.d(25): Error: integer overflow
-fail_compilation/lexer23465.d(26): Error: unterminated /+ +/ comment
-fail_compilation/lexer23465.d(27): Error: found `End of File` instead of array initializer
-fail_compilation/lexer23465.d(27): Error: semicolon needed to end declaration of `arr`, instead of `End of File`
-fail_compilation/lexer23465.d(20): `arr` declared here
+fail_compilation/lexer23465.d(21): Error: character 0x1f37a is not allowed as a continue character in an identifier
+fail_compilation/lexer23465.d(22): Error: character '\' is not a valid token
+fail_compilation/lexer23465.d(23): Error: octal digit expected, not `9`
+fail_compilation/lexer23465.d(23): Error: octal literals larger than 7 are no longer supported
+fail_compilation/lexer23465.d(24): Error: integer overflow
+fail_compilation/lexer23465.d(25): Error: unterminated /+ +/ comment
+fail_compilation/lexer23465.d(26): Error: found `End of File` instead of array initializer
+fail_compilation/lexer23465.d(26): Error: semicolon needed to end declaration of `arr`, instead of `End of File`
+fail_compilation/lexer23465.d(19): `arr` declared here
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/mangle1.d(8): Error: pragma `mangle` can only apply to a single declaration
+fail_compilation/mangle1.d(8): Error: `pragma(mangle)` can only apply to a single declaration
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/mangle2.d(20): Error: pragma `mangle` char 0x20 not allowed in mangled name
-fail_compilation/mangle2.d(21): Error: pragma `mangle` char 0x20 not allowed in mangled name
-fail_compilation/mangle2.d(24): Error: pragma `mangle` char 0x0a not allowed in mangled name
-fail_compilation/mangle2.d(25): Error: pragma `mangle` char 0x0a not allowed in mangled name
-fail_compilation/mangle2.d(28): Error: pragma `mangle` char 0x07 not allowed in mangled name
-fail_compilation/mangle2.d(29): Error: pragma `mangle` char 0x07 not allowed in mangled name
-fail_compilation/mangle2.d(32): Error: pragma `mangle` char 0x01 not allowed in mangled name
-fail_compilation/mangle2.d(33): Error: pragma `mangle` char 0x01 not allowed in mangled name
-fail_compilation/mangle2.d(36): Error: pragma `mangle` char 0x00 not allowed in mangled name
-fail_compilation/mangle2.d(37): Error: pragma `mangle` char 0x00 not allowed in mangled name
-fail_compilation/mangle2.d(40): Error: pragma `mangle` Outside Unicode code space
-fail_compilation/mangle2.d(41): Error: pragma `mangle` Outside Unicode code space
+fail_compilation/mangle2.d(9): Error: pragma `mangle` null character not allowed in mangled name
+fail_compilation/mangle2.d(10): Error: pragma `mangle` null character not allowed in mangled name
---
*/
-
-//spaces
-__gshared pragma(mangle, "test 9") ubyte test9_1;
-__gshared extern pragma(mangle, "test 9") ubyte test9_1_e;
-
-//\n chars
-__gshared pragma(mangle, "test\n9") ubyte test9_2;
-__gshared extern pragma(mangle, "test\n9") ubyte test9_2_e;
-
-//\a chars
-__gshared pragma(mangle, "test\a9") ubyte test9_3;
-__gshared extern pragma(mangle, "test\a9") ubyte test9_3_e;
-
-//\x01 chars
-__gshared pragma(mangle, "test\x019") ubyte test9_4;
-__gshared extern pragma(mangle, "test\x019") ubyte test9_4_e;
-
//\0 chars
__gshared pragma(mangle, "test\09") ubyte test9_5;
__gshared extern pragma(mangle, "test\09") ubyte test9_5_e;
-
-//\xff chars
-__gshared pragma(mangle, "test\xff9") ubyte test9_6;
-__gshared extern pragma(mangle, "test\xff9") ubyte test9_6_e;
--- /dev/null
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/opApply_return.d(18): Deprecation: cannot return non-zero compile-time value from `opApply`
+fail_compilation/opApply_return.d(18): Any non-zero value must be the result of calling its delegate
+fail_compilation/opApply_return.d(19): Deprecation: cannot return non-zero compile-time value from `opApply`
+fail_compilation/opApply_return.d(19): Any non-zero value must be the result of calling its delegate
+fail_compilation/opApply_return.d(20): Deprecation: cannot return non-zero compile-time value from `opApply`
+fail_compilation/opApply_return.d(20): Any non-zero value must be the result of calling its delegate
+---
+*/
+
+class Tree {
+ Tree lhs;
+ Tree rhs;
+ int opApply(int delegate(Tree) dg) {
+ if (lhs && lhs.opApply(dg)) return 1;
+ if (dg(this)) return 1;
+ if (rhs && rhs.opApply(dg)) return 1;
+ return 0;
+ }
+}
/* TEST_OUTPUT:
---
-fail_compilation/placenew.d(23): Error: PlacementExpression `3` is an rvalue, but must be an lvalue
-fail_compilation/placenew.d(28): Error: undefined identifier `x`
-fail_compilation/placenew.d(36): Error: `new ( i )` PlacementExpression cannot be evaluated at compile time
-fail_compilation/placenew.d(39): called from here: `xxx()`
-fail_compilation/placenew.d(39): while evaluating: `static assert(xxx() == 1)`
-fail_compilation/placenew.d(48): Error: new placement size 24 must be >= object size 40
-fail_compilation/placenew.d(54): Error: placement new cannot be used with associative arrays
-fail_compilation/placenew.d(67): Error: new placement size 4 must be >= class object size $?:32=16|64=24$
-fail_compilation/placenew.d(77): Error: `@safe` function `test7` cannot use placement `new` is not allowed in a `@safe` function
+fail_compilation/placenew.d(26): Error: PlacementExpression `3` is an rvalue, but must be an lvalue
+fail_compilation/placenew.d(31): Error: undefined identifier `x`
+fail_compilation/placenew.d(39): Error: `new ( i )` PlacementExpression cannot be evaluated at compile time
+fail_compilation/placenew.d(42): called from here: `xxx()`
+fail_compilation/placenew.d(42): while evaluating: `static assert(xxx() == 1)`
+fail_compilation/placenew.d(51): Error: new placement size 24 must be >= object size 40
+fail_compilation/placenew.d(57): Error: placement new cannot be used with associative arrays
+fail_compilation/placenew.d(70): Error: new placement size 4 must be >= class object size $?:32=16|64=24$
+fail_compilation/placenew.d(80): Error: placement `new` is not allowed in a `@safe` function
+fail_compilation/placenew.d(89): Error: PlacementExpression cannot be a dynamic array
+fail_compilation/placenew.d(92): Error: placement new cannot be used with dynamic arrays
+fail_compilation/placenew.d(95): Error: PlacementExpression `s` of type `const(int)` must be unshared and mutable
---
*/
void test0()
{
int i;
- int* pi = new (i) int;
+ int* pi = new (i) int; // OK
}
void test1()
}
/*************************************************/
+
+void test8()
+{
+ void[] a;
+ a.reserve(int.sizeof);
+ int* ps = new(a) int;
+
+ ubyte[a.sizeof] sa;
+ int[] ia = new(sa) int[4];
+
+ const int s;
+ ps = new(s) int;
+}
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/pragmamangle1.d(35): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle1.d(37): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle1.d(44): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/pragmamangle1.d(46): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/pragmamangle1.d(53): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle1.d(56): Error: `pragma(mangle)` zero-length string not allowed for mangled name
+fail_compilation/pragmamangle1.d(59): Error: `pragma(mangle)` expects 1 or 2 arguments
+fail_compilation/pragmamangle1.d(62): Error: `pragma(mangle)` cannot apply to a template declaration
+fail_compilation/pragmamangle1.d(62): use `template cannot_apply(Args...) { pragma(mangle, "template") ... }`
+fail_compilation/pragmamangle1.d(65): Error: `pragma(mangle)` takes a single argument that must be a string literal
+fail_compilation/pragmamangle1.d(68): Error: `pragma(mangle)` takes a single argument that must be a string literal
+fail_compilation/pragmamangle1.d(71): Error: `string` expected for pragma mangle argument, not `(T)` of type `T`
+fail_compilation/pragmamangle1.d(74): Error: `string` expected for pragma mangle argument, not `(T[T])` of type `T[T]`
+fail_compilation/pragmamangle1.d(77): Error: `pragma(mangle)` can only apply to a single declaration
+fail_compilation/pragmamangle1.d(85): Error: `class` or `struct` type expected for pragma mangle argument, not `"mangle"` of type `string`
+fail_compilation/pragmamangle1.d(88): Error: `class` or `struct` type expected for pragma mangle argument, not `F()` of type `T`
+fail_compilation/pragmamangle1.d(91): Error: `class` or `struct` type expected for pragma mangle argument, not `F()` of type `T`
+fail_compilation/pragmamangle1.d(94): Error: `string` expected for pragma mangle argument, not `(F())` of type `T`
+fail_compilation/pragmamangle1.d(97): Error: `string` expected for pragma mangle argument, not `(F())` of type `T`
+fail_compilation/pragmamangle1.d(100): Error: `string` expected for pragma mangle argument, not `(V)` of type `T`
+fail_compilation/pragmamangle1.d(103): Error: `string` expected for pragma mangle argument, not `(V)` of type `T`
+fail_compilation/pragmamangle1.d(106): Error: `string` expected for pragma mangle argument, not `T` of type `T`
+fail_compilation/pragmamangle1.d(109): Error: `class` or `struct` type expected for pragma mangle argument, not `int` of type `int`
+fail_compilation/pragmamangle1.d(112): Error: `class` or `struct` type expected for pragma mangle argument, not `T[T]` of type `T[T]`
+---
+*/
+struct T { }
+T F() { return T(); }
+T V;
+alias A = T[T];
+
+pragma(mangle);
+
+pragma(mangle)
+{
+ nothrow: pure: @nogc: @property
+ {
+ }
+}
+
+pragma(mangle, "no_declaration");
+
+pragma(mangle, "no_declaration")
+{
+ nothrow: pure: @nogc: @property
+ {
+ }
+}
+
+pragma(mangle)
+void no_string_literal();
+
+pragma(mangle, "")
+void empty_string_mangle();
+
+pragma(mangle, "too", "many", "arguments")
+void expects_less_arguments();
+
+pragma(mangle, "template")
+template cannot_apply(T) { }
+
+pragma(mangle, "func", "mangle")
+void func_too_many_arguments();
+
+pragma(mangle, "var", "mangle")
+int var_too_many_arguments;
+
+pragma(mangle, T)
+void func_arg1_not_a_string();
+
+pragma(mangle, A)
+int var_arg1_not_a_string;
+
+pragma(mangle, "too_many_declarations")
+{
+ nothrow pure:
+ void func1();
+ @nogc:
+ extern(C++) void func2();
+}
+
+pragma(mangle, "struct", "mangle")
+struct arg2_must_be_symbol { }
+
+pragma(mangle, "struct", F)
+struct struct_wrong_symbol { }
+
+pragma(mangle, "class", F)
+class class_wrong_symbol { }
+
+pragma(mangle, F(), T)
+struct arg1_not_ctfe_string { }
+
+pragma(mangle, T, F())
+struct arg2_not_ctfe_string { }
+
+pragma(mangle, V, T)
+struct arg1_not_string { }
+
+pragma(mangle, T, V)
+struct arg2_not_string { }
+
+pragma(mangle, T, T)
+struct arg2_expect_string { }
+
+pragma(mangle, int)
+struct arg1_not_class_or_struct { }
+
+pragma(mangle, "struct", A)
+struct arg2_not_class_or_struct { }
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/pragmamangle2.d(37): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle2.d(39): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle2.d(46): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/pragmamangle2.d(48): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/pragmamangle2.d(55): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle2.d(58): Error: `pragma(mangle)` zero-length string not allowed for mangled name
+fail_compilation/pragmamangle2.d(61): Error: `pragma(mangle)` expects 1 or 2 arguments
+fail_compilation/pragmamangle2.d(64): Error: `pragma(mangle)` cannot apply to a template declaration
+fail_compilation/pragmamangle2.d(64): use `template cannot_apply(Args...) { pragma(mangle, "template") ... }`
+fail_compilation/pragmamangle2.d(67): Error: `pragma(mangle)` takes a single argument that must be a string literal
+fail_compilation/pragmamangle2.d(70): Error: `pragma(mangle)` takes a single argument that must be a string literal
+fail_compilation/pragmamangle2.d(73): Error: `string` expected for pragma mangle argument, not `(T)` of type `T`
+fail_compilation/pragmamangle2.d(76): Error: `string` expected for pragma mangle argument, not `(T[T])` of type `T[T]`
+fail_compilation/pragmamangle2.d(79): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/pragmamangle2.d(85): Error: `class` or `struct` type expected for pragma mangle argument, not `"mangle"` of type `string`
+fail_compilation/pragmamangle2.d(88): Error: `class` or `struct` type expected for pragma mangle argument, not `F()` of type `T`
+fail_compilation/pragmamangle2.d(91): Error: `class` or `struct` type expected for pragma mangle argument, not `F()` of type `T`
+fail_compilation/pragmamangle2.d(94): Error: `string` expected for pragma mangle argument, not `(F())` of type `T`
+fail_compilation/pragmamangle2.d(97): Error: `string` expected for pragma mangle argument, not `(F())` of type `T`
+fail_compilation/pragmamangle2.d(100): Error: `string` expected for pragma mangle argument, not `(V)` of type `T`
+fail_compilation/pragmamangle2.d(103): Error: `string` expected for pragma mangle argument, not `(V)` of type `T`
+fail_compilation/pragmamangle2.d(106): Error: `string` expected for pragma mangle argument, not `T` of type `T`
+fail_compilation/pragmamangle2.d(109): Error: `class` or `struct` type expected for pragma mangle argument, not `int` of type `int`
+fail_compilation/pragmamangle2.d(112): Error: `class` or `struct` type expected for pragma mangle argument, not `T[T]` of type `T[T]`
+---
+*/
+void pragma_statement_test()
+{
+ struct T { }
+ T F() { return T(); }
+ T V;
+ alias A = T[T];
+
+ pragma(mangle);
+
+ pragma(mangle)
+ {
+ synchronized
+ {
+ }
+ }
+
+ pragma(mangle, "no_declaration");
+
+ pragma(mangle, "no_declaration")
+ {
+ synchronized
+ {
+ }
+ }
+
+ pragma(mangle)
+ void no_string_literal();
+
+ pragma(mangle, "")
+ void empty_string_mangle();
+
+ pragma(mangle, "too", "many", "arguments")
+ void expects_less_arguments();
+
+ pragma(mangle, "template")
+ template cannot_apply(T) { }
+
+ pragma(mangle, "func", "mangle")
+ void func_too_many_arguments();
+
+ pragma(mangle, "var", "mangle")
+ static int var_too_many_arguments;
+
+ pragma(mangle, T)
+ void func_arg1_not_a_string();
+
+ pragma(mangle, A)
+ static int var_arg1_not_a_string;
+
+ pragma(mangle, "too_many_declarations")
+ {
+ nothrow pure void func1();
+ @nogc extern(C++) void func2();
+ }
+
+ pragma(mangle, "struct", "mangle")
+ struct arg2_must_be_symbol { }
+
+ pragma(mangle, "struct", F)
+ struct struct_wrong_symbol { }
+
+ pragma(mangle, "class", F)
+ class class_wrong_symbol { }
+
+ pragma(mangle, F(), T)
+ struct arg1_not_ctfe_string { }
+
+ pragma(mangle, T, F())
+ struct arg2_not_ctfe_string { }
+
+ pragma(mangle, V, T)
+ struct arg1_not_string { }
+
+ pragma(mangle, T, V)
+ struct arg2_not_string { }
+
+ pragma(mangle, T, T)
+ struct arg2_expect_string { }
+
+ pragma(mangle, int)
+ struct arg1_not_class_or_struct { }
+
+ pragma(mangle, "struct", A)
+ struct arg2_not_class_or_struct { }
+}
fail_compilation/reserved_version.d(226): Error: version identifier `D_Invariants` is reserved and cannot be set
fail_compilation/reserved_version.d(227): Error: version identifier `D_Optimized` is reserved and cannot be set
fail_compilation/reserved_version.d(228): Error: version identifier `VisionOS` is reserved and cannot be set
+fail_compilation/reserved_version.d(229): Error: version identifier `D_Profile` is reserved and cannot be set
---
*/
// Some extra empty lines to help fixup the manual line numbering after adding new version identifiers
-
#line 105
version = MSP430;
version = D_P16;
version = D_Invariants;
version = D_Optimized;
version = VisionOS;
+version = D_Profile;
// This should work though
debug = DigitalMars;
// REQUIRED_ARGS: -version=none
// REQUIRED_ARGS: -version=D_PreConditions
// REQUIRED_ARGS: -version=D_PostConditions
+// REQUIRED_ARGS: -version=D_Profile
// REQUIRED_ARGS: -version=D_ProfileGC
// REQUIRED_ARGS: -version=D_Invariants
// REQUIRED_ARGS: -version=D_Optimized
Error: version identifier `none` is reserved and cannot be set
Error: version identifier `D_PreConditions` is reserved and cannot be set
Error: version identifier `D_PostConditions` is reserved and cannot be set
+Error: version identifier `D_Profile` is reserved and cannot be set
Error: version identifier `D_ProfileGC` is reserved and cannot be set
Error: version identifier `D_Invariants` is reserved and cannot be set
Error: version identifier `D_Optimized` is reserved and cannot be set
/*
TEST_OUTPUT:
---
-fail_compilation/retref2.d(18): Error: function `ref int retref2.D.foo(return ref int)` does not override any function, did you mean to override `ref int retref2.C.foo(ref int)`?
-fail_compilation/retref2.d(19): Error: function `ref int retref2.D.bar() scope return` does not override any function, did you mean to override `ref int retref2.C.bar()`?
+fail_compilation/retref2.d(20): Error: function `ref int retref2.D.foo(return ref int)` does not override any function
+fail_compilation/retref2.d(14): did you mean to override `ref int retref2.C.foo(ref int)`?
+fail_compilation/retref2.d(21): Error: function `ref int retref2.D.bar() scope return` does not override any function
+fail_compilation/retref2.d(15): did you mean to override `ref int retref2.C.bar()`?
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(197): Error: returning scope variable `e` is not allowed in a `@safe` function
+fail_compilation/retscope.d(198): Error: returning scope variable `e` is not allowed in a `@safe` function
+fail_compilation/retscope.d(197): `e` inferred `scope` because of `e.DG = d`
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(212): Error: assigning scope variable `p` to non-scope `e.e` is not allowed in a `@safe` function
+fail_compilation/retscope.d(213): Error: assigning scope variable `p` to non-scope `e.e` is not allowed in a `@safe` function
---
*/
struct Escaper3 { void* e; }
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(229): Error: scope parameter `ptr` may not be returned
+fail_compilation/retscope.d(230): Error: scope parameter `ptr` may not be returned
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
-fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
+fail_compilation/retscope.d(250): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
+fail_compilation/retscope.d(250): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(266): Error: taking address of `scope` variable `p` with pointers is not allowed in a `@safe` function
+fail_compilation/retscope.d(267): Error: taking address of `scope` variable `p` with pointers is not allowed in a `@safe` function
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(286): Error: escaping a reference to local variable `b` by returning `foo6(& b)` is not allowed in a `@safe` function
+fail_compilation/retscope.d(287): Error: escaping a reference to local variable `b` by returning `foo6(& b)` is not allowed in a `@safe` function
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(315): Error: scope parameter `p` may not be returned
-fail_compilation/retscope.d(316): Error: returning `p[]` escapes a reference to parameter `p`
-fail_compilation/retscope.d(319): Error: scope parameter `p` may not be returned
+fail_compilation/retscope.d(316): Error: scope parameter `p` may not be returned
+fail_compilation/retscope.d(317): Error: returning `p[]` escapes a reference to parameter `p`
+fail_compilation/retscope.d(320): Error: scope parameter `p` may not be returned
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(335): Error: assigning reference to local variable `i` to non-scope `f` is not allowed in a `@safe` function
+fail_compilation/retscope.d(336): Error: assigning reference to local variable `i` to non-scope `f` is not allowed in a `@safe` function
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(358): Error: taking address of `scope` variable `aa` with pointers is not allowed in a `@safe` function
+fail_compilation/retscope.d(359): Error: taking address of `scope` variable `aa` with pointers is not allowed in a `@safe` function
---
*/
TEST_OUTPUT:
---
fail_compilation/retscope.d(1205): Error: scope variable `f14` calling non-scope member function `Foo14.foo()` is not allowed in a `@safe` function
+fail_compilation/retscope.d(1204): `f14` inferred `scope` because of `f14.v = & i`
---
*/
---
fail_compilation/retscope.d(1907): Error: assigning scope variable `x` to `ref` variable `this` with longer lifetime is not allowed in a `@safe` function
fail_compilation/retscope.d(1913): Error: returning scope variable `x` is not allowed in a `@safe` function
+fail_compilation/retscope.d(1912): `x` inferred `scope` because of `x = __param_0`
---
*/
#line 1900
TEST_OUTPUT:
---
fail_compilation/retscope2.d(1107): Error: returning scope variable `dg` is not allowed in a `@safe` function
+fail_compilation/retscope2.d(1106): `dg` inferred `scope` because of `dg = &s.foo`
---
*/
fail_compilation/retscope6.d(8016): Error: assigning address of variable `i` to `p` with longer lifetime is not allowed in a `@safe` function
fail_compilation/retscope6.d(8031): Error: assigning reference to local variable `i` to non-scope parameter `p` calling `betty` is not allowed in a `@safe` function
fail_compilation/retscope6.d(8031): Error: assigning reference to local variable `j` to non-scope parameter `q` calling `betty` is not allowed in a `@safe` function
-fail_compilation/retscope6.d(8023): which is not `scope` because of `p = q`
+fail_compilation/retscope6.d(8023): `q` is not `scope` because of `p = q`
fail_compilation/retscope6.d(8048): Error: assigning reference to local variable `i` to non-scope parameter `p` calling `archie` is not allowed in a `@safe` function
-fail_compilation/retscope6.d(8039): which is not `scope` because of `r = p`
+fail_compilation/retscope6.d(8039): `p` is not `scope` because of `r = p`
fail_compilation/retscope6.d(8048): Error: assigning reference to local variable `j` to non-scope parameter `q` calling `archie` is not allowed in a `@safe` function
-fail_compilation/retscope6.d(8038): which is not `scope` because of `p = q`
+fail_compilation/retscope6.d(8038): `q` is not `scope` because of `p = q`
---
*/
/* TEST_OUTPUT:
---
fail_compilation/retscope6.d(14019): Error: assigning scope variable `scopePtr` to non-scope parameter `x` calling `noInfer23021` is not allowed in a `@safe` function
-fail_compilation/retscope6.d(14009): which is not `scope` because of `*escapeHole = cast(const(int)*)x`
+fail_compilation/retscope6.d(14009): `x` is not `scope` because of `*escapeHole = cast(const(int)*)x`
fail_compilation/retscope6.d(14022): Error: returning scope variable `scopePtr` is not allowed in a `@safe` function
---
*/
/* TEST_OUTPUT:
---
fail_compilation/retscope6.d(14050): Error: assigning scope variable `z` to non-scope parameter `y` calling `f23294` is not allowed in a `@safe` function
-fail_compilation/retscope6.d(14044): which is not `scope` because of `x = y`
+fail_compilation/retscope6.d(14044): `y` is not `scope` because of `x = y`
---
*/
--- /dev/null
+/*
+DISABLED: win32 win64 freebsd32 openbsd32 linux32 osx32
+TEST_OUTPUT:
+---
+fail_compilation/safeprintf.d(20): Error: `@safe` function `safeprintf.func` cannot call `@system` function `safeprintf.printf`
+fail_compilation/safeprintf.d(15): `safeprintf.printf` is declared here
+fail_compilation/safeprintf.d(21): Error: `@safe` function `safeprintf.func` cannot call `@system` function `safeprintf.printf`
+fail_compilation/safeprintf.d(15): `safeprintf.printf` is declared here
+fail_compilation/safeprintf.d(22): Error: `@safe` function `safeprintf.func` cannot call `@system` function `safeprintf.printf`
+fail_compilation/safeprintf.d(15): `safeprintf.printf` is declared here
+fail_compilation/safeprintf.d(22): Deprecation: format specifier `"%Z"` is invalid
+---
+*/
+
+extern (C) @system pragma(printf) int printf(const(char)* format, ...);
+
+@safe void func(int i, char* s, dchar* d)
+{
+ printf("i: %d\n", i);
+ printf("s: %s\n", s);
+ printf("s: %S\n", d);
+ printf("s: %Z\n", s);
+}
--- /dev/null
+/*
+REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/scope_infer_diagnostic.d(34): Error: `@safe` function `scope_infer_diagnostic.outer` cannot call `@system` function `scope_infer_diagnostic.inner!(void delegate(const(char)[]) pure nothrow @nogc @safe).inner`
+fail_compilation/scope_infer_diagnostic.d(28): and assigning scope variable `w` to non-scope parameter `w` calling `callee` makes it fail to infer `@safe`
+fail_compilation/scope_infer_diagnostic.d(21): `w` is not `scope` because of `globalPtr = & w`
+fail_compilation/scope_infer_diagnostic.d(25): `scope_infer_diagnostic.inner!(void delegate(const(char)[]) pure nothrow @nogc @safe).inner` is declared here
+---
+*/
+
+// Test that scope violation error messages show WHY the callee's parameter
+// is not scope when going through the @safe inference chain.
+// This is similar to how @nogc violations show the full inference chain.
+
+void* globalPtr;
+
+void callee(Writer)(Writer w) @trusted
+{
+ // This escapes w, preventing it from being inferred as scope
+ globalPtr = cast(void*)&w;
+ w("x");
+}
+
+void inner(Writer)(scope Writer w)
+{
+ // Scope violation: passing scope w to non-scope parameter
+ callee(w);
+}
+
+void outer() @safe
+{
+ int x;
+ inner((const(char)[] s) { x++; });
+}
fail_compilation/systemvariables_void_init.d(48): Error: `void` initializing a type with unsafe bit patterns is not allowed in a `@safe` function
fail_compilation/systemvariables_void_init.d(49): Error: `void` initializing a type with unsafe bit patterns is not allowed in a `@safe` function
fail_compilation/systemvariables_void_init.d(50): Error: `void` initializing a type with unsafe bit patterns is not allowed in a `@safe` function
-fail_compilation/systemvariables_void_init.d(51): Error: void intializing a bool (which must always be 0 or 1) is not allowed in a `@safe` function
-fail_compilation/systemvariables_void_init.d(52): Error: void intializing a bool (which must always be 0 or 1) is not allowed in a `@safe` function
+fail_compilation/systemvariables_void_init.d(51): Error: `void` initializing a `bool` (which must always be 0 or 1) is not allowed in a `@safe` function
+fail_compilation/systemvariables_void_init.d(52): Error: `void` initializing a `bool` (which must always be 0 or 1) is not allowed in a `@safe` function
fail_compilation/systemvariables_void_init.d(53): Error: `void` initializing a type with unsafe bit patterns is not allowed in a `@safe` function
fail_compilation/systemvariables_void_init.d(54): Error: `void` initializing a type with unsafe bit patterns is not allowed in a `@safe` function
---
/*
TEST_OUTPUT:
---
-fail_compilation/test13867.d(12): Error: function `void test13867.X.blah()` does not override any function, did you mean to override `extern (C++) void test13867.Base.blah()`?
-fail_compilation/test13867.d(19): Error: function `void test13867.Z.blah()` does not override any function, did you mean to override `extern (C++) void test13867.Base.blah()`?
+fail_compilation/test13867.d(14): Error: function `void test13867.X.blah()` does not override any function
+fail_compilation/test13867.d(11): did you mean to override `extern (C++) void test13867.Base.blah()`?
+fail_compilation/test13867.d(21): Error: function `void test13867.Z.blah()` does not override any function
+fail_compilation/test13867.d(11): did you mean to override `extern (C++) void test13867.Base.blah()`?
---
*/
extern (C++) class Base {
-/* REQUIRED_ARGS: -preview=bitfields
+/*
* TEST_OUTPUT:
---
fail_compilation/test16188.d(101): Error: no property `name` for `Where()` of type `test16188.Where`
fail_compilation/test17284.d(17): Error: accessing overlapped field `U.c` with pointers is not allowed in a `@safe` function
pure nothrow @safe void(U t)
---
-REQUIRED_ARGS: -preview=bitfields
+
*/
// https://issues.dlang.org/show_bug.cgi?id=17284
REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test17422.d(23): Error: returning scope variable `p` is not allowed in a `@safe` function
+fail_compilation/test17422.d(24): Error: returning scope variable `p` is not allowed in a `@safe` function
+fail_compilation/test17422.d(23): `p` inferred `scope` because of `p = rc.get()`
---
*/
struct RC
/* REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test17423.d(27): Error: assigning reference to local `this` to non-scope parameter `dlg` calling `opApply` is not allowed in a `@safe` function
-fail_compilation/test17423.d(16): which is not `scope` because of `this.myDlg = dlg`
+fail_compilation/test17423.d(28): Error: assigning reference to local `this` to non-scope parameter `dlg` calling `opApply` is not allowed in a `@safe` function
+fail_compilation/test17423.d(17): `dlg` is not `scope` because of `this.myDlg = dlg`
+fail_compilation/test17423.d(28): `this` inferred `scope` because of `int(int _) => 0`
---
*/
/* REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test18282.d(25): Error: returning scope variable `aa` is not allowed in a `@safe` function
-fail_compilation/test18282.d(34): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test18282.d(26): Error: returning scope variable `aa` is not allowed in a `@safe` function
fail_compilation/test18282.d(35): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function
-fail_compilation/test18282.d(36): Error: returning scope variable `staa` is not allowed in a `@safe` function
-fail_compilation/test18282.d(44): Error: escaping a reference to local variable `i` by copying `S2000(& i)` into allocated memory is not allowed in a `@safe` function
-fail_compilation/test18282.d(53): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function
-fail_compilation/test18282.d(53): Error: escaping a reference to local variable `c` by copying `& c` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test18282.d(36): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test18282.d(37): Error: returning scope variable `staa` is not allowed in a `@safe` function
+fail_compilation/test18282.d(34): `staa` inferred `scope` because of `staa = [& i]`
+fail_compilation/test18282.d(45): Error: escaping a reference to local variable `i` by copying `S2000(& i)` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test18282.d(54): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test18282.d(54): Error: escaping a reference to local variable `c` by copying `& c` into allocated memory is not allowed in a `@safe` function
---
*/
/* REQUIRED_ARGS: -preview=dip1000
* TEST_OUTPUT:
---
-fail_compilation/test19097.d(44): Error: returning scope variable `s` is not allowed in a `@safe` function
-fail_compilation/test19097.d(48): Error: returning scope variable `s1` is not allowed in a `@safe` function
-fail_compilation/test19097.d(77): Error: assigning scope variable `z` to `ref` variable `refPtr` with longer lifetime is not allowed in a `@safe` function
-fail_compilation/test19097.d(108): Error: returning scope variable `s4` is not allowed in a `@safe` function
-fail_compilation/test19097.d(126): Error: returning scope variable `s5c` is not allowed in a `@safe` function
-fail_compilation/test19097.d(130): Error: returning scope variable `s5m` is not allowed in a `@safe` function
-fail_compilation/test19097.d(147): Error: returning scope variable `s6c` is not allowed in a `@safe` function
-fail_compilation/test19097.d(151): Error: returning scope variable `s6m` is not allowed in a `@safe` function
+fail_compilation/test19097.d(51): Error: returning scope variable `s` is not allowed in a `@safe` function
+fail_compilation/test19097.d(50): `s` inferred `scope` because of `s = & i`
+fail_compilation/test19097.d(55): Error: returning scope variable `s1` is not allowed in a `@safe` function
+fail_compilation/test19097.d(54): `s1` inferred `scope` because of `s1 = & i`
+fail_compilation/test19097.d(84): Error: assigning scope variable `z` to `ref` variable `refPtr` with longer lifetime is not allowed in a `@safe` function
+fail_compilation/test19097.d(115): Error: returning scope variable `s4` is not allowed in a `@safe` function
+fail_compilation/test19097.d(114): `s4` inferred `scope` because of `s4 = & x`
+fail_compilation/test19097.d(133): Error: returning scope variable `s5c` is not allowed in a `@safe` function
+fail_compilation/test19097.d(132): `s5c` inferred `scope` because of `s5c = i`
+fail_compilation/test19097.d(137): Error: returning scope variable `s5m` is not allowed in a `@safe` function
+fail_compilation/test19097.d(136): `s5m` inferred `scope` because of `s5m = i`
+fail_compilation/test19097.d(154): Error: returning scope variable `s6c` is not allowed in a `@safe` function
+fail_compilation/test19097.d(153): `s6c` inferred `scope` because of `s6c = i`
+fail_compilation/test19097.d(158): Error: returning scope variable `s6m` is not allowed in a `@safe` function
+fail_compilation/test19097.d(157): `s6m` inferred `scope` because of `s6m = i`
---
*/
REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test20245.d(21): Error: assigning reference to local variable `x` to non-scope parameter `ptr` calling `escape` is not allowed in a `@safe` function
-fail_compilation/test20245.d(22): Error: escaping a reference to parameter `x` by copying `&x` into allocated memory is not allowed in a `@safe` function
-fail_compilation/test20245.d(23): Error: returning scope variable `a` is not allowed in a `@safe` function
-fail_compilation/test20245.d(27): Error: taking address of `scope` variable `x` with pointers is not allowed in a `@safe` function
-fail_compilation/test20245.d(33): Error: assigning reference to local variable `x` to non-scope parameter `ptr` calling `escape` is not allowed in a `@safe` function
-fail_compilation/test20245.d(34): Error: escaping a reference to parameter `x` by copying `&x` into allocated memory is not allowed in a `@safe` function
-fail_compilation/test20245.d(50): Error: assigning reference to local variable `price` to non-scope `this.minPrice` is not allowed in a `@safe` function
-fail_compilation/test20245.d(69): Error: reference to local variable `this.content[]` calling non-scope member function `Exception.this()` is not allowed in a `@safe` function
-fail_compilation/test20245.d(89): Error: assigning reference to local variable `this` to non-scope parameter `content` calling `listUp` is not allowed in a `@safe` function
-fail_compilation/test20245.d(82): which is not `scope` because of `charPtr = content`
+fail_compilation/test20245.d(26): Error: assigning reference to local variable `x` to non-scope parameter `ptr` calling `escape` is not allowed in a `@safe` function
+fail_compilation/test20245.d(25): `x` inferred `scope` because of `a = &x`
+fail_compilation/test20245.d(27): Error: escaping a reference to parameter `x` by copying `&x` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test20245.d(28): Error: returning scope variable `a` is not allowed in a `@safe` function
+fail_compilation/test20245.d(25): `a` inferred `scope` because of `a = &x`
+fail_compilation/test20245.d(32): Error: taking address of `scope` variable `x` with pointers is not allowed in a `@safe` function
+fail_compilation/test20245.d(38): Error: assigning reference to local variable `x` to non-scope parameter `ptr` calling `escape` is not allowed in a `@safe` function
+fail_compilation/test20245.d(37): `x` inferred `scope` because of `a = &x`
+fail_compilation/test20245.d(39): Error: escaping a reference to parameter `x` by copying `&x` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test20245.d(55): Error: assigning reference to local variable `price` to non-scope `this.minPrice` is not allowed in a `@safe` function
+fail_compilation/test20245.d(74): Error: reference to local variable `this.content[]` calling non-scope member function `Exception.this()` is not allowed in a `@safe` function
+fail_compilation/test20245.d(74): `this` inferred `scope` because of `this.content[]`
+fail_compilation/test20245.d(94): Error: assigning reference to local variable `this` to non-scope parameter `content` calling `listUp` is not allowed in a `@safe` function
+fail_compilation/test20245.d(87): `content` is not `scope` because of `charPtr = content`
+fail_compilation/test20245.d(94): `this` inferred `scope` because of `&this.content`
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/test20489.d(19): Error: function `pure nothrow @nogc @safe int test20489.D.f(int delegate(int) pure nothrow @nogc @safe body)` does not override any function, did you mean to override `pure nothrow @nogc @safe int test20489.B.f(scope int delegate(int) pure nothrow @nogc @safe)`?
-fail_compilation/test20489.d(19): Did you intend to override:
-fail_compilation/test20489.d(19): `pure nothrow @nogc @safe int test20489.B.f(scope int delegate(int) pure nothrow @nogc @safe)`
-fail_compilation/test20489.d(19): Parameter 1 is missing `scope`
+fail_compilation/test20489.d(18): Error: function `pure nothrow @nogc @safe int test20489.D.f(int delegate(int) pure nothrow @nogc @safe body)` does not override any function
+fail_compilation/test20489.d(14): did you mean to override `pure nothrow @nogc @safe int test20489.B.f(scope int delegate(int) pure nothrow @nogc @safe)`?
+fail_compilation/test20489.d(18): parameter 1 is missing `scope`
---
*/
--- /dev/null
+// must not assert/crash with empty declarations
+/* TEST_OUTPUT:
+---
+fail_compilation/test20863a.d(21): Error: no property `Entry` for type `object.TypeInfo_AssociativeArray`
+fail_compilation/test20863a.d(17): class `TypeInfo_AssociativeArray` defined here
+fail_compilation/test20863a.d(21): Error: no property `aaGetHash` for type `object.TypeInfo_AssociativeArray`
+fail_compilation/test20863a.d(17): class `TypeInfo_AssociativeArray` defined here
+fail_compilation/test20863a.d(21): Error: no property `aaOpEqual` for type `object.TypeInfo_AssociativeArray`
+fail_compilation/test20863a.d(17): class `TypeInfo_AssociativeArray` defined here
+---
+*/
+
+module object;
+
+class Object { }
+class TypeInfo { }
+class TypeInfo_AssociativeArray { }
+
+extern(C) int main()
+{
+ int[int] aa;
+ return 0;
+}
--- /dev/null
+// must not assert/crash with mismatched declarations
+/* TEST_OUTPUT:
+---
+fail_compilation/test20863b.d(23): Error: `Entry` isn't a template
+fail_compilation/test20863b.d(23): Error: `aaGetHash` isn't a template function
+fail_compilation/test20863b.d(23): Error: `aaOpEqual` isn't a template function
+---
+*/
+
+module object;
+
+class Object { }
+class TypeInfo { }
+class TypeInfo_AssociativeArray
+{
+ int Entry;
+ struct aaOpEqual(K, V) { }
+ struct aaGetHash(K, V) { }
+}
+
+extern(C) int main()
+{
+ int[int] aa;
+ return 0;
+}
/*
TEST_OUTPUT:
---
-fail_compilation/test21246.d(16): Error: function `void test21246.C.set(Clock clock)` does not override any function, did you mean to override `void imports.test21246.B.set(imports.test21246.Clock clock)`?
+fail_compilation/test21246.d(17): Error: function `void test21246.C.set(Clock clock)` does not override any function
+fail_compilation/imports/test21246.d(7): did you mean to override `void imports.test21246.B.set(imports.test21246.Clock clock)`?
---
*/
module test21246;
--- /dev/null
+/*
+REQUIRED_ARGS: -verrors=context
+TEST_OUTPUT:
+---
+fail_compilation/test21634.d(22): Error: function literal `(int x) { }` is not callable using argument types `(string)`
+ (int x) {} ("%s");
+ ^
+fail_compilation/test21634.d(22): cannot pass argument `"%s"` of type `string` to parameter `int x`
+ (int x) {} ("%s");
+ ^
+fail_compilation/test21634.d(24): Error: declaration `test21634.main.foo` is already defined
+ int foo;
+ ^
+fail_compilation/test21634.d(23): `variable` `foo` is defined here
+ float foo;
+ ^
+---
+*/
+void f(int x) {}
+void main()
+{
+ (int x) {} ("%s");
+ float foo;
+ int foo;
+}
TEST_OUTPUT:
---
fail_compilation/test23073.d(28): Error: assigning scope variable `c` to non-scope parameter `c` calling `assignNext` is not allowed in a `@safe` function
-fail_compilation/test23073.d(22): which is not `scope` because of `c.next = c`
+fail_compilation/test23073.d(22): `c` is not `scope` because of `c.next = c`
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/test23159.d(14): Error: `scope(failure)` cannot be used with -betterC
-fail_compilation/test23159.d(18): Error: `scope(success)` cannot be used with -betterC
+fail_compilation/test23159.d(14): Error: `scope(failure)` cannot be used with `-betterC`
+fail_compilation/test23159.d(18): Error: `scope(success)` cannot be used with `-betterC`
---
*/
TEST_OUTPUT:
---
fail_compilation/test23982.d(35): Error: assigning scope variable `a` to non-scope parameter `a` calling `foo2` is not allowed in a `@safe` function
-fail_compilation/test23982.d(26): which is not `scope` because of `b = a`
+fail_compilation/test23982.d(26): `a` is not `scope` because of `b = a`
---
*/
// https://issues.dlang.org/show_bug.cgi?id=23982
/* REQUIRED_ARGS: -nothrow
* TEST_OUTPUT:
---
-fail_compilation/test24084.d(110): Error: cannot use `throw` statements with -nothrow
-fail_compilation/test24084.d(112): Error: cannot use try-catch statements with -nothrow
+fail_compilation/test24084.d(110): Error: cannot use `throw` statements with `-nothrow`
+fail_compilation/test24084.d(112): Error: cannot use try-catch statements with `-nothrow`
---
*/
// https://issues.dlang.org/show_bug.cgi?id=24353
-/**
+/*
+REQUIRED_ARGS: -verrors=context
TEST_OUTPUT:
---
-fail_compilation/test24353.d(23): Error: mutable method `test24353.S.opApply` is not callable using a `const` object
-fail_compilation/test24353.d(14): Consider adding `const` or `inout` here
+fail_compilation/test24353.d(37): Error: mutable method `test24353.S.opApply` is not callable using a `const(S)` foreach aggregate
+ foreach (e; s) {} // mod error
+ ^
+fail_compilation/test24353.d(28): Consider adding a method type qualifier here
+ int opApply(int delegate(int) dg)
+ ^
+fail_compilation/test24353.d(40): Error: shared const method `test24353.S2.opApply` is not callable using a `const(S2)` foreach aggregate
+ foreach (i, e; s2) {} // mod error
+ ^
+fail_compilation/test24353.d(47): Consider adding a method type qualifier here
+ int opApply(int delegate(int, int) dg) const shared;
+ ^
+fail_compilation/test24353.d(42): Error: cannot uniquely infer `foreach` argument types
+ foreach (i, e; const S3()) {} // cannot infer
+ ^
---
*/
void example()
{
const S s;
- foreach (e; s) {} // Error expected here
+ foreach (e; s) {} // mod error
+
+ const S2 s2;
+ foreach (i, e; s2) {} // mod error
+
+ foreach (i, e; const S3()) {} // cannot infer
+}
+
+struct S2
+{
+ int opApply(int delegate(int, int) dg) const shared;
+}
+
+struct S3
+{
+ int opApply(int delegate(int) dg);
+ int opApply(int delegate(int, int) dg);
}
TEST_OUTPUT:
---
fail_compilation/testrvaluecpctor.d(16): Error: cannot define both an rvalue constructor and a copy constructor for `struct Foo`
-fail_compilation/testrvaluecpctor.d(24): Template instance `testrvaluecpctor.Foo!int.Foo.__ctor!(immutable(Foo!int), immutable(Foo!int))` creates an rvalue constructor for `struct Foo`
+fail_compilation/testrvaluecpctor.d(24): Template instance `testrvaluecpctor.Foo!int.Foo.this!(immutable(Foo!int), immutable(Foo!int))` creates an rvalue constructor for `struct Foo`
fail_compilation/testrvaluecpctor.d(24): Error: none of the overloads of `this` can construct an immutable object with argument types `(immutable(Foo!int))`. Expected `immutable(immutable(Foo!int))`
fail_compilation/testrvaluecpctor.d(18): Candidates are: `testrvaluecpctor.Foo!int.Foo.this(ref scope Foo!int rhs)`
fail_compilation/testrvaluecpctor.d(16): `this(Rhs, this This)(scope Rhs rhs)`
TEST_OUTPUT:
---
fail_compilation/testrvaluecpctor.d(40): Error: cannot define both an rvalue constructor and a copy constructor for `struct Test`
-fail_compilation/testrvaluecpctor.d(46): Template instance `testrvaluecpctor.Test.__ctor!()` creates an rvalue constructor for `struct Test`
+fail_compilation/testrvaluecpctor.d(46): Template instance `testrvaluecpctor.Test.this!()` creates an rvalue constructor for `struct Test`
---
*/
-/* REQUIRED_ARGS: -preview=bitfields
+/*
*/
struct T
}
}
+/************************************/
+// https://github.com/dlang/dmd/issues/20917
+
+auto test20917a(int a) @nogc
+{
+ int foo() @nogc { return a + 3; }
+ enum yes = __traits(compiles, { return a; });
+ assert(foo() == a + 3);
+ return 1;
+}
+
+void test20917b() @nogc
+{
+ int i;
+ static if (is(typeof(() @safe @nogc
+ {
+ i++;
+ })))
+ {
+ () @safe @nogc {
+ i++;
+ }();
+ }
+ assert(i == 1);
+}
+
+void test20917c() @nogc
+{
+ int i;
+ static if (is(typeof(() @safe @nogc
+ {
+ return (){
+ i++;
+ };
+ })))
+ {
+ () @safe @nogc {
+ i++;
+ }();
+ }
+ assert(i == 1);
+}
+
/************************************/
int main()
test12406();
test14730();
//test14730x();
+ test20917a(1);
+ test20917b();
+ test20917c();
printf("Success\n");
return 0;
-/* REQUIRED_ARGS: -preview=bitfields
+/*
*/
struct S
--- /dev/null
+/* test bitfields for Digital Mars
+ * Note that this test is for win32 only
+ *
+ *
+ * DISABLED: win32mscoff win64 linux freebsd osx
+ * RUN_OUTPUT:
+---
+ DM | MS | P32 | P64
+T0 = 1 1 || 1 1 | 1 1 | 1 1 | 1 1
+T1 = 2 2 || 2 2 | 2 2 | 2 2 | 2 2
+T2 = 4 4 || 4 4 | 4 4 | 4 4 | 4 4
+T3 = 16 8 || 16 8 | 16 8 | 8 4 | 8 8
+T4 = 16 8 || 16 8 | 16 8 | 12 4 | 16 8
+T5 = 16 8 || 16 8 | 16 8 | 8 4 | 8 8
+S1 = 8 8 || 8 8 | 8 8 | 4 4 | 8 8
+S2 = 4 4 || 4 4 | 4 4 | 4 4 | 4 4
+S3 = 8 4 || 8 4 | 8 4 | 4 4 | 4 4
+S4 = 8 4 || 8 4 | 8 4 | 4 4 | 4 4
+S5 = 8 4 || 8 4 | 8 4 | 4 4 | 4 4
+S6 = 2 2 || 2 2 | 2 2 | 2 2 | 2 2
+S7 = 16 8 || 16 8 | 16 8 | 4 4 | 8 8
+S8 = 4 2 || 4 2 | 4 2 | 2 2 | 2 2
+S8A = 4 2 || 4 2 | 4 2 | 2 2 | 2 2
+S8B = 6 2 || 6 2 | 6 2 | 2 2 | 2 2
+S8C = 8 4 || 8 4 | 8 4 | 4 4 | 4 4
+S9 = 4 2 || 4 2 | 4 2 | 4 2 | 4 2
+S10 = 1 1 || 0 0 | * * | 0 1 | 0 1
+S11 = 1 1 || 0 0 | 4 1 | 0 1 | 0 1
+S12 = 4 4 || 4 4 | 4 4 | 4 4 | 4 4
+S13 = 8 4 || 8 4 | 8 4 | 8 4 | 8 4
+S14 = 8 4 || 8 4 | 8 4 | 8 4 | 8 4
+S15 = 8 4 || 8 4 | 8 4 | 4 4 | 4 4
+S16 = 4 4 || 0 0 | 4 4 | 4 1 | 4 1
+S17 = 4 4 || 4 4 | 4 4 | 4 4 | 4 4
+S18 = 2 1 || 2 1 | 2 1 | 5 1 | 9 1
+A0 = 16 8 || 16 8 | 16 8 | 12 4 | 16 8
+A1 = 12 4 || 12 4 | 12 4 | 12 4 | 12 4
+A2 = 12 4 || 12 4 | 12 4 | 12 4 | 12 4
+A3 = 16 4 || 16 4 | 16 4 | 16 4 | 16 4
+A4 = 12 4 || 12 4 | 12 4 | 8 4 | 8 4
+A5 = 2 1 || 2 1 | 2 1 | 2 1 | 2 1
+A6 = 4 2 || 4 2 | 4 2 | 2 2 | 2 2
+A7 = 16 4 || 16 4 | 16 4 | 12 4 | 16 8
+A8 = 12 4 || 12 4 | 12 4 | 8 4 | 8 8
+A9 = 32 8 || 32 8 | 32 8 | 16 4 | 16 8
+A10 = 4 2 || 4 2 | 4 2 | 2 2 | 2 2
+A11 = 16 4 || 16 4 | 16 4 | 12 4 | 12 4
+S9 = x30200
+S14 = x300000201
+S15 = x201
+S18 = 1 should be 4
+A0 = x1
+---
+ */
+
+import core.stdc.stdio;
+
+int is64bit() { return size_t.sizeof == 8; } // otherwise assume 32 bit
+
+/*************************************************************/
+
+struct T0 { ubyte x:1; }; //
+struct T1 { short x:1; }; //
+struct T2 { int x:1; }; //
+struct T3 { ubyte a,b,c,d; long x:1; }; //
+struct T4 { ubyte a,b,c,d,e,f,g,h; long x:1; }; //
+struct T5 { ubyte a,b,c,d,e,f,g; long x:1; }; //
+struct S1 { long f:1; }; //
+struct S2 { int x:1; int y:1; }; //
+struct S3 { short c; int x:1; uint y:1; }; //
+struct S4 { int x:1; short y:1; }; //
+struct S5 { short x:1; int y:1; }; //
+struct S6 { short x:1; short y:1; }; //
+struct S7 { short x:1; int y:1; long z:1; }; //
+struct S8 { ubyte a; ubyte b:1; short c:2; }; //
+struct S8A { ubyte b:1; short c:2; }; //
+struct S8B { ubyte a; short b:1; ubyte c:2; }; //
+struct S8C { ubyte a; int b:1; }; //
+struct S9 { ubyte a; ubyte b:2; short c:9; }; //
+struct S10 { }; // sizeof differs from C treatment
+struct S11 { int :0; }; // sizeof differs from C treatment
+struct S12 { int :0; int x; }; //
+struct S13 { uint x:12; uint x1:1; uint x2:1; uint x3:1; uint x4:1; int w; }; //
+struct S14 { ubyte a; ubyte b:4; int c:30; }; //
+struct S15 { ubyte a; ubyte b:2; int c:9; }; //
+struct S16 { int :32; }; // sizeof differs from C treatment
+struct S17 { int a:32; }; //
+struct S18 { ubyte a; long :0; ubyte b; }; //
+struct A0 { int a; long b:34, c:4; }; //
+struct A1 { int a; uint b:11; int c; }; //
+struct A2 { int a; uint b:11, c:5, d:16; //
+ int e; };
+struct A3 { int a; uint b:11, c:5, :0, d:16; //
+ int e; };
+struct A4 { int a:8; short b:7; //
+ uint c:29; };
+struct A5 { ubyte a:7, b:2; }; //
+struct A6 { ubyte a:7; short b:2; }; //
+struct A7 { short a:8; int b:16; int c; //
+ ubyte d:7; };
+struct A8 { short a:8; int b:16; int :0; //
+ ubyte c:7; };
+struct A9 { ushort a:8; int b:16; //
+ uint c:29; long d:9;
+ uint e:2, f:31; };
+struct A10 { ushort a:8; ubyte b; }; //
+struct A11 { ubyte a; int b:5, c:11, :0, d:8; //
+ struct { int ee:8; } };
+
+int main()
+{
+ /* MS produces identical results for 32 and 64 bit compiles,
+ * DM is 32 bit only
+ */
+ printf(" DM | MS | P32 | P64\n");
+ printf("T0 = %2d %d || 1 1 | 1 1 | 1 1 | 1 1\n", cast(int)T0.sizeof, cast(int)T0.alignof);
+ printf("T1 = %2d %d || 2 2 | 2 2 | 2 2 | 2 2\n", cast(int)T1.sizeof, cast(int)T1.alignof);
+ printf("T2 = %2d %d || 4 4 | 4 4 | 4 4 | 4 4\n", cast(int)T2.sizeof, cast(int)T2.alignof);
+ printf("T3 = %2d %d || 16 8 | 16 8 | 8 4 | 8 8\n", cast(int)T3.sizeof, cast(int)T3.alignof);
+ printf("T4 = %2d %d || 16 8 | 16 8 | 12 4 | 16 8\n", cast(int)T4.sizeof, cast(int)T4.alignof);
+ printf("T5 = %2d %d || 16 8 | 16 8 | 8 4 | 8 8\n", cast(int)T5.sizeof, cast(int)T5.alignof);
+ printf("S1 = %2d %d || 8 8 | 8 8 | 4 4 | 8 8\n", cast(int)S1.sizeof, cast(int)S1.alignof);
+ printf("S2 = %2d %d || 4 4 | 4 4 | 4 4 | 4 4\n", cast(int)S2.sizeof, cast(int)S2.alignof);
+ printf("S3 = %2d %d || 8 4 | 8 4 | 4 4 | 4 4\n", cast(int)S3.sizeof, cast(int)S3.alignof);
+ printf("S4 = %2d %d || 8 4 | 8 4 | 4 4 | 4 4\n", cast(int)S4.sizeof, cast(int)S4.alignof);
+ printf("S5 = %2d %d || 8 4 | 8 4 | 4 4 | 4 4\n", cast(int)S5.sizeof, cast(int)S5.alignof);
+ printf("S6 = %2d %d || 2 2 | 2 2 | 2 2 | 2 2\n", cast(int)S6.sizeof, cast(int)S6.alignof);
+ printf("S7 = %2d %d || 16 8 | 16 8 | 4 4 | 8 8\n", cast(int)S7.sizeof, cast(int)S7.alignof);
+ printf("S8 = %2d %d || 4 2 | 4 2 | 2 2 | 2 2\n", cast(int)S8.sizeof, cast(int)S8.alignof);
+ printf("S8A = %2d %d || 4 2 | 4 2 | 2 2 | 2 2\n", cast(int)S8A.sizeof, cast(int)S8A.alignof);
+ printf("S8B = %2d %d || 6 2 | 6 2 | 2 2 | 2 2\n", cast(int)S8B.sizeof, cast(int)S8B.alignof);
+ printf("S8C = %2d %d || 8 4 | 8 4 | 4 4 | 4 4\n", cast(int)S8C.sizeof, cast(int)S8C.alignof);
+ printf("S9 = %2d %d || 4 2 | 4 2 | 4 2 | 4 2\n", cast(int)S9.sizeof, cast(int)S9.alignof);
+ printf("S10 = %2d %d || 0 0 | * * | 0 1 | 0 1\n", cast(int)S10.sizeof, cast(int)S10.alignof); // MS doesn't compile
+ printf("S11 = %2d %d || 0 0 | 4 1 | 0 1 | 0 1\n", cast(int)S11.sizeof, cast(int)S11.alignof);
+ printf("S12 = %2d %d || 4 4 | 4 4 | 4 4 | 4 4\n", cast(int)S12.sizeof, cast(int)S12.alignof);
+ printf("S13 = %2d %d || 8 4 | 8 4 | 8 4 | 8 4\n", cast(int)S13.sizeof, cast(int)S13.alignof);
+ printf("S14 = %2d %d || 8 4 | 8 4 | 8 4 | 8 4\n", cast(int)S14.sizeof, cast(int)S14.alignof);
+ printf("S15 = %2d %d || 8 4 | 8 4 | 4 4 | 4 4\n", cast(int)S15.sizeof, cast(int)S15.alignof);
+ printf("S16 = %2d %d || 0 0 | 4 4 | 4 1 | 4 1\n", cast(int)S16.sizeof, cast(int)S16.alignof);
+ printf("S17 = %2d %d || 4 4 | 4 4 | 4 4 | 4 4\n", cast(int)S17.sizeof, cast(int)S17.alignof);
+ printf("S18 = %2d %d || 2 1 | 2 1 | 5 1 | 9 1\n", cast(int)S18.sizeof, cast(int)S18.alignof);
+ printf("A0 = %2d %d || 16 8 | 16 8 | 12 4 | 16 8\n", cast(int)A0.sizeof, cast(int)A0.alignof);
+ printf("A1 = %2d %d || 12 4 | 12 4 | 12 4 | 12 4\n", cast(int)A1.sizeof, cast(int)A1.alignof);
+ printf("A2 = %2d %d || 12 4 | 12 4 | 12 4 | 12 4\n", cast(int)A2.sizeof, cast(int)A2.alignof);
+ printf("A3 = %2d %d || 16 4 | 16 4 | 16 4 | 16 4\n", cast(int)A3.sizeof, cast(int)A3.alignof);
+ printf("A4 = %2d %d || 12 4 | 12 4 | 8 4 | 8 4\n", cast(int)A4.sizeof, cast(int)A4.alignof);
+ printf("A5 = %2d %d || 2 1 | 2 1 | 2 1 | 2 1\n", cast(int)A5.sizeof, cast(int)A5.alignof);
+ printf("A6 = %2d %d || 4 2 | 4 2 | 2 2 | 2 2\n", cast(int)A6.sizeof, cast(int)A6.alignof);
+ printf("A7 = %2d %d || 16 4 | 16 4 | 12 4 | 16 8\n", cast(int)A7.sizeof, cast(int)A7.alignof);
+ printf("A8 = %2d %d || 12 4 | 12 4 | 8 4 | 8 8\n", cast(int)A8.sizeof, cast(int)A8.alignof);
+ printf("A9 = %2d %d || 32 8 | 32 8 | 16 4 | 16 8\n", cast(int)A9.sizeof, cast(int)A9.alignof);
+ printf("A10 = %2d %d || 4 2 | 4 2 | 2 2 | 2 2\n", cast(int)A10.sizeof, cast(int)A10.alignof);
+ printf("A11 = %2d %d || 16 4 | 16 4 | 12 4 | 12 4\n", cast(int)A11.sizeof, cast(int)A11.alignof);
+
+ {
+ S9 s;
+ uint x;
+ *cast(uint *)&s = 0;
+ s.b = 2; s.c = 3;
+ x = *cast(uint *)&s;
+ printf("S9 = x%x\n", x);
+ }
+ {
+ S14 s = { 1, 2, 3 };
+ ulong v;
+ *cast(long *)&s = 0;
+ s.a = 1;
+ s.b = 2;
+ s.c = 3;
+ v = *cast(ulong *)&s;
+ printf("S14 = x%llx\n", v);
+ }
+ {
+ S15 s = { 1,2,3 };
+ uint x;
+ *cast(uint *)&s = 0;
+ s.a = 1; s.b = 2; s.c = 3;
+ x = *cast(uint *)&s;
+ printf("S15 = x%x\n", x);
+ }
+ {
+ S18 s;
+ printf("S18 = %d should be %d\n", cast(int)(&s.b - &s.a), is64bit() ? 8 : 4);
+ }
+ {
+ A0 s;
+ long x;
+ *cast(long *)&s = 0;
+ s.a = 1; s.b = 15;
+ x = *cast(long *)&s;
+ printf("A0 = x%llx\n", x);
+ }
+
+ return 0;
+}
--- /dev/null
+module imports.pragmainline_a;
+
+pragma(inline, true)
+int foo()
+{
+ return 1;
+}
+
+pragma(inline, true)
+auto bar()
+{
+ return &foo;
+}
+
+mixin template LengthField(alias sym)
+{
+ pragma(inline, true)
+ size_t length() const
+ {
+ return sym.length;
+ }
+}
+
+struct Data
+{
+ string data;
+ mixin LengthField!data;
+
+ pragma(inline, true)
+ int opApply(scope int delegate(const Data) dg) {
+ return dg(this);
+ }
+}
+
+pragma(inline, true)
+int value()
+{
+ return 10;
+}
--- /dev/null
+module imports.test10442a;
+
+struct S
+{
+ int x;
+ void* p;
+}
--- /dev/null
+module imports.你好;
+
+int foo()
+{
+ return 0;
+}
--- /dev/null
+struct S
+{
+ int a;
+ int b;
+ int c;
+
+ bool test(int a_, int b_, int c_) inout
+ {
+ return a_ == a && b_ == b && c_ == c;
+ }
+}
+
+__gshared S globalS = S(2, 3, 4);
+__gshared S globalS2 = S();
+immutable S immutableS = S(9, 8, 7);
+
+S getVal()
+{
+ // Returns by value
+ return globalS;
+}
+
+ref S getRef()
+{
+ // Returns by reference
+ return globalS;
+}
+
+ref S getRefRvalue() __rvalue
+{
+ // Takes ownership of globalS
+ return globalS;
+}
+
+S getImmutable()
+{
+ return immutableS;
+}
+
+__gshared int globalDtorCount;
+
+struct D
+{
+ int d;
+
+ this(D rhs)
+ {
+ d = rhs.d;
+ rhs.d = 0xdeadbeef;
+ }
+
+ this(ref D rhs)
+ {
+ d = 12345;
+ }
+
+ ~this()
+ {
+ if (d == 123456)
+ globalDtorCount++;
+ d = 0xdeadbeef;
+ }
+}
+
+__gshared D globalD = D(123456);
+__gshared D globalD2 = D(10000);
+
+void resetD()
+{
+ globalDtorCount = 0;
+ globalD.d = 123456;
+ globalD2.d = 10000;
+}
+
+D getD()
+{
+ // Returns by value
+ return globalD;
+}
+
+D getRvalueD()
+{
+ // Returns by moving globalD, but should not have reference semantics
+ // Tests if the inliner incorrectly applies reference semantics
+ return __rvalue(globalD);
+}
+
+ref D getRefD()
+{
+ // Returns by reference
+ return globalD;
+}
+
+ref D getRefRvalueD() __rvalue
+{
+ // Takes ownership of globalS
+ return globalD;
+}
+
+/************************************/
+// Test inlining of return by value
+
+S funcVal1()
+{
+ return getVal();
+}
+
+S funcVal2()
+{
+ return getRef();
+}
+
+S funcVal3()
+{
+ return getRefRvalue();
+}
+
+S funcVal4()
+{
+ return globalS.a ? immutableS : globalS;
+}
+
+S funcVal5()
+{
+ return globalS.a ? globalS : S();
+}
+
+D funcVal6()
+{
+ return getD();
+}
+
+D funcVal7()
+{
+ return getRvalueD();
+}
+
+D funcVal8()
+{
+ return getRefD();
+}
+
+D funcVal9()
+{
+ return getRefRvalueD();
+}
+
+D funcVal10()
+{
+ return __rvalue(globalS.a ? globalD : D());
+}
+
+void consumeD(D d) {}
+
+void testValueReturn()
+{
+ // Returning by value should not change the source object
+ getVal().a++;
+ assert(globalS.test(2, 3, 4));
+ getImmutable().a++;
+ assert(immutableS.test(9, 8, 7));
+ funcVal1().a++;
+ assert(globalS.test(2, 3, 4));
+ funcVal2().c++;
+ assert(globalS.test(2, 3, 4));
+ funcVal3().c++;
+ assert(globalS.test(2, 3, 4));
+ funcVal4().a++;
+ assert(immutableS.test(9, 8, 7));
+ funcVal5().a++;
+ assert(globalS.test(2, 3, 4));
+
+ consumeD(getD());
+ assert(globalD.d == 123456);
+ assert(globalDtorCount == 0);
+
+ consumeD(__rvalue(getD()));
+ assert(globalD.d == 123456);
+ assert(globalDtorCount == 0);
+
+ consumeD(getRvalueD());
+ assert(globalD.d == 0xdeadbeef);
+ assert(globalDtorCount == 1);
+ resetD();
+
+ consumeD(__rvalue(getRvalueD()));
+ assert(globalD.d == 0xdeadbeef);
+ assert(globalDtorCount == 1);
+ resetD();
+
+ consumeD(funcVal6());
+ assert(globalD.d == 123456);
+ assert(globalDtorCount == 0);
+
+ consumeD(__rvalue(funcVal6()));
+ assert(globalD.d == 123456);
+ assert(globalDtorCount == 0);
+
+ consumeD(funcVal7());
+ assert(globalD.d == 0xdeadbeef);
+ assert(globalDtorCount == 1);
+ resetD();
+
+ consumeD(__rvalue(funcVal7()));
+ assert(globalD.d == 0xdeadbeef);
+ assert(globalDtorCount == 1);
+ resetD();
+
+ consumeD(funcVal8());
+ assert(globalD.d == 123456);
+ assert(globalDtorCount == 0);
+
+ consumeD(__rvalue(funcVal8()));
+ assert(globalD.d == 123456);
+ assert(globalDtorCount == 0);
+
+ consumeD(funcVal9());
+ assert(globalD.d == 0xdeadbeef);
+ assert(globalDtorCount == 1);
+ resetD();
+
+ consumeD(__rvalue(funcVal9()));
+ assert(globalD.d == 0xdeadbeef);
+ assert(globalDtorCount == 1);
+ resetD();
+
+ consumeD(funcVal10());
+ assert(globalD.d == 123456);
+ assert(globalDtorCount == 0);
+
+ consumeD(__rvalue(funcVal10()));
+ assert(globalD.d == 123456);
+ assert(globalDtorCount == 0);
+}
+
+
+/************************************/
+// Test inlining of return by reference
+
+ref S funcRef1()
+{
+ return getRef();
+}
+
+ref S funcRef2()
+{
+
+ return globalS.a ? globalS : globalS2;
+}
+
+ref D funcRef3()
+{
+ return getRefD();
+}
+
+ref D funcRef4()
+{
+ return __rvalue(globalS.a ? globalD : globalD2);
+}
+
+void consumeRefD(ref D d)
+{
+ assert(d.d == 123456);
+ consumeD(__rvalue(d));
+ assert(d.d == 0xdeadbeef);
+}
+
+void testRefReturn()
+{
+ // Returning by ref should mutate the source object
+ funcRef1().a++;
+ assert(globalS.test(3, 3, 4));
+ funcRef2().c++;
+ assert(globalS.test(3, 3, 5));
+
+ // Passing ref to value parameter should trigger a copy
+ resetD();
+ consumeD(funcRef3());
+ assert(globalD.d == 123456);
+ assert(globalDtorCount == 0);
+
+ // ... but not if there is __rvalue
+ consumeD(__rvalue(funcRef3()));
+ assert(globalD.d == 0xdeadbeef);
+ assert(globalDtorCount == 1);
+
+ // ditto
+ resetD();
+ consumeD(funcRef4());
+ assert(globalD.d == 123456);
+ assert(globalDtorCount == 0);
+ consumeD(__rvalue(funcRef4()));
+ assert(globalD.d == 0xdeadbeef);
+ assert(globalDtorCount == 1);
+
+ resetD();
+ consumeRefD(funcRef3());
+
+ resetD();
+ consumeRefD(funcRef4());
+}
+
+/************************************/
+// Test inlining of return by rvalue reference
+
+ref S funcRvalueRef1() __rvalue
+{
+ return getRef();
+}
+
+ref S funcRvalueRef2() __rvalue
+{
+ return globalS.a ? globalS : globalS2;
+}
+
+ref D funcRvalueRef3() __rvalue
+{
+ return getRefD();
+}
+
+ref D funcRvalueRef4() __rvalue
+{
+ return globalS.a ? globalD : globalD2;
+}
+
+void consumeRvalueRefD(D d)
+{
+ assert(d.d == 123456);
+ consumeD(__rvalue(d));
+ assert(d.d == 0xdeadbeef);
+}
+
+void testRvalueRefReturn()
+{
+ // rvalue ref is ref
+ funcRvalueRef1().a++;
+ assert(globalS.test(4, 3, 5));
+ funcRvalueRef2().c++;
+ assert(globalS.test(4, 3, 6));
+
+ resetD();
+ consumeRvalueRefD(funcRvalueRef3());
+
+ resetD();
+ consumeRvalueRefD(funcRvalueRef4());
+}
+
+/************************************/
+// https://github.com/dlang/dmd/issues/22157
+class Test22157
+{
+ override string toString()
+ {
+ auto e = { return cast() super; } ();
+ return null;
+ }
+}
+
+/************************************/
+// https://issues.dlang.org/show_bug.cgi?id=22089
+
+struct S22089
+{
+ @disable this(ref return scope typeof(this) rhs);
+ @disable this(this);
+ void* cons;
+ this(int i)
+ {
+ cons = cast(void*)&this;
+ }
+ void start()
+ {
+ void* s = cast(void*)&this;
+ assert(cons == s);
+ }
+}
+
+auto fun22089()
+{
+ return S22089(42);
+}
+
+void test22089()
+{
+ auto op = fun22089();
+ op.start();
+}
+
+/************************************/
+
+void main()
+{
+ testValueReturn();
+ testRefReturn();
+ testRvalueRefReturn();
+ test22089();
+}
--- /dev/null
+// REQUIRED_ARGS: -O
+// https://github.com/dlang/dmd/issues/22069
+
+int recurse(int* i)
+{
+ L1:
+ int j = 0;
+ int* p = &j;
+
+ if (*i)
+ {
+ return recurse(p);
+ }
+ else
+ {
+ if (p is i)
+ assert(0);
+ else
+ return j;
+ }
+}
+
+void main()
+{
+ int i = 1;
+ recurse(&i);
+}
///////////////////////
+void testIntegralPromotions()
+{
+ uint uconv1(int q, ubyte p) { return p; } assert(uconv1(0,0xFF) == 0xFF);
+ uint uconv2(int q, ushort p) { return p; } assert(uconv2(0,0xFFFF) == 0xFFFF);
+ uint uconv3(int q, uint p) { return p; } assert(uconv3(0,0xFFFF_FFFF) == 0xFFFF_FFFF);
+ //uint uconv4(int q, ulong p) { return p; }
+
+ ulong uconv5(int q, ubyte p) { return p; } assert(uconv5(0,0xFF) == 0xFF);
+ ulong uconv6(int q, ushort p) { return p; } assert(uconv6(0,0xFFFF) == 0xFFFF);
+ ulong uconv7(int q, uint p) { return p; } assert(uconv7(0,0xFFFF_FFFF) == 0xFFFF_FFFF);
+ ulong uconv8(int q, ulong p) { return p; } assert(uconv8(0,0xFFFF_FFFF_FFFF_FFFF) == 0xFFFF_FFFF_FFFF_FFFF);
+
+ uint sconv1(int q, byte p) { return p; } assert(sconv1(0,cast(byte)0xFF) == 0xFFFF_FFFF);
+ uint sconv2(int q, short p) { return p; } assert(sconv2(0,cast(short)0xFFFF) == 0xFFFF_FFFF);
+ uint sconv3(int q, int p) { return p; } assert(sconv3(0,0xFFFF_FFFF) == 0xFFFF_FFFF);
+ //uint sconv4(int q, long p) { return p; }
+
+ ulong sconv5(int q, byte p) { return p; } assert(sconv5(0,cast(byte)0xFF) == 0xFFFF_FFFF_FFFF_FFFF);
+ ulong sconv6(int q, short p) { return p; } assert(sconv6(0,cast(short)0xFFFF) == 0xFFFF_FFFF_FFFF_FFFF);
+ ulong sconv7(int q, int p) { return p; } assert(sconv7(0,0xFFFF_FFFF) == 0xFFFF_FFFF_FFFF_FFFF);
+ ulong sconv8(int q, long p) { return p; } assert(sconv8(0,0xFFFF_FFFF_FFFF_FFFF) == 0xFFFF_FFFF_FFFF_FFFF);
+}
+
+///////////////////////
+
// https://github.com/dlang/dmd/pull/11441
long sdiv1(long l)
int main()
{
+ testIntegralPromotions();
+
// All the various integer divide tests
testsdiv2();
testulldiv();
foo14846({ S* p = new S(1); });
foo14846({ S[] a = [S()]; });
foo14846({ S[] a = [S(1)]; });
+
+ void named1()
+ {
+ S s;
+ }
+
+ foo14846(&named1);
+
+ void named2()
+ {
+ S s = S(1);
+ }
+
+ foo14846(&named2);
+
+ void named3()
+ {
+ S[3] s;
+ }
+
+ foo14846(&named3);
}
/*******************************************/
--- /dev/null
+// REQUIRED_ARGS: -betterC
+
+/* This test triggers an address miscalculation bug in DMD 2.111
+ * with -inline.
+ * May not crash if the data segment has a different layout,
+ * e.g. when pasted into another file.
+ */
+
+struct S {
+ int i;
+}
+
+__gshared S gs = S(1);
+ref S get()
+{
+ return gs;
+}
+
+extern (C)
+int main()
+{
+ (get().i ? get() : get()).i++;
+ return 0;
+}
// speculative opCall instantiation for diagnostic message should not cause false errors
}
+/**************************************/
+// https://github.com/dlang/dmd/issues/20927
+
+struct NoStatic0 { auto opCall() => 2; }
+struct NoStatic1 { int x; NoStatic1 opCall() => NoStatic1(3); }
+
+struct Yes0 { static opCall() => 2; }
+struct Yes1 { static opCall()() => 2; }
+struct Yes2
+{
+ auto call()(int x) => this.init;
+ template call() { static call() => 2; }
+ alias opCall = call;
+}
+
+void test20927()
+{
+ assert(NoStatic0() == NoStatic0.init);
+ assert(NoStatic1()() == NoStatic1(3));
+
+ assert(Yes0() == 2);
+ assert(Yes1() == 2);
+ assert(Yes2() == 2);
+}
+
/**************************************/
void main()
test3c();
test4();
test12070();
+ test20927();
}
/***************************************************/
+// https://github.com/dlang/dmd/issues/21920
+
+struct S21920
+{
+ void funB2(char a) {}
+ void funB(int b) {}
+ alias funB = funB2;
+ void funB()(float t) {}
+}
+
+void test21920()
+{
+ static assert(__traits(getOverloads, S21920, "funB", true).length == 3);
+}
+
+/***************************************************/
+
int main()
{
test1528a();
test14965();
test21481();
test21522();
+ test21920();
printf("Success\n");
return 0;
--- /dev/null
+// REQUIRED_ARGS: -wi
+// EXTRA_FILES: imports/pragmainline_a.d
+/* TEST_OUTPUT:
+---
+---
+*/
+
+
+import imports.pragmainline_a;
+
+auto anonclass()
+{
+ return new class {
+ pragma(inline, true)
+ final size_t foo()
+ {
+ return value();
+ }
+ };
+}
+
+auto testAlwaysInline()
+{
+ size_t var;
+
+ foreach (d; Data("string"))
+ {
+ var = d.length();
+ }
+
+ assert(var == 6);
+
+ var = anonclass().foo();
+
+ assert(var == 10);
+
+ auto nested = (size_t i) {
+ return i - value();
+ };
+
+ var = nested(var);
+
+ assert(var == 0);
+}
+
+void main()
+{
+ immutable baz = () => 1;
+ assert(foo() == bar()());
+ assert(foo() == baz());
+ assert(bar()() == baz());
+
+ testAlwaysInline();
+}
--- /dev/null
+/* The test related to https://github.com/dlang/dmd/issues/22322
+ * The issue title:
+ * "converting real to float uses double rounding for 64-bit code
+ * causing unexpected results"
+ */
+pragma(inline, false)
+void test(real r)
+{
+ assert(r == 0x1.000002fffffffcp-1);
+ double d = r;
+ assert(d == 0x1.000003p-1);
+ float f = r;
+ assert(f == 0x1.000002p-1);
+ float fd = d;
+ assert(fd == 0x1.000004p-1);
+ real rd = d;
+ assert(rd == 0x1.000003p-1);
+ float frd = rd;
+ assert(frd == 0x1.000004p-1);
+}
+
+void main()
+{
+ static if (real.sizeof > 8)
+ {
+ test(0x1.000002fffffffcp-1);
+ }
+}
-/* PERMUTE_ARGS: -preview=rvaluerefparam
+/* REQUIRED_ARGS: -preview=rvaluerefparam
/* testing __rvalue */
import core.stdc.stdio;
assert(s.p.s == "hello");
}
+/********************************/
+// https://github.com/dlang/dmd/issues/22111
+
+__gshared int copyCount11;
+__gshared int moveCount11;
+__gshared int dtorCount11;
+
+struct S11
+{
+ int i;
+ this(S11 rhs) { moveCount11++; }
+ this(ref S rhs) { copyCount11++; }
+ ~this() { dtorCount11++; }
+}
+
+__gshared S11 s11obj;
+
+ref S11 refS11() { return s11obj; }
+ref S11 moveS11() __rvalue { return s11obj; }
+S11 copyRefS11() { return __rvalue(refS11()); }
+S11 copyMoveS11() { return moveS11(); }
+
+void test11()
+{
+ copyRefS11();
+ assert(copyCount11 == 0 && moveCount11 == 1 && dtorCount11 == 2);
+ moveCount11 = dtorCount11 = 0;
+ copyMoveS11();
+ assert(copyCount11 == 0 && moveCount11 == 1 && dtorCount11 == 2);
+}
+
+/********************************/
+
+struct S12{
+ S12* ptr;
+ this(int) { ptr = &this; }
+ this(ref inout S12) { ptr = &this; }
+ this(S12) { ptr = &this; }
+}
+
+struct V12
+{
+ S12 s;
+ this(int) { s = S12(1); }
+}
+
+S12 foo12()
+{
+ return __rvalue(V12(1).s);
+}
+
+void test12()
+{
+ S12 s = foo12();
+ assert(&s == s.ptr);
+}
+
/********************************/
int main()
test7();
test8();
test9();
+ test10();
+ test11();
+ test12();
return 0;
}
// https://github.com/dlang/dmd/issues/21690
void testMultiDim()
{
- // int[int][int] aa1 = [ 1: [2: 3] ]; // Error: invalid value `[2:3]` in initializer (see #17804)
+ int[int][int] aa1 = [ 1: [2: 3] ]; // Error: invalid value `[2:3]` in initializer (see #17804)
int[int][int] aa2 = ([ 1: [2: 3] ]); // workaround
auto aa3 = [ 1: [2: 3] ]; // works, too
static auto aa4 = [ 1: [2: 3] ]; // Error: internal compiler error: failed to detect static initialization of associative array
+ assert(aa1 == aa2);
assert(aa2 == aa3);
assert(aa3 == aa4);
}
/////////////////////////////////////////////
+// https://github.com/dlang/dmd/issues/17804
+void testMultiDimInit()
+{
+ static struct D
+ {
+ string[string][string] aa;
+ this(string[string][string] _aa) { aa = _aa; }
+ }
+ static struct S
+ {
+ //Error: not an associative array initializer
+ D a = ["fdsa": ["fdsafd": "fdsfa"]];
+
+ // OK
+ D b = (["fdsa": ["fdsafd": "fdsfa"]]);
+ }
+ immutable string[7] a = [
+ 3 : null,
+ 2 : "D",
+ 1 : "C",
+ ];
+ static int[char][char] arr = ['A' : ['B': 0]]; // error
+ assert(arr.length == 1);
+ assert(arr['A'] == ['B':0]);
+
+ S s;
+ assert(s.a.aa["fdsa"]["fdsafd"] == "fdsfa");
+ assert(s.b.aa["fdsa"]["fdsafd"] == "fdsfa");
+}
+
+/////////////////////////////////////////////
+
void main()
{
testSimple();
testStaticArray();
testClassLiteral();
testMultiDim();
+ testMultiDimInit();
}
--- /dev/null
+void refCounted(ProcessPipes val)
+{
+ val = ProcessPipes.init;
+}
+
+struct ProcessPipes
+{
+ char[33] arr;
+ ~this() @safe {}
+}
+
+void main()
+{
+ refCounted(ProcessPipes.init);
+}
void test12186()
{
- immutable int[][] mat;
+ immutable int[][] mat = new int[][](1);
mat.map_front12186!((in r) => 0); // OK
mat.map_front12186!((const r) => 0); // OK
--- /dev/null
+// EXTRA_FILES: imports/test10442a.d
+module test10442;
+import imports.test10442a;
+
+struct T
+{
+ int x;
+ void* p;
+}
+
+void main()
+{
+ // assumes enum RTInfo(T) merges identical bitmaps
+ assert(typeid(T).rtInfo !is null); // ok
+ assert(typeid(S).rtInfo is typeid(T).rtInfo); // fails
+}
--- /dev/null
+// EXTRA_SOURCES: imports/你好.d
+
+import imports.你好;
+
+int main()
+{
+ return foo();
+}
--- /dev/null
+// https://github.com/dlang/dmd/issues/20301
+void main()
+{
+}
+
+string genline(string i)
+{
+ return "struct S" ~ i ~ " { S" ~ i ~ " *p; int x, y, z; } S" ~ i ~ "* foo" ~ i ~ "() { return null; }";
+}
+
+static foreach(i; 0..12288)
+ mixin(genline(i.stringof));
--- /dev/null
+// https://github.com/dlang/dmd/issues/21478
+
+// Test struct that implements "rule of five"
+
+struct S21478
+{
+ // 1. destructor
+ ~this() { }
+ // 2. copy constructor
+ this(ref return scope S21478) { assert(0); }
+ // 3. copy assign
+ void opAssign(const ref S21478) { }
+ // 4. move constructor
+ this(return scope S21478) { assert(0); }
+ // 5. move assign
+ void opAssign(const S21478) { assert(0); }
+}
+
+void main()
+{
+ S21478 sa, sb;
+ sb = sa; // Should call 3, not 2 + 5.
+}
--- /dev/null
+// https://github.com/dlang/dmd/issues/21478
+
+// Test struct that implements copy constructor follows rvalue expression spec:
+// If both ref and non-ref parameter overloads are present,
+// an rvalue is preferably matched to the non-ref parameters,
+// and an lvalue is preferably matched to the ref parameter.
+// An RvalueExpression will preferably match with the non-ref parameter.
+
+struct S21478
+{
+ this(ref return scope S21478) { assert(0); }
+}
+
+struct P21478
+{
+ int plain_old_data;
+}
+
+int overload(const S21478) { return 1; }
+int overload(const ref S21478) { return 2; }
+
+int overload(const P21478) { return 1; }
+int overload(const ref P21478) { return 2; }
+
+void main()
+{
+ S21478 s;
+ assert(overload(s) == 2);
+ assert(overload(S21478()) == 1);
+ assert(overload(__rvalue(s)) == 1);
+
+ P21478 p;
+ assert(overload(p) == 2);
+ assert(overload(P21478()) == 1);
+ assert(overload(__rvalue(p)) == 1);
+}
--- /dev/null
+// https://github.com/dlang/dmd/issues/21757
+
+struct S
+{
+ void*[300_000] arr; // generates stupidly large symbols for RTImfoImpl!() that crash MS link.exe
+}
+
+void main()
+{
+}
--- /dev/null
+struct S {
+ ~this() {}
+}
+
+__gshared S s = S();
+
+ref S refS() {
+ return s;
+}
+
+void takeS(S s2) {
+ assert(&s !is &s2);
+}
+
+void main()
+{
+ takeS(refS());
+}
+// PERMUTE_ARGS: -fPIC -inline -release -g -O
+
module test;
import core.stdc.stdio;
/*******************************************/
+struct S67
+{
+ S67* ptr;
+
+ this(int)
+ {
+ pragma(inline, false);
+ ptr = &this;
+ }
+
+ @disable this(this);
+}
+
+pragma(inline, false)
+S67 make67()
+{
+ return S67(1);
+}
+
+__gshared int i67;
+
+S67 f67()
+out (s; s.ptr == &s)
+{
+ i67++;
+ return make67();
+}
+
+void test67()
+{
+ S67 s = f67();
+ assert(s.ptr == &s);
+}
+
+/*******************************************/
+// https://github.com/dlang/dmd/issues/22160
+
+struct Vector22160(T)
+{
+ T[] _payload;
+
+ ~this() const nothrow {}
+
+ @property size_t length() const
+ {
+ return _payload.length;
+ }
+
+ @property T* ptr() inout {
+ return cast(T*) _payload.ptr;
+ }
+}
+
+struct DEREncoder
+{
+ Vector22160!ubyte getContentsUnlocked()
+ {
+ return Vector22160!ubyte();
+ }
+
+ Vector22160!ubyte m_contents;
+}
+
+Vector22160!ubyte putInSequence()(const auto ref Vector22160!ubyte contents)
+{
+ return DEREncoder().getContentsUnlocked();
+}
+
+void foo22160(T)(Vector22160!T*, T* ptr, size_t)
+{
+ assert(ptr is null);
+}
+
+void bar22160(Vector22160!ubyte val)
+{
+ ulong[16] padding = 0x1234567890123456UL;
+ foo22160(&val, val.ptr, val.length);
+}
+
+void test22160()
+{
+ bar22160(putInSequence(Vector22160!ubyte()));
+}
+
+/*******************************************/
+// https://github.com/dlang/dmd/issues/22292
+
+struct Vector22292(T)
+{
+ T[] payload = [1];
+
+ ~this() nothrow
+ {
+ payload = null;
+ }
+
+ void check(ref Vector22292 val)
+ {
+ assert(val.payload.length == 1);
+ }
+}
+
+struct DEREncoder2
+{
+ ref Vector22292!int getContentsUnlocked()
+ {
+ return m_contents;
+ }
+
+ Vector22292!int m_contents;
+}
+
+void test22292()
+{
+ Vector22292!int o;
+ o.check(DEREncoder2().getContentsUnlocked());
+}
+
+/*******************************************/
+
void main()
{
printf("Start\n");
test64();
test65();
test18576();
+ test67();
+ test22160();
+ test22292();
printf("Success\n");
}
{
ubyte a = 10;
float f = 6;
- ubyte b = a += f;
+ ubyte b = a += cast(ubyte)f;
assert(b == 16);
}
--- /dev/null
+struct S
+{
+ string[] delegate() dg;
+}
+
+S s = {
+ dg: () => ["hello"] // SEGFAULT without explicit `delegate`
+};
+
+void main()
+{
+ auto result = s.dg();
+ assert(result.length == 1);
+ assert(result[0] == "hello");
+}
--- /dev/null
+void fn(int* x, real[1] arr)
+{
+ auto y = *x; // should not segfault
+ assert(y == 42, "x parameter corrupted");
+ assert(arr[0] == 1.0, "arr (real[1]) corrupted");
+}
+
+void main()
+{
+ real[1] arr = [1.0];
+ int x = 42;
+ fn(&x, arr);
+
+ assert(x == 42, "x value corrupted");
+ assert(arr[0] == 1.0, "arr (real[1]) value corrupted");
+}
foo();
}
+/************************************************/
+void foo() @safe
+{
+ immutable key = 1;
+ int[int] aa;
+ aa[key] = 123;
+}
+
/************************************************/
void testEvaluationOrder()
assert(m.get(0, 1) == 1);
}
+/***************************************************/
+// regressions after converting AA to template
+void test21066()
+{
+ const(int) getValue() { return 0; }
+ int[] arr;
+ arr = [getValue()]; // works
+ int[][string] aa;
+ aa["a"] = [getValue()]; // fails: cannot implicitly convert expression `__aaval` of type `const(int)[]` to `int[]`
+
+ string[int[]] aa2;
+ aa2[[getValue()]] = "a"; // no problem because key type is automatically const(int)[]
+}
+
/***************************************************/
void main()
testTypeinfo();
test12220();
test12403();
+ test21066();
}
void test12500()
{
size_t foo;
- ++foo *= 1.5; // Rewrite to: (foo += 1) *= 1.5;
+ ++foo *= cast(size_t)1.5; // Rewrite to: (foo += 1) *= 1.5;
}
/***************************************************/
assert(init == (cast(void*) ac)[0 .. init.length]);
}
+void testFuncParam()
+{
+ // https://github.com/dlang/dmd/issues/22135
+ struct S {}
+
+ void foo(T)(T _) {}
+
+ void bar()
+ {
+ foo(__traits(initSymbol, S));
+ }
+}
+
void main()
{
testZero();
testAlignedStruct();
testAlignedClass();
testExternCppClass();
+ testFuncParam();
}
/************************************************/
+// https://github.com/dlang/dmd/issues/19788
+@10 void f(@20 int x)
+{
+ static assert([__traits(getAttributes, x)] == [20]);
+}
+
/************************************************/
// https://issues.dlang.org/show_bug.cgi?id=15804
lappend out "-Wdeprecated"
lappend out "-Wno-error"
+ } elseif [regexp -- {^-edition=(\w+)} $arg pattern value] {
+ lappend out "-std=d[string tolower $value]"
+
} elseif [regexp -- {^-extern-std=([\w+]+)} $arg pattern value] {
lappend out "-fextern-std=$value"
-e1f6680f50d147846316c2fa3363461a2aa7ac1d
+24a41073c2dbf456d4f7a6fe6a7965d6ce6fc5cb
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
-/* This D file is implicitly imported by all ImportC source files.
- * It provides definitions for C compiler builtin functions and declarations.
- * The purpose is to make it unnecessary to hardwire them into the compiler.
- * As the leading double underscore suggests, this is for internal use only.
- *
- * Copyright: Copyright D Language Foundation 2022-2025
- * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright
- * Source: $(DRUNTIMESRC __importc_builtins.di)
- */
+/* GDC -- D front-end for GCC
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ GCC is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3, or (at your option) any later
+ version.
-module __builtins;
-
-/* gcc relies on internal __builtin_xxxx functions and templates to
- * accomplish <stdarg.h>. D does the same thing with templates in core.stdc.stdarg.
- * Here, we redirect the gcc builtin declarations to the equivalent
- * ones in core.stdc.stdarg, thereby avoiding having to hardwire them
- * into the D compiler.
- */
-
-alias va_list = imported!"core.stdc.stdarg".va_list;
-
-version (Posix)
-{
- version (X86_64)
- alias __va_list_tag = imported!"core.stdc.stdarg".__va_list_tag;
-}
-
-alias __builtin_va_start = imported!"core.stdc.stdarg".va_start;
-
-alias __builtin_va_end = imported!"core.stdc.stdarg".va_end;
-
-alias __builtin_va_copy = imported!"core.stdc.stdarg".va_copy;
-
-/* dmd's ImportC rewrites __builtin_va_arg into an instantiation of va_arg
- */
-alias va_arg = imported!"core.stdc.stdarg".va_arg;
-
-version (CRuntime_Microsoft)
-{
- //https://docs.microsoft.com/en-us/cpp/cpp/int8-int16-int32-int64?view=msvc-170
- alias __int8 = byte;
- alias __int16 = short;
- alias __int32 = int;
- alias __int64 = long;
-}
-
-/*********** floating point *************/
-
-/* https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
- */
+ GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
-version (DigitalMars)
-{
- immutable float __nan = float.nan;
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>.
+*/
- float __builtin_nanf()(char*) { return float.nan; }
+/* This module is implicitly imported by all ImportC source files. */
- double __builtin_inf()() { return double.infinity; }
- float __builtin_inff()() { return float.infinity; }
- real __builtin_infl()() { return real.infinity; }
-
- alias __builtin_huge_val = __builtin_inf;
- alias __builtin_huge_valf = __builtin_inff;
- alias __builtin_huge_vall = __builtin_infl;
-
- alias __builtin_fabs = imported!"core.stdc.math".fabs;
- alias __builtin_fabsf = imported!"core.stdc.math".fabsf;
- alias __builtin_fabsl = imported!"core.stdc.math".fabsl;
-
- ushort __builtin_bswap16()(ushort value)
- {
- return cast(ushort) (((value >> 8) & 0xFF) | ((value << 8) & 0xFF00U));
- }
-
- uint __builtin_bswap32()(uint value)
- {
- import core.bitop;
- return core.bitop.bswap(value);
- }
-
- ulong __builtin_bswap64()(ulong value)
- {
- import core.bitop;
- return core.bitop.bswap(value);
- }
-
- uint __builtin__popcount()(ulong value)
- {
- import core.bitop;
- return core.bitop._popcnt(value);
- }
-
- // Lazily imported on first use
- private alias c_long = imported!"core.stdc.config".c_long;
-
- // Stub these out to no-ops
- int __builtin_constant_p(T)(T exp) { return 0; } // should be something like __traits(compiles, enum X = expr)
- c_long __builtin_expect()(c_long exp, c_long c) { return exp; }
- void* __builtin_assume_aligned()(const void* p, size_t align_, ...) { return cast(void*)p; }
-
- // https://releases.llvm.org/13.0.0/tools/clang/docs/LanguageExtensions.html#builtin-assume
- void __builtin_assume(T)(lazy T arg) { }
+module __builtins;
- /* Header on macOS for arm64 references this.
- * Don't need to implement it, it just needs to compile
- */
- align (16) struct __uint128_t
- {
- ulong a, b;
- }
-}
+public import gcc.builtins;
{
version (D_InlineAsm_X86_64)
{
- if (!__ctfe)
- return mulu(x, ulong(y), overflow);
+ return __ctfe ? mulu_generic(x, y, overflow)
+ : mulu(x, ulong(y), overflow);
+ }
+ else
+ {
+ return mulu_generic(x, y, overflow);
}
-
- ulong r = x * y;
- if (x >> 32 &&
- r / x != y)
- overflow = true;
- return r;
}
/// ditto
return r;
}
+private ulong mulu_generic()(ulong x, uint y, ref bool overflow)
+{
+ pragma(inline, true)
+ ulong r = x * y;
+ if (x >> 32 &&
+ r / x != y)
+ overflow = true;
+ return r;
+}
+
@betterC
unittest
{
// behavior should occur within the handler itself. This delegate
// is __gshared for now based on the assumption that it will only
// set by the main thread during program initialization.
-private __gshared AssertHandler _assertHandler = null;
+private __gshared
+{
+ AssertHandler _assertHandler = null;
+ FilterThreadThrowableHandler _filterThreadThrowableHandler = null;
+}
/**
_assertHandler = handler;
}
+/**
+Gets/sets the Throwable filter function for threads. null means no handler is called.
+*/
+alias FilterThreadThrowableHandler = void function(ref Throwable) @system nothrow;
+
+/// ditto
+@property FilterThreadThrowableHandler filterThreadThrowableHandler() @trusted nothrow @nogc
+{
+ return _filterThreadThrowableHandler;
+}
+
+/// ditto
+@property void filterThreadThrowableHandler(FilterThreadThrowableHandler handler) @trusted nothrow @nogc
+{
+ _filterThreadThrowableHandler = handler;
+}
///////////////////////////////////////////////////////////////////////////////
// Overridable Callbacks
* (from the registry hook) should assume this function may not be called
* on termination if the GC is never initialized.
*
- * Most times, this is called from the thread that is the given thread
- * reference, but it's possible the `thread` parameter is not the same as
- * the current thread.
- *
- * There is no guarantee the thread is still registered as
- * `ThreadBase.getThis()`.
+ * This function is only called from a thread that was started from the D
+ * runtime, and is terminating. The GC can assume the thread is still in
+ * the list of running threads and can be paused for a GC cycle. This is
+ * not called for the main thread which initialized the GC.
*/
void cleanupThread(ThreadBase thread) nothrow @nogc;
}
return c;
}
+/*****************************
+ * Absolute value
+ * Note: This is a signed operation.
+ * Params:
+ * c = Cent to get absolute value of
+ * Returns:
+ * absolute value of c
+ */
+pure
+Cent abs(Cent c)
+{
+ return (cast(I)c.hi < 0) ? neg(c) : c;
+}
+
/*****************************
* Increment
* Params:
/*****************************
* Arithmetic shift right n bits
+ *
+ * Note: This is a signed shift (preserves the sign bit).
+ *
* Params:
* c = Cent to shift
* n = number of bits to shift
/****************************
* Multiply c1 * c2.
+ * Note: The algorithm is identical for both signed and unsigned multiplication.
* Params:
* c1 = operand 1
* c2 = operand 2
/****************************
* Signed divide c1 / c2.
+ * Note: Performs signed division. Use udiv() for unsigned division.
* Params:
* c1 = dividend
* c2 = divisor
return udivmod(c1, c2, modulus);
}
+/*****************************
+ * Unsigned remainder c1 % c2.
+ * Params:
+ * c1 = dividend
+ * c2 = divisor
+ * Returns:
+ * remainder c1 % c2
+ */
+pure
+Cent urem(Cent c1, Cent c2)
+{
+ Cent modulus;
+ udivmod(c1, c2, modulus);
+ return modulus;
+}
+
+/*****************************
+ * Signed remainder c1 % c2.
+ * Params:
+ * c1 = dividend
+ * c2 = divisor
+ * Returns:
+ * remainder c1 % c2
+ */
+pure
+Cent rem(Cent c1, Cent c2)
+{
+ Cent modulus;
+ divmod(c1, c2, modulus);
+ return modulus;
+}
+
/****************************
* If c1 > c2 unsigned
* Params:
assert(ror(C7_9, 1) == ror1(C7_9));
assert(rol(C7_9, 0) == C7_9);
assert(ror(C7_9, 0) == C7_9);
+
+ // Test abs()
+ assert(abs(Cm10) == C10);
+ assert(abs(C10) == C10);
+ assert(abs(C0) == C0);
+
+ // Test rem/urem
+ assert(rem(C10, C3) == C1); // 10 % 3 = 1
+ assert(urem(C10, C3) == C1);
+ assert(rem(Cm10, C3) == Cm1); // -10 % 3 = -1
+
+ assert(urem(Cm10, C3) == C0);
}
version (D_TypeInfo)
{
import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure;
- mixin(TraceHook!(Tarr.stringof, "_d_arrayappendcTX"));
+ mixin(TraceHook!("Tarr", "_d_arrayappendcTX"));
return _d_arrayappendcTX(px, n);
}
version (D_TypeInfo)
{
import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure;
- mixin(TraceHook!(Tarr.stringof, "_d_arrayappendT"));
+ mixin(TraceHook!("Tarr", "_d_arrayappendT"));
return _d_arrayappendT(x, y);
}
}
enum sizeelem = T.sizeof;
- enum hasPostblit = __traits(hasMember, T, "__postblit");
- enum hasEnabledPostblit = hasPostblit && !__traits(isDisabled, T.__postblit);
+ enum hasPostblit = __traits(hasMember, T, "__xpostblit");
+ enum hasEnabledPostblit = hasPostblit && !__traits(isDisabled, T.__xpostblit);
bool overflow = false;
const newsize = mulu(sizeelem, newlength, overflow);
* Returns:
* A newly allocated array that contains all the elements from `froms`.
*/
-Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted
+Tret _d_arraycatnTX(Tret: Tret_El[], Tret_El, Tarr...)(auto ref Tarr froms) @trusted
{
import core.exception : onOutOfMemoryError;
import core.internal.array.utils : __arrayAlloc;
return res; // Return an empty array if no elements are present
// Allocate memory for mutable arrays using __arrayAlloc
- res = cast(Tret) __arrayAlloc!(UnqT)(elemSize * totalLen);
+ res = (cast(Tret_El*) &__arrayAlloc!(UnqT)(elemSize * totalLen)[0])[0 .. totalLen];
if (res.ptr is null)
onOutOfMemoryError(); // Abort if allocation fails
version (D_TypeInfo)
{
import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure;
- mixin(TraceHook!(Tarr.stringof, "_d_arraycatnTX"));
+ mixin(TraceHook!("Tarr", "_d_arraycatnTX"));
import core.lifetime: forward;
return _d_arraycatnTX!Tret(forward!froms);
version (D_ProfileGC)
{
/**
- * TraceGC wrapper around $(REF _d_newitemT, core,lifetime).
+ * TraceGC wrapper around $(REF _d_newarrayT, core,internal.array.construction).
*/
T[] _d_newarrayTTrace(T)(size_t length, bool isShared, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted
{
version (D_TypeInfo)
{
import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
- mixin(TraceHook!(T.stringof, "_d_newarrayT"));
+ mixin(TraceHook!("T", "_d_newarrayT"));
return _d_newarrayT!T(length, isShared);
}
else
assert(0, "Cannot create new array if compiling without support for runtime type information!");
}
+
+ /**
+ * TraceGC wrapper around $(REF _d_newarrayU, core,internal.array.construction).
+ */
+ T[] _d_newarrayUTrace(T)(size_t length, bool isShared, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted
+ {
+ version (D_TypeInfo)
+ {
+ import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
+ mixin(TraceHook!("T", "_d_newarrayU"));
+
+ return _d_newarrayUPureNothrow!T(length, isShared);
+ }
+ else
+ assert(0, "Cannot create new array if compiling without support for runtime type information!");
+ }
}
/**
version (D_TypeInfo)
{
import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
- mixin(TraceHook!(T.stringof, "_d_newarraymTX"));
+ mixin(TraceHook!("T", "_d_newarraymTX"));
return _d_newarraymTX!(Tarr, T)(dims, isShared);
}
return (cast(PureType)&isEqual!(T1,T2))(lhs, rhs, lhs.length);
}
+pragma(inline, true)
+bool __equals(T1, T2, size_t N)(scope ref T1[N] lhs, scope T2[] rhs) @trusted {
+ return __equals(lhs[], rhs);
+}
+
+pragma(inline, true)
+bool __equals(T1, T2, size_t N)(scope T1[] lhs, scope ref T2[N] rhs) @trusted {
+ return __equals(lhs, rhs[]);
+}
+
+pragma(inline, true)
+bool __equals(T1, T2, size_t N, size_t M)(scope ref T1[N] lhs, scope ref T2[M] rhs) @trusted {
+ return __equals(lhs[], rhs[]);
+}
+
/******************************
* Helper function for __equals().
* Outlined to enable __equals() to be inlined, as dmd cannot inline loops.
/**
* TraceGC wrapper generator around the runtime hook `Hook`.
* Params:
- * Type = The type of hook to report to accumulate
+ * TypeIdent = The symbol of the type of hook to report to accumulate
* Hook = The name hook to wrap
*/
- template TraceHook(string Type, string Hook)
+ template TraceHook(string TypeIdent, string Hook)
{
const char[] TraceHook = q{
import core.internal.array.utils : gcStatsPure, accumulatePure;
pragma(inline, false);
- string name = } ~ "`" ~ Type ~ "`;" ~ q{
+ string name = } ~ TypeIdent ~ q{.stringof;
// FIXME: use rt.tracegc.accumulator when it is accessable in the future.
ulong currentlyAllocated = gcStatsPure().allocatedInCurrentThread;
{
version (D_TypeInfo)
{
- mixin(TraceHook!(T.stringof, __traits(identifier, Hook)));
+ mixin(TraceHook!("T", __traits(identifier, Hook)));
return Hook(parameters);
}
else
*/
void* _d_cast(To, From)(From o) @trusted
{
- static if (is(From == class) && is(To == interface))
+ static if (is(From == To))
+ {
+ return *cast(void**) &o;
+ }
+ else static if (is(From == class) && is(To == interface))
{
return _d_dynamic_cast!To(o);
}
-
- static if (is(From == class) && is(To == class))
+ else static if (is(From == class) && is(To == class))
{
- static if (is(From FromSupers == super) && is(To ToSupers == super))
- {
- /* Check for:
- * class A { }
- * final class B : A { }
- * ... cast(B) A ...
- */
- // Multiple inheritance is not allowed, so we can safely assume
- // that the second super can only be an interface.
- static if (__traits(isFinalClass, To) && is(ToSupers[0] == From) &&
- ToSupers.length == 1 && FromSupers.length <= 1)
- {
- return _d_paint_cast!To(o);
- }
- }
- static if (is (To : From))
+ /* Check for:
+ * class A { }
+ * final class B : A { }
+ * ... cast(B) A ...
+ */
+ /* Multiple inheritance is not allowed, so we can safely assume
+ * that the second super can only be an interface.
+ */
+ static if (is(From FromSupers == super) && is(To ToSupers == super) &&
+ __traits(isFinalClass, To) && is(ToSupers[0] == From) &&
+ ToSupers.length == 1 && FromSupers.length <= 1)
{
- static if (is (To == From))
- {
- return cast(void*)o;
- }
- else
- {
- return _d_class_cast!To(o);
- }
+ return _d_paint_cast!To(o);
}
-
- return null;
- }
-
- static if (is(From == interface))
- {
- static if (is(From == To))
+ else static if (is (To : From))
{
- return cast(void*)o;
+ return _d_class_cast!To(o);
}
else
{
- return _d_interface_cast!To(cast(void*)o);
+ return null;
}
}
+ else static if (is(From == interface))
+ {
+ return _d_interface_cast!To(cast(void*)o);
+ }
else
{
return null;
assert(_d_cast!I1(ci) !is null); // I3(c) to I1
assert(_d_interface_cast!I1(cast(void*)ci) !is null);
}
+
+// https://github.com/dlang/dmd/issues/21646
+@system pure unittest {
+ static class C {
+ @disable void opCast(T)();
+ }
+
+ const(C) const_c = new C();
+ C mutable_c = cast() const_c;
+}
{
// Overflow ary.length.
auto ary = Array!size_t(cast(size_t*)0xdeadbeef, -1);
+ scope(failure)
+ {
+ // prevent destructor from cleaning up invalid memory.
+ ary._length = 0;
+ ary._ptr = null;
+ }
+
ary.insertBack(0);
}
catch (OutOfMemoryError)
{
// Overflow requested memory size for common.xrealloc().
auto ary = Array!size_t(cast(size_t*)0xdeadbeef, -2);
+ scope(failure)
+ {
+ // prevent destructor from cleaning up invalid memory.
+ ary._length = 0;
+ ary._ptr = null;
+ }
ary.insertBack(0);
}
catch (OutOfMemoryError)
return cast(const(ubyte)[])arr;
}
-@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof > 1)
+private const(ubyte)[] toUbyte_array_ctfe(T)(return scope const T[] arr)
{
- if (__ctfe)
+ pragma(inline, false);
+ ubyte[] ret = ctfe_alloc(T.sizeof * arr.length);
+ static if (is(T EType == enum)) // Odd style is to avoid template instantiation in most cases.
+ alias E = OriginalType!EType;
+ else
+ alias E = T;
+ static if (is(E == struct) || is(E == union) || __traits(isStaticArray, E) || !is(typeof(arr[0] is null)))
{
- ubyte[] ret = ctfe_alloc(T.sizeof * arr.length);
- static if (is(T EType == enum)) // Odd style is to avoid template instantiation in most cases.
- alias E = OriginalType!EType;
- else
- alias E = T;
- static if (is(E == struct) || is(E == union) || __traits(isStaticArray, E) || !is(typeof(arr[0] is null)))
+ size_t offset = 0;
+ foreach (ref cur; arr)
{
- size_t offset = 0;
- foreach (ref cur; arr)
- {
- ret[offset .. offset + T.sizeof] = toUbyte(cur)[0 .. T.sizeof];
- offset += T.sizeof;
- }
+ ret[offset .. offset + T.sizeof] = toUbyte(cur)[0 .. T.sizeof];
+ offset += T.sizeof;
}
- else
- {
- foreach (cur; arr)
- assert(cur is null, "Unable to compute byte representation of non-null pointer at compile time");
- }
- return ret;
}
else
{
- return (cast(const(ubyte)*)(arr.ptr))[0 .. T.sizeof*arr.length];
+ foreach (cur; arr)
+ assert(cur is null, "Unable to compute byte representation of non-null pointer at compile time");
}
+ return ret;
}
@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector))
+const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof > 1)
+{
+ pragma(inline, true);
+ return __ctfe ? toUbyte_array_ctfe(arr)
+ : (cast(const(ubyte)*)(arr.ptr))[0 .. T.sizeof*arr.length];
+}
+
+private const(ubyte)[] toUbyte_integral_ctfe(T)(const return ref scope T val)
{
+ pragma(inline, false);
static if (T.sizeof == 1)
{
- pragma(inline, true);
- if (__ctfe)
- {
- ubyte[] result = ctfe_alloc(1);
- result[0] = cast(ubyte) val;
- return result;
- }
- else
- {
- return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
- }
+ ubyte[] result = ctfe_alloc(1);
+ result[0] = cast(ubyte) val;
+ return result;
}
- else if (__ctfe)
+ else
{
import core.internal.traits : Unqual;
ubyte[] tmp = ctfe_alloc(T.sizeof);
}
return tmp;
}
- else
- {
- return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
- }
}
@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector))
+const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector))
{
pragma(inline, true);
- if (!__ctfe)
- return (cast(const ubyte*) &val)[0 .. T.sizeof];
- else static if (is(typeof(val[0]) : void))
+ return __ctfe ? toUbyte_integral_ctfe(val)
+ : (cast(const ubyte*) &val)[0 .. T.sizeof];
+}
+
+private const(ubyte)[] toUbyte_vector_ctfe(T)(const return ref scope T val)
+{
+ pragma(inline, false);
+ static if (is(typeof(val[0]) : void))
assert(0, "Unable to compute byte representation of " ~ T.stringof ~ " at compile time.");
else
{
}
}
+@trusted pure nothrow @nogc
+const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector))
+{
+ pragma(inline, true);
+ return __ctfe ? toUbyte_vector_ctfe(val)
+ : (cast(const ubyte*) &val)[0 .. T.sizeof];
+}
+
+private const(ubyte)[] toUbyte_enum_ctfe(T)(const return ref scope T val)
+{
+ pragma(inline, false);
+ static if (is(T V == enum)){}
+ return toUbyte(*cast(const V*) &val);
+}
+
@trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(const ref return scope T val) if (is(T == enum))
{
pragma(inline, true);
- if (__ctfe)
- {
- static if (is(T V == enum)){}
- return toUbyte(*cast(const V*) &val);
- }
- else
- {
- return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
- }
+ return __ctfe ? toUbyte_enum_ctfe(val)
+ : (cast(const(ubyte)*)&val)[0 .. T.sizeof];
}
nothrow pure @safe unittest
enum ctfe_works = (() { Month x = Month.jan; return toUbyte(x).length > 0; })();
}
+private const(ubyte)[] toUbyte_delegate_ctfe(T)(const return ref scope T val)
+{
+ pragma(inline, false);
+ if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time");
+ return ctfe_alloc(T.sizeof);
+}
+
@trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V) && __traits(getAliasThis, T).length == 0)
{
pragma(inline, true);
- if (__ctfe)
- {
- if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time");
- return ctfe_alloc(T.sizeof);
- }
- else
+ return __ctfe ? toUbyte_delegate_ctfe(val)
+ : (cast(const(ubyte)*)&val)[0 .. T.sizeof];
+}
+
+private const(ubyte)[] toUbyte_aggregate_ctfe(T)(const return ref scope T val)
+{
+ pragma(inline, false);
+ ubyte[] bytes = ctfe_alloc(T.sizeof);
+ foreach (key, ref cur; val.tupleof)
{
- return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
+ static if (is(typeof(cur) EType == enum)) // Odd style is to avoid template instantiation in most cases.
+ alias CurType = OriginalType!EType;
+ else
+ alias CurType = typeof(cur);
+ static if (is(CurType == struct) || is(CurType == union) || __traits(isStaticArray, CurType) || !is(typeof(cur is null)))
+ {
+ bytes[val.tupleof[key].offsetof .. val.tupleof[key].offsetof + CurType.sizeof] = toUbyte(cur)[];
+ }
+ else
+ {
+ assert(cur is null, "Unable to compute byte representation of non-null reference field at compile time");
+ //skip, because val bytes are zeros
+ }
}
+ return bytes;
}
@trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(const return ref scope T val) if (is(T == struct) || is(T == union))
{
pragma(inline, true);
- if (__ctfe)
- {
- ubyte[] bytes = ctfe_alloc(T.sizeof);
- foreach (key, ref cur; val.tupleof)
- {
- static if (is(typeof(cur) EType == enum)) // Odd style is to avoid template instantiation in most cases.
- alias CurType = OriginalType!EType;
- else
- alias CurType = typeof(cur);
- static if (is(CurType == struct) || is(CurType == union) || __traits(isStaticArray, CurType) || !is(typeof(cur is null)))
- {
- bytes[val.tupleof[key].offsetof .. val.tupleof[key].offsetof + CurType.sizeof] = toUbyte(cur)[];
- }
- else
- {
- assert(cur is null, "Unable to compute byte representation of non-null reference field at compile time");
- //skip, because val bytes are zeros
- }
- }
- return bytes;
- }
- else
- {
- // We're escaping a reference to `val` here because we cannot express
- // ref return + scope, it's currently seen as ref + return scope
- // https://issues.dlang.org/show_bug.cgi?id=22541
- // Once fixed, the @system lambda should be removed
- return (() @system => (cast(const(ubyte)*)&val)[0 .. T.sizeof])();
- }
+ return __ctfe ? toUbyte_aggregate_ctfe(val)
+ : (cast(const(ubyte)*)&val)[0 .. T.sizeof];
}
// Strips off all `enum`s from type `T`.
*/
module core.internal.gc.bits;
-import core.internal.gc.os : os_mem_map, os_mem_unmap, HaveFork;
+import core.internal.gc.os;
import core.bitop;
import core.exception : onOutOfMemoryError;
{
if (data)
{
- static if (!HaveFork)
+ if (!AllocSupportsShared || !share)
free(data);
- else if (share)
- os_mem_unmap(data, nwords * data[0].sizeof);
+ else static if (AllocSupportsShared)
+ os_mem_unmap_shared(data, nwords * data[0].sizeof);
else
- free(data);
+ assert(false); // unreachable
data = null;
}
}
void alloc(size_t nbits, bool share = false) nothrow
{
this.nbits = nbits;
- static if (!HaveFork)
+ if (!AllocSupportsShared || !share)
data = cast(typeof(data[0])*)calloc(nwords, data[0].sizeof);
- else if (share)
- data = cast(typeof(data[0])*)os_mem_map(nwords * data[0].sizeof, true); // Allocate as MAP_SHARED
+ else static if (AllocSupportsShared)
+ data = cast(typeof(data[0])*)os_mem_map_shared(nwords * data[0].sizeof); // Allocate as MAP_SHARED
else
- data = cast(typeof(data[0])*)calloc(nwords, data[0].sizeof);
+ assert(false); // unreachable
if (!data)
onOutOfMemoryError();
}
// to avoid inlining - see https://issues.dlang.org/show_bug.cgi?id=13725.
noreturn onInvalidMemoryOperationError(void* pretend_sideffect = null, string file = __FILE__, size_t line = __LINE__) @trusted pure nothrow @nogc;
noreturn onOutOfMemoryError(void* pretend_sideffect = null, string file = __FILE__, size_t line = __LINE__) @trusted pure nothrow @nogc;
-
- version (COLLECT_FORK)
- version (OSX)
- pid_t __fork() nothrow;
}
enum
auto existingUsed = slice.length + offset;
size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
- if (__setArrayAllocLengthImpl(info, offset + newUsed, atomic, existingUsed, typeInfoSize))
+ if (__setArrayAllocLengthImpl(info, newUsed, atomic, existingUsed, typeInfoSize))
{
// could expand without extending
if (!bic && !atomic)
}
debug(INVARIANT) initialized = true;
version (COLLECT_FORK)
- shouldFork = config.fork;
+ shouldFork = AllocSupportsShared && config.fork;
}
{
fflush(null); // avoid duplicated FILE* output
}
- version (OSX)
- {
- auto pid = __fork(); // avoids calling handlers (from libc source code)
- }
- else version (linux)
+ version (linux)
{
// clone() fits better as we don't want to do anything but scanning in the child process.
// no fork-handlers are called, so we can avoid deadlocks due to malloc locks. Probably related:
auto stack = stackbuf.ptr + (isStackGrowingDown ? stackbuf.length : 0);
auto pid = clone(&wrap_delegate, stack, flags, &dg);
}
+ else static if (__traits(compiles, _Fork))
+ {
+ auto pid = _Fork();
+ }
else
{
- fork_needs_lock = false;
- auto pid = fork();
- fork_needs_lock = true;
+ auto pid = -1; // fork based GC not supported
}
switch (pid)
{
// before a fork.
// This must not happen if fork is called from the GC with the lock already held
- __gshared bool fork_needs_lock = true; // racing condition with cocurrent calls of fork?
-
+ __gshared int fork_cancel_state = 0;
extern(C) static void _d_gcx_atfork_prepare()
{
- if (instance && fork_needs_lock)
+ static if (__traits(compiles, os_unblock_gc_signals))
+ os_unblock_gc_signals();
+
+ if (instance)
+ {
ConservativeGC.lockNR();
+ fork_cancel_state = thread_cancelDisable();
+ }
}
extern(C) static void _d_gcx_atfork_parent()
{
- if (instance && fork_needs_lock)
+ if (instance)
+ {
+ thread_cancelRestore(fork_cancel_state);
ConservativeGC.gcLock.unlock();
+ }
}
extern(C) static void _d_gcx_atfork_child()
{
- if (instance && fork_needs_lock)
+ if (instance)
{
+ thread_cancelRestore(fork_cancel_state);
ConservativeGC.gcLock.unlock();
// make sure the threads and event handles are reinitialized in a fork
// https://issues.dlang.org/show_bug.cgi?id=19281
debug (SENTINEL) {} else // cannot allow >= 4 GB with SENTINEL
debug (MEMSTOMP) {} else // might take too long to actually touch the memory
+version (OnlyLowMemUnittests) {} else
version (D_LP64) unittest
{
static if (__traits(compiles, os_physical_mem))
}
}
+// https://github.com/dlang/dmd/issues/22004
+@safe unittest
+{
+outer:
+ foreach (i; 1 .. 100)
+ {
+ foreach (j; 0 .. i)
+ {
+ int[] orig;
+ orig.length = i;
+ if (orig.capacity == orig.length)
+ // skip legitimate realloc cases
+ continue outer;
+
+ int[] slice = orig[j .. $];
+ assert(slice.capacity != 0);
+
+ auto ptr = &slice[0];
+ slice.length += 1;
+ // should not have realloc'd
+ assert(&slice[0] is ptr);
+ }
+ }
+}
+
// https://github.com/dlang/dmd/issues/21615
debug(SENTINEL) {} else // no additional capacity with SENTINEL
+version (OnlyLowMemUnittests) {} else
@safe unittest
{
size_t numReallocations = 0;
else version (WatchOS)
version = Darwin;
- public import core.sys.posix.unistd : fork, pid_t;
- import core.stdc.errno : ECHILD, EINTR, errno;
- import core.sys.posix.sys.mman : MAP_ANON, MAP_FAILED, MAP_PRIVATE, MAP_SHARED, mmap, munmap, PROT_READ, PROT_WRITE;
- import core.sys.posix.sys.wait : waitpid, WNOHANG;
+ public import core.sys.posix.unistd : pid_t;
+
+ static import core.sys.posix.unistd;
+ static if (__traits(compiles, core.sys.posix.unistd._Fork))
+ public import core.sys.posix.unistd : _Fork;
+
+ static import core.sys.posix.sys.mman;
+ static if (__traits(compiles, core.sys.posix.sys.mman.mmap))
+ import core.sys.posix.sys.mman : MAP_ANON, MAP_FAILED, MAP_PRIVATE, MAP_SHARED, mmap, munmap, PROT_READ, PROT_WRITE;
+
+ static import core.sys.posix.stdlib;
+ static if (__traits(compiles, core.sys.posix.stdlib.valloc))
+ import core.sys.posix.stdlib : valloc;
+
+ import core.stdc.stdlib : free, malloc;
/// Possible results for the wait_pid() function.
ChildStatus wait_pid(pid_t pid, bool block = true) nothrow @nogc
{
import core.exception : onForkError;
+ import core.stdc.errno : ECHILD, EINTR, errno;
+ import core.sys.posix.sys.wait : waitpid, WNOHANG;
int status = void;
pid_t waited_pid = void;
return ChildStatus.done;
}
+ version (DragonFlyBSD)
+ version = GCSignalsUnblock;
+ version (FreeBSD)
+ version = GCSignalsUnblock;
+ version (Solaris)
+ version = GCSignalsUnblock;
+
//version = GC_Use_Alloc_MMap;
}
else
else static assert(false, "No supported allocation methods available.");
+/
-static if (is(typeof(VirtualAlloc))) // version (GC_Use_Alloc_Win32)
+version (CoreDdoc)
{
- /**
- * Indicates if an implementation supports fork().
- *
- * The value shown here is just demostrative, the real value is defined based
- * on the OS it's being compiled in.
- * enum HaveFork = true;
- */
- enum HaveFork = false;
-
/**
* Map memory.
*/
void *os_mem_map(size_t nbytes) nothrow @nogc
{
- return VirtualAlloc(null, nbytes, MEM_RESERVE | MEM_COMMIT,
- PAGE_READWRITE);
+ return null;
}
+ /**
+ * Unmap memory allocated with os_mem_map()
+ * Returns:
+ * 0 success
+ * !=0 failure
+ */
+ int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
+ {
+ return 0;
+ }
+
+ /**
+ * Map memory that will be shared by child processes.
+ * Note: only available if the OS supports this feature. Use `AllocSupportsShared` to test.
+ */
+ void *os_mem_map_shared(size_t nbytes) nothrow @nogc
+ {
+ return null;
+ }
/**
- * Unmap memory allocated with os_mem_map().
+ * Unmap memory allocated with os_mem_map_shared()
* Returns:
* 0 success
* !=0 failure
*/
+ int os_mem_unmap_shared(void *base, size_t nbytes) nothrow @nogc
+ {
+ return 0;
+ }
+}
+else static if (is(typeof(VirtualAlloc))) // version (GC_Use_Alloc_Win32)
+{
+ void *os_mem_map(size_t nbytes) nothrow @nogc
+ {
+ return VirtualAlloc(null, nbytes, MEM_RESERVE | MEM_COMMIT,
+ PAGE_READWRITE);
+ }
+
int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
{
return cast(int)(VirtualFree(base, 0, MEM_RELEASE) == 0);
}
else static if (is(typeof(mmap))) // else version (GC_Use_Alloc_MMap)
{
- enum HaveFork = true;
+ void *os_mem_map(size_t nbytes) nothrow @nogc
+ {
+ void* p = mmap(null, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+ return (p == MAP_FAILED) ? null : p;
+ }
- void *os_mem_map(size_t nbytes, bool share = false) nothrow @nogc
- { void *p;
+ int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
+ {
+ return munmap(base, nbytes);
+ }
- auto map_f = share ? MAP_SHARED : MAP_PRIVATE;
- p = mmap(null, nbytes, PROT_READ | PROT_WRITE, map_f | MAP_ANON, -1, 0);
+ void *os_mem_map_shared(size_t nbytes) nothrow @nogc
+ {
+ void* p = mmap(null, nbytes, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
return (p == MAP_FAILED) ? null : p;
}
-
- int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
+ int os_mem_unmap_shared(void *base, size_t nbytes) nothrow @nogc
{
return munmap(base, nbytes);
}
}
else static if (is(typeof(valloc))) // else version (GC_Use_Alloc_Valloc)
{
- enum HaveFork = false;
-
void *os_mem_map(size_t nbytes) nothrow @nogc
{
return valloc(nbytes);
// to PAGESIZE alignment, there will be space for a void* at the end
// after PAGESIZE bytes used by the GC.
- enum HaveFork = false;
-
import core.internal.gc.impl.conservative.gc;
static assert(false, "No supported allocation methods available.");
}
+/**
+* Indicates if an allocation method supports sharing between processes.
+*/
+enum AllocSupportsShared = __traits(compiles, os_mem_map_shared);
+
/**
Check for any kind of memory pressure.
return pageSize * pages;
}
}
+
+/**
+ The GC signals might be blocked by `fork` when the atfork prepare
+ handler is invoked. This guards us from the scenario where we are
+ waiting for a GC action in another thread to complete, and that thread
+ decides to call thread_suspendAll, then we must be able to response to
+ that request, otherwise we end up in a deadlock situation.
+ */
+version (GCSignalsUnblock)
+{
+ void os_unblock_gc_signals() nothrow @nogc
+ {
+ import core.sys.posix.signal : pthread_sigmask, sigaddset, sigemptyset, sigset_t, SIG_UNBLOCK;
+ import core.thread : thread_getGCSignals;
+
+ int suspendSignal = void, resumeSignal = void;
+ thread_getGCSignals(suspendSignal, resumeSignal);
+
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, suspendSignal);
+ sigaddset(&set, resumeSignal);
+
+ auto sigmask = pthread_sigmask(SIG_UNBLOCK, &set, null);
+ assert(sigmask == 0, "failed to unblock GC signals");
+ }
+}
static if (is(T V : V*))
{
pragma(inline, true);
- if (__ctfe)
- {
- if (val is null) return hashOf(size_t(0), seed);
- assert(0, "Unable to calculate hash of non-null pointer at compile time");
- }
- return hashOf(cast(size_t) val, seed);
+ return __ctfe ? (val is null ? hashOf(size_t(0), seed) :
+ assert(0, "Unable to calculate hash of non-null pointer at compile time"))
+ : hashOf(cast(size_t) val, seed);
}
else static if (is(T EType == enum) && is(typeof(val[0])))
{
size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && is(T == delegate))
{
pragma(inline, true);
- if (__ctfe)
- {
- if (val is null) return hashOf(size_t(0), hashOf(size_t(0), seed));
- assert(0, "unable to compute hash of "~T.stringof~" at compile time");
- }
- return hashOf(val.ptr, hashOf(cast(void*) val.funcptr, seed));
+ return __ctfe ? (val is null ? hashOf(size_t(0), hashOf(size_t(0), seed)) :
+ assert(0, "unable to compute hash of "~T.stringof~" at compile time"))
+ : hashOf(val.ptr, hashOf(cast(void*) val.funcptr, seed));
}
//address-based class hash. CTFE only if null.
&& canBitwiseHash!T)
{
pragma(inline, true);
- if (__ctfe) if (val is null) return 0;
- return hashOf(cast(const void*) val);
+ return __ctfe && (val is null) ? 0
+ : hashOf(cast(const void*) val);
}
//address-based class hash. CTFE only if null.
&& canBitwiseHash!T)
{
pragma(inline, true);
- if (__ctfe) if (val is null) return hashOf(size_t(0), seed);
- return hashOf(cast(const void*) val, seed);
+ return __ctfe && (val is null) ? hashOf(size_t(0), seed)
+ : hashOf(cast(const void*) val, seed);
}
//class or interface hash. CTFE depends on toHash
// backward compatibility conversions
private ref compat_key(K, K2)(ref K2 key)
{
- pragma(inline, true);
+ static if(!(is(K2 == struct) && __traits(isNested, K2)))
+ pragma(inline, true);
static if (is(K2 == const(char)[]) && is(K == string))
return (ref (ref return K2 k2) @trusted => *cast(string*)&k2)(key);
else
}
// mimick behaviour of rt.aaA for initialization
-Entry!(K, V)* _newEntry(K, V)(ref K key, ref V value)
+Entry!(K, V)* _newEntry(K, V)(ref K key, auto ref V value)
{
static if (__traits(compiles, new Entry!(K, V)(key, value)))
{
template pure_hashOf(K)
{
- static if (__traits(compiles, function hash_t(scope const ref K key) pure nothrow @nogc @trusted { return hashOf(cast()key); }))
+ static if (!(is(K == struct) && __traits(isNested, K)) &&
+ __traits(compiles, function hash_t(scope const ref K key) pure nothrow @nogc @trusted { return hashOf(cast()key); }))
{
// avoid wrapper call in debug builds if pure nothrow @nogc is inferred
pragma(inline, true)
// this also breaks cyclic inference on recursive data types
template pure_keyEqual(K1, K2 = K1)
{
- static if (__traits(compiles, function bool(ref const K1 k1, ref const K2 k2) pure nothrow @nogc @trusted { return cast()k1 == cast()k2; }))
+ static if (!(is(K1 == struct) && __traits(isNested, K1)) &&
+ !(is(K2 == struct) && __traits(isNested, K2)) &&
+ __traits(compiles, function bool(ref const K1 k1, ref const K2 k2) pure nothrow @nogc @trusted { return cast()k1 == cast()k2; }))
{
// avoid wrapper call in debug builds if pure nothrow @nogc is inferred
pragma(inline, true)
V* _d_aaGetY(K, V, T : V1[K1], K1, V1, K2)(auto ref scope T aa, auto ref K2 key, out bool found)
{
ref aax = cast(V[K])cast(V1[K1])aa; // remove outer const from T
- return _aaGetX!(K, V)(aax, key, found);
+ return _aaGetX!(K, V, K2)(aax, key, found, _noV2());
}
+private struct _noV2 {}
+
/******************************
* Lookup key in aa.
* Called only from implementation of require, update and _d_aaGetY
* a = associative array
* key = reference to the key value
* found = true if the value was found
+ * v2 = if key not found, init new value to this (except if type _noV2)
* Returns:
* if key was in the aa, a mutable pointer to the existing value.
* If key was not in the aa, a mutable pointer to newly inserted value which
- * is set to V.init
+ * is set to v2 or is zero-initialized
*/
-V* _aaGetX(K, V, K2)(auto ref scope V[K] a, auto ref K2 key, out bool found)
+V* _aaGetX(K, V, K2, V2)(auto ref scope V[K] a, auto ref K2 key, out bool found, lazy V2 v2)
{
ref aa = _refAA!(K, V)(a);
}
auto pi = aa.findSlotInsert(hash);
- if (aa.buckets[pi].deleted)
- --aa.deleted;
// check load factor and possibly grow
- else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM)
+ if (!aa.buckets[pi].deleted && (aa.used + 1) * GROW_DEN > aa.dim * GROW_NUM)
{
aa.grow();
pi = aa.findSlotInsert(hash);
assert(aa.buckets[pi].empty);
}
- // update search cache and allocate entry
- aa.firstUsed = min(aa.firstUsed, cast(uint)pi);
+ // allocate entry and update search cache (if not throwing in _newEntry)
ref p = aa.buckets[pi];
+ static if (is(V2 == _noV2))
+ p.entry = _newEntry!(K, V)(key2);
+ else
+ p.entry = _newEntry!(K, V)(key2, v2);
+ if (p.deleted)
+ --aa.deleted;
+ else
+ aa.used++;
p.hash = hash;
- p.entry = _newEntry!(K, V)(key2);
+ aa.firstUsed = min(aa.firstUsed, cast(uint)pi);
return &p.entry.value;
}
static if (__FreeBSD_version >= 1400000)
{
+ // FreeBSD changed qsort_r function signature to POSIX in FreeBSD 14.0
alias extern (C) int function(scope const void*, scope const void*, scope void*) Cmp;
extern (C) void qsort_r(scope void* base, size_t nmemb, size_t size, Cmp cmp, scope void* thunk);
- // https://cgit.freebsd.org/src/tree/include/stdlib.h?h=stable/14#n350
- pragma(mangle, "qsort_r@FBSD_1.0")
- private extern (C) void __qsort_r_compat(scope void* base, size_t nmemb, size_t size, scope void* thunk, OldCmp cmp);
- alias extern (C) int function(scope void*, scope const void*, scope const void*) OldCmp;
-
- deprecated("In FreeBSD 14, qsort_r's signature was fixed to match POSIX. This extern(D) overload has been " ~
- "provided to avoid breaking code, but code should be updated to use the POSIX version.")
- extern (D) void qsort_r(scope void* base, size_t nmemb, size_t size, scope void* thunk, OldCmp cmp)
- {
- __qsort_r_compat(base, nmemb, size, thunk, cmp);
- }
-
extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
{
extern (C) int cmp(scope const void* p1, scope const void* p2, scope void* ti)
--- /dev/null
+/**
+ * Varargs implementation for the GNU compilers (Gnu D Compiler)
+ * Used by core.stdc.stdarg and core.vararg.
+ *
+ * Reference: https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#appendix-variable-argument-lists
+ *
+ * Copyright: Copyright D Language Foundation 2025
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Various
+ * Source: $(DRUNTIMESRC core/internal/vararg/gnu.d)
+ */
+
+module core.internal.vararg.gnu;
+
+version (GNU):
+
+import gcc.builtins;
+
+@nogc:
+nothrow:
+
+/**
+ * The argument pointer type.
+ */
+alias va_list = __gnuc_va_list;
+alias __gnuc_va_list = __builtin_va_list;
+
+/**
+ * Initialize ap.
+ * parmn should be the last named parameter.
+ */
+void va_start(T)(out va_list ap, ref T parmn);
+
+/**
+ * Retrieve and return the next value that is of type T.
+ */
+T va_arg(T)(ref va_list ap); // intrinsic
+
+/**
+ * Retrieve and store in parmn the next value that is of type T.
+ */
+void va_arg(T)(ref va_list ap, ref T parmn); // intrinsic
+
+/**
+ * End use of ap.
+ */
+alias va_end = __builtin_va_end;
+
+/**
+ * Make a copy of ap.
+ */
+alias va_copy = __builtin_va_copy;
}
// wipes source after moving
-pragma(inline, true)
private void wipe(T, Init...)(return scope ref T source, ref const scope Init initializer) @trusted
if (!Init.length ||
((Init.length == 1) && (is(immutable T == immutable Init[0]))))
{
+ static if (!is(T == struct) || !__traits(isNested, T))
+ pragma(inline, true);
+
static if (__traits(isStaticArray, T) && hasContextPointers!T)
{
for (auto i = 0; i < T.length; i++)
version (D_TypeInfo)
{
import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
- mixin(TraceHook!(T.stringof, "_d_newclassT"));
+ mixin(TraceHook!("T", "_d_newclassT"));
return _d_newclassT!T();
}
}
import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
- mixin(TraceHook!(T.stringof, "_d_newitemT"));
+ mixin(TraceHook!("T", "_d_newitemT"));
return _d_newitemT!T();
}
@nogc:
///
-struct lconv
+version (Windows)
{
- char* decimal_point;
- char* thousands_sep;
- char* grouping;
- char* int_curr_symbol;
- char* currency_symbol;
- char* mon_decimal_point;
- char* mon_thousands_sep;
- char* mon_grouping;
- char* positive_sign;
- char* negative_sign;
- byte int_frac_digits;
- byte frac_digits;
- byte p_cs_precedes;
- byte p_sep_by_space;
- byte n_cs_precedes;
- byte n_sep_by_space;
- byte p_sign_posn;
- byte n_sign_posn;
- byte int_p_cs_precedes;
- byte int_p_sep_by_space;
- byte int_n_cs_precedes;
- byte int_n_sep_by_space;
- byte int_p_sign_posn;
- byte int_n_sign_posn;
+ import core.stdc.wchar_ : wchar_t;
+
+ // ...Windows Kits\10\Include\10.0.14393.0\ucrt\locale.h
+ struct lconv
+ {
+ char* decimal_point;
+ char* thousands_sep;
+ char* grouping;
+ char* int_curr_symbol;
+ char* currency_symbol;
+ char* mon_decimal_point;
+ char* mon_thousands_sep;
+ char* mon_grouping;
+ char* positive_sign;
+ char* negative_sign;
+ char int_frac_digits = 0; // " = 0" For zero init
+ char frac_digits = 0;
+ char p_cs_precedes = 0;
+ char p_sep_by_space = 0;
+ char n_cs_precedes = 0;
+ char n_sep_by_space = 0;
+ char p_sign_posn = 0;
+ char n_sign_posn = 0;
+ wchar_t* _W_decimal_point;
+ wchar_t* _W_thousands_sep;
+ wchar_t* _W_int_curr_symbol;
+ wchar_t* _W_currency_symbol;
+ wchar_t* _W_mon_decimal_point;
+ wchar_t* _W_mon_thousands_sep;
+ wchar_t* _W_positive_sign;
+ wchar_t* _W_negative_sign;
+ }
+}
+else
+{
+ struct lconv
+ {
+ char* decimal_point;
+ char* thousands_sep;
+ char* grouping;
+ char* int_curr_symbol;
+ char* currency_symbol;
+ char* mon_decimal_point;
+ char* mon_thousands_sep;
+ char* mon_grouping;
+ char* positive_sign;
+ char* negative_sign;
+ byte int_frac_digits;
+ byte frac_digits;
+ byte p_cs_precedes;
+ byte p_sep_by_space;
+ byte n_cs_precedes;
+ byte n_sep_by_space;
+ byte p_sign_posn;
+ byte n_sign_posn;
+ byte int_p_cs_precedes;
+ byte int_p_sep_by_space;
+ byte int_n_cs_precedes;
+ byte int_n_sep_by_space;
+ byte int_p_sign_posn;
+ byte int_n_sign_posn;
+ }
}
version (CRuntime_Glibc)
@nogc:
nothrow:
+version (GNU)
+ public import core.internal.vararg.gnu;
+else:
+
version (X86_64)
{
version (Windows) { /* different ABI */ }
else version = SysV_x64;
}
-version (GNU)
-{
- import gcc.builtins;
-}
-else version (SysV_x64)
+version (SysV_x64)
{
static import core.internal.vararg.sysv_x64;
-
- version (DigitalMars)
- {
- align(16) struct __va_argsave_t
- {
- size_t[6] regs; // RDI,RSI,RDX,RCX,R8,R9
- real[8] fpregs; // XMM0..XMM7
- __va_list va;
- }
- }
}
version (ARM) version = ARM_Any;
version (RISCV32) version = RISCV_Any;
version (RISCV64) version = RISCV_Any;
-version (GNU)
-{
- // Uses gcc.builtins
-}
-else version (ARM_Any)
+version (ARM_Any)
{
// Darwin uses a simpler varargs implementation
version (OSX) {}
else version (AArch64)
{
version = AAPCS64;
- static import core.internal.vararg.aarch64;
+ static import core.internal.vararg.aapcs64;
}
}
/**
* The argument pointer type.
*/
-version (GNU)
+version (SysV_x64)
{
- alias va_list = __gnuc_va_list;
- alias __gnuc_va_list = __builtin_va_list;
-}
-else version (SysV_x64)
-{
- alias va_list = core.internal.vararg.sysv_x64.va_list;
- public import core.internal.vararg.sysv_x64 : __va_list, __va_list_tag;
+ public import core.internal.vararg.sysv_x64 : va_list, __va_list, __va_list_tag;
}
else version (AAPCS32)
{
}
else version (AAPCS64)
{
- alias va_list = core.internal.vararg.aarch64.va_list;
+ alias va_list = core.internal.vararg.aapcs64.va_list;
version (DigitalMars)
- public import core.internal.vararg.aarch64 : __va_list, __va_list_tag;
+ public import core.internal.vararg.aapcs64 : __va_list, __va_list_tag;
}
else version (RISCV_Any)
{
* Initialize ap.
* parmn should be the last named parameter.
*/
-version (GNU)
-{
- void va_start(T)(out va_list ap, ref T parmn);
-}
-else version (LDC)
+version (LDC)
{
pragma(LDC_va_start)
void va_start(T)(out va_list ap, ref T parmn) @nogc;
/**
* Retrieve and return the next value that is of type T.
*/
-version (GNU)
- T va_arg(T)(ref va_list ap); // intrinsic
-else
T va_arg(T)(ref va_list ap)
{
version (X86)
}
else version (AAPCS64)
{
- return core.internal.vararg.aarch64.va_arg!T(ap);
+ return core.internal.vararg.aapcs64.va_arg!T(ap);
}
else version (ARM_Any)
{
/**
* Retrieve and store in parmn the next value that is of type T.
*/
-version (GNU)
- void va_arg(T)(ref va_list ap, ref T parmn); // intrinsic
-else
void va_arg(T)(ref va_list ap, ref T parmn)
{
parmn = va_arg!T(ap);
/**
* End use of ap.
*/
-version (GNU)
-{
- alias va_end = __builtin_va_end;
-}
-else version (LDC)
+version (LDC)
{
pragma(LDC_va_end)
void va_end(va_list ap);
/**
* Make a copy of ap.
*/
-version (GNU)
-{
- alias va_copy = __builtin_va_copy;
-}
-else version (LDC)
+version (LDC)
{
pragma(LDC_va_copy)
void va_copy(out va_list dest, va_list src);
{
assert(obj !is null);
- final switch (order)
- {
- case memory_order.memory_order_relaxed:
- atomicStore!(memory_order.memory_order_relaxed)(&obj.b, false);
- break;
+ // use series of ternary expressions instead of switch to help the dmd inliner
+ order == memory_order.memory_order_relaxed ?
+ atomicStore!(memory_order.memory_order_relaxed)(&obj.b, false) :
- case memory_order.memory_order_acquire:
- case memory_order.memory_order_acq_rel:
- // Ideally this would error at compile time but alas it is not an intrinsic.
- // Note: this is not a valid memory order for this operation.
- atomicStore!(memory_order.memory_order_seq_cst)(&obj.b, false);
- break;
+ order == memory_order.memory_order_acquire ||
+ order == memory_order.memory_order_acq_rel ?
+ // Ideally this would error at compile time but alas it is not an intrinsic.
+ // Note: this is not a valid memory order for this operation.
+ atomicStore!(memory_order.memory_order_seq_cst)(&obj.b, false) :
- case memory_order.memory_order_release:
- atomicStore!(memory_order.memory_order_release)(&obj.b, false);
- break;
+ order == memory_order.memory_order_release ?
+ atomicStore!(memory_order.memory_order_release)(&obj.b, false) :
- case memory_order.memory_order_seq_cst:
- atomicStore(&obj.b, false);
- break;
- }
+ order == memory_order.memory_order_seq_cst ?
+ atomicStore(&obj.b, false) :
+
+ cast(void) assert(0);
}
///
{
assert(obj !is null);
- final switch (order)
- {
- case memory_order.memory_order_relaxed:
- return atomicExchange!(memory_order.memory_order_relaxed)(&obj.b, true);
+ // use series of ternary expressions instead of switch to help the dmd inliner
+ return
+ order == memory_order.memory_order_relaxed ?
+ atomicExchange!(memory_order.memory_order_relaxed)(&obj.b, true) :
- case memory_order.memory_order_acquire:
+ order == memory_order.memory_order_acquire ?
// Ideally this would error at compile time but alas it is not an intrinsic.
// Note: this is not a valid memory order for this operation.
- return atomicExchange!(memory_order.memory_order_seq_cst)(&obj.b, true);
+ atomicExchange!(memory_order.memory_order_seq_cst)(&obj.b, true) :
- case memory_order.memory_order_release:
- return atomicExchange!(memory_order.memory_order_release)(&obj.b, true);
+ order == memory_order.memory_order_release ?
+ atomicExchange!(memory_order.memory_order_release)(&obj.b, true) :
- case memory_order.memory_order_acq_rel:
- return atomicExchange!(memory_order.memory_order_acq_rel)(&obj.b, true);
+ order == memory_order.memory_order_acq_rel ?
+ atomicExchange!(memory_order.memory_order_acq_rel)(&obj.b, true) :
- case memory_order.memory_order_seq_cst:
- return atomicExchange(&obj.b, true);
- }
+ order == memory_order.memory_order_seq_cst ?
+ atomicExchange(&obj.b, true) :
+
+ assert(0);
}
///
pragma(inline, true)
void atomic_signal_fence_impl()(memory_order order)
{
- final switch (order)
- {
- case memory_order.memory_order_relaxed:
- // This is a no-op operation for relaxed memory orders.
- break;
+ // use series of ternary expressions instead of switch to help the dmd inliner
+ order == memory_order.memory_order_relaxed ?
+ // This is a no-op operation for relaxed memory orders.
+ cast(void)0 :
- case memory_order.memory_order_acquire:
- atomicSignalFence!(memory_order.memory_order_acquire);
- break;
+ order == memory_order.memory_order_acquire ?
+ atomicSignalFence!(memory_order.memory_order_acquire) :
- case memory_order.memory_order_release:
- atomicSignalFence!(memory_order.memory_order_release);
- break;
+ order == memory_order.memory_order_release ?
+ atomicSignalFence!(memory_order.memory_order_release) :
- case memory_order.memory_order_acq_rel:
- atomicSignalFence!(memory_order.memory_order_acq_rel);
- break;
+ order == memory_order.memory_order_acq_rel ?
+ atomicSignalFence!(memory_order.memory_order_acq_rel) :
- case memory_order.memory_order_seq_cst:
- atomicSignalFence!(memory_order.memory_order_seq_cst);
- break;
- }
+ order == memory_order.memory_order_seq_cst ?
+ atomicSignalFence!(memory_order.memory_order_seq_cst) :
+
+ cast(void) assert(0);
}
///
pragma(inline, true)
void atomic_thread_fence_impl()(memory_order order)
{
- final switch (order)
- {
- case memory_order.memory_order_relaxed:
- // This is a no-op operation for relaxed memory orders.
- break;
+ // use series of ternary expressions instead of switch to help the dmd inliner
+ order == memory_order.memory_order_relaxed ?
+ // This is a no-op operation for relaxed memory orders.
+ cast(void)0 :
- case memory_order.memory_order_acquire:
- atomicFence!(memory_order.memory_order_acquire);
- break;
+ order == memory_order.memory_order_acquire ?
+ atomicFence!(memory_order.memory_order_acquire) :
- case memory_order.memory_order_release:
- atomicFence!(memory_order.memory_order_release);
- break;
+ order == memory_order.memory_order_release ?
+ atomicFence!(memory_order.memory_order_release) :
- case memory_order.memory_order_acq_rel:
- atomicFence!(memory_order.memory_order_acq_rel);
- break;
+ order == memory_order.memory_order_acq_rel ?
+ atomicFence!(memory_order.memory_order_acq_rel) :
- case memory_order.memory_order_seq_cst:
- atomicFence!(memory_order.memory_order_seq_cst);
- break;
- }
+ order == memory_order.memory_order_seq_cst ?
+ atomicFence!(memory_order.memory_order_seq_cst) :
+
+ cast(void) assert(0);
}
///
{
assert(obj !is null);
- final switch (order)
- {
- case memory_order.memory_order_relaxed:
- atomicStore!(memory_order.memory_order_relaxed)(cast(A*)obj, cast(A)desired);
- break;
+ // use series of ternary expressions instead of switch to help the dmd inliner
+ order == memory_order.memory_order_relaxed ?
+ atomicStore!(memory_order.memory_order_relaxed)(cast(A*)obj, cast(A)desired) :
- case memory_order.memory_order_acquire:
- case memory_order.memory_order_acq_rel:
- // Ideally this would error at compile time but alas it is not an intrinsic.
- // Note: this is not a valid memory order for this operation.
- atomicStore!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired);
- break;
+ order == memory_order.memory_order_acquire ||
+ order == memory_order.memory_order_acq_rel ?
+ // Ideally this would error at compile time but alas it is not an intrinsic.
+ // Note: this is not a valid memory order for this operation.
+ atomicStore!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired) :
- case memory_order.memory_order_release:
- atomicStore!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired);
- break;
+ order == memory_order.memory_order_release ?
+ atomicStore!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired) :
- case memory_order.memory_order_seq_cst:
- atomicStore!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired);
- break;
- }
+ order == memory_order.memory_order_seq_cst ?
+ atomicStore!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired) :
+
+ cast(void) assert(0);
}
///
{
assert(obj !is null);
- final switch (order)
- {
- case memory_order.memory_order_relaxed:
- return atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
+ // use series of ternary expressions instead of switch to help the dmd inliner
+ return
+ order == memory_order.memory_order_relaxed ?
+ atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj) :
- case memory_order.memory_order_acquire:
- return atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj);
+ order == memory_order.memory_order_acquire ?
+ atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj) :
- case memory_order.memory_order_release:
- case memory_order.memory_order_acq_rel:
+ order == memory_order.memory_order_release ||
+ order == memory_order.memory_order_acq_rel ?
// Ideally this would error at compile time but alas it is not an intrinsic.
// Note: this is not a valid memory order for this operation.
- return atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj);
+ atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj) :
- case memory_order.memory_order_seq_cst:
- return atomicLoad!(memory_order.memory_order_seq_cst)(cast(A*)obj);
- }
+ order == memory_order.memory_order_seq_cst ?
+ atomicLoad!(memory_order.memory_order_seq_cst)(cast(A*)obj) :
+
+ assert(0);
}
///
{
assert(obj !is null);
- final switch (order)
- {
- case memory_order.memory_order_relaxed:
- return atomicExchange!(memory_order.memory_order_relaxed)(cast(A*)obj, cast(A)desired);
+ // use series of ternary expressions instead of switch to help the dmd inliner
+ return
+ order == memory_order.memory_order_relaxed ?
+ atomicExchange!(memory_order.memory_order_relaxed)(cast(A*)obj, cast(A)desired) :
- case memory_order.memory_order_acquire:
+ order == memory_order.memory_order_acquire ?
// Ideally this would error at compile time but alas it is not an intrinsic.
// Note: this is not a valid memory order for this operation.
- return atomicExchange!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired);
+ atomicExchange!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired) :
- case memory_order.memory_order_release:
- return atomicExchange!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired);
+ order == memory_order.memory_order_release ?
+ atomicExchange!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired) :
- case memory_order.memory_order_acq_rel:
- return atomicExchange!(memory_order.memory_order_acq_rel)(cast(A*)obj, cast(A)desired);
+ order == memory_order.memory_order_acq_rel ?
+ atomicExchange!(memory_order.memory_order_acq_rel)(cast(A*)obj, cast(A)desired) :
- case memory_order.memory_order_seq_cst:
- return atomicExchange!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired);
- }
+ order == memory_order.memory_order_seq_cst ?
+ atomicExchange!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired) :
+
+ assert(0);
}
///
assert(obj !is null);
// NOTE: To not have to deal with all invalid cases, the failure model is ignored for now.
- final switch(succ)
- {
- case memory_order.memory_order_relaxed:
- return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
- case memory_order.memory_order_acquire:
- return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
- case memory_order.memory_order_release:
- return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
- case memory_order.memory_order_acq_rel:
- return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
- case memory_order.memory_order_seq_cst:
- return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
- }
+ // use series of ternary expressions instead of switch to help the dmd inliner
+ return
+ succ == memory_order.memory_order_relaxed ?
+ atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+ succ == memory_order.memory_order_acquire ?
+ atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+ succ == memory_order.memory_order_release ?
+ atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+ succ == memory_order.memory_order_acq_rel ?
+ atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+ succ == memory_order.memory_order_seq_cst ?
+ atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+ assert(0);
}
///
assert(obj !is null);
// NOTE: To not have to deal with all invalid cases, the failure model is ignored for now.
- final switch(succ)
- {
- case memory_order.memory_order_relaxed:
- return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
- case memory_order.memory_order_acquire:
- return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
- case memory_order.memory_order_release:
- return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
- case memory_order.memory_order_acq_rel:
- return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
- case memory_order.memory_order_seq_cst:
- return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
- }
+ // use series of ternary expressions instead of switch to help the dmd inliner
+ return
+ succ == memory_order.memory_order_relaxed ?
+ atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+ succ == memory_order.memory_order_acquire ?
+ atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+ succ == memory_order.memory_order_release ?
+ atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+ succ == memory_order.memory_order_acq_rel ?
+ atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+ succ == memory_order.memory_order_seq_cst ?
+ atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+ assert(0);
}
///
{
assert(obj !is null);
- final switch(order)
- {
- case memory_order.memory_order_relaxed:
- return atomic_fetch_op!(memory_order.memory_order_relaxed, "+=")(cast(A*)obj, arg);
- case memory_order.memory_order_acquire:
- return atomic_fetch_op!(memory_order.memory_order_acquire, "+=")(cast(A*)obj, arg);
- case memory_order.memory_order_release:
- return atomic_fetch_op!(memory_order.memory_order_release, "+=")(cast(A*)obj, arg);
- case memory_order.memory_order_acq_rel:
- return atomic_fetch_op!(memory_order.memory_order_acq_rel, "+=")(cast(A*)obj, arg);
- case memory_order.memory_order_seq_cst:
- return atomic_fetch_op!(memory_order.memory_order_seq_cst, "+=")(cast(A*)obj, arg);
- }
+ // use series of ternary expressions instead of switch to help the dmd inliner
+ return
+ order == memory_order.memory_order_relaxed ?
+ atomic_fetch_op!(memory_order.memory_order_relaxed, "+=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_acquire ?
+ atomic_fetch_op!(memory_order.memory_order_acquire, "+=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_release ?
+ atomic_fetch_op!(memory_order.memory_order_release, "+=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_acq_rel ?
+ atomic_fetch_op!(memory_order.memory_order_acq_rel, "+=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_seq_cst ?
+ atomic_fetch_op!(memory_order.memory_order_seq_cst, "+=")(cast(A*)obj, arg) :
+ assert(0);
}
///
{
assert(obj !is null);
- final switch(order)
- {
- case memory_order.memory_order_relaxed:
- return atomic_fetch_op!(memory_order.memory_order_relaxed, "-=")(cast(A*)obj, arg);
- case memory_order.memory_order_acquire:
- return atomic_fetch_op!(memory_order.memory_order_acquire, "-=")(cast(A*)obj, arg);
- case memory_order.memory_order_release:
- return atomic_fetch_op!(memory_order.memory_order_release, "-=")(cast(A*)obj, arg);
- case memory_order.memory_order_acq_rel:
- return atomic_fetch_op!(memory_order.memory_order_acq_rel, "-=")(cast(A*)obj, arg);
- case memory_order.memory_order_seq_cst:
- return atomic_fetch_op!(memory_order.memory_order_seq_cst, "-=")(cast(A*)obj, arg);
- }
+ return
+ order == memory_order.memory_order_relaxed ?
+ atomic_fetch_op!(memory_order.memory_order_relaxed, "-=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_acquire ?
+ atomic_fetch_op!(memory_order.memory_order_acquire, "-=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_release ?
+ atomic_fetch_op!(memory_order.memory_order_release, "-=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_acq_rel ?
+ atomic_fetch_op!(memory_order.memory_order_acq_rel, "-=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_seq_cst ?
+ atomic_fetch_op!(memory_order.memory_order_seq_cst, "-=")(cast(A*)obj, arg) :
+ assert(0);
}
///
{
assert(obj !is null);
- final switch(order)
- {
- case memory_order.memory_order_relaxed:
- return atomic_fetch_op!(memory_order.memory_order_relaxed, "|=")(cast(A*)obj, arg);
- case memory_order.memory_order_acquire:
- return atomic_fetch_op!(memory_order.memory_order_acquire, "|=")(cast(A*)obj, arg);
- case memory_order.memory_order_release:
- return atomic_fetch_op!(memory_order.memory_order_release, "|=")(cast(A*)obj, arg);
- case memory_order.memory_order_acq_rel:
- return atomic_fetch_op!(memory_order.memory_order_acq_rel, "|=")(cast(A*)obj, arg);
- case memory_order.memory_order_seq_cst:
- return atomic_fetch_op!(memory_order.memory_order_seq_cst, "|=")(cast(A*)obj, arg);
- }
+ return
+ order == memory_order.memory_order_relaxed ?
+ atomic_fetch_op!(memory_order.memory_order_relaxed, "|=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_acquire ?
+ atomic_fetch_op!(memory_order.memory_order_acquire, "|=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_release ?
+ atomic_fetch_op!(memory_order.memory_order_release, "|=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_acq_rel ?
+ atomic_fetch_op!(memory_order.memory_order_acq_rel, "|=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_seq_cst ?
+ atomic_fetch_op!(memory_order.memory_order_seq_cst, "|=")(cast(A*)obj, arg) :
+ assert(0);
}
///
{
assert(obj !is null);
- final switch(order)
- {
- case memory_order.memory_order_relaxed:
- return atomic_fetch_op!(memory_order.memory_order_relaxed, "^=")(cast(A*)obj, arg);
- case memory_order.memory_order_acquire:
- return atomic_fetch_op!(memory_order.memory_order_acquire, "^=")(cast(A*)obj, arg);
- case memory_order.memory_order_release:
- return atomic_fetch_op!(memory_order.memory_order_release, "^=")(cast(A*)obj, arg);
- case memory_order.memory_order_acq_rel:
- return atomic_fetch_op!(memory_order.memory_order_acq_rel, "^=")(cast(A*)obj, arg);
- case memory_order.memory_order_seq_cst:
- return atomic_fetch_op!(memory_order.memory_order_seq_cst, "^=")(cast(A*)obj, arg);
- }
+ return
+ order == memory_order.memory_order_relaxed ?
+ atomic_fetch_op!(memory_order.memory_order_relaxed, "^=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_acquire ?
+ atomic_fetch_op!(memory_order.memory_order_acquire, "^=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_release ?
+ atomic_fetch_op!(memory_order.memory_order_release, "^=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_acq_rel ?
+ atomic_fetch_op!(memory_order.memory_order_acq_rel, "^=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_seq_cst ?
+ atomic_fetch_op!(memory_order.memory_order_seq_cst, "^=")(cast(A*)obj, arg) :
+ assert(0);
}
///
{
assert(obj !is null);
- final switch(order)
- {
- case memory_order.memory_order_relaxed:
- return atomic_fetch_op!(memory_order.memory_order_relaxed, "&=")(cast(A*)obj, arg);
- case memory_order.memory_order_acquire:
- return atomic_fetch_op!(memory_order.memory_order_acquire, "&=")(cast(A*)obj, arg);
- case memory_order.memory_order_release:
- return atomic_fetch_op!(memory_order.memory_order_release, "&=")(cast(A*)obj, arg);
- case memory_order.memory_order_acq_rel:
- return atomic_fetch_op!(memory_order.memory_order_acq_rel, "&=")(cast(A*)obj, arg);
- case memory_order.memory_order_seq_cst:
- return atomic_fetch_op!(memory_order.memory_order_seq_cst, "&=")(cast(A*)obj, arg);
- }
+ return
+ order == memory_order.memory_order_relaxed ?
+ atomic_fetch_op!(memory_order.memory_order_relaxed, "&=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_acquire ?
+ atomic_fetch_op!(memory_order.memory_order_acquire, "&=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_release ?
+ atomic_fetch_op!(memory_order.memory_order_release, "&=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_acq_rel ?
+ atomic_fetch_op!(memory_order.memory_order_acq_rel, "&=")(cast(A*)obj, arg) :
+ order == memory_order.memory_order_seq_cst ?
+ atomic_fetch_op!(memory_order.memory_order_seq_cst, "&=")(cast(A*)obj, arg) :
+ assert(0);
}
///
private:
-pragma(inline, true)
A atomic_fetch_op(memory_order order, string op, A, M)(A* obj, M arg) @trusted
{
static if (is(A : ulong) && (op == "+=" || op == "-="))
{
+ pragma(inline, true)
// these cannot handle floats
static if (op == "+=")
{
///
L_tmpnam = 1024
}
-
- struct __sbuf
- {
- ubyte *_base;
- int _size;
- }
}
else version (DragonFlyBSD)
{
///
struct __sFILE
{
- ubyte* _p;
- int _r;
- int _w;
- short _flags;
- short _file;
- __sbuf _bf;
- int _lbfsize;
-
- void* _cookie;
- int function(void*) _close;
- int function(void*, scope char*, int) _read;
- fpos_t function(void*, fpos_t, int) _seek;
- int function(void*, scope const char*, int) _write;
-
- __sbuf _ext;
- ubyte* _up;
- int _ur;
-
- ubyte[3] _ubuf;
- ubyte[1] _nbuf;
-
- __sbuf _lb;
-
- int _blksize;
- fpos_t _offset;
+ void* dummy;
}
///
_IONBF = 2,
}
- private extern shared FILE[3] __sF;
- @property auto __stdin()() { return &__sF[0]; }
- @property auto __stdout()() { return &__sF[1]; }
- @property auto __stderr()() { return &__sF[2]; }
+ struct __sFstub { long _stub; }
+
+ private extern shared __sFstub[1] __stdin;
+ private extern shared __sFstub[1] __stdout;
+ private extern shared __sFstub[1] __stderr;
+
+ @property auto __stdin1()() { return cast(FILE*)__stdin; }
+ @property auto __stdout1()() { return cast(FILE*)__stdout; }
+ @property auto __stderr1()() { return cast(FILE*)__stderr; }
+
///
- alias __stdin stdin;
+ alias __stdin1 stdin;
///
- alias __stdout stdout;
+ alias __stdout1 stdout;
///
- alias __stderr stderr;
+ alias __stderr1 stderr;
}
else version (DragonFlyBSD)
{
alias vprintf = __vprintfieee128;
///
pragma(scanf)
- int __isoc99_vfscanfieee128(scope const char* format, va_list arg);
+ int __isoc99_vscanfieee128(scope const char* format, va_list arg);
///
- alias vscanf = __isoc99_vfscanfieee128;
+ alias vscanf = __isoc99_vscanfieee128;
///
pragma(printf)
int __printfieee128(scope const char* format, scope const ...);
{
///
void rewind(FILE*);
- }
- @trusted private
- {
///
- pragma(mangle, "clearerr")
- pure void __clearerr(FILE*);
+ pure void clearerr(FILE*);
///
- pragma(mangle, "feof")
- pure int __feof(FILE*);
+ pure int feof(FILE*);
///
- pragma(mangle, "ferror")
- pure int __ferror(FILE*);
+ pure int ferror(FILE*);
///
- pragma(mangle, "fileno")
- int __fileno(FILE*);
- }
-
- enum __SLBF = 0x0001;
- enum __SNBF = 0x0002;
- enum __SRD = 0x0004;
- enum __SWR = 0x0008;
- enum __SRW = 0x0010;
- enum __SEOF = 0x0020;
- enum __SERR = 0x0040;
- enum __SMBF = 0x0080;
- enum __SAPP = 0x0100;
- enum __SSTR = 0x0200;
- enum __SOPT = 0x0400;
- enum __SNPT = 0x0800;
- enum __SOFF = 0x1000;
- enum __SMOD = 0x2000;
- enum __SALC = 0x4000;
- enum __SIGN = 0x8000;
-
- extern immutable __gshared int __isthreaded;
-
- extern (D) @trusted
- {
- void __sclearerr()(FILE* p)
- {
- p._flags = p._flags & ~(__SERR|__SEOF);
- }
-
- int __sfeof()(FILE* p)
- {
- return (p._flags & __SEOF) != 0;
- }
-
- int __sferror()(FILE* p)
- {
- return (p._flags & __SERR) != 0;
- }
-
- int __sfileno()(FILE* p)
- {
- return p._file;
- }
-
- pure void clearerr()(FILE* file)
- {
- !__isthreaded ? __sclearerr(file) : __clearerr(file);
- }
-
- pure int feof()(FILE* file)
- {
- return !__isthreaded ? __sfeof(file) : __feof(file);
- }
-
- pure int ferror()(FILE* file)
- {
- return !__isthreaded ? __sferror(file) : __ferror(file);
- }
-
- int fileno()(FILE* file)
- {
- return !__isthreaded ? __sfileno(file) : __fileno(file);
- }
+ int fileno(FILE*);
}
///
wchar_t __wc = 0;
}
}
+else version (Windows)
+{
+ ///
+ struct __mbstate_t
+ {
+ int __count;
+ union ___value
+ {
+ wint_t __wch = 0;
+ char[4] __wchb;
+ }
+ ___value __value;
+ }
+
+ ///
+ alias mbstate_t = __mbstate_t;
+}
else
{
///
// NOTE: When adding newer versions of FreeBSD, verify all current versioned
// bindings are still compatible with the release.
- version (FreeBSD_14) enum __FreeBSD_version = 1400000;
+ version (FreeBSD_15) enum __FreeBSD_version = 1500063;
+else version (FreeBSD_14) enum __FreeBSD_version = 1400097;
else version (FreeBSD_13) enum __FreeBSD_version = 1301000;
else version (FreeBSD_12) enum __FreeBSD_version = 1203000;
else version (FreeBSD_11) enum __FreeBSD_version = 1104000;
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+module core.sys.linux.hdlc.ioctl;
+
+import core.sys.linux.net.if_ : IFNAMSIZ;
+
+version (linux):
+extern(C):
+@nogc:
+nothrow:
+
+enum GENERIC_HDLC_VERSION = 4; /* For synchronization with sethdlc utility */
+
+enum CLOCK_DEFAULT = 0; /* Default setting */
+enum CLOCK_EXT = 1; /* External TX and RX clock - DTE */
+enum CLOCK_INT = 2; /* Internal TX and RX clock - DCE */
+enum CLOCK_TXINT = 3; /* Internal TX and external RX clock */
+enum CLOCK_TXFROMRX = 4; /* TX clock derived from external RX clock */
+
+
+enum ENCODING_DEFAULT = 0; /* Default setting */
+enum ENCODING_NRZ = 1;
+enum ENCODING_NRZI = 2;
+enum ENCODING_FM_MARK = 3;
+enum ENCODING_FM_SPACE = 4;
+enum ENCODING_MANCHESTER = 5;
+
+
+enum PARITY_DEFAULT = 0; /* Default setting */
+enum PARITY_NONE = 1; /* No parity */
+enum PARITY_CRC16_PR0 = 2; /* CRC16, initial value 0x0000 */
+enum PARITY_CRC16_PR1 = 3; /* CRC16, initial value 0xFFFF */
+enum PARITY_CRC16_PR0_CCITT = 4; /* CRC16, initial 0x0000, ITU-T version */
+enum PARITY_CRC16_PR1_CCITT = 5; /* CRC16, initial 0xFFFF, ITU-T version */
+enum PARITY_CRC32_PR0_CCITT = 6; /* CRC32, initial value 0x00000000 */
+enum PARITY_CRC32_PR1_CCITT = 7; /* CRC32, initial value 0xFFFFFFFF */
+
+enum LMI_DEFAULT = 0; /* Default setting */
+enum LMI_NONE = 1; /* No LMI, all PVCs are static */
+enum LMI_ANSI = 2; /* ANSI Annex D */
+enum LMI_CCITT = 3; /* ITU-T Annex A */
+enum LMI_CISCO = 4; /* The "original" LMI, aka Gang of Four */
+
+struct sync_serial_settings {
+ uint clock_rate; /* bits per second */
+ uint clock_type; /* internal, external, TX-internal etc. */
+ ushort loopback;
+} /* V.35, V.24, X.21 */
+
+struct te1_settings{
+ uint clock_rate; /* bits per second */
+ uint clock_type; /* internal, external, TX-internal etc. */
+ ushort loopback;
+ uint slot_map;
+} /* T1, E1 */
+
+struct raw_hdlc_proto {
+ ushort encoding;
+ ushort parity;
+}
+
+struct fr_proto {
+ uint t391;
+ uint t392;
+ uint n391;
+ uint n392;
+ uint n393;
+ ushort lmi;
+ ushort dce; /* 1 for DCE (network side) operation */
+}
+
+struct fr_proto_pvc {
+ uint dlci;
+} /* for creating/deleting FR PVCs */
+
+struct fr_proto_pvc_info {
+ uint dlci;
+ char[IFNAMSIZ] master = 0; /* Name of master FRAD device */
+} /* for returning PVC information only */
+
+struct cisco_proto {
+ uint interval;
+ uint timeout;
+}
+
+struct x25_hdlc_proto {
+ ushort dce; /* 1 for DCE (network side) operation */
+ uint modulo; /* modulo (8 = basic / 128 = extended) */
+ uint window; /* frame window size */
+ uint t1; /* timeout t1 */
+ uint t2; /* timeout t2 */
+ uint n2; /* frame retry counter */
+}
--- /dev/null
+/++
+ D header file for Linux's net/if.h.
+
+ Copyright: Copyright 2025
+ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ +/
+module core.sys.linux.net.if_;
+
+public import core.sys.posix.sys.socket : sockaddr;
+public import core.sys.linux.hdlc.ioctl;
+
+version (linux):
+extern(C):
+@nogc:
+nothrow:
+
+enum IFNAMSIZ = 16;
+enum IFALIASZ = 256;
+enum ALTIFNAMSIZ = 128;
+enum IFHWADDRLEN = 6;
+
+enum IFF_UP = 0x1;
+enum IFF_BROADCAST = 0x2;
+enum IFF_DEBUG = 0x4;
+enum IFF_LOOPBACK = 0x8;
+enum IFF_POINTOPOINT = 0x10;
+enum IFF_NOTRAILERS = 0x20;
+enum IFF_RUNNING = 0x40;
+enum IFF_NOARP = 0x80;
+enum IFF_PROMISC = 0x100;
+enum IFF_ALLMULTI = 0x200;
+enum IFF_MASTER = 0x400;
+enum IFF_SLAVE = 0x800;
+enum IFF_MULTICAST = 0x1000;
+enum IFF_PORTSEL = 0x2000;
+enum IFF_AUTOMEDIA = 0x4000;
+enum IFF_DYNAMIC = 0x8000;
+enum IFF_LOWER_UP = 0x10000;
+enum IFF_DORMANT = 0x20000;
+enum IFF_ECHO = 0x40000;
+
+enum IF_GET_IFACE = 0x0001; /* for querying only */
+enum IF_GET_PROTO = 0x0002;
+
+/* For definitions see hdlc.h */
+enum IF_IFACE_V35 = 0x1000; /* V.35 serial interface */
+enum IF_IFACE_V24 = 0x1001; /* V.24 serial interface */
+enum IF_IFACE_X21 = 0x1002; /* X.21 serial interface */
+enum IF_IFACE_T1 = 0x1003; /* T1 telco serial interface */
+enum IF_IFACE_E1 = 0x1004; /* E1 telco serial interface */
+enum IF_IFACE_SYNC_SERIAL = 0x1005; /* can't be set by software */
+enum IF_IFACE_X21D = 0x1006; /* X.21 Dual Clocking (FarSite) */
+
+/* For definitions see hdlc.h */
+enum IF_PROTO_HDLC = 0x2000; /* raw HDLC protocol */
+enum IF_PROTO_PPP = 0x2001; /* PPP protocol */
+enum IF_PROTO_CISCO = 0x2002; /* Cisco HDLC protocol */
+enum IF_PROTO_FR = 0x2003; /* Frame Relay protocol */
+enum IF_PROTO_FR_ADD_PVC = 0x2004; /* Create FR PVC */
+enum IF_PROTO_FR_DEL_PVC = 0x2005; /* Delete FR PVC */
+enum IF_PROTO_X25 = 0x2006; /* X.25 */
+enum IF_PROTO_HDLC_ETH = 0x2007; /* raw HDLC, Ethernet emulation */
+enum IF_PROTO_FR_ADD_ETH_PVC = 0x2008; /* Create FR Ethernet-bridged PVC */
+enum IF_PROTO_FR_DEL_ETH_PVC = 0x2009; /* Delete FR Ethernet-bridged PVC */
+enum IF_PROTO_FR_PVC = 0x200A; /* for reading PVC status */
+enum IF_PROTO_FR_ETH_PVC = 0x200B;
+enum IF_PROTO_RAW = 0x200C; /* RAW Socket */
+
+enum IF_OPER_UNKNOWN = 0;
+enum IF_OPER_NOTPRESENT = 1;
+enum IF_OPER_DOWN = 2;
+enum IF_OPER_LOWERLAYERDOWN = 3;
+enum IF_OPER_TESTING = 4;
+enum IF_OPER_DORMANT = 5;
+enum IF_OPER_UP = 6;
+
+enum IF_LINK_MODE_DEFAULT = 0;
+enum IF_LINK_MODE_DORMANT = 1; /* limit upward transition to dormant */
+enum IF_LINK_MODE_TESTING = 2; /* limit upward transition to testing */
+
+struct ifmap {
+ uint mem_start;
+ uint mem_end;
+ ushort base_addr;
+ ubyte irq;
+ ubyte dma;
+ ubyte port;
+ /* 3 bytes spare */
+}
+
+struct if_settings {
+ uint type; /* Type of physical device or protocol */
+ uint size; /* Size of the data allocated by the caller */
+ union {
+ /* {atm/eth/dsl}_settings anyone ? */
+ raw_hdlc_proto* raw_hdlc;
+ cisco_proto* cisco;
+ fr_proto* fr;
+ fr_proto_pvc* fr_pvc;
+ fr_proto_pvc_info* fr_pvc_info;
+ x25_hdlc_proto* x25;
+
+ /* interface settings */
+ sync_serial_settings* sync;
+ te1_settings* te1;
+ };
+}
+
+struct ifreq {
+ union
+ {
+ char[IFNAMSIZ] ifrn_name = 0; /* if name, e.g. "en0" */
+ }
+
+ union {
+
+ short ifru_flags;
+ int ifru_ivalue;
+ int ifru_mtu;
+ ifmap ifru_map;
+ char[IFNAMSIZ] ifru_slave; /* Just fits the size */
+ char[IFNAMSIZ] ifru_newname;
+ void* ifru_data;
+ if_settings ifru_settings;
+ }
+}
+
+struct ifconf {
+ int ifc_len; /* size of buffer */
+ union {
+ char* ifcu_buf;
+ ifreq* ifcu_req;
+ }
+}
static if (_GNU_SOURCE)
{
pure void* memmem(return scope const void* haystack, size_t haystacklen, scope const void* needle, size_t needlelen);
+ /// return string describing error number
+ const(char)* strerrorname_np(int);
+ /// ditto
+ const(char)* strerrordesc_np(int);
}
enum: uint
{
- IN_ACCESS = 0x00000000,
+ IN_ACCESS = 0x00000001,
IN_MODIFY = 0x00000002,
IN_ATTRIB = 0x00000004,
IN_CLOSE_WRITE = 0x00000008,
public import core.sys.posix.sys.types;
-int getentropy(void*, size_t);
+public import core.sys.posix.unistd : getentropy;
int getthrname(pid_t, char*, size_t);
int pledge(const scope char*, const scope char*);
int setthrname(pid_t, const scope char*);
version (Posix):
extern (C) nothrow @nogc:
-enum _XOPEN_SOURCE = 600;
+enum _XOPEN_SOURCE = 700;
enum _POSIX_SOURCE = true;
-enum _POSIX_C_SOURCE = 200112L;
+enum _POSIX_C_SOURCE = 200809L;
version (CRuntime_Glibc)
{
// Not present in Musl sources
enum __REDIRECT = false;
- // Those three are irrelevant for Musl as it always uses 64 bits off_t
- enum __USE_FILE_OFFSET64 = false;
+ // Always use code paths that are compatible with 64 bits off_t
+ enum __USE_FILE_OFFSET64 = true;
enum __USE_LARGEFILE = __USE_FILE_OFFSET64 && !__REDIRECT;
enum __USE_LARGEFILE64 = __USE_FILE_OFFSET64 && !__REDIRECT;
short l_type;
short l_whence;
}
+
+ enum AT_FDCWD = -2;
+ enum AT_EACCESS = 0x0010;
+ enum AT_SYMLINK_NOFOLLOW = 0x0020;
+ enum AT_SYMLINK_FOLLOW = 0x0040;
+ enum AT_REMOVEDIR = 0x0080;
}
else version (FreeBSD)
{
short l_whence;
}
- enum AT_SYMLINK_NOFOLLOW = 0x200;
enum AT_FDCWD = -100;
+ enum AT_EACCESS = 0x100;
+ enum AT_SYMLINK_NOFOLLOW = 0x200;
+ enum AT_SYMLINK_FOLLOW = 0x400;
+ enum AT_REMOVEDIR = 0x800;
}
else version (OpenBSD)
{
short l_type;
short l_whence;
}
+
+ enum AT_FDCWD = -100;
+ enum AT_EACCESS = 0x100;
+ enum AT_SYMLINK_NOFOLLOW = 0x200;
+ enum AT_SYMLINK_FOLLOW = 0x400;
+ enum AT_REMOVEDIR = 0x800;
}
else version (DragonFlyBSD)
{
}
alias oflock = flock;
+
+ enum AT_FDCWD = 0xFFFAFDCD;
+ enum AT_SYMLINK_NOFOLLOW = 1;
+ enum AT_REMOVEDIR = 2;
+ enum AT_EACCESS = 4;
+ enum AT_SYMLINK_FOLLOW = 8;
}
else version (Solaris)
{
c_long[4] l_pad;
}
}
+ enum AT_FDCWD = -3041965;
+ enum AT_SYMLINK_NOFOLLOW = 0x1000;
+ enum AT_SYMLINK_FOLLOW = 0x2000;
+ enum AT_REMOVEDIR = 0x1;
+ enum AT_EACCESS = 0x4;
}
else
{
int creat(const scope char*, mode_t);
int fcntl(int, int, ...);
int open(const scope char*, int, ...);
+int openat(int, const scope char*, int, ...);
*/
version (CRuntime_Glibc)
{
int open64(const scope char*, int, ...);
alias open64 open;
+
+ int openat64(int, const scope char*, int, ...);
+ alias openat64 openat;
}
else
{
int creat(const scope char*, mode_t);
int open(const scope char*, int, ...);
+ int openat(int, const scope char*, int, ...);
}
}
else version (Darwin)
{
int creat(const scope char*, mode_t);
int open(const scope char*, int, ...);
+ int openat(int, const scope char*, int, ...);
}
else version (FreeBSD)
{
int creat(const scope char*, mode_t);
int open(const scope char*, int, ...);
+ int openat(int, const scope char*, int, ...);
}
else version (OpenBSD)
{
int creat(const scope char*, mode_t);
int open(const scope char*, int, ...);
+ int openat(int, const scope char*, int, ...);
}
else version (NetBSD)
{
int creat(const scope char*, mode_t);
int open(const scope char*, int, ...);
+ int openat(int, const scope char*, int, ...);
}
else version (DragonFlyBSD)
{
int creat(const scope char*, mode_t);
int open(const scope char*, int, ...);
+ int openat(int, const scope char*, int, ...);
}
else version (Solaris)
{
{
int creat(const scope char*, mode_t);
int open(const scope char*, int, ...);
+ int openat(int, const scope char*, int, ...);
static if (__USE_LARGEFILE64)
{
alias creat creat64;
alias open open64;
+ alias openat openat64;
}
}
else
int open64(const scope char*, int, ...);
alias open64 open;
+
+ int openat64(int, const scope char*, int, ...);
+ alias openat64 openat;
}
else
{
int creat(const scope char*, mode_t);
int open(const scope char*, int, ...);
+ int openat(int, const scope char*, int, ...);
}
}
}
{
int creat(const scope char*, mode_t);
int open(const scope char*, int, ...);
+ int openat(int, const scope char*, int, ...);
}
else version (CRuntime_Musl)
{
+ int creat(const scope char*, mode_t);
int open(const scope char*, int, ...);
+ int openat(int, const scope char*, int, ...);
}
else version (CRuntime_UClibc)
{
int open64(const scope char*, int, ...);
alias open64 open;
+
+ int openat64(int, const scope char*, int, ...);
+ alias openat64 openat;
}
else
{
int creat(const scope char*, mode_t);
int open(const scope char*, int, ...);
+ int openat(int, const scope char*, int, ...);
}
}
else
PTHREAD_CREATE_DETACHED
}
+ enum
+ {
+ PTHREAD_EXPLICIT_SCHED = 0,
+ PTHREAD_INHERIT_SCHED = 1,
+ }
+
enum PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t.init;
enum PTHREAD_ONCE_INIT = pthread_once_t.init;
}
else version (CRuntime_Musl)
{
+ enum
+ {
+ PTHREAD_CANCEL_ENABLE = 0,
+ PTHREAD_CANCEL_DISABLE = 1,
+ PTHREAD_CANCEL_DEFERRED = 0,
+ PTHREAD_CANCEL_ASYNCHRONOUS = 1,
+ }
+
+ enum PTHREAD_CANCELED = cast(void*) -1;
+
enum
{
PTHREAD_CREATE_JOINABLE = 0,
PTHREAD_CREATE_DETACHED = 1
}
+ enum
+ {
+ PTHREAD_INHERIT_SCHED = 0,
+ PTHREAD_EXPLICIT_SCHED = 1,
+ }
+
enum PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t.init;
enum PTHREAD_ONCE_INIT = pthread_once_t.init;
/**
- * D header file for C99.
+ * D header file for C99/C11.
*
* $(C_HEADER_DESCRIPTION pubs.opengroup.org/onlinepubs/009695399/basedefs/_time.h.html, _time.h)
*
}
public import core.sys.posix.sys.types : time_t, clock_t;
+public import core.sys.posix.time : timespec;
+
+/// timespec_get introduced in C11
+@system int timespec_get(timespec* ts, int base);
+
+/// Base Value used for timespec_get
+enum TIME_UTC = 1;
///
version (CRuntime_Glibc)
char* gets(char*);
int pclose(FILE*);
FILE* popen(const scope char*, const scope char*);
+int renameat(int, const scope char*, int, const scope char*);
*/
version (CRuntime_Glibc)
ssize_t getdelim(char**, size_t*, int, FILE*);
ssize_t getline(char**, size_t*, FILE*);
+ int renameat(int, const scope char*, int, const scope char*);
}
else version (CRuntime_UClibc)
{
ssize_t getdelim(char**, size_t*, int, FILE*);
ssize_t getline(char**, size_t*, FILE*);
+ int renameat(int, const scope char*, int, const scope char*);
}
else version (CRuntime_Musl)
{
ssize_t getdelim(char**, size_t*, int, FILE*);
ssize_t getline(char**, size_t*, FILE*);
+ int renameat(int, const scope char*, int, const scope char*);
}
else version (CRuntime_Bionic)
{
ssize_t getdelim(char**, size_t*, int, FILE*);
ssize_t getline(char**, size_t*, FILE*);
+ int renameat(int, const scope char*, int, const scope char*);
}
else version (Darwin)
{
ssize_t getdelim(char**, size_t*, int, FILE*);
ssize_t getline(char**, size_t*, FILE*);
+ int renameat(int, const scope char*, int, const scope char*);
}
else version (FreeBSD)
{
{
ssize_t getdelim(char**, size_t*, int, FILE*);
ssize_t getline(char**, size_t*, FILE*);
+ int renameat(int, const scope char*, int, const scope char*);
}
}
else version (NetBSD)
ssize_t getdelim(char**, size_t*, int, FILE*);
ssize_t getline(char**, size_t*, FILE*);
+ int renameat(int, const scope char*, int, const scope char*);
}
else version (DragonFlyBSD)
{
ssize_t getdelim(char**, size_t*, int, FILE*);
ssize_t getline(char**, size_t*, FILE*);
+ int renameat(int, const scope char*, int, const scope char*);
}
else version (Solaris)
{
ssize_t getdelim(char**, size_t*, int, FILE*);
ssize_t getline(char**, size_t*, FILE*);
+ int renameat(int, const scope char*, int, const scope char*);
}
else version (Posix)
{
ipc_perm shm_perm;
size_t shm_segsz;
time_t shm_atime;
- version (X86_64) {} else c_ulong __unused1;
+ static if (time_t.sizeof == 4) c_ulong __unused1;
time_t shm_dtime;
- version (X86_64) {} else c_ulong __unused2;
+ static if (time_t.sizeof == 4) c_ulong __unused2;
time_t shm_ctime;
- version (X86_64) {} else c_ulong __unused3;
+ static if (time_t.sizeof == 4) c_ulong __unused3;
pid_t shm_cpid;
pid_t shm_lpid;
shmatt_t shm_nattch;
{
version (X86)
{
- struct stat_t
+ version (CRuntime_Musl)
{
- dev_t st_dev;
- ushort __pad1;
- static if (!__USE_FILE_OFFSET64)
+ struct stat_t
{
+ dev_t st_dev;
+ ushort __pad1;
+ static if (!__USE_FILE_OFFSET64)
+ {
+ ino_t st_ino;
+ }
+ else
+ {
+ uint __st_ino;
+ }
+ mode_t st_mode;
+ nlink_t st_nlink;
+ uid_t st_uid;
+ gid_t st_gid;
+ dev_t st_rdev;
+ ushort __pad2;
+ off_t st_size;
+ blksize_t st_blksize;
+ blkcnt_t st_blocks;
+ private struct __timespec32
+ {
+ c_long tv_sec;
+ c_long tv_nsec;
+ }
+ __timespec32 __st_atim32;
+ __timespec32 __st_mtim32;
+ __timespec32 __st_ctim32;
ino_t st_ino;
- }
- else
- {
- uint __st_ino;
- }
- mode_t st_mode;
- nlink_t st_nlink;
- uid_t st_uid;
- gid_t st_gid;
- dev_t st_rdev;
- ushort __pad2;
- off_t st_size;
- blksize_t st_blksize;
- blkcnt_t st_blocks;
- static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
- {
timespec st_atim;
timespec st_mtim;
timespec st_ctim;
ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
}
}
- else
- {
- time_t st_atime;
- ulong_t st_atimensec;
- time_t st_mtime;
- ulong_t st_mtimensec;
- time_t st_ctime;
- ulong_t st_ctimensec;
- }
- static if (__USE_FILE_OFFSET64)
- {
- ino_t st_ino;
- }
- else
+ static assert(stat_t.sizeof == 144);
+ }
+ else
+ {
+ struct stat_t
{
- c_ulong __unused4;
- c_ulong __unused5;
+ dev_t st_dev;
+ ushort __pad1;
+ static if (!__USE_FILE_OFFSET64)
+ {
+ ino_t st_ino;
+ }
+ else
+ {
+ uint __st_ino;
+ }
+ mode_t st_mode;
+ nlink_t st_nlink;
+ uid_t st_uid;
+ gid_t st_gid;
+ dev_t st_rdev;
+ ushort __pad2;
+ off_t st_size;
+ blksize_t st_blksize;
+ blkcnt_t st_blocks;
+ static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
+ {
+ timespec st_atim;
+ timespec st_mtim;
+ timespec st_ctim;
+ extern(D) @safe @property inout pure nothrow
+ {
+ ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
+ ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
+ ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
+ }
+ }
+ else
+ {
+ time_t st_atime;
+ ulong_t st_atimensec;
+ time_t st_mtime;
+ ulong_t st_mtimensec;
+ time_t st_ctime;
+ ulong_t st_ctimensec;
+ }
+ static if (__USE_FILE_OFFSET64)
+ {
+ ino_t st_ino;
+ }
+ else
+ {
+ c_ulong __unused4;
+ c_ulong __unused5;
+ }
}
}
}
}
else version (MIPS_O32)
{
- struct stat_t
+ version (CRuntime_Musl)
{
- version (CRuntime_Musl)
+ struct stat_t
{
dev_t st_dev;
- c_long[2] st_pad1;
- }
- else
- {
- c_ulong st_dev;
- c_long[3] st_pad1;
- }
- ino_t st_ino;
- mode_t st_mode;
- nlink_t st_nlink;
- uid_t st_uid;
- gid_t st_gid;
- c_ulong st_rdev;
- static if (!__USE_FILE_OFFSET64)
- {
- c_long[2] st_pad2;
- off_t st_size;
- c_long st_pad3;
- }
- else
- {
- c_long[3] st_pad2;
+ c_long[2] __pad1;
+ ino_t st_ino;
+ mode_t st_mode;
+ nlink_t st_nlink;
+ uid_t st_uid;
+ gid_t st_gid;
+ dev_t st_rdev;
+ c_long[2] __pad2;
off_t st_size;
- }
- static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
- {
+ private struct __timespec32
+ {
+ c_long tv_sec;
+ c_long tv_nsec;
+ }
+ __timespec32 __st_atim32;
+ __timespec32 __st_mtim32;
+ __timespec32 __st_ctim32;
+ blksize_t st_blksize;
+ c_long __pad3;
+ blkcnt_t st_blocks;
timespec st_atim;
timespec st_mtim;
timespec st_ctim;
ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
}
+ c_long[2] __pad4;
}
- else
- {
- time_t st_atime;
- c_ulong st_atimensec;
- time_t st_mtime;
- c_ulong st_mtimensec;
- time_t st_ctime;
- c_ulong st_ctimensec;
- }
- blksize_t st_blksize;
- static if (!__USE_FILE_OFFSET64)
- {
- blkcnt_t st_blocks;
- }
- else
+ }
+ else
+ {
+ struct stat_t
{
- c_long st_pad4;
- blkcnt_t st_blocks;
+ c_ulong st_dev;
+ c_long[3] st_pad1;
+ ino_t st_ino;
+ mode_t st_mode;
+ nlink_t st_nlink;
+ uid_t st_uid;
+ gid_t st_gid;
+ c_ulong st_rdev;
+ static if (!__USE_FILE_OFFSET64)
+ {
+ c_long[2] st_pad2;
+ off_t st_size;
+ c_long st_pad3;
+ }
+ else
+ {
+ c_long[3] st_pad2;
+ off_t st_size;
+ }
+ static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
+ {
+ timespec st_atim;
+ timespec st_mtim;
+ timespec st_ctim;
+ extern(D) @safe @property inout pure nothrow
+ {
+ ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
+ ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
+ ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
+ }
+ }
+ else
+ {
+ time_t st_atime;
+ c_ulong st_atimensec;
+ time_t st_mtime;
+ c_ulong st_mtimensec;
+ time_t st_ctime;
+ c_ulong st_ctimensec;
+ }
+ blksize_t st_blksize;
+ static if (!__USE_FILE_OFFSET64)
+ {
+ blkcnt_t st_blocks;
+ }
+ else
+ {
+ c_long st_pad4;
+ blkcnt_t st_blocks;
+ }
+ c_long[14] st_pad6;
}
- c_long[14] st_pad5;
}
static if (!__USE_FILE_OFFSET64)
static assert(stat_t.sizeof == 144);
}
else version (ARM)
{
- private
- {
- alias __dev_t = ulong;
- alias __ino_t = c_ulong;
- alias __ino64_t = ulong;
- alias __mode_t = uint;
- alias __nlink_t = size_t;
- alias __uid_t = uint;
- alias __gid_t = uint;
- alias __off_t = c_long;
- alias __off64_t = long;
- alias __blksize_t = c_long;
- alias __blkcnt_t = c_long;
- alias __blkcnt64_t = long;
- alias __timespec = timespec;
- alias __time_t = time_t;
- }
- struct stat_t
+ version (CRuntime_Musl)
{
- __dev_t st_dev;
- ushort __pad1;
-
- static if (!__USE_FILE_OFFSET64)
- {
- __ino_t st_ino;
- }
- else
- {
- __ino_t __st_ino;
- }
- __mode_t st_mode;
- __nlink_t st_nlink;
- __uid_t st_uid;
- __gid_t st_gid;
- __dev_t st_rdev;
- ushort __pad2;
-
- static if (!__USE_FILE_OFFSET64)
- {
- __off_t st_size;
- }
- else
- {
- __off64_t st_size;
- }
- __blksize_t st_blksize;
-
- static if (!__USE_FILE_OFFSET64)
- {
- __blkcnt_t st_blocks;
- }
- else
- {
- __blkcnt64_t st_blocks;
- }
-
- static if ( _DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
- {
- __timespec st_atim;
- __timespec st_mtim;
- __timespec st_ctim;
+ // Matches struct stat from musl arch/arm/bits/stat.h
+ // See: https://git.musl-libc.org/cgit/musl/tree/arch/arm/bits/stat.h?h=v1.2.3
+ //
+ // Type definitions from https://git.musl-libc.org/cgit/musl/tree/include/alltypes.h.in?h=v1.2.3
+ // with ARM-specific _Int64=long long and _Reg=int from
+ // https://git.musl-libc.org/cgit/musl/tree/arch/arm/bits/alltypes.h.in?h=v1.2.3#n3
+ //
+ // Key 64-bit LFS types (always 64-bit on musl):
+ // dev_t = unsigned _Int64 = unsigned long long (line 31)
+ // off_t = _Int64 = long long (line 29)
+ // ino_t = unsigned _Int64 = unsigned long long (line 30)
+ // blkcnt_t = _Int64 = long long (line 33)
+ // blksize_t = long = long (32-bit) (line 32)
+ // nlink_t = unsigned _Reg = unsigned int (line 28)
+ struct stat_t
+ {
+ ulong st_dev;
+ int __st_dev_padding;
+ c_long __st_ino_truncated;
+ mode_t st_mode;
+ uint st_nlink;
+ uid_t st_uid;
+ gid_t st_gid;
+ ulong st_rdev;
+ int __st_rdev_padding;
+ long st_size;
+ c_long st_blksize;
+ long st_blocks;
+ struct __timespec32
+ {
+ c_long tv_sec;
+ c_long tv_nsec;
+ }
+ __timespec32 __st_atim32, __st_mtim32, __st_ctim32;
+ ulong st_ino;
+ timespec st_atim;
+ timespec st_mtim;
+ timespec st_ctim;
extern(D) @safe @property inout pure nothrow
{
ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
}
}
- else
- {
- __time_t st_atime;
- c_ulong st_atimensec;
- __time_t st_mtime;
- c_ulong st_mtimensec;
- __time_t st_ctime;
- c_ulong st_ctimensec;
- }
+ static assert(stat_t.sizeof == 152);
+ }
+ else
+ {
+ private
+ {
+ alias __dev_t = ulong;
+ alias __ino_t = c_ulong;
+ alias __ino64_t = ulong;
+ alias __mode_t = uint;
+ alias __nlink_t = size_t;
+ alias __uid_t = uint;
+ alias __gid_t = uint;
+ alias __off_t = c_long;
+ alias __off64_t = long;
+ alias __blksize_t = c_long;
+ alias __blkcnt_t = c_long;
+ alias __blkcnt64_t = long;
+ alias __timespec = timespec;
+ alias __time_t = time_t;
+ }
+ struct stat_t
+ {
+ __dev_t st_dev;
+ ushort __pad1;
- static if (!__USE_FILE_OFFSET64)
- {
- c_ulong __unused4;
- c_ulong __unused5;
- }
- else
- {
- __ino64_t st_ino;
+ static if (!__USE_FILE_OFFSET64)
+ {
+ __ino_t st_ino;
+ }
+ else
+ {
+ __ino_t __st_ino;
+ }
+ __mode_t st_mode;
+ __nlink_t st_nlink;
+ __uid_t st_uid;
+ __gid_t st_gid;
+ __dev_t st_rdev;
+ ushort __pad2;
+
+ static if (!__USE_FILE_OFFSET64)
+ {
+ __off_t st_size;
+ }
+ else
+ {
+ __off64_t st_size;
+ }
+ __blksize_t st_blksize;
+
+ static if (!__USE_FILE_OFFSET64)
+ {
+ __blkcnt_t st_blocks;
+ }
+ else
+ {
+ __blkcnt64_t st_blocks;
+ }
+
+ static if ( _DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
+ {
+ __timespec st_atim;
+ __timespec st_mtim;
+ __timespec st_ctim;
+ extern(D) @safe @property inout pure nothrow
+ {
+ ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
+ ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
+ ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
+ }
+ }
+ else
+ {
+ __time_t st_atime;
+ c_ulong st_atimensec;
+ __time_t st_mtime;
+ c_ulong st_mtimensec;
+ __time_t st_ctime;
+ c_ulong st_ctimensec;
+ }
+
+ static if (!__USE_FILE_OFFSET64)
+ {
+ c_ulong __unused4;
+ c_ulong __unused5;
+ }
+ else
+ {
+ __ino64_t st_ino;
+ }
}
+ static if (__USE_FILE_OFFSET64)
+ static assert(stat_t.sizeof == 104);
+ else
+ static assert(stat_t.sizeof == 88);
}
- static if (__USE_FILE_OFFSET64)
- static assert(stat_t.sizeof == 104);
- else
- static assert(stat_t.sizeof == 88);
}
else version (AArch64)
{
extern bool S_TYPEISSEM( stat_t* buf ) { return false; }
extern bool S_TYPEISSHM( stat_t* buf ) { return false; }
}
-
- enum UTIME_NOW = 0x3fffffff;
- enum UTIME_OMIT = 0x3ffffffe;
}
else version (Darwin)
{
enum S_ISUID = 0x800; // octal 0004000
enum S_ISGID = 0x400; // octal 0002000
enum S_ISVTX = 0x200; // octal 0001000
-
- enum UTIME_NOW = -1;
- enum UTIME_OMIT = -2;
}
else version (NetBSD)
{
extern (D) bool S_ISREG()( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); }
extern (D) bool S_ISLNK()( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); }
extern (D) bool S_ISSOCK()( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); }
-
- int utimensat(int dirfd, const char *pathname,
- ref const(timespec)[2] times, int flags);
- int futimens(int fd, ref const(timespec)[2] times);
}
else version (Darwin)
{
extern (D) bool S_ISREG()( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); }
extern (D) bool S_ISLNK()( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); }
extern (D) bool S_ISSOCK()( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); }
-
- // Since FreeBSD 11:
- version (none)
- {
- int utimensat(int dirfd, const char *pathname,
- ref const(timespec)[2] times, int flags);
- int futimens(int fd, ref const(timespec)[2] times);
- }
}
else version (NetBSD)
{
extern (D) bool S_ISREG()( uint mode ) { return S_ISTYPE( mode, S_IFREG ); }
extern (D) bool S_ISLNK()( uint mode ) { return S_ISTYPE( mode, S_IFLNK ); }
extern (D) bool S_ISSOCK()( uint mode ) { return S_ISTYPE( mode, S_IFSOCK ); }
-
- // Added since Lollipop
- int utimensat(int dirfd, const char *pathname,
- ref const(timespec)[2] times, int flags);
}
else version (CRuntime_Musl)
{
extern (D) bool S_ISREG()( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); }
extern (D) bool S_ISLNK()( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); }
extern (D) bool S_ISSOCK()( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); }
-
- int utimensat(int dirfd, const char *pathname,
- ref const(timespec)[2] times, int flags);
}
else version (CRuntime_UClibc)
{
extern (D) bool S_ISREG()( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); }
extern (D) bool S_ISLNK()( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); }
extern (D) bool S_ISSOCK()( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); }
-
- int utimensat(int dirfd, const char *pathname,
- ref const(timespec)[2] times, int flags);
- int futimens(int fd, ref const(timespec)[2] times);
}
else
{
/*
int chmod(const scope char*, mode_t);
int fchmod(int, mode_t);
-int fstat(int, stat*);
-int lstat(const scope char*, stat*);
+int fchmodat(int, const scope char*, mode_t, int);
+int fstat(int, stat_t*);
+int fstatat(int, const scope char*, stat_t*, int);
+int futimens(int, ref const(timespec)[2]);
+int lstat(const scope char*, stat_t*);
int mkdir(const scope char*, mode_t);
+int mkdirat(int, const scope char*, mode_t);
int mkfifo(const scope char*, mode_t);
-int stat(const scope char*, stat*);
+int mkfifoat(int, const scope char*, mode_t);
+int stat(const scope char*, stat_t*);
mode_t umask(mode_t);
+int utimensat(int, const scope char*, ref const(timespec)[2], int);
*/
int chmod(const scope char*, mode_t);
int stat64(const scope char*, stat_t*);
alias stat64 stat;
+
+ int fstatat64(int, const scope char*, stat_t*, int);
+ alias fstatat64 fstatat;
+
}
else
{
int lstat(const scope char*, stat_t*);
int stat(const scope char*, stat_t*);
}
+ int fchmodat(int, const scope char*, mode_t, int);
+ int futimens(int, ref const(timespec)[2]);
+ int mkdirat(int, const scope char*, mode_t);
+ int mkfifoat(int, const scope char*, mode_t);
+ int utimensat(int, const scope char*, ref const(timespec)[2], int);
}
else version (Solaris)
{
int fstat(int, stat_t*) @trusted;
int lstat(const scope char*, stat_t*);
int stat(const scope char*, stat_t*);
+ int fstatat(int, const scope char*, stat_t*, int);
static if (__USE_LARGEFILE64)
{
alias fstat fstat64;
alias lstat lstat64;
alias stat stat64;
+ alias fstatat fstatat64;
}
}
else
int stat64(const scope char*, stat_t*);
alias stat64 stat;
+
+ int fstatat64(int, const scope char*, stat_t*, int);
+ alias fstatat64 fstatat;
}
else
{
int fstat(int, stat_t*) @trusted;
int lstat(const scope char*, stat_t*);
int stat(const scope char*, stat_t*);
+ int fstatat(int, const scope char*, stat_t*, int);
}
}
+ int fchmodat(int, const scope char*, mode_t, int);
+ int futimens(int, ref const(timespec)[2]);
+ int mkdirat(int, const scope char*, mode_t);
+ int mkfifoat(int, const scope char*, mode_t);
+ int utimensat(int, const scope char*, ref const(timespec)[2], int);
}
else version (Darwin)
{
int lstat(const scope char*, stat_t*);
int stat(const scope char*, stat_t*);
}
+ int fchmodat(int, const scope char*, mode_t, int);
+ int fstatat(int, const scope char*, stat_t*, int);
+ int futimens(int, ref const(timespec)[2]);
+ int mkdirat(int, const scope char*, mode_t);
+ int mkfifoat(int, const scope char*, mode_t);
+ int utimensat(int, const scope char*, ref const(timespec)[2], int);
}
else version (FreeBSD)
{
pragma(mangle, "stat@FBSD_1.0") int stat(const scope char*, stat_t*);
}
}
+ static if (__FreeBSD_version >= 800000)
+ {
+ int fchmodat(int, const scope char*, mode_t, int);
+ int fstatat(int, const scope char*, stat_t*, int);
+ int mkdirat(int, const scope char*, mode_t);
+ int mkfifoat(int, const scope char*, mode_t);
+ }
+ static if (__FreeBSD_version >= 1003000)
+ {
+ int futimens(int, ref const(timespec)[2]);
+ int utimensat(int, const scope char*, ref const(timespec)[2], int);
+ }
}
else version (NetBSD)
{
alias __fstat50 fstat;
alias __lstat50 lstat;
alias __stat50 stat;
+ int fchmodat(int, const scope char*, mode_t, int);
+ int fstatat(int, const scope char*, stat_t*, int);
+ int futimens(int, ref const(timespec)[2]);
+ int mkdirat(int, const scope char*, mode_t);
+ int mkfifoat(int, const scope char*, mode_t);
+ int utimensat(int, const scope char*, ref const(timespec)[2], int);
}
else version (OpenBSD)
{
int fstat(int, stat_t*);
int lstat(const scope char*, stat_t*);
int stat(const scope char*, stat_t*);
+ int fchmodat(int, const scope char*, mode_t, int);
+ int fstatat(int, const scope char*, stat_t*, int);
+ int futimens(int, ref const(timespec)[2]);
+ int mkdirat(int, const scope char*, mode_t);
+ int mkfifoat(int, const scope char*, mode_t);
+ int utimensat(int, const scope char*, ref const(timespec)[2], int);
}
else version (DragonFlyBSD)
{
int fstat(int, stat_t*);
int lstat(const scope char*, stat_t*);
int stat(const scope char*, stat_t*);
+ int fchmodat(int, const scope char*, mode_t, int);
+ int fstatat(int, const scope char*, stat_t*, int);
+ int futimens(int, ref const(timespec)[2]);
+ int mkdirat(int, const scope char*, mode_t);
+ int mkfifoat(int, const scope char*, mode_t);
+ int utimensat(int, const scope char*, ref const(timespec)[2], int);
}
else version (CRuntime_Bionic)
{
int fstat(int, stat_t*) @trusted;
int lstat(const scope char*, stat_t*);
int stat(const scope char*, stat_t*);
+ int fchmodat(int, const scope char*, mode_t, int);
+ int fstatat(int, const scope char*, stat_t*, int);
+ int futimens(int, ref const(timespec)[2]);
+ int mkdirat(int, const scope char*, mode_t);
+ int mkfifoat(int, const scope char*, mode_t);
+ int utimensat(int, const scope char*, ref const(timespec)[2], int);
}
else version (CRuntime_Musl)
{
int fstat(int, stat_t*);
pragma(mangle, muslRedirTime64Mangle!("lstat", "__lstat_time64"))
int lstat(const scope char*, stat_t*);
+ pragma(mangle, muslRedirTime64Mangle!("fstatat", "__fstatat_time64"))
+ int fstatat(int, const scope char*, stat_t*, int);
alias fstat fstat64;
alias lstat lstat64;
alias stat stat64;
+ int fchmodat(int, const scope char*, mode_t, int);
+ int futimens(int, ref const(timespec)[2]);
+ int mkdirat(int, const scope char*, mode_t);
+ int mkfifoat(int, const scope char*, mode_t);
+ int utimensat(int, const scope char*, ref const(timespec)[2], int);
}
else version (CRuntime_UClibc)
{
int stat64(const scope char*, stat_t*);
alias stat64 stat;
+
+ int fstatat64(int, const scope char*, stat_t*, int);
+ alias fstatat64 fstatat;
}
else
{
int fstat(int, stat_t*) @trusted;
int lstat(const scope char*, stat_t*);
int stat(const scope char*, stat_t*);
+ int fstatat(int, const scope char*, stat_t*, int);
}
+ int fchmodat(int, const scope char*, mode_t, int);
+ int futimens(int, ref const(timespec)[2]);
+ int mkdirat(int, const scope char*, mode_t);
+ int mkfifoat(int, const scope char*, mode_t);
+ int utimensat(int, const scope char*, ref const(timespec)[2], int);
+}
+
+/*
+UTIME_NOW
+UTIME_OMIT
+*/
+version (linux)
+{
+ enum UTIME_NOW = 0x3fffffff;
+ enum UTIME_OMIT = 0x3ffffffe;
+}
+else version (Darwin)
+{
+ enum UTIME_NOW = -1;
+ enum UTIME_OMIT = -2;
+}
+else version (FreeBSD)
+{
+ enum UTIME_NOW = -1;
+ enum UTIME_OMIT = -2;
+}
+else version (NetBSD)
+{
+ enum UTIME_NOW = 0x3fffffff;
+ enum UTIME_OMIT = 0x3ffffffe;
}
+else version (OpenBSD)
+{
+ enum UTIME_NOW = -2;
+ enum UTIME_OMIT = -1;
+}
+else version (DragonFlyBSD)
+{
+ enum UTIME_NOW = -1;
+ enum UTIME_OMIT = -2;
+}
+else version (Solaris)
+{
+ enum UTIME_NOW = -1;
+ enum UTIME_OMIT = -2;
+}
+else
+{
+ static assert(false, "Unsupported platform");
+}
+
//
// Typed Memory Objects (TYM)
/*
int mknod(const scope char*, mode_t, dev_t);
+int mknodat(int, const scope char*, mode_t, dev_t);
*/
version (CRuntime_Glibc)
{
int mknod(const scope char*, mode_t, dev_t);
+ int mknodat(int, const scope char*, mode_t, dev_t);
}
else version (Darwin)
{
int mknod(const scope char*, mode_t, dev_t);
+ int mknodat(int, const scope char*, mode_t, dev_t);
}
else version (FreeBSD)
{
else
pragma(mangle, "mknod@FBSD_1.0") int mknod(const scope char*, mode_t, dev_t);
}
+ int mknodat(int, const scope char*, mode_t, dev_t);
}
else version (NetBSD)
{
int mknod(const scope char*, mode_t, dev_t);
+ int mknodat(int, const scope char*, mode_t, dev_t);
}
else version (OpenBSD)
{
int mknod(const scope char*, mode_t, dev_t);
+ int mknodat(int, const scope char*, mode_t, dev_t);
}
else version (DragonFlyBSD)
{
int mknod(const scope char*, mode_t, dev_t);
+ int mknodat(int, const scope char*, mode_t, dev_t);
}
else version (Solaris)
{
int mknod(const scope char*, mode_t, dev_t);
+ int mknodat(int, const scope char*, mode_t, dev_t);
}
else version (CRuntime_Bionic)
{
int mknod(const scope char*, mode_t, dev_t);
+ int mknodat(int, const scope char*, mode_t, dev_t);
}
else version (CRuntime_Musl)
{
int mknod(const scope char*, mode_t, dev_t);
+ int mknodat(int, const scope char*, mode_t, dev_t);
}
else version (CRuntime_UClibc)
{
int mknod(const scope char*, mode_t, dev_t);
+ int mknodat(int, const scope char*, mode_t, dev_t);
}
else
{
version (linux)
{
- static if ( __USE_FILE_OFFSET64 )
+ // Musl always uses 64-bit off_t, blkcnt_t, ino_t on all arches (LFS by default).
+ // See: https://git.musl-libc.org/cgit/musl/tree/include/alltypes.h.in?h=v1.2.3#n29
+ // off_t: _Int64 -> long long (signed 64-bit)
+ // ino_t: unsigned _Int64 -> unsigned long long (64-bit)
+ // blkcnt_t: _Int64 -> long long (signed 64-bit)
+ // For ARM, _Int64 = long long: https://git.musl-libc.org/cgit/musl/tree/arch/arm/bits/alltypes.h.in?h=v1.2.3#n3
+ version (CRuntime_Musl)
+ {
+ alias long blkcnt_t;
+ alias ulong ino_t;
+ alias long off_t;
+ }
+ else static if ( __USE_FILE_OFFSET64 )
{
alias long blkcnt_t;
alias ulong ino_t;
alias ulong_t ino_t;
alias slong_t off_t;
}
- alias slong_t blksize_t;
+ // musl overrides blksize_t to int on some 64-bit architectures.
+ // Default: long (https://git.musl-libc.org/cgit/musl/tree/include/alltypes.h.in?h=v1.2.3#n32)
+ // AArch64: int (https://git.musl-libc.org/cgit/musl/tree/arch/aarch64/bits/alltypes.h.in?h=v1.2.3#n18)
+ // RISCV64: int (https://git.musl-libc.org/cgit/musl/tree/arch/riscv64/bits/alltypes.h.in?h=v1.2.3#n12)
+ // LoongArch64: int (https://git.musl-libc.org/cgit/musl/tree/arch/loongarch64/bits/alltypes.h.in?id=522bd54e#n18)
+ version (CRuntime_Musl)
+ {
+ version (AArch64)
+ alias int blksize_t;
+ else version (RISCV64)
+ alias int blksize_t;
+ else version (LoongArch64)
+ alias int blksize_t;
+ else
+ alias slong_t blksize_t;
+ }
+ else
+ alias slong_t blksize_t;
+
alias ulong dev_t;
alias uint gid_t;
alias uint mode_t;
- alias ulong_t nlink_t;
+
+ // musl defines nlink_t as unsigned _Reg (= unsigned int on 32-bit, unsigned long on 64-bit),
+ // with arch-specific overrides.
+ // Default: unsigned _Reg (https://git.musl-libc.org/cgit/musl/tree/include/alltypes.h.in?h=v1.2.3#n28)
+ // MIPS64: unsigned (uint) (https://git.musl-libc.org/cgit/musl/tree/arch/mips64/bits/alltypes.h.in?h=v1.2.3#n22)
+ // X86_64: _Reg=long, so unsigned long (https://git.musl-libc.org/cgit/musl/tree/arch/x86_64/bits/alltypes.h.in?h=v1.2.3#n3)
+ version (CRuntime_Musl)
+ {
+ version (MIPS64)
+ alias uint nlink_t;
+ else version (X86_64)
+ alias ulong nlink_t;
+ else
+ alias uint nlink_t;
+ }
+ else
+ {
+ version (X86_64)
+ alias ulong nlink_t;
+ else version (S390)
+ alias size_t nlink_t;
+ else version (PPC64)
+ alias size_t nlink_t;
+ else version (MIPS64)
+ alias size_t nlink_t;
+ else version (HPPA64)
+ alias size_t nlink_t;
+ else
+ alias uint nlink_t;
+ }
+
alias int pid_t;
//size_t (defined in core.stdc.stddef)
alias c_long ssize_t;
version (CRuntime_Musl)
{
+ static assert(off_t.sizeof == 8);
/**
* Musl versions before v1.2.0 (up to v1.1.24) had different
* definitions for `time_t` for 32 bits.
version (linux)
{
- static if ( __USE_FILE_OFFSET64 )
+ // Musl always uses 64-bit fsblkcnt_t, fsfilcnt_t on all arches (LFS by default).
+ // See: https://git.musl-libc.org/cgit/musl/tree/include/alltypes.h.in?h=v1.2.3#n34
+ // fsblkcnt_t: unsigned _Int64 -> unsigned long long (64-bit)
+ // fsfilcnt_t: unsigned _Int64 -> unsigned long long (64-bit)
+ // For ARM, _Int64 = long long: https://git.musl-libc.org/cgit/musl/tree/arch/arm/bits/alltypes.h.in?h=v1.2.3#n3
+ version (CRuntime_Musl)
+ {
+ alias ulong fsblkcnt_t;
+ alias ulong fsfilcnt_t;
+ }
+ else static if ( __USE_FILE_OFFSET64 )
{
alias ulong fsblkcnt_t;
alias ulong fsfilcnt_t;
else version (Solaris)
{
alias uint pthread_t;
+ alias int lwpid_t; // non-standard
struct pthread_attr_t
{
module core.sys.posix.time;
import core.sys.posix.config;
+import core.sys.posix.endian;
public import core.stdc.time;
public import core.sys.posix.sys.types;
public import core.sys.posix.signal; // for sigevent
struct timespec
{
time_t tv_sec;
+ version (CRuntime_Musl)
+ int : 8 * (time_t.sizeof - c_long.sizeof) * (BYTE_ORDER == BIG_ENDIAN);
c_long tv_nsec;
+ version (CRuntime_Musl)
+ int : 8 * (time_t.sizeof - c_long.sizeof) * (BYTE_ORDER != BIG_ENDIAN);
}
}
else version (Darwin)
}
else version (CRuntime_Musl)
{
+ static assert(timespec.sizeof == 16);
+
alias int clockid_t;
alias void* timer_t;
enum CLOCK_SGI_CYCLE = 10;
enum CLOCK_TAI = 11;
+ pragma(mangle, muslRedirTime64Mangle!("nanosleep", "__nanosleep_time64"))
int nanosleep(const scope timespec*, timespec*);
pragma(mangle, muslRedirTime64Mangle!("clock_getres", "__clock_getres_time64"))
}
else version (Solaris)
{
- import core.stdc.stdint;
-
- alias uint[4] upad128_t;
-
- version (SPARC64)
- {
- enum _NGREG = 21;
- alias long greg_t;
- }
- else version (SPARC)
- {
- enum _NGREG = 19;
- alias int greg_t;
- }
- else version (X86_64)
- {
- enum _NGREG = 28;
- alias long greg_t;
- }
- else version (X86)
- {
- enum _NGREG = 19;
- alias int greg_t;
- }
- else
- static assert(0, "unimplemented");
-
- alias greg_t[_NGREG] gregset_t;
-
- version (SPARC64)
- {
- private
- {
- struct _fpq
- {
- uint *fpq_addr;
- uint fpq_instr;
- }
-
- struct fq
- {
- union
- {
- double whole;
- _fpq fpq;
- }
- }
- }
-
- struct fpregset_t
- {
- union
- {
- uint[32] fpu_regs;
- double[32] fpu_dregs;
- real[16] fpu_qregs;
- }
- fq *fpu_q;
- ulong fpu_fsr;
- ubyte fpu_qcnt;
- ubyte fpu_q_entrysize;
- ubyte fpu_en;
- }
- }
- else version (SPARC)
- {
- private
- {
- struct _fpq
- {
- uint *fpq_addr;
- uint fpq_instr;
- }
-
- struct fq
- {
- union
- {
- double whole;
- _fpq fpq;
- }
- }
- }
-
- struct fpregset_t
- {
- union
- {
- uint[32] fpu_regs;
- double[16] fpu_dregs;
- }
- fq *fpu_q;
- uint fpu_fsr;
- ubyte fpu_qcnt;
- ubyte fpu_q_entrysize;
- ubyte fpu_en;
- }
- }
- else version (X86_64)
- {
- private
- {
- union _u_st
- {
- ushort[5] fpr_16;
- upad128_t __fpr_pad;
- }
- }
-
- struct fpregset_t
- {
- union fp_reg_set
- {
- struct fpchip_state
- {
- ushort cw;
- ushort sw;
- ubyte fctw;
- ubyte __fx_rsvd;
- ushort fop;
- ulong rip;
- ulong rdp;
- uint mxcsr;
- uint mxcsr_mask;
- _u_st[8] st;
- upad128_t[16] xmm;
- upad128_t[6] __fx_ign2;
- uint status;
- uint xstatus;
- }
- uint[130] f_fpregs;
- }
- }
- }
- else version (X86)
- {
- struct fpregset_t
- {
- union u_fp_reg_set
- {
- struct s_fpchip_state
- {
- uint[27] state;
- uint status;
- uint mxcsr;
- uint xstatus;
- uint[2] __pad;
- upad128_t[8] xmm;
- }
- s_fpchip_state fpchip_state;
-
- struct s_fp_emul_space
- {
- ubyte[246] fp_emul;
- ubyte[2] fp_epad;
- }
- s_fp_emul_space fp_emul_space;
- uint[95] f_fpregs;
- }
- u_fp_reg_set fp_reg_set;
- }
- }
- else
- static assert(0, "unimplemented");
-
version (SPARC_Any)
{
- private
- {
- struct rwindow
- {
- greg_t[8] rw_local;
- greg_t[8] rw_in;
- }
-
- struct gwindows_t
- {
- int wbcnt;
- greg_t[31] *spbuf;
- rwindow[31] wbuf;
- }
-
- struct xrs_t
- {
- uint xrs_id;
- caddr_t xrs_ptr;
- }
-
- struct cxrs_t
- {
- uint cxrs_id;
- caddr_t cxrs_ptr;
- }
-
- alias int64_t[16] asrset_t;
- }
+ import core.sys.solaris.sys.regset : gregset_t, fpregset_t,
+ gwindows_t, xrs_t, asrset_t, cxrs_t;
struct mcontext_t
{
}
else version (X86_Any)
{
- private
- {
- struct xrs_t
- {
- uint xrs_id;
- caddr_t xrs_ptr;
- }
- }
+ import core.sys.solaris.sys.regset : gregset_t, fpregset_t, xrs_t;
struct mcontext_t
{
* Copyright: Copyright Sean Kelly 2005 - 2009.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Sean Kelly
- * Standards: The Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition
+ * Standards: The Open Group Base Specifications Issue 8, IEEE Std 1003.1, 2024 Edition
*/
/* Copyright Sean Kelly 2005 - 2009.
size_t confstr(int, char*, size_t);
int dup(int) @trusted;
int dup2(int, int) @trusted;
+//int dup3(int, int, int) @trusted;
int execl(const scope char*, const scope char*, ...);
int execle(const scope char*, const scope char*, ...);
int execlp(const scope char*, const scope char*, ...);
int execv(const scope char*, const scope char**);
int execve(const scope char*, const scope char**, const scope char**);
int execvp(const scope char*, const scope char**);
-void _exit(int) @trusted;
+noreturn _exit(int) @trusted;
+//int faccessat(int, const scope char*, int, int);
int fchown(int, uid_t, gid_t) @trusted;
+//int fchownat(int, const scope char*, uid_t, gid_t, int);
+//int fexecve(int, const scope char**, const scope char**);
pid_t fork() @trusted;
+//pid_t _Fork() @trusted;
c_long fpathconf(int, int) @trusted;
//int ftruncate(int, off_t);
char* getcwd(char*, size_t);
+//int getentropy(void*, size_t);
gid_t getegid() @trusted;
uid_t geteuid() @trusted;
gid_t getgid() @trusted;
pid_t getpgrp() @trusted;
pid_t getpid() @trusted;
pid_t getppid() @trusted;
+//int getresgid(gid_t*, gid_t*, gid_t*);
+//int getresuid(uid_t*, uid_t*, uid_t*);
uid_t getuid() @trusted;
int isatty(int) @trusted;
int link(const scope char*, const scope char*);
+//int linkat(int, const scope char*, int, const scope char*, int);
//off_t lseek(int, off_t, int);
c_long pathconf(const scope char*, int);
int pause() @trusted;
int pipe(ref int[2]) @trusted;
+//int pipe2(ref int[2], int) @trusted;
ssize_t read(int, void*, size_t);
ssize_t readlink(const scope char*, char*, size_t);
+//ssize_t readlinkat(int, const scope char*, char*, size_t);
int rmdir(const scope char*);
int setegid(gid_t) @trusted;
int seteuid(uid_t) @trusted;
int setgid(gid_t) @trusted;
int setgroups(size_t, const scope gid_t*) @trusted;
int setpgid(pid_t, pid_t) @trusted;
+//int setresgid(gid_t, gid_t, gid_t) @trusted;
+//int setresuid(uid_t, uid_t, uid_t) @trusted;
pid_t setsid() @trusted;
int setuid(uid_t) @trusted;
uint sleep(uint) @trusted;
int symlink(const scope char*, const scope char*);
+//int symlinkat(const scope char*, int, const scope char*);
c_long sysconf(int) @trusted;
pid_t tcgetpgrp(int) @trusted;
int tcsetpgrp(int, pid_t) @trusted;
char* ttyname(int) @trusted;
int ttyname_r(int, char*, size_t);
int unlink(const scope char*);
+//int unlinkat(int, const scope char*, int);
ssize_t write(int, const scope void*, size_t);
version (CRuntime_Glibc)
{
int ftruncate(int, off_t) @trusted;
}
+ int faccessat(int, const scope char*, int, int);
+ int fchownat(int, const scope char*, uid_t, gid_t, int);
+ int fexecve(int, const scope char**, const scope char**);
+ pid_t _Fork() @trusted;
+ int getentropy(void*, size_t);
+ int getresgid(gid_t*, gid_t*, gid_t*);
+ int getresuid(uid_t*, uid_t*, uid_t*);
+ int linkat(int, const scope char*, int, const scope char*, int);
+ int pipe2(ref int[2], int) @trusted;
+ ssize_t readlinkat(int, const scope char*, char*, size_t);
+ int setresgid(gid_t, gid_t, gid_t) @trusted;
+ int setresuid(uid_t, uid_t, uid_t) @trusted;
+ int symlinkat(const scope char*, int, const scope char*);
+ int unlinkat(int, const scope char*, int);
}
else version (FreeBSD)
{
off_t lseek(int, off_t, int) @trusted;
int ftruncate(int, off_t) @trusted;
+ int getresgid(gid_t*, gid_t*, gid_t*);
+ int getresuid(uid_t*, uid_t*, uid_t*);
+ int setresgid(gid_t, gid_t, gid_t) @trusted;
+ int setresuid(uid_t, uid_t, uid_t) @trusted;
+
+ import core.sys.freebsd.config : __FreeBSD_version;
+ static if (__FreeBSD_version >= 800000)
+ {
+ int faccessat(int, const scope char*, int, int);
+ int fchownat(int, const scope char*, uid_t, gid_t, int);
+ int fexecve(int, const scope char**, const scope char**);
+ int linkat(int, const scope char*, int, const scope char*, int);
+ ssize_t readlinkat(int, const scope char*, char*, size_t);
+ int symlinkat(const scope char*, int, const scope char*);
+ int unlinkat(int, const scope char*, int);
+ }
+ static if (__FreeBSD_version >= 1000000)
+ {
+ int dup3(int, int, int) @trusted;
+ int pipe2(ref int[2], int) @trusted;
+ }
+ static if (__FreeBSD_version >= 1200000)
+ {
+ int getentropy(void*, size_t);
+ }
+ static if (__FreeBSD_version >= 1301000)
+ {
+ pid_t _Fork() @trusted;
+ }
}
else version (NetBSD)
{
off_t lseek(int, off_t, int) @trusted;
int ftruncate(int, off_t) @trusted;
+ int faccessat(int, const scope char*, int, int);
+ int fchownat(int, const scope char*, uid_t, gid_t, int);
+ int fexecve(int, const scope char**, const scope char**);
+ int getentropy(void*, size_t);
+ int linkat(int, const scope char*, int, const scope char*, int);
+ int pipe2(ref int[2], int) @trusted;
+ ssize_t readlinkat(int, const scope char*, char*, size_t);
+ int symlinkat(const scope char*, int, const scope char*);
+ int unlinkat(int, const scope char*, int);
}
else version (OpenBSD)
{
off_t lseek(int, off_t, int) @trusted;
int ftruncate(int, off_t) @trusted;
+ int dup3(int, int, int) @trusted;
+ int faccessat(int, const scope char*, int, int);
+ int fchownat(int, const scope char*, uid_t, gid_t, int);
+ int getentropy(void*, size_t);
+ int getresgid(gid_t*, gid_t*, gid_t*);
+ int getresuid(uid_t*, uid_t*, uid_t*);
+ int linkat(int, const scope char*, int, const scope char*, int);
+ int pipe2(ref int[2], int) @trusted;
+ ssize_t readlinkat(int, const scope char*, char*, size_t);
+ int setresgid(gid_t, gid_t, gid_t) @trusted;
+ int setresuid(uid_t, uid_t, uid_t) @trusted;
+ int symlinkat(const scope char*, int, const scope char*);
+ int unlinkat(int, const scope char*, int);
}
else version (DragonFlyBSD)
{
off_t lseek(int, off_t, int) @trusted;
int ftruncate(int, off_t) @trusted;
+ int dup3(int, int, int) @trusted;
+ int faccessat(int, const scope char*, int, int);
+ int fchownat(int, const scope char*, uid_t, gid_t, int);
+ int fexecve(int, const scope char**, const scope char**);
+ int getentropy(void*, size_t);
+ int getresgid(gid_t*, gid_t*, gid_t*);
+ int getresuid(uid_t*, uid_t*, uid_t*);
+ int linkat(int, const scope char*, int, const scope char*, int);
+ int pipe2(ref int[2], int) @trusted;
+ ssize_t readlinkat(int, const scope char*, char*, size_t);
+ int setresgid(gid_t, gid_t, gid_t) @trusted;
+ int setresuid(uid_t, uid_t, uid_t) @trusted;
+ int symlinkat(const scope char*, int, const scope char*);
+ int unlinkat(int, const scope char*, int);
}
else version (Solaris)
{
int ftruncate(int, off_t) @trusted;
}
}
+ int dup3(int, int, int) @trusted;
+ int faccessat(int, const scope char*, int, int);
+ int fchownat(int, const scope char*, uid_t, gid_t, int);
+ int fexecve(int, const scope char**, const scope char**);
+ int getentropy(void*, size_t);
+ int linkat(int, const scope char*, int, const scope char*, int);
+ int pipe2(ref int[2], int) @trusted;
+ ssize_t readlinkat(int, const scope char*, char*, size_t);
+ int symlinkat(const scope char*, int, const scope char*);
+ int unlinkat(int, const scope char*, int);
}
else version (Darwin)
{
off_t lseek(int, off_t, int) @trusted;
int ftruncate(int, off_t) @trusted;
+ int faccessat(int, const scope char*, int, int);
+ int fchownat(int, const scope char*, uid_t, gid_t, int);
+ int linkat(int, const scope char*, int, const scope char*, int);
+ ssize_t readlinkat(int, const scope char*, char*, size_t);
+ int symlinkat(const scope char*, int, const scope char*);
+ int unlinkat(int, const scope char*, int);
}
else version (CRuntime_Bionic)
{
off_t lseek(int, off_t, int) @trusted;
int ftruncate(int, off_t) @trusted;
+ int dup3(int, int, int) @trusted;
+ int faccessat(int, const scope char*, int, int);
+ int fchownat(int, const scope char*, uid_t, gid_t, int);
+ int fexecve(int, const scope char**, const scope char**);
+ pid_t _Fork() @trusted;
+ int getentropy(void*, size_t);
+ int getresgid(gid_t*, gid_t*, gid_t*);
+ int getresuid(uid_t*, uid_t*, uid_t*);
+ int linkat(int, const scope char*, int, const scope char*, int);
+ int pipe2(ref int[2], int) @trusted;
+ ssize_t readlinkat(int, const scope char*, char*, size_t);
+ int setresgid(gid_t, gid_t, gid_t) @trusted;
+ int setresuid(uid_t, uid_t, uid_t) @trusted;
+ int symlinkat(const scope char*, int, const scope char*);
+ int unlinkat(int, const scope char*, int);
}
else version (CRuntime_Musl)
{
off_t lseek(int, off_t, int) @trusted;
alias ftruncate ftruncate64;
alias lseek lseek64;
+ int dup3(int, int, int) @trusted;
+ int faccessat(int, const scope char*, int, int);
+ int fchownat(int, const scope char*, uid_t, gid_t, int);
+ int fexecve(int, const scope char**, const scope char**);
+ pid_t _Fork() @trusted;
+ int getentropy(void*, size_t);
+ int getresgid(gid_t*, gid_t*, gid_t*);
+ int getresuid(uid_t*, uid_t*, uid_t*);
+ int linkat(int, const scope char*, int, const scope char*, int);
+ int pipe2(ref int[2], int) @trusted;
+ ssize_t readlinkat(int, const scope char*, char*, size_t);
+ int setresgid(gid_t, gid_t, gid_t) @trusted;
+ int setresuid(uid_t, uid_t, uid_t) @trusted;
+ int symlinkat(const scope char*, int, const scope char*);
+ int unlinkat(int, const scope char*, int);
}
else version (CRuntime_UClibc)
{
{
int ftruncate(int, off_t) @trusted;
}
+ int dup3(int, int, int) @trusted;
+ int faccessat(int, const scope char*, int, int);
+ int fchownat(int, const scope char*, uid_t, gid_t, int);
+ int getresgid(gid_t*, gid_t*, gid_t*);
+ int getresuid(uid_t*, uid_t*, uid_t*);
+ int linkat(int, const scope char*, int, const scope char*, int);
+ int pipe2(ref int[2], int) @trusted;
+ ssize_t readlinkat(int, const scope char*, char*, size_t);
+ int setresgid(gid_t, gid_t, gid_t) @trusted;
+ int setresuid(uid_t, uid_t, uid_t) @trusted;
+ int symlinkat(const scope char*, int, const scope char*);
+ int unlinkat(int, const scope char*, int);
}
version (CRuntime_Glibc)
--- /dev/null
+/**
+ * D header file for Solaris sys/procfs.h.
+ *
+ * Copyright: Copyright © 2025, The D Language Foundation
+ * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
+ * Authors: Iain Buclaw
+ */
+module core.sys.solaris.sys.procfs;
+
+version (Solaris):
+extern (C):
+nothrow:
+@nogc:
+
+import core.stdc.config : c_long, c_ulong;
+import core.stdc.stdint : uintptr_t;
+import core.sys.posix.signal : sigaction_t, siginfo_t, sigset_t, stack_t;
+import core.sys.posix.time : timestruc_t;
+
+version (X86) version = X86_Any;
+version (X86_64) version = X86_Any;
+version (SPARC) version = SPARC_Any;
+version (SPARC64) version = SPARC_Any;
+
+version (X86_Any)
+{
+ /*
+ * Holds one i386 or and64 instruction
+ */
+ alias instr_t = ubyte;
+
+ public import core.sys.solaris.sys.regset :
+ NPRGREG = _NGREG,
+ prgreg_t = greg_t,
+ prgregset_t = gregset_t,
+ prfpregset_t = fpregset_t;
+
+ /*
+ * The following defines are for portability
+ */
+ version (X86_64)
+ {
+ public import core.sys.solaris.sys.regset :
+ R_PC = REG_RIP,
+ R_PS = REG_RFL,
+ R_SP = REG_RSP,
+ R_FP = REG_RBP,
+ R_R0 = REG_RAX,
+ R_R1 = REG_RDX;
+ }
+ else
+ {
+ public import core.sys.solaris.sys.regset :
+ R_PC = REG_EIP,
+ R_PS = REG_EFL,
+ R_SP = REG_UESP,
+ R_FP = REG_EBP,
+ R_R0 = REG_EAX,
+ R_R1 = REG_EDX;
+ }
+}
+else version (SPARC_Any)
+{
+ import core.sys.solaris.sys.regset : fq;
+
+ /*
+ * Holds one sparc instruction, both ILP32 and LP64
+ */
+ alias instr_t = uint;
+
+ /*
+ * General register access (sparc).
+ * Registers are 32 bits for ILP32, 64 bits for LP64.
+ */
+ enum NPRGREG = 38;
+
+ version (D_LP64)
+ alias prgreg_t = c_long;
+ else
+ alias prgreg_t = int;
+
+ alias prgregset_t = prgreg_t[NPRGREG];
+
+ /*
+ * Floating-point register access (sparc FPU).
+ */
+ version (SPARC64)
+ {
+ struct prfpregset_t
+ {
+ union
+ {
+ uint[32] pr_regs;
+ double[32] pr_dregs;
+ real[16] pr_qregs;
+ }
+ ulong pr_filler;
+ ulong pr_fsr;
+ ubyte pr_qcnt;
+ ubyte pr_q_entrysize;
+ ubyte pr_en;
+ byte[13] pr_pad;
+ fq[16] pr_q;
+ }
+ }
+ else
+ {
+ struct prfpregset_t
+ {
+ union
+ {
+ uint[32] pr_regs;
+ double[16] pr_dregs;
+ }
+ uint pr_filler;
+ uint pr_fsr;
+ ubyte pr_qcnt;
+ ubyte pr_q_entrysize;
+ ubyte pr_en;
+ fq[32] pr_q;
+ }
+ }
+
+ enum R_G0 = 0;
+ enum R_G1 = 1;
+ enum R_G2 = 2;
+ enum R_G3 = 3;
+ enum R_G4 = 4;
+ enum R_G5 = 5;
+ enum R_G6 = 6;
+ enum R_G7 = 7;
+ enum R_O0 = 8;
+ enum R_O1 = 9;
+ enum R_O2 = 10;
+ enum R_O3 = 11;
+ enum R_O4 = 12;
+ enum R_O5 = 13;
+ enum R_O6 = 14;
+ enum R_O7 = 15;
+ enum R_L0 = 16;
+ enum R_L1 = 17;
+ enum R_L2 = 18;
+ enum R_L3 = 19;
+ enum R_L4 = 20;
+ enum R_L5 = 21;
+ enum R_L6 = 22;
+ enum R_L7 = 23;
+ enum R_I0 = 24;
+ enum R_I1 = 25;
+ enum R_I2 = 26;
+ enum R_I3 = 27;
+ enum R_I4 = 28;
+ enum R_I5 = 29;
+ enum R_I6 = 30;
+ enum R_I7 = 31;
+ enum R_PC = 33;
+ enum R_nPC = 34;
+ enum R_Y = 35;
+
+ version (SPARC64)
+ {
+ enum R_CCR = 32;
+ enum R_ASI = 36;
+ enum R_FPRS = 37;
+ enum R_PS = R_CCR;
+ }
+ else
+ {
+ enum R_PSR = 32;
+ enum R_WIM = 36;
+ enum R_TBR = 37;
+ enum R_PS = R_PSR;
+ }
+
+ enum R_SP = R_O6;
+ enum R_FP = R_I6;
+ enum R_R0 = R_O0;
+ enum R_R1 = R_O1;
+}
+
+/*
+ * lwp status file. /proc/<pid>/lwp/<lwpid>/lwpstatus
+ */
+private enum PRCLSZ = 8; // maximum size of scheduling class name
+private enum PRSYSARGS = 8; // maximum number of syscall arguments
+
+struct lwpstatus_t
+{
+ int pr_flags; // flags (see below)
+ int pr_lwpid; // specific lwp identifier
+ short pr_why; // reason for lwp stop, if stopped
+ short pr_what; // more detailed reason
+ short pr_cursig; // current signal, if any
+ ubyte pr_adi;
+ byte pr_pad1;
+ siginfo_t pr_info; // info associated with signal or fault
+ sigset_t pr_lwppend; // set of signals pending to the lwp
+ sigset_t pr_lwphold; // set of signals blocked by the lwp
+ sigaction_t pr_action; // signal action for current signal
+ stack_t pr_altstack; // alternate signal stack info
+ uintptr_t pr_oldcontext; // address of previous ucontext
+ short pr_syscall; // system call number (if in syscall)
+ short pr_nsysarg; // number of arguments to this syscall
+ int pr_errno; // errno for failed syscall, 0 if successful
+ c_long[PRSYSARGS] pr_sysarg; // arguments to this syscall
+ c_long pr_rval1; // primary syscall return value
+ c_long pr_rval2; // second syscall return value, if any
+ char[PRCLSZ] pr_clname = void; // scheduling class name
+ timestruc_t pr_tstamp; // real-time time stamp of stop
+ timestruc_t pr_utime; // lwp user cpu time
+ timestruc_t pr_stime; // lwp system cpu time
+ int[11 - 2 * timestruc_t.sizeof / int.sizeof] pr_filler;
+ int pr_errpriv; // missing privilege
+ uintptr_t pr_ustack; // address of stack boundary data (stack_t)
+ c_ulong pr_instr; // current instruction
+ prgregset_t pr_reg; // general registers
+ prfpregset_t pr_fpreg; // floating-point registers
+}
+
+/*
+ * pr_flags (same values appear in both pstatus_t and lwpstatus_t pr_flags).
+ *
+ * These flags do *not* apply to psinfo_t.pr_flag or lwpsinfo_t.pr_flag
+ * (which are both deprecated).
+ */
+// The following flags apply to the specific or representative lwp
+enum PR_STOPPED = 0x00000001; // lwp is stopped
+enum PR_ISTOP = 0x00000002; // lwp is stopped on an event of interest
+enum PR_DSTOP = 0x00000004; // lwp has a stop directive in effect
+enum PR_STEP = 0x00000008; // lwp has a single-step directive in effect
+enum PR_ASLEEP = 0x00000010; // lwp is sleeping in a system call
+enum PR_PCINVAL = 0x00000020; // contents of pr_instr undefined
+enum PR_ASLWP = 0x00000040; // obsolete flag; never set
+enum PR_AGENT = 0x00000080; // this lwp is the /proc agent lwp
+enum PR_DETACH = 0x00000100; // this is a detached lwp
+enum PR_DAEMON = 0x00000200; // this is a daemon lwp
+enum PR_IDLE = 0x00000400; // lwp is a cpu's idle thread
+// The following flags apply to the process, not to an individual lwp
+enum PR_ISSYS = 0x00001000; // this is a system process
+enum PR_VFORKP = 0x00002000; // process is the parent of a vfork()d child
+enum PR_ORPHAN = 0x00004000; // process's process group is orphaned
+enum PR_NOSIGCHLD = 0x00008000; // process will not generate SIGCHLD on exit
+enum PR_WAITPID = 0x00010000; // only waitid(P_PID, pid) can reap the child
+// The following process flags are modes settable by PCSET/PCUNSET
+enum PR_FORK = 0x00100000; // inherit-on-fork is in effect
+enum PR_RLC = 0x00200000; // run-on-last-close is in effect
+enum PR_KLC = 0x00400000; // kill-on-last-close is in effect
+enum PR_ASYNC = 0x00800000; // asynchronous-stop is in effect
+enum PR_MSACCT = 0x01000000; // micro-state usage accounting is in effect
+enum PR_BPTADJ = 0x02000000; // breakpoint trap pc adjustment is in effect
+enum PR_PTRACE = 0x04000000; // ptrace-compatibility mode is in effect
+enum PR_MSFORK = 0x08000000; // micro-state accounting inherited on fork
--- /dev/null
+/**
+ * D header file for Solaris sys/regset.h.
+ *
+ * Copyright: Copyright © 2025, The D Language Foundation
+ * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
+ * Authors: Iain Buclaw
+ */
+module core.sys.solaris.sys.regset;
+
+version (Solaris):
+extern (C):
+nothrow:
+@nogc:
+
+import core.stdc.config : c_long, c_ulong;
+import core.sys.posix.sys.types : caddr_t;
+
+version (X86) version = X86_Any;
+version (X86_64) version = X86_Any;
+version (SPARC) version = SPARC_Any;
+version (SPARC64) version = SPARC_Any;
+
+version (X86_64)
+{
+ enum REG_GSBASE = 27;
+ enum REG_FSBASE = 26;
+ enum REG_DS = 25;
+ enum REG_ES = 24;
+
+ enum REG_GS = 23;
+ enum REG_FS = 22;
+ enum REG_SS = 21;
+ enum REG_RSP = 20;
+ enum REG_RFL = 19;
+ enum REG_CS = 18;
+ enum REG_RIP = 17;
+ enum REG_ERR = 16;
+ enum REG_TRAPNO = 15;
+ enum REG_RAX = 14;
+ enum REG_RCX = 13;
+ enum REG_RDX = 12;
+ enum REG_RBX = 11;
+ enum REG_RBP = 10;
+ enum REG_RSI = 9;
+ enum REG_RDI = 8;
+ enum REG_R8 = 7;
+ enum REG_R9 = 6;
+ enum REG_R10 = 5;
+ enum REG_R11 = 4;
+ enum REG_R12 = 3;
+ enum REG_R13 = 2;
+ enum REG_R14 = 1;
+ enum REG_R15 = 0;
+
+ enum REG_PC = REG_RIP;
+ enum REG_FP = REG_RBP;
+ enum REG_SP = REG_RSP;
+ enum REG_PS = REG_RFL;
+ enum REG_R0 = REG_RAX;
+ enum REG_R1 = REG_RDX;
+}
+else version (X86)
+{
+ enum REG_SS = 18;
+ enum REG_UESP = 17;
+ enum REG_EFL = 16;
+ enum REG_CS = 15;
+ enum REG_EIP = 14;
+ enum REG_ERR = 13;
+ enum REG_TRAPNO = 12;
+ enum REG_EAX = 11;
+ enum REG_ECX = 10;
+ enum REG_EDX = 9;
+ enum REG_EBX = 8;
+ enum REG_ESP = 7;
+ enum REG_EBP = 6;
+ enum REG_ESI = 5;
+ enum REG_EDI = 4;
+ enum REG_DS = 3;
+ enum REG_ES = 2;
+ enum REG_FS = 1;
+ enum REG_GS = 0;
+
+ enum REG_PC = REG_EIP;
+ enum REG_FP = REG_EBP;
+ enum REG_SP = REG_UESP;
+ enum REG_PS = REG_EFL;
+ enum REG_R0 = REG_EAX;
+ enum REG_R1 = REG_EDX;
+}
+else version (SPARC_Any)
+{
+ enum REG_PC = 1;
+ enum REG_nPC = 2;
+ enum REG_Y = 3;
+ enum REG_G1 = 4;
+ enum REG_G2 = 5;
+ enum REG_G3 = 6;
+ enum REG_G4 = 7;
+ enum REG_G5 = 8;
+ enum REG_G6 = 9;
+ enum REG_G7 = 10;
+ enum REG_O0 = 11;
+ enum REG_O1 = 12;
+ enum REG_O2 = 13;
+ enum REG_O3 = 14;
+ enum REG_O4 = 15;
+ enum REG_O5 = 16;
+ enum REG_O6 = 17;
+ enum REG_O7 = 18;
+
+ enum REG_SP = REG_O6;
+ enum REG_R0 = REG_O0;
+ enum REG_R1 = REG_O1;
+
+ version (SPARC64)
+ {
+ enum REG_CCR = 0;
+ enum REG_ASI = 19;
+ enum REG_FPRS = 20;
+ }
+ else
+ {
+ enum REG_PSR = 0;
+ enum REG_PS = REG_PSR;
+ }
+}
+else
+ static assert(false, "Architecture not supported.");
+
+/*
+ * A gregset_t is defined as an array type.
+ */
+version (X86_64)
+{
+ enum _NGREG = 28;
+ alias greg_t = c_long;
+}
+else version (X86)
+{
+ enum _NGREG = 19;
+ alias greg_t = int;
+}
+else version (SPARC64)
+{
+ enum _NGREG = 21;
+ alias greg_t = c_long;
+}
+else version (SPARC)
+{
+ enum _NGREG = 19;
+ alias greg_t = int;
+}
+else
+ static assert(0, "unimplemented");
+
+alias gregset_t = greg_t[_NGREG];
+
+version (X86_Any)
+{
+ /*
+ * Xregs extension
+ */
+ struct xrs_t
+ {
+ c_ulong xrs_id;
+ caddr_t xrs_ptr;
+ }
+}
+else version (SPARC_Any)
+{
+ /*
+ * Defines the minimal format of a floating point instruction queue entry.
+ */
+ struct _fpq
+ {
+ uint *fpq_addr;
+ uint fpq_instr;
+ }
+
+ struct fq
+ {
+ union
+ {
+ double whole;
+ _fpq fpq;
+ }
+ }
+
+ /*
+ * Defines how a register window can appear on the stack
+ */
+ struct rwindow
+ {
+ greg_t[8] rw_local;
+ greg_t[8] rw_in;
+ }
+
+ struct gwindows_t
+ {
+ int wbcnt;
+ greg_t[31] *spbuf;
+ rwindow[31] wbuf;
+ }
+
+ /*
+ * For associating extra register state with ucontext structure and is kept
+ * within the uc_mcontext filler area
+ */
+ struct xrs_t
+ {
+ uint xrs_id;
+ caddr_t xrs_ptr;
+ }
+
+ struct cxrs_t
+ {
+ uint cxrs_id;
+ caddr_t cxrs_ptr;
+ }
+
+ alias asrset_t = long[16];
+}
+
+/*
+ * The floating point processor state
+ */
+version (X86_64)
+{
+ private
+ {
+ alias upad128_t = uint[4];
+ union _u_st
+ {
+ ushort[5] fpr_16;
+ upad128_t __fpr_pad;
+ }
+ }
+
+ struct fpu
+ {
+ union fp_reg_set
+ {
+ struct fpchip_state
+ {
+ ushort cw;
+ ushort sw;
+ ubyte fctw;
+ ubyte __fx_rsvd;
+ ushort fop;
+ ulong rip;
+ ulong rdp;
+ uint mxcsr;
+ uint mxcsr_mask;
+ _u_st[8] st;
+ upad128_t[16] xmm;
+ upad128_t[6] __fx_ign2;
+ uint status;
+ uint xstatus;
+ }
+ uint[130] f_fpregs;
+ }
+ }
+}
+else version (X86)
+{
+ private alias upad128_t = uint[4];
+
+ struct fpu
+ {
+ union u_fp_reg_set
+ {
+ struct s_fpchip_state
+ {
+ uint[27] state;
+ uint status;
+ uint mxcsr;
+ uint xstatus;
+ uint[2] __pad;
+ upad128_t[8] xmm;
+ }
+ s_fpchip_state fpchip_state;
+
+ struct s_fp_emul_space
+ {
+ ubyte[246] fp_emul;
+ ubyte[2] fp_epad;
+ }
+ s_fp_emul_space fp_emul_space;
+ uint[95] f_fpregs;
+ }
+ u_fp_reg_set fp_reg_set;
+ }
+}
+else version (SPARC64)
+{
+ struct fpu
+ {
+ union
+ {
+ uint[32] fpu_regs;
+ double[32] fpu_dregs;
+ real[16] fpu_qregs;
+ }
+ fq *fpu_q;
+ ulong fpu_fsr;
+ ubyte fpu_qcnt;
+ ubyte fpu_q_entrysize;
+ ubyte fpu_en;
+ }
+}
+else version (SPARC)
+{
+ struct fpu
+ {
+ union
+ {
+ uint[32] fpu_regs;
+ double[16] fpu_dregs;
+ }
+ fq *fpu_q;
+ uint fpu_fsr;
+ ubyte fpu_qcnt;
+ ubyte fpu_q_entrysize;
+ ubyte fpu_en;
+ }
+}
+else
+ static assert(0, "unimplemented");
+
+alias fpregset_t = fpu;
--- /dev/null
+/**
+ * D header file for Solaris thread.h.
+ *
+ * Copyright: Copyright © 2025, The D Language Foundation
+ * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
+ * Authors: Iain Buclaw
+ */
+module core.sys.solaris.thread;
+
+version (Solaris):
+extern (C):
+nothrow:
+@nogc:
+
+import core.stdc.config : c_long;
+import core.sys.posix.signal : sigset_t, stack_t;
+
+/*
+ * definitions needed to use the thread interface except synchronization.
+ */
+alias thread_t = int;
+alias thread_key_t = int;
+
+int thr_create(void*, size_t, void* function(void*), void*, c_long, thread_t*);
+int thr_join(thread_t, thread_t*, void**);
+int thr_setconcurrency(int);
+int thr_getconcurrency();
+noreturn thr_exit(void*);
+thread_t thr_self();
+
+int thr_sigsetmask(int, const scope sigset_t*, sigset_t*);
+
+int thr_stksegment(stack_t*);
+
+int thr_main();
+int thr_kill(thread_t, int);
+int thr_suspend(thread_t);
+int thr_continue(thread_t);
+void thr_yield();
+int thr_setprio(thread_t, int);
+int thr_getprio(thread_t, int*);
+int thr_keycreate(thread_key_t*, void function(void*));
+int thr_keycreate_once(thread_key_t*, void function(void*));
+int thr_setspecific(thread_key_t, void*);
+int thr_getspecific(thread_key_t, void**);
+size_t thr_min_stack();
+
+alias THR_MIN_STACK = thr_min_stack;
+
+/*
+ * thread flags (one word bit mask)
+ */
+enum : c_long
+{
+ THR_BOUND = 0x00000001, // = PTHREAD_SCOPE_SYSTEM
+ THR_NEW_LWP = 0x00000002,
+ THR_DETACHED = 0x00000040, // = PTHREAD_CREATE_DETACHED
+ THR_SUSPENDED = 0x00000080,
+ THR_DAEMON = 0x00000100,
+}
+
+/*
+ * The key to be created by thr_keycreate_once()
+ * must be statically initialized with THR_ONCE_KEY.
+ */
+enum thread_key_t THR_ONCE_KEY = -1;
/**
- * D header file for C99.
+ * D header file for C99/C11.
*
* $(C_HEADER_DESCRIPTION pubs.opengroup.org/onlinepubs/009695399/basedefs/_time.h.html, _time.h)
*
///
extern __gshared const(char)*[2] tzname; // non-standard
+
+// timespec functions, introduced in C11
+alias __time64_t = long;
+alias __time32_t = int;
+
+/// 32-bit timespec struct
+struct _timespec32
+{
+ __time32_t tv_sec;
+ c_long tv_nsec;
+}
+
+/// 64-bit timespec struct
+struct _timespec64
+{
+ __time64_t tv_sec;
+ c_long tv_nsec;
+}
+
+/// Timespec structure, introduced in C11
+alias timespec = _timespec64;
+
+/// Base Value used for timespec_get
+enum TIME_UTC = 1;
+
+/// 64-bit version of timespec_get for Windows
+@system int _timespec64_get(scope _timespec64* ts, int base);
+
+/// 32-bit version of timespec_get for Windows
+@system int _timespec32_get(scope _timespec32* ts, int base);
+
+/// timespec_get, introduced in C11
+alias timespec_get = _timespec64_get;
}
else
{
- version (Posix) import core.sys.posix.sys.mman : MAP_ANON, MAP_FAILED, MAP_PRIVATE, mmap,
- mprotect, PROT_NONE, PROT_READ, PROT_WRITE;
+ version (Posix)
+ {
+ static import core.sys.posix.sys.mman;
+ static if (__traits(compiles, core.sys.posix.sys.mman.mmap))
+ {
+ import core.sys.posix.sys.mman : MAP_ANON, MAP_FAILED, MAP_PRIVATE, mmap,
+ mprotect, PROT_NONE, PROT_READ, PROT_WRITE;
+ }
+ static import core.sys.posix.stdlib;
+ static if (__traits(compiles, core.sys.posix.stdlib.valloc))
+ import core.sys.posix.stdlib : valloc;
+ }
version (OpenBSD) import core.sys.posix.sys.mman : MAP_STACK;
static if ( __traits( compiles, ucontext_t ) )
{
push( 0x00000000_00000000 ); // Return address of fiber_entryPoint call
push( cast(size_t) &fiber_entryPoint ); // RIP
- push( cast(size_t) m_ctxt.bstack ); // RBP
+ version (OSX)
+ {
+ // backtrace() needs this to be null to terminate
+ // the stack walk on macOS x86_64
+ push( 0x00000000_00000000 ); // RBP
+ }
+ else
+ {
+ push( cast(size_t) m_ctxt.bstack ); // RBP
+ }
push( 0x00000000_00000000 ); // RBX
push( 0x00000000_00000000 ); // R12
push( 0x00000000_00000000 ); // R13
}
else static if ( __traits( compiles, ucontext_t ) )
{
- getcontext( &m_utxt );
+ const status = getcontext( &m_utxt );
+ assert( status == 0 );
+
m_utxt.uc_stack.ss_sp = m_pmem;
m_utxt.uc_stack.ss_size = m_size;
makecontext( &m_utxt, &fiber_entryPoint, 0 );
version (Darwin)
{
+ // Use macOS threads for suspend/resume
import core.sys.darwin.mach.kern_return : KERN_SUCCESS;
import core.sys.darwin.mach.port : mach_port_t;
import core.sys.darwin.mach.thread_act : mach_msg_type_number_t,
PPC_THREAD_STATE64, PPC_THREAD_STATE64_COUNT, ppc_thread_state64_t;
}
}
-}
-
-version (Solaris)
-{
- import core.sys.posix.sys.wait : idtype_t;
- import core.sys.solaris.sys.priocntl : PC_CLNULL, PC_GETCLINFO, PC_GETPARMS, PC_SETPARMS, pcinfo_t, pcparms_t, priocntl;
- import core.sys.solaris.sys.types : P_MYID, pri_t;
+ else version (Solaris)
+ {
+ // Use Solaris threads for suspend/resume
+ import core.sys.posix.sys.wait : idtype_t;
+ import core.sys.solaris.sys.priocntl : PC_CLNULL, PC_GETCLINFO, PC_GETPARMS, PC_SETPARMS, pcinfo_t, pcparms_t, priocntl;
+ import core.sys.solaris.sys.types : P_MYID, pri_t;
+ import core.sys.solaris.thread : thr_stksegment, thr_suspend, thr_continue;
+ import core.sys.solaris.sys.procfs : PR_STOPPED, lwpstatus_t;
+ }
+ else
+ {
+ // Use POSIX threads for suspend/resume
+ }
}
version (GNU)
static assert(false, "Architecture not supported." );
}
}
+ else version (Solaris)
+ {
+ version (X86)
+ {
+ uint[8] m_reg; // edi,esi,ebp,esp,ebx,edx,ecx,eax
+ }
+ else version (X86_64)
+ {
+ ulong[16] m_reg; // rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax
+ // r8,r9,r10,r11,r12,r13,r14,r15
+ }
+ else version (SPARC)
+ {
+ int[33] m_reg; // g0-7, o0-7, l0-7, i0-7, pc
+ }
+ else version (SPARC64)
+ {
+ long[33] m_reg; // g0-7, o0-7, l0-7, i0-7, pc
+ }
+ else
+ {
+ static assert(false, "Architecture not supported." );
+ }
+ }
+ override final void[] savedRegisters() nothrow @nogc
+ {
+ version (Windows)
+ {
+ return m_reg;
+ }
+ else version (Darwin)
+ {
+ return m_reg;
+ }
+ else version (Solaris)
+ {
+ return m_reg;
+ }
+ else
+ {
+ return null;
+ }
+ }
///////////////////////////////////////////////////////////////////////////
// General Actions
extern (C) void thread_setGCSignals(int suspendSignalNo, int resumeSignalNo) nothrow @nogc
{
}
+
+ /**
+ * Get the GC signals set by the thread module. This function should be called either
+ * after thread_init() has finished, or after a call thread_setGCSignals().
+ * This function is Posix-only.
+ */
+ extern (C) void thread_getGCSignals(out int suspendSignalNo, out int resumeSignalNo) nothrow @nogc
+ {
+ }
}
else version (Posix)
{
suspendSignalNumber = suspendSignalNo;
resumeSignalNumber = resumeSignalNo;
}
+
+ extern (C) void thread_getGCSignals(out int suspendSignalNo, out int resumeSignalNo) nothrow @nogc
+ in
+ {
+ assert(suspendSignalNumber != 0);
+ assert(resumeSignalNumber != 0);
+ }
+ out
+ {
+ assert(suspendSignalNo != 0);
+ assert(resumeSignalNo != 0);
+ }
+ do
+ {
+ suspendSignalNo = suspendSignalNumber;
+ resumeSignalNo = resumeSignalNumber;
+ }
}
version (Posix)
enum int j = 13 + i; // source register
asm pure nothrow @nogc
{
- "stw "~regname~j.stringof~", %0" : "=m" (regs[i]);
+ ("stw "~regname~j.stringof~", %0") : "=m" (regs[i]);
}
}}
sp = cast(void*)®s[0];
enum int j = 13 + i; // source register
asm pure nothrow @nogc
{
- "std "~regname~j.stringof~", %0" : "=m" (regs[i]);
+ ("std "~regname~j.stringof~", %0") : "=m" (regs[i]);
}
}}
sp = cast(void*)®s[0];
fn(sp);
}
-version (Windows)
-private extern (D) void scanWindowsOnly(scope ScanAllThreadsTypeFn scan, ThreadBase _t) nothrow
-{
- auto t = _t.toThread;
-
- scan( ScanType.stack, t.m_reg.ptr, t.m_reg.ptr + t.m_reg.length );
-}
-
-
/**
* Returns the process ID of the calling process, which is guaranteed to be
* unique on the system. This call is always successful.
version (PThread_Getattr_NP) int pthread_getattr_np(pthread_t thread, pthread_attr_t* attr);
version (PThread_Attr_Get_NP) int pthread_attr_get_np(pthread_t thread, pthread_attr_t* attr);
- version (Solaris) int thr_stksegment(stack_t* stk);
version (OpenBSD) int pthread_stackseg_np(pthread_t thread, stack_t* sinfo);
}
static assert(false, "Architecture not supported." );
}
}
+ else version (Solaris)
+ {
+ if (t.m_addr != pthread_self())
+ {
+ if (thr_suspend(t.m_addr) != 0)
+ {
+ if (!t.isRunning)
+ {
+ Thread.remove(t);
+ return false;
+ }
+ onThreadError("Unable to suspend thread");
+ }
+
+ static int getLwpStatus(ulong lwpid, out lwpstatus_t status)
+ {
+ import core.sys.posix.fcntl : open, O_RDONLY;
+ import core.sys.posix.unistd : pread, close;
+ import core.internal.string : unsignedToTempString;
+
+ char[100] path = void;
+ auto pslice = path[0 .. $];
+ immutable n = unsignedToTempString(lwpid);
+ immutable ndigits = n.length;
+
+ // Construct path "/proc/self/lwp/%u/lwpstatus"
+ pslice[0 .. 15] = "/proc/self/lwp/";
+ pslice = pslice[15 .. $];
+ pslice[0 .. ndigits] = n[];
+ pslice = pslice[ndigits .. $];
+ pslice[0 .. 10] = "/lwpstatus";
+ pslice[10] = '\0';
+
+ // Read in lwpstatus data
+ int fd = open(path.ptr, O_RDONLY, 0);
+ if (fd >= 0)
+ {
+ while (pread(fd, &status, status.sizeof, 0) == status.sizeof)
+ {
+ // Should only attempt to read the thread state once it
+ // has been stopped by thr_suspend
+ if (status.pr_flags & PR_STOPPED)
+ {
+ close(fd);
+ return 0;
+ }
+ // Give it a chance to stop
+ thread_yield();
+ }
+ close(fd);
+ }
+ return -1;
+ }
+
+ lwpstatus_t status = void;
+ if (getLwpStatus(t.m_addr, status) != 0)
+ onThreadError("Unable to load thread state");
+
+ version (X86)
+ {
+ import core.sys.solaris.sys.regset; // REG_xxx
+
+ if (!t.m_lock)
+ t.m_curr.tstack = cast(void*) status.pr_reg[REG_ESP];
+ // eax,ebx,ecx,edx,edi,esi,ebp,esp
+ t.m_reg[0] = status.pr_reg[REG_EAX];
+ t.m_reg[1] = status.pr_reg[REG_EBX];
+ t.m_reg[2] = status.pr_reg[REG_ECX];
+ t.m_reg[3] = status.pr_reg[REG_EDX];
+ t.m_reg[4] = status.pr_reg[REG_EDI];
+ t.m_reg[5] = status.pr_reg[REG_ESI];
+ t.m_reg[6] = status.pr_reg[REG_EBP];
+ t.m_reg[7] = status.pr_reg[REG_ESP];
+ }
+ else version (X86_64)
+ {
+ import core.sys.solaris.sys.regset; // REG_xxx
+
+ if (!t.m_lock)
+ t.m_curr.tstack = cast(void*) status.pr_reg[REG_RSP];
+ // rax,rbx,rcx,rdx,rdi,rsi,rbp,rsp
+ t.m_reg[0] = status.pr_reg[REG_RAX];
+ t.m_reg[1] = status.pr_reg[REG_RBX];
+ t.m_reg[2] = status.pr_reg[REG_RCX];
+ t.m_reg[3] = status.pr_reg[REG_RDX];
+ t.m_reg[4] = status.pr_reg[REG_RDI];
+ t.m_reg[5] = status.pr_reg[REG_RSI];
+ t.m_reg[6] = status.pr_reg[REG_RBP];
+ t.m_reg[7] = status.pr_reg[REG_RSP];
+ // r8,r9,r10,r11,r12,r13,r14,r15
+ t.m_reg[8] = status.pr_reg[REG_R8];
+ t.m_reg[9] = status.pr_reg[REG_R9];
+ t.m_reg[10] = status.pr_reg[REG_R10];
+ t.m_reg[11] = status.pr_reg[REG_R11];
+ t.m_reg[12] = status.pr_reg[REG_R12];
+ t.m_reg[13] = status.pr_reg[REG_R13];
+ t.m_reg[14] = status.pr_reg[REG_R14];
+ t.m_reg[15] = status.pr_reg[REG_R15];
+ }
+ else version (SPARC)
+ {
+ import core.sys.solaris.sys.procfs : R_SP, R_PC;
+
+ if (!t.m_lock)
+ t.m_curr.tstack = cast(void*) status.pr_reg[R_SP];
+ // g0..g7, o0..o7, l0..l7, i0..i7
+ t.m_reg[0 .. 32] = status.pr_reg[0 .. 32];
+ // pc
+ t.m_reg[32] = status.pr_reg[R_PC];
+ }
+ else version (SPARC64)
+ {
+ import core.sys.solaris.sys.procfs : R_SP, R_PC;
+
+ if (!t.m_lock)
+ {
+ // SPARC V9 has a stack bias of 2047 bytes which must be added to get
+ // the actual data of the stack frame.
+ auto tstack = status.pr_reg[R_SP] + 2047;
+ assert(tstack % 16 == 0);
+ t.m_curr.tstack = cast(void*) tstack;
+ }
+ // g0..g7, o0..o7, l0..l7, i0..i7
+ t.m_reg[0 .. 32] = status.pr_reg[0 .. 32];
+ // pc
+ t.m_reg[32] = status.pr_reg[R_PC];
+ }
+ else
+ {
+ static assert(false, "Architecture not supported.");
+ }
+ }
+ else if (!t.m_lock)
+ {
+ t.m_curr.tstack = getStackTop();
+ }
+ }
else version (Posix)
{
if ( t.m_addr != pthread_self() )
version (Darwin)
{}
+ else version (Solaris)
+ {}
else version (Posix)
{
// Subtract own thread if we called suspend() on ourselves.
t.m_curr.tstack = t.m_curr.bstack;
t.m_reg[0 .. $] = 0;
}
+ else version (Solaris)
+ {
+ if (t.m_addr != pthread_self() && thr_continue(t.m_addr) != 0)
+ {
+ if (!t.isRunning)
+ {
+ Thread.remove(t);
+ return;
+ }
+ onThreadError("Unable to resume thread");
+ }
+
+ if (!t.m_lock)
+ t.m_curr.tstack = t.m_curr.bstack;
+ t.m_reg[0 .. $] = 0;
+ }
else version (Posix)
{
if ( t.m_addr != pthread_self() )
}
pthread_atfork(null, null, &initChildAfterFork);
}
+ else version (Solaris)
+ {
+ }
else version (Posix)
{
version (OpenBSD)
scope (exit)
{
+ // allow the GC to clean up any resources it allocated for this thread.
+ import core.internal.gc.proxy : gc_getProxy;
+ gc_getProxy().cleanupThread(obj);
+
Thread.remove(obj);
obj.destroyDataStorage();
}
void append( Throwable t )
{
- obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t);
+ obj.filterCaughtThrowable(t);
+ if (t !is null)
+ obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t);
}
version (D_InlineAsm_X86)
}
else version (Posix)
{
+ // NOTE: A thread's cancelability state, determined by pthread_setcancelstate,
+ // can be enabled (the default for new threads) or disabled.
+ // If a thread has disabled cancelation, then a cancelation request remains
+ // queued until the thread enables cancelation. If a thread has enabled
+ // cancelation, then its cancelability type determines when cancelation occurs.
+ //
+ // Call these routines when entering/leaving critical sections of the code that
+ // are not cancellation points.
+
+ extern (C) int thread_cancelDisable() nothrow
+ {
+ static if (__traits(compiles, core.sys.posix.pthread.PTHREAD_CANCEL_DISABLE))
+ {
+ import core.sys.posix.pthread : pthread_setcancelstate, PTHREAD_CANCEL_DISABLE;
+ int oldstate;
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
+ return oldstate;
+ }
+ else
+ {
+ return 0; // No thread cancellation on platform
+ }
+ }
+
+ extern (C) void thread_cancelRestore(int oldstate) nothrow
+ {
+ static if (__traits(compiles, core.sys.posix.pthread.PTHREAD_CANCEL_DISABLE))
+ {
+ import core.sys.posix.pthread : pthread_setcancelstate;
+ pthread_setcancelstate(oldstate, null);
+ }
+ }
+
private
{
//
scope (exit)
{
+ // allow the GC to clean up any resources it allocated for this thread.
+ import core.internal.gc.proxy : gc_getProxy;
+ gc_getProxy().cleanupThread(obj);
+
Thread.remove(obj);
atomicStore!(MemoryOrder.raw)(obj.m_isRunning, false);
obj.destroyDataStorage();
void append( Throwable t )
{
- obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t);
+ obj.filterCaughtThrowable(t);
+ if (t !is null)
+ obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t);
}
try
{
{
void op(void* sp) nothrow
{
+ int cancel_state = thread_cancelDisable();
+ scope(exit) thread_cancelRestore(cancel_state);
+
bool supported = thread_preSuspend(getStackTop());
assert(supported, "Tried to suspend a detached thread!");
package void destroyDataStorage() nothrow @nogc
{
- // allow the GC to clean up any resources it allocated for this thread.
- import core.internal.gc.proxy : gc_getProxy;
- gc_getProxy().cleanupThread(this);
-
rt_tlsgc_destroy(m_tlsrtdata);
m_tlsrtdata = null;
}
*/
abstract Throwable join(bool rethrow = true);
+ /**
+ * Filter any exceptions that escaped the thread entry point.
+ * This enables a 'grave digger' approach to exceptions.
+ *
+ * By default this method will call the global handler in core.exception.
+ *
+ * Overriding this method allows a per-thread behavior.
+ *
+ * Params:
+ * thrown = The thrown exception, may be null after returned.
+ */
+ void filterCaughtThrowable(ref Throwable thrown) @system nothrow
+ {
+ import core.exception : filterThreadThrowableHandler;
+ if (thrown is null)
+ return;
+ else if (auto handler = filterThreadThrowableHandler())
+ handler(thrown);
+ }
///////////////////////////////////////////////////////////////////////////
// General Properties
return m_curr;
}
+ /**
+ * Get an array of the current saved registers for this thread.
+ *
+ * Returns:
+ * A slice of the array representing all saved registers or null.
+ */
+ public abstract void[] savedRegisters() nothrow @nogc;
package(core.thread):
///////////////////////////////////////////////////////////////////////////
* Deregisters the calling thread from use with the runtime. If this routine
* is called for a thread which is not registered, the result is undefined.
*
+ * Once the thread is removed from the runtime, it must not use the GC because
+ * it does not participate in the Stop-The-World mechanisms. With the default
+ * GC, that has a global lock, this might not cause races, but in GCs with
+ * regional locks, it definitely can cause races.
+ *
* NOTE: This routine does not run thread-local static destructors when called.
* If full functionality as a D thread is desired, the following function
* must be called before thread_detachThis, particularly if the thread is
*
* $(D extern(C) void rt_moduleTlsDtor();)
*
+ * This also does not call the GC thread cleanup routine. After running
+ * module dtors, it is recommended to call
+ * $(D gc_getProxy().cleanupThread(Thread.getThis());)
+ *
* See_Also:
* $(REF thread_attachThis, core,thread,osthread)
*/
* Deregisters the given thread from use with the runtime. If this routine
* is called for a thread which is not registered, the result is undefined.
*
+ * Once the thread is removed from the runtime, it must not use the GC because
+ * it does not participate in the Stop-The-World mechanisms. With the default
+ * GC, that has a global lock, this might not cause races, but in GCs with
+ * regional locks, it definitely can cause races.
+ *
* NOTE: This routine does not run thread-local static destructors when called.
* If full functionality as a D thread is desired, the following function
- * must be called by the detached thread, particularly if the thread is
- * being detached at some indeterminate time before program termination:
+ * must be called by the detached thread before calling this function,
+ * particularly if the thread is being detached at some indeterminate
+ * time before program termination:
*
* $(D extern(C) void rt_moduleTlsDtor();)
+ *
+ * This also does not call the GC thread cleanup routine. After running
+ * module dtors, it is recommended to call (from the thread itself)
+ * $(D gc_getProxy().cleanupThread(Thread.getThis());)
*/
extern (C) void thread_detachByAddr(ThreadID addr)
{
for (ThreadBase t = ThreadBase.sm_tbeg; t; t = t.next)
{
- version (Windows)
- {
- // Ideally, we'd pass ScanType.regs or something like that, but this
- // would make portability annoying because it only makes sense on Windows.
- scanWindowsOnly(scan, t);
- }
+ if (auto regs = t.savedRegisters())
+ scan(ScanType.stack, regs.ptr, regs.ptr + regs.length);
if (t.m_tlsrtdata !is null)
rt_tlsgc_scan(t.m_tlsrtdata, (p1, p2) => scan(ScanType.tls, p1, p2));
}
}
-version (Windows)
-{
- // Currently scanWindowsOnly can't be handled properly by externDFunc
- // https://github.com/dlang/druntime/pull/3135#issuecomment-643673218
- pragma(mangle, "_D4core6thread8osthread15scanWindowsOnlyFNbMDFNbEQBvQBt10threadbase8ScanTypePvQcZvCQDdQDbQBi10ThreadBaseZv")
- private extern (D) void scanWindowsOnly(scope ScanAllThreadsTypeFn scan, ThreadBase) nothrow;
-}
-
/**
* The main entry point for garbage collection. The supplied delegate
* will be passed ranges representing both stack and register values.
}
else version (AAPCS64)
{
- static import core.internal.vararg.aarch64;
- core.internal.vararg.aarch64.va_arg(ap, ti, parmn);
+ static import core.internal.vararg.aapcs64;
+ core.internal.vararg.aapcs64.va_arg(ap, ti, parmn);
}
else version (ARM_Any)
{
ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
{
bool found;
- auto p = _aaGetX(aa, key, found);
- if (found)
- return *p;
- *p = value; // Not `return (*p = value)` since if `=` is overloaded
- return *p; // this might not return a ref to the left-hand side.
+ auto p = _aaGetX(aa, key, found, value);
+ return *p;
}
///
if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void)))
{
bool found;
- auto p = _aaGetX(aa, key, found);
- if (!found)
- *p = create();
- else
+ auto p = _aaGetX(aa, key, found, create());
+ if (found)
{
static if (is(typeof(update(*p)) == void))
update(*p);
return hash;
}
-/// Provide the .dup array property.
+/// Provide the .dup array property, which creates a duplicate.
@property auto dup(T)(T[] a)
if (!is(const(T) : T))
{
}
-/// Provide the .idup array property.
+/// Provide the .idup array property, which creates an immutable duplicate.
@property immutable(T)[] idup(T)(T[] a)
{
import core.internal.array.duplication : _dup;
public import core.internal.array.concatenation : _d_arraycatnTXTrace;
public import core.lifetime : _d_newitemTTrace;
public import core.internal.array.construction : _d_newarrayTTrace;
+ public import core.internal.array.construction : _d_newarrayUTrace;
public import core.internal.array.construction : _d_newarraymTXTrace;
public import core.internal.array.capacity: _d_arraysetlengthTTrace;
public import core.internal.array.construction : _d_arrayliteralTXTrace;
public import core.internal.array.construction : _d_arrayctor;
public import core.internal.array.construction : _d_arraysetctor;
public import core.internal.array.construction : _d_newarrayT;
+public import core.internal.array.construction : _d_newarrayU;
public import core.internal.array.construction : _d_newarraymTX;
public import core.internal.array.construction : _d_arrayliteralTX;
public import core.internal.array.arrayassign : _d_arrayassign_l;
private int cmp3(T)(const T f1, const T f2)
if (isComplex!T)
{
- if (int result = cmp3(f1.re, f2.re))
- return result;
- return cmp3(f1.im, f2.im);
+ int result;
+ return (result = cmp3(f1.re, f2.re)) != 0 ? result : cmp3(f1.im, f2.im);
}
unittest
-f87979028bd0e0f3e67dbf5b0cd1335ca3f7ed67
+808314eb236c7a7566d3ab798ba9b7be33f5473e
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
found value plus one is returned.
See_Also:
-$(REF_ALTTEXT find, find, std,algorithm,searching) and $(REF_ALTTEXT canFind, canFind, std,algorithm,searching) for finding a value in a
-range.
+ $(UL
+ $(LI $(REF_SHORT find, std,algorithm,searching) and
+ $(REF_SHORT canFind, std,algorithm,searching) for finding a value in a range.)
+ $(LI $(REF_ALTTEXT `value in iota(start, end)`, iota, std,range) to find a value in
+ a particular interval.)
+ )
*/
uint among(alias pred = (a, b) => a == b, Value, Values...)
(Value value, Values values)
}
/**
-Executes and returns one of a collection of handlers based on the type of the
-switch object.
+Executes one of a series of handlers based on the dynamic type of a class instance.
-The first choice that `switchObject` can be casted to the type
-of argument it accepts will be called with `switchObject` casted to that
-type, and the value it'll return will be returned by `castSwitch`.
-
-If a choice's return type is void, the choice must throw an exception, unless
-all the choices are void. In that case, castSwitch itself will return void.
+$(P
+`switchObject`'s dynamic type is checked for compatibility with the argument type of each
+choice in turn until a match is found. When there's a match, the choice will be called with
+`switchObject` cast to that type. If the choice returns a value, `castSwitch` will return it.
+)
+- If one choice's return type is void and one can return a value, `SwitchError`
+ will be thrown if the void handler was matched and completed execution without throwing.
+- A void handler is allowed to return when all the choices' return types convert to void -
+ in that case, `castSwitch` itself will return void.
+- A choice's return type can be `noreturn` (e.g. when a handler always throws an exception).
-Throws: If none of the choice matches, a `SwitchError` will be thrown. $(D
-SwitchError) will also be thrown if not all the choices are void and a void
-choice was executed without throwing anything.
+Throws: If none of the choices match, a `SwitchError` will be thrown. $(D
+SwitchError) will also be thrown if one choice can return a value but a void
+choice was matched and completed execution.
Params:
- choices = The `choices` needs to be composed of function or delegate
- handlers that accept one argument. There can also be a choice that
- accepts zero arguments. That choice will be invoked if the $(D
+ choices = A sequence of function and/or delegate
+ handlers that each accept one argument. There can also be one choice that
+ accepts zero arguments, which will be invoked if the $(D
switchObject) is null.
switchObject = the object against which the tests are being made.
Returns:
- The value of the selected choice.
-
-Note: `castSwitch` can only be used with object types.
+ The return value of the selected choice.
*/
auto castSwitch(choices...)(Object switchObject)
{
{
alias CastClass = choiceParameterTypes[0];
static assert(is(CastClass == class) || is(CastClass == interface),
- "A choice handler can have only class or interface typed argument.");
+ "A choice handler can only have a class or interface argument type.");
// Check for overshadowing:
immutable indexOfOvershadowingChoice =
class A
{
int a;
- this(int a) {this.a = a;}
- @property int i() { return a; }
+ this(int a) { this.a = a; }
}
interface I { }
class B : I { }
assert(results[2] == "null reference");
}
-/// Using with void handlers:
+/// Using with noreturn/void handlers:
@system unittest
{
+ import core.exception : SwitchError;
import std.exception : assertThrown;
class A { }
class B { }
- // Void handlers are allowed if they throw:
+
+ // B's handler never returns, so `i` does not need a value
+ int i;
assertThrown!Exception(
- new B().castSwitch!(
+ i = new B().castSwitch!(
+ (A a) => 1,
+ (B b) { throw new Exception("B is not allowed!"); }
+ )()
+ );
+
+ // Void handler call will throw if another handler returns a value
+ assertThrown!SwitchError(
+ i = new B().castSwitch!(
(A a) => 1,
- (B d) { throw new Exception("B is not allowed!"); }
+ (B b) {}
)()
);
- // Void handlers are also allowed if all the handlers are void:
+ // Void handlers are allowed if all the handlers convert to void:
new A().castSwitch!(
(A a) { },
(B b) { assert(false); },
foreach (f; _funs)
{
static assert(!is(typeof(f(RE.init)) == void),
- "Mapping function(s) must not return void: " ~ _funs.stringof);
+ "Mapping function(s) must not return void. " ~
+ " Consider using `each` instead of `map!(" ~ _funs.stringof ~ ")`");
}
return MapResult!(_fun, Range)(r);
Range,
Separator)(Range r, Separator s)
if (is(typeof(binaryFun!pred(r.front, s)) : bool)
- && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range))
+ && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range)
+ && (is(ElementType!Range : Separator)
+ || !(isForwardRange!Separator && (hasLength!Separator
+ || isNarrowString!Separator))))
{
import std.algorithm.searching : find;
import std.conv : unsigned;
assert(!empty, "Attempting to fetch the front of an empty splitter.");
static if (keepSeparators)
{
+ if (_frontLength != _unComputed && !_wasSeparator) return _input[0 .. _frontLength];
if (!_wasSeparator)
{
_frontLength = _separatorLength;
assert("abXcdxef".splitter!((a, b) => a.toLower == b)('x').equal(["ab", "cd", "ef"]));
}
+// https://github.com/dlang/phobos/issues/10759
+@safe unittest
+{
+ import std.algorithm.iteration : splitter;
+ import std.algorithm.searching : canFind;
+ import std.range.primitives;
+ import std.typecons : Yes;
+
+ auto range = "16x13+0-2".splitter!((a, b) => "x+-".canFind(a), Yes.keepSeparators)(0);
+ assert(range.front == "16");
+ assert(range.front == "16");
+ assert(range.front == "16");
+}
+
/// ditto
auto splitter(alias pred = "a == b",
Flag!"keepSeparators" keepSeparators = No.keepSeparators,
import std.algorithm.searching : find;
import std.conv : unsigned;
- static struct Result
+ struct Result
{
private:
Range _input;
assert(words.equal([ "i", "am", "pointing" ]));
}
+// https://github.com/dlang/phobos/issues/10760
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.typecons : Yes;
+ import std.algorithm.comparison : equal;
+
+ auto r = "16x16+0-2".splitter!((a, b) => "x+-".canFind(a), Yes.keepSeparators)("x");
+ assert(r.equal(["16", "x", "16", "+", "0", "-", "2"]));
+}
+
/++
Lazily splits a range `r` whenever a predicate `isTerminator` returns true for an element.
$(T2 multiwayUnion,
Computes the union of a range of sorted ranges.)
$(T2 setDifference,
- Lazily computes the set difference of two or more sorted ranges.)
+ Lazily computes the set difference of two sorted ranges.)
$(T2 setIntersection,
Lazily computes the intersection of two or more sorted ranges.)
$(T2 setSymmetricDifference,
- Lazily computes the symmetric set difference of two or more sorted
- ranges.)
+ Lazily computes the symmetric set difference of two sorted ranges.)
)
Copyright: Andrei Alexandrescu 2008-.
$(TD Returns a new $(LREF Appender) or $(LREF RefAppender) initialized with a given array.
))
$(TR $(TD $(LREF assocArray))
- $(TD Returns a newly allocated associative array from a range of key/value tuples.
+ $(TD Returns a newly allocated associative array from a range/ranges of keys and values.
))
$(TR $(TD $(LREF byPair))
$(TD Construct a range iterating over an associative array by key/value tuples.
}
/**
-Returns a newly allocated associative array from a range of key/value tuples
-or from a range of keys and a range of values.
+Creates an associative array from a range of key/value tuples.
Params:
r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
- of tuples of keys and values.
- keys = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of keys
- values = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of values
+ of $(REF_SHORT Tuple, std,typecons)`!(Key, Value)`.
-Returns:
+Duplicates: Associative arrays have unique keys. For any duplicate key in `r`,
+the result will contain the corresponding value from the last occurrence of that key in `r`.
- A newly allocated associative array out of elements of the input
- range, which must be a range of tuples (Key, Value) or
- a range of keys and a range of values. If given two ranges of unequal
- lengths after the elements of the shorter are exhausted the remaining
- elements of the longer will not be considered.
- Returns a null associative array reference when given an empty range.
- Duplicates: Associative arrays have unique keys. If r contains duplicate keys,
- then the result will contain the value of the last pair for that key in r.
-
-See_Also: $(REF Tuple, std,typecons), $(REF zip, std,range)
+Returns:
+ A newly allocated associative array, or a null associative array reference when
+ given an empty range. The type is `Value[Key]`.
*/
-
auto assocArray(Range)(Range r)
if (isInputRange!Range)
{
return aa;
}
-/// ditto
+///
+@safe pure nothrow unittest
+{
+ import std.typecons : tuple;
+
+ auto b = assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]);
+ static assert(is(typeof(b) == string[string]));
+ assert(b == ["foo":"bar", "baz":"quux"]);
+}
+
+/**
+Creates an associative array from a range of keys and a range of values.
+
+If given two ranges of unequal lengths after the elements of the shorter are exhausted,
+the remaining elements of the longer will not be considered.
+
+Params:
+ keys = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of keys
+ values = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of values
+
+Duplicates: Associative arrays have unique keys. For any key with duplicates in `keys`,
+the result will have the corresponding value for the last occurrence of
+that key in `keys`.
+
+Returns:
+ A newly allocated associative array, or a null associative array reference when
+ given empty ranges.
+ */
auto assocArray(Keys, Values)(Keys keys, Values values)
if (isInputRange!Values && isInputRange!Keys)
{
}
///
-@safe pure /*nothrow*/ unittest
+@safe pure nothrow unittest
{
- import std.range : repeat, zip;
- import std.typecons : tuple;
import std.range.primitives : autodecodeStrings;
- auto a = assocArray(zip([0, 1, 2], ["a", "b", "c"])); // aka zipMap
- static assert(is(typeof(a) == string[int]));
- assert(a == [0:"a", 1:"b", 2:"c"]);
-
- auto b = assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]);
- static assert(is(typeof(b) == string[string]));
- assert(b == ["foo":"bar", "baz":"quux"]);
+ import std.range : repeat;
static if (autodecodeStrings)
alias achar = dchar;
else
alias achar = immutable(char);
+
auto c = assocArray("ABCD", true.repeat);
static assert(is(typeof(c) == bool[achar]));
bool[achar] expected = ['D':true, 'A':true, 'B':true, 'C':true];
Params: c = The character to test.
Returns: Whether `c` is a letter or a number (0 .. 9, a .. z, A .. Z).
+/
+pragma(inline, true)
bool isAlphaNum(dchar c) @safe pure nothrow @nogc
{
const hc = c | 0x20;
Params: c = The character to test.
Returns: Whether `c` is an ASCII letter (A .. Z, a .. z).
+/
+pragma(inline, true)
bool isAlpha(dchar c) @safe pure nothrow @nogc
{
// Optimizer can turn this into a bitmask operation on 64 bit code
Params: c = The character to test.
Returns: Whether `c` is a lowercase ASCII letter (a .. z).
+/
+pragma(inline, true)
bool isLower(dchar c) @safe pure nothrow @nogc
{
return c >= 'a' && c <= 'z';
Params: c = The character to test.
Returns: Whether `c` is an uppercase ASCII letter (A .. Z).
+/
+pragma(inline, true)
bool isUpper(dchar c) @safe pure nothrow @nogc
{
return c <= 'Z' && 'A' <= c;
Params: c = The character to test.
Returns: Whether `c` is a digit (0 .. 9).
+/
+pragma(inline, true)
bool isDigit(dchar c) @safe pure nothrow @nogc
{
return '0' <= c && c <= '9';
Params: c = The character to test.
Returns: Whether `c` is a digit in base 8 (0 .. 7).
+/
+pragma(inline, true)
bool isOctalDigit(dchar c) @safe pure nothrow @nogc
{
return c >= '0' && c <= '7';
Params: c = The character to test.
Returns: Whether `c` is a digit in base 16 (0 .. 9, A .. F, a .. f).
+/
+pragma(inline, true)
bool isHexDigit(dchar c) @safe pure nothrow @nogc
{
const hc = c | 0x20;
space, tab, vertical tab, form feed, carriage return, and linefeed
characters.
+/
+pragma(inline, true)
bool isWhite(dchar c) @safe pure nothrow @nogc
{
return c == ' ' || (c >= 0x09 && c <= 0x0D);
Params: c = The character to test.
Returns: Whether `c` is a control character.
+/
+pragma(inline, true)
bool isControl(dchar c) @safe pure nothrow @nogc
{
return c < 0x20 || c == 0x7F;
all ASCII characters which are not control characters, letters, digits, or
whitespace.
+/
+pragma(inline, true)
bool isPunctuation(dchar c) @safe pure nothrow @nogc
{
return c <= '~' && c >= '!' && !isAlphaNum(c);
Returns: Whether or not `c` is a printable character other than the
space character.
+/
+pragma(inline, true)
bool isGraphical(dchar c) @safe pure nothrow @nogc
{
return '!' <= c && c <= '~';
Returns: Whether or not `c` is a printable character - including the
space character.
+/
+pragma(inline, true)
bool isPrintable(dchar c) @safe pure nothrow @nogc
{
return c >= ' ' && c <= '~';
* $(BOOKTABLE,
* $(TR $(TH Category) $(TH Symbols))
* $(TR $(TD Tid) $(TD
+ * $(MYREF join)
* $(MYREF locate)
* $(MYREF ownerTid)
* $(MYREF register)
else
{
auto t = new Thread(&exec);
+ spawnTid.mbox.m_thread = t;
t.start();
}
thisInfo.links[spawnTid] = linked;
}
}
+/**
+ * Waits for the thread associated with `tid` to complete.
+ *
+ * This function blocks until the thread represented by `tid` finishes executing
+ * and then releases all OS resources associated with it (thread stack, thread-local
+ * storage, etc.). This is essential for preventing resource leaks in long-running
+ * applications that create many short-lived threads.
+ *
+ * For threads created by $(LREF spawn), this function must be called to properly
+ * free OS resources. Without calling `join`, thread stacks (~8 MB each on typical
+ * systems) will accumulate in virtual memory for the lifetime of the process.
+ *
+ * Params:
+ * tid = The $(LREF Tid) of the thread to join.
+ *
+ * Throws:
+ * $(REF ThreadError, core,thread,osthread) if the thread has already been joined
+ * or if the thread was created by a custom $(LREF Scheduler).
+ *
+ * Example:
+ * ---
+ * auto tid = spawn(&someFunction);
+ * // ... do other work ...
+ * join(tid); // Wait for thread to complete and free resources
+ * ---
+ *
+ * Note:
+ * It is an error to call `join` on the same `Tid` more than once.
+ * The function will throw if the thread was created by a $(LREF Scheduler)
+ * rather than directly as a system thread.
+ */
+void join(Tid tid)
+{
+ import core.thread.threadbase : ThreadError;
+
+ if (tid.mbox is null)
+ throw new ThreadError("Cannot join Tid with null MessageBox");
+
+ if (tid.mbox.m_thread is null)
+ throw new ThreadError("Cannot join Tid created by a Scheduler");
+
+ tid.mbox.m_thread.join();
+}
+
+///
+@system unittest
+{
+ import core.time : msecs;
+ import core.thread : Thread;
+
+ auto tid = spawn((int x) {
+ Thread.sleep(10.msecs);
+ ownerTid.send(x * 2);
+ }, 21);
+
+ join(tid); // Wait for thread to finish
+ auto result = receiveOnly!int();
+ assert(result == 42);
+}
+
/**
* Encapsulates all implementation-level data needed for scheduling.
*
size_t m_localMsgs;
size_t m_maxMsgs;
bool m_closed;
+ package Thread m_thread; // For join(Tid)
}
/*
/**
This module defines generic containers.
-Construction:
+$(H3 $(LNAME2 construction, Construction))
-To implement the different containers both struct and class based
+To implement the different containers, both struct and class based
approaches have been used. $(REF make, std,container,util) allows for
uniform construction with either approach.
+$(RUNNABLE_EXAMPLE
---
import std.container;
// Construct a red-black tree and an array both containing the values 1, 2, 3.
RedBlackTree!int rbTree = new RedBlackTree!int(1, 2, 3);
// But `new` should not be used with Array
Array!int array = Array!int(1, 2, 3);
+
// `make` hides the differences
RedBlackTree!int rbTree2 = make!(RedBlackTree!int)(1, 2, 3);
Array!int array2 = make!(Array!int)(1, 2, 3);
+
+assert(rbTree == rbTree2);
+assert(array == array2);
---
+)
Note that `make` can infer the element type from the given arguments.
---
import std.container;
+
auto rbTree = make!RedBlackTree(1, 2, 3); // RedBlackTree!int
auto array = make!Array("1", "2", "3"); // Array!string
---
-Reference_semantics:
+$(H3 $(LNAME2 reference-semantics, Reference Semantics))
All containers have reference semantics, which means that after
assignment both variables refer to the same underlying data.
To make a copy of a container, use the `c.dup` container primitive.
+
+$(RUNNABLE_EXAMPLE
---
-import std.container, std.range;
+import std.algorithm, std.container, std.range;
+
Array!int originalArray = make!(Array!int)(1, 2, 3);
Array!int secondArray = originalArray;
assert(equal(originalArray[], secondArray[]));
// assert that originalArray has not been affected
assert(originalArray[0] == 12);
---
+)
$(B Attention:) If the container is implemented as a class, using an
uninitialized instance can cause a null pointer dereference.
have an identity and assignment does not create two references to the same
data.
+$(RUNNABLE_EXAMPLE
---
import std.container;
array1.removeBack();
assert(array2.empty);
---
+)
+
It is therefore recommended to always construct containers using
$(REF make, std,container,util).
auto arrOfArrs = make!Array(generate!(() => make!(Array!int)).take(10));
---
-Submodules:
+$(H3 $(LNAME2 submodules, Submodules))
This module consists of the following submodules:
)
)
-The_primary_range_of_a_container:
+$(H3 $(LNAME2 primary-range, The Primary Range of a Container))
While some containers offer direct access to their elements e.g. via
`opIndex`, `c.front` or `c.back`, access
as the one being worked with. It is important to note that many generic range
algorithms return the same range type as their input range.
+$(RUNNABLE_EXAMPLE
---
import std.algorithm.comparison : equal;
-import std.algorithm.iteration : find;
+import std.algorithm.searching : find;
import std.container;
import std.range : take;
assert(array[].equal([1, 3]));
---
+)
When any $(MREF_ALTTEXT range, std, range) can be passed as an argument to
a member function, the documention usually refers to the parameter's templated
type as `Stuff`.
+$(RUNNABLE_EXAMPLE
---
import std.algorithm.comparison : equal;
import std.container;
assert(array[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
---
+)
-Container_primitives:
+$(H3 $(LNAME2 primitives, Container Primitives))
Containers do not form a class hierarchy, instead they implement a
common set of primitives (see table below). These primitives each guarantee
Complexity: $(BIGOH m log(n)) (where m is the number of elements to remove)
Example:
+$(RUNNABLE_EXAMPLE
--------------------
+import std.algorithm, std.container;
+
auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7);
rbt.removeKey(1, 4, 7);
assert(equal(rbt[], [0, 1, 1, 5]));
+
rbt.removeKey(1, 1, 0);
assert(equal(rbt[], [5]));
--------------------
+)
+/
size_t removeKey(U...)(U elems)
if (allSatisfy!(isImplicitlyConvertibleToElem, U))
`r` and `m` is the length of `stuff`.
Example:
+$(RUNNABLE_EXAMPLE
--------------------
+import std.algorithm, std.container, std.range;
+
auto sl = SList!string(["a", "b", "d"]);
sl.insertAfter(sl[], "e"); // insert at the end (slowest)
-assert(std.algorithm.equal(sl[], ["a", "b", "d", "e"]));
-sl.insertAfter(std.range.take(sl[], 2), "c"); // insert after "b"
-assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"]));
+assert(equal(sl[], ["a", "b", "d", "e"]));
+
+sl.insertAfter(take(sl[], 2), "c"); // insert after "b"
+assert(equal(sl[], ["a", "b", "c", "d", "e"]));
--------------------
+)
*/
size_t insertAfter(Stuff)(Range r, Stuff stuff)
assert(convFails!(Floating, Integral, ConvOverflowException)(a));
}
// convert to the smallest integral value
- a = 0.0 + Integral.min;
+ a = 0.0L + Integral.min;
static if (Integral.min < 0)
{
a = -a; // -Integral.min not representable as an Integral
|| Floating.sizeof <= Integral.sizeof
|| floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
}
- a = 0.0 + Integral.min;
+ a = 0.0L + Integral.min;
assert(to!Integral(a) == Integral.min);
--a; // no more representable as an Integral
assert(convFails!(Floating, Integral, ConvOverflowException)(a)
|| Floating.sizeof <= Integral.sizeof
|| floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
- a = 0.0 + Integral.max;
+ a = 0.0L + Integral.max;
assert(to!Integral(a) == Integral.max
|| Floating.sizeof <= Integral.sizeof
|| floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
assert(a.deallocateAll());
}
+//BUG: this test freezes spuriously on FreeBSD, see also https://github.com/dlang/phobos/issues/10730.
+// The lock in a.allocate() below blocks in all remaining threads causing the join to never complete.
+// This might also hint at problems in the spinlock implementation.
+version (FreeBSD) {} else
@system unittest
{
import std.experimental.allocator.mallocator : Mallocator;
{
// ignore hidden context pointer
}
- else static if (0 < i && T.tupleof[i-1].offsetof == T.tupleof[i].offsetof)
+ /* https://github.com/dlang/phobos/issues/10840
+ * handle possible bitfields by doing overlap comparisons
+ * using bit counts rather than byte counts.
+ * However, the overlap
+ * check in general does not take into account staggered unions.
+ * This can be fixed using the correct algorithm implemented in
+ * the compiler function dmd.declaration.isOverlappedWith().
+ * For the moment we will not change to that because the `#(overlap ...)` output
+ * needs to be re-thought, as it was never correct.
+ */
+ else static if (0 < i &&
+ T.tupleof[i-1].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i-1]) ==
+ T.tupleof[i ].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i ]))
{
- static if (i == T.tupleof.length - 1 || T.tupleof[i].offsetof != T.tupleof[i+1].offsetof)
+ static if (i == T.tupleof.length - 1 ||
+ T.tupleof[i ].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i ]) !=
+ T.tupleof[i+1].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i+1]))
{
enum el = separator ~ __traits(identifier, T.tupleof[i]) ~ "}";
put(w, el);
put(w, el);
}
}
- else static if (i+1 < T.tupleof.length && T.tupleof[i].offsetof == T.tupleof[i+1].offsetof)
+ else static if (i+1 < T.tupleof.length &&
+ T.tupleof[i ].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i ]) ==
+ T.tupleof[i+1].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i+1]))
{
enum el = (i > 0 ? separator : "") ~ "#{overlap " ~ __traits(identifier, T.tupleof[i]);
put(w, el);
import std.format : formatValue;
void func() @system { __gshared int x; ++x; throw new Exception("msg"); }
- version (linux)
- {
- FormatSpec!char f;
- auto w = appender!string();
- formatValue(w, &func, f);
- assert(w.data.length >= 15 && w.data[0 .. 15] == "void delegate()");
- }
+ FormatSpec!char f;
+ auto w = appender!string();
+ formatValue(w, &func, f);
+ assert(w.data.length >= 15 && w.data[0 .. 15] == "void delegate()");
}
// string elements are formatted like UTF-8 string literals.
4.59084371199880305320475827592915200343410999829340L) >= real.mant_dig-1);
}
+
+/* This is the lower bound on x for when the Stirling approximation can be used
+ * to compute ln(Γ(x)).
+ */
+private enum real LN_GAMMA_STIRLING_LB = 13.0L;
+
+
/*****************************************************
* Natural logarithm of gamma function.
*
return z;
}
- if ( x < 13.0L )
+ if ( x < LN_GAMMA_STIRLING_LB )
{
z = 1.0L;
nx = floor( x + 0.5L );
}
+/** sgn($(GAMMA)(x))
+ *
+ * Params:
+ * x = the argument of $(GAMMA)
+ *
+ * Returns:
+ * -1 if $(GAMMA)(x) < 0, +1 if $(GAMMA)(x) > 0, and $(NAN) if $(GAMMA)(x)
+ * does not exist.
+ *
+ * Authors: Don Clugston
+ */
+real sgnGamma(in real x)
+{
+ if (isNaN(x)) return x;
+ if (x > 0) return 1.0;
+
+ // -x is so large that x + 1 is indistinguishable from x.
+ if (x < -1 / real.epsilon) return real.nan;
+
+ const n = trunc(x);
+ if (x == n) return x == 0 ? copysign(1, x) : real.nan;
+ return cast(long) n & 1 ? 1.0 : -1.0;
+}
+
+@safe unittest
+{
+ assert(sgnGamma(5.0) == 1.0);
+ assert(isNaN(sgnGamma(-3.0)));
+ assert(sgnGamma(-0.1) == -1.0);
+ assert(sgnGamma(-0.6) == -1.0);
+ assert(sgnGamma(-55.1) == 1.0);
+ assert(isNaN(sgnGamma(-real.infinity)));
+ assert(isIdentical(sgnGamma(NaN(0xABC)), NaN(0xABC)));
+}
+
+
+/* A method for computing B(x,y) when gamma would return infinity. It uses
+ * logGamma and exp instead.
+ */
+private pragma(inline, true) real betaLarge(in real x, in real y)
+{
+ const sgnB = sgnGamma(x) * sgnGamma(y) / sgnGamma(x+y);
+ return sgnB * exp(logGamma(x) + logGamma(y) - logGamma(x+y));
+}
+
+@safe unittest
+{
+ assert(betaLarge(2*MAXGAMMA, -0.5) < 0);
+ assert(betaLarge(-0.1, 2*MAXGAMMA) < 0);
+ assert(betaLarge(-1.6, 2*MAXGAMMA) > 0);
+ assert(betaLarge(+0., 2*MAXGAMMA) == real.infinity);
+ assert(betaLarge(-0., 2*MAXGAMMA) == -real.infinity);
+ assert(betaLarge(-MAXGAMMA-1.5, MAXGAMMA+1) < 0);
+ assert(isNaN(betaLarge(-1, 2*MAXGAMMA)));
+}
+
+/** B(x,y)
+ *
+ * This computes B(x,y). It will use the formula when i$(GAMMA)(x)$(GAMMA)(y)/$(GAMMA)(x+y) when
+ * `gamma` can compute the $(GAMMA) terms, otherwise it will use `logGamma`.
+ *
+ * There are many edge cases that generate NaN instead of the actual value. The main algorithm works
+ * for most (x,y) pairs and only generates a NaN when it doesn't. In order to not penalize every
+ * computation with a bunch of branching logic, the main algorithm is used, and only if it results
+ * in a NaN will the edge cases be checked.
+ *
+ * Params:
+ * x = the first argument of B
+ * y = the second argument of B
+ *
+ * Returns:
+ * B(x,y) if it can be computed, otherwise $(NAN)
+ */
+real beta(in real x, in real y)
+{
+ real res;
+
+ // the main algorithm
+ if (x > MAXGAMMA || y > MAXGAMMA || x + y > MAXGAMMA)
+ {
+ res = betaLarge(x, y);
+ }
+ else
+ {
+ res = gamma(x) * gamma(y) / gamma(x+y);
+
+ // There are several regions near the asymptotes and inflection lines
+ // gamma cannot be computed but logGamma can.
+ if (!isFinite(res)) res = betaLarge(x,y);
+ }
+
+ if (!isNaN(res)) return res;
+
+ // For valid NaN results, always return the response from the main algorithm
+ // in order to preserve signaling NaNs.
+
+ if (isNaN(x) || isNaN(y)) return res;
+
+ // Take advantage of the symmetry B(x,y) = B(y,x)
+ // smaller ≤ larger
+ const larger = cmp(x, y) >= 0 ? x : y;
+ const smaller = cmp(x, y) >= 0 ? y : x;
+ const sum = larger + smaller;
+
+ // in a quadrant of the (smaller,larger) cartesian plane
+ const inQ1 = cmp(smaller, +0.0L) >= 0;
+ const inQ2 = !inQ1 && cmp(larger, +0.0L) >= 0;
+ const inQ3 = !inQ1 && !inQ2;
+
+ const nextToSmallAxis = smaller == 0;
+ const nextToLargeAxis = larger == 0;
+ const nextToOrigin = nextToSmallAxis && nextToLargeAxis;
+
+ // on an asymptote, excluding the one at the axis
+ const onSmallAsymptote = smaller < 0 && smaller == trunc(smaller);
+ const onLargeAsymptote = larger < 0 && larger == trunc(larger);
+
+ // on an inflection line segment
+ const onInflection =
+ sum <= 0 && sum == trunc(sum) && !onSmallAsymptote && !onLargeAsymptote && !nextToOrigin;
+
+ // 1) Either is -∞, B = nan
+ if (larger == -real.infinity || smaller == -real.infinity) return res;
+
+ // 2) On an asymptote, B = nan
+ if (onSmallAsymptote || onLargeAsymptote) return res;
+
+ // 3) On an inflection line segment
+ if (onInflection) return copysign(0.0L, sgnGamma(smaller)*sgnGamma(larger));
+
+ if (inQ1)
+ {
+ // 4) On the larger axis and larger is finite, B = +∞
+ // 5) On the larger axis, and larger is +∞, B = nan
+ if (nextToSmallAxis) return larger < +real.infinity ? +real.infinity : res;
+
+ // 6) Not on the larger axis, and the larger is +∞, B = +0
+ if (!nextToSmallAxis && larger == +real.infinity) return +0.;
+ }
+
+ if (inQ2)
+ {
+ // 7) Next to the origin, B = nan
+ // 8) Next to the larger axis, but not the origin, B = -∞
+ if (nextToSmallAxis) return nextToOrigin ? res : -real.infinity;
+
+ // 9) Larger is +∞, B = ∞ * sgn(Γ(smaller))
+ if (larger == +real.infinity) return copysign(real.infinity, sgnGamma(smaller));
+
+ // 10) next to smaller axis, but not on an asymptote or at the origin,
+ // B = +∞.
+ if (nextToLargeAxis && !onSmallAsymptote && !nextToOrigin) return +real.infinity;
+
+ // larger very large, case 9
+ // larger so large that ln|Γ(larger)| and ln|Γ(sum)| are too large to
+ // represent as reals. Thus they each are approximated as ∞, and the
+ // main algorithm resolves to NaN instead of ±∞.
+ if (sum > 1) return copysign(real.infinity, sgnGamma(smaller));
+ }
+
+ if (inQ3)
+ {
+ // 11) next to the smaller axis, but not on an asymptote, B = -∞.
+ if (nextToLargeAxis && !onSmallAsymptote) return -real.infinity;
+
+ // near origin, case 11
+ // -larger and -sum are so small that ln|Γ(larger)| and ln|Γ(sum)| are
+ // too large to be represented as reals. Thus they each are approximated
+ // as ∞, and the main algorithm resolves to NaN instead of -∞.
+ if (smaller > -0.25) return -real.infinity;
+ }
+
+ // Unknown case
+ return res;
+}
+
+@safe unittest
+{
+ assert(isIdentical(beta(2, NaN(0xABC)), NaN(0xABC)));
+
+ // Test symmetry
+
+ // Test first quadrant
+ assert(beta(+0., 1) == +real.infinity);
+ assert(beta(nextUp(+0.0L), nextUp(+0.0L) > 0), "B(εₓ,ε𞁟) > 0");
+ assert(!isNaN(beta(nextUp(+0.0L), 1)), "B(ε,y), y > 0 should exist");
+ assert(beta(1, +real.infinity) is +0.0L, "lim{y→+∞} B(x,y) = 0⁺, x > 0");
+ assert(beta(1, 1) > 0);
+ assert(beta(0.6*MAXGAMMA, 0.5*MAXGAMMA) > 0);
+ assert(beta(1, 2*MAXGAMMA) > 0);
+ assert(beta(+0., 2*MAXGAMMA) == real.infinity);
+ assert(beta(nextUp(+0.0L), 2*MAXGAMMA) > 0);
+
+ // Test second quadrant above inflection lines
+ assert(isNaN(beta(-0., +0.)), "lim{x→0⁻, y→0⁺} B(x,y) should not exist");
+ assert(beta(-0., real.infinity) == -real.infinity, "lim{x→0⁻, y→+∞} B(x,y) = -∞");
+ assert(isNaN(beta(-2, 3)));
+ assert(beta(-0.5, 1) < 0);
+ assert(beta(-1.5, 3) > 0);
+ assert(beta(nextDown(-0.0L), 1) < 0);
+ assert(beta(nextUp(-1.0L), 2) < 0);
+ assert(beta(nextUp(-0.5L), 0.5) < 0);
+ assert(beta(-0.5, nextUp(0.5L)) < 0);
+ assert(beta(-0.5, real.infinity) == -real.infinity);
+ assert(cmp(beta(nextDown(-0.0L), 2*nextUp(+0.0L)), -0.0L) <= 0);
+ assert(beta(nextUp(-1.0L), 1) < 0);
+ assert(beta(nextDown(-0.0L), +real.infinity) == -real.infinity);
+ assert(beta(nextDown(-0.0L), nextDown(+real.infinity)) < 0, "B(-ε,y) < 0, y large");
+ assert(
+ beta(nextUp(-1.0L), real.infinity) == -real.infinity, "lim{y→+∞} B(-n+ε, y) = -∞, n odd");
+ assert(beta(nextUp(-1.0L), nextDown(real.infinity)) < 0, "B(-n+ε, y) < 0, n odd, y large");
+ assert(beta(nextDown(-1.0L), 2) > 0);
+ assert(beta(nextUp(-2.0L), 3) > 0);
+ assert(beta(nextUp(-1.5L), 1.5) > 0);
+ assert(beta(-1.5, nextUp(1.5L)) > 0);
+ assert(beta(nextDown(-1.0L), nextDown(real.infinity)) > 0);
+ assert(
+ beta(nextUp(-2.0L), real.infinity) == real.infinity, "lim{y→+∞} B(-n+ε, y) = +∞, n even");
+ assert(beta(nextUp(-2.0L), nextDown(real.infinity)) > 0, "B(-n+ε, y) > 0, n even, y large");
+ assert(
+ beta(-1.5, real.infinity) == +real.infinity, "lim{y→+∞} B(x,y) = +∞, -n-1 < x < -n, n odd");
+
+ // Test second quadrant within inflection lines
+ assert(beta(nextDown(-1.0L), nextUp(nextUp(1.0L))) > 0);
+ assert(beta(nextUp(-2.0L), 2) > 0);
+ assert(beta(nextDown(-0.0L), +0.) == +real.infinity);
+ assert(isNaN(beta(-1, +0)), "lim{y→0⁺} B(-n,y), should not exist");
+ assert(beta(nextUp(-1.0L), +0.) == +real.infinity);
+ assert(beta(nextDown(-1.0L), +0.) == +real.infinity);
+ assert(isNaN(beta(-real.infinity, real.infinity)));
+ assert(beta(nextDown(-0.0L), nextUp(+0.0L)) is -0.0L);
+ assert(beta(nextUp(-1.0L), nextDown(1.0L)) is -0.0L);
+ assert(beta(nextDown(-1.0L), nextUp(1.0L)) is +0.0L);
+ assert(beta(-0.5, 0.25) > 0);
+ assert(beta(nextUp(-1.0L), 0.25) > 0);
+ assert(beta(-0.5, nextUp(+0.0L)) > 0);
+ assert(beta(nextDown(-0.5L), 0.5) >= 0);
+ assert(beta(-0.5, nextDown(0.5L)) >= 0);
+ assert(beta(nextUp(-1.0L), nextDown(nextDown(1.0L))) >= 0);
+ assert(beta(nextUp(-1.0L), nextUp(+0.0L)));
+ assert(beta(-1.5, 0.25) > 0);
+ assert(beta(nextUp(2.0L), 0.25) > 0);
+ assert(beta(-1.5, nextUp(+0.0L)) > 0);
+ assert(beta(nextDown(-1.5L), 0.5) >= 0);
+ assert(beta(-1.5, nextDown(0.5L)) >= 0);
+ assert(beta(nextUp(-2.0L), nextUp(+0.0L)) > 0);
+ assert(beta(nextUp(-2.0L), nextDown(nextDown(1.0L))) >= 0);
+ assert(beta(nextDown(nextDown(-1.0L)), nextUp(+0.0L)) >= 0);
+ assert(beta(-1.5, 1) < 0);
+ assert(beta(nextDown(-1.0L), 0.5) < 0);
+ assert(beta(nextUp(-2.0L), 1.5) < 0);
+ assert(beta(nextUp(-1.5L), 0.5) <= 0);
+ assert(beta(-1.5L, nextUp(0.5L)) <= 0);
+ assert(beta(nextDown(-1.5L), 1.5) <= 0);
+ assert(beta(-1.5, nextDown(1.5L)) <= 0);
+ assert(beta(nextDown(-1.0L), 2*(nextUp(+1.0L) - 1)) <= 0);
+ assert(beta(nextUp(-2.0L), 1.) <= 0);
+ assert(beta(nextUp(-2.0L), nextDown(nextDown(2.0L))) <= 0);
+ assert(beta(nextDown(-1.0L), 1.) <= 0);
+ assert(beta(-0.0L, 2*MAXGAMMA) == -real.infinity, "lim{x→0⁻} B(x,y) = -∞, y > MAXGAMMA");
+ assert(beta(nextDown(-0.0L), 2*MAXGAMMA) < 0, "B(-ε,y) < 0, y > MAXGAMMA");
+
+ // Test third quadrant
+ assert(beta(-0., -0.) == -real.infinity);
+ assert(isNaN(beta(-2, -0.5)));
+ assert(beta(-0.5, -0.) == -real.infinity);
+ assert(isNaN(beta(-1, -0.)));
+ assert(isNaN(beta(-real.infinity, -0.)));
+ assert(beta(nextDown(-0.0L), -0.) == -real.infinity);
+ assert(isNaN(beta(-1, -0.)));
+ assert(beta(nextUp(-1.0L), -0.) == -real.infinity);
+ assert(beta(nextDown(-1.0L), -0.) == -real.infinity);
+ assert(isNaN(beta(-1.5, -1)));
+ assert(isNaN(beta(-3, -1)));
+ assert(beta(-0.75, -0.25) == +0.);
+ assert(beta(nextUp(-1.0L), nextDown(1.0L) - 1) == +0., "B(-n+ε, -ε) = 0⁺, n odd");
+ assert(beta(nextDown(-1.0L), nextUp(1.0L)) == -0.);
+ assert(beta(-0.5, -0.25) < 0);
+ assert(beta(-0.5, nextDown(-0.0L)) < 0);
+ assert(beta(-0.5, nextUp(-0.5L)) <= 0);
+ assert(beta(nextDown(-0.0L), nextDown(-0.0L)) < 0, "B(-εₓ,-ε𞁟) < 0");
+ assert(beta(nextUp(nextUp(-1.0L)), nextDown(-0.0L)) <= 0);
+ assert(beta(-2.25, -1.25) < 0);
+ assert(beta(nextUp(-2.5L), -1.5) <= 0);
+ assert(beta(-2.5, nextDown(-1.0L)) < 0);
+ assert(beta(nextDown(-2.0L), -1.5) < 0);
+ assert(beta(nextDown(-2.0L), nextDown(-1.0L)) < 0);
+ assert(beta(nextUp(nextUp(-3.0L)), nextDown(-1.0L)) <= 0);
+ assert(beta(nextDown(-2.0L), nextUp(nextUp(-2.0L))) <= 0);
+ assert(beta(-2.75, -1.75) > 0);
+ assert(beta(nextDown(-2.5L), -1.5) >= 0);
+ assert(beta(nextUp(-3.0L), -1.5) > 0);
+ assert(beta(-2.5, nextUp(-2.0L)) > 0);
+ assert(beta(nextUp(-3.0L), nextUp(-2.0L)) > 0);
+ assert(beta(nextUp(-3.0L), nextDown(nextDown(-1.0L))) >= 0);
+ assert(beta(nextDown(nextDown(-2.0L)), nextUp(-2.0L)) >= 0);
+}
+
+
+/* This is the natural logarithm of the absolute value of the beta function. It
+ * tries to eliminate reduce the loss of precision that happens when subtracting
+ * large numbers by combining the Stirling approximations of the individual
+ * logGamma calls.
+ *
+ * ln|B(x,y)| = ln|Γ(x)| + ln|Γ(y)| - ln|Γ(x+y)|. Stirling's approximation for
+ * ln|Γ(z)| is ln|Γ(z)| ~ zln(z) - z + ln(2𝜋/z)/2 + 𝚺ₙ₌₁ᴺB₂ₙ/[2n(2n-1)z²ⁿ⁻¹],
+ * where Bₙ is the nᵗʰ Bernoulli number.
+ * 𝚺ₙ₌₁ᴺB₂ₙ/[2n(2n-1)z²ⁿ⁻¹] = 𝚺ₙ₌₁ᴺB₂ₙ/[2n(2n-1)z²ⁿ⁻²]/z
+ * = 𝚺ₙ₌₀ᴺ⁻¹B₂₍ₙ₊₁₎/[(2n+2)(2n+1)z²ⁿ]/z
+ * = [𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/z²)ⁿ]/z,
+ * where Cₙ = B₂₍ₙ₊₁₎/[(2n+2)(2n+1)].
+ * ln|Γ(z)| ~ zln(z) - z + ln(2𝜋/z)/2 +[𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/z²)ⁿ]/z.
+ */
+private real logBeta(real x, in real y)
+{
+ const larger = x > y ? x : y;
+ const smaller = x < y ? x : y;
+ const sum = larger + smaller;
+
+ if (larger >= LN_GAMMA_STIRLING_LB && sum >= LN_GAMMA_STIRLING_LB && larger - smaller > 10.0L)
+ {
+ // Assume x > y
+ // ln|Γ(x)| - ln|Γ(x+y)|
+ // ~ x⋅ln(x) - (x+y)ln(x+y) + y + ln(2𝜋/x)/2 - ln(2𝜋/[x+y])/2
+ // + [𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/x²)ⁿ]/x - [𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/{x+y}²)ⁿ]/{x+y}.
+ // x⋅ln(x) - (x+y)ln(x+y) + y + ln(2𝜋/x)/2 - ln(2𝜋/[x+y])/2
+ // = ln(xˣ) - ln([x+y]ˣ⁺ʸ) + y + ln(√[2𝜋/x]) - ln(√[2𝜋/{x+y}])
+ // = ln(xˣ⁻¹ᐟ²/[x+y]ˣ⁺ʸ⁻¹ᐟ²) + y = ln([x/{x+y}]ˣ⁺ʸ⁻¹ᐟ²x⁻ʸ) + y
+ // = y - y⋅ln(x) + (.5 - x - y)ln(1 + y/x)
+ // ln|B(x,y)|
+ // ~ ln|Γ(y)| + y - y⋅ln(x) + (.5 - x - y)ln(1 + y/x)
+ // + [𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/x²)ⁿ]/x - [𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/{x+y}²)ⁿ]/{x+y}.
+ const gamDiffApprox = smaller - smaller*log(larger) + (0.5L - sum)*log1p(smaller/larger);
+
+ const gamDiffCorr
+ = poly(1.0L/larger^^2, logGammaStirlingCoeffs) / larger
+ - poly(1.0L/sum^^2, logGammaStirlingCoeffs) / sum;
+
+ return logGamma(smaller) + gamDiffApprox + gamDiffCorr;
+ }
+
+ return logGamma(smaller) + logGamma(larger) - logGamma(sum);
+}
+
+@safe unittest
+{
+ assert(isClose(logBeta(1, 1), log(beta(1, 1))));
+ assert(isClose(logBeta(3, 2), logBeta(2, 3)));
+ assert(isClose(exp(logBeta(20, 4)), beta(20, 4)));
+ assert(isClose(logBeta(30, 40), log(beta(30, 40))));
+
+ // The following were generated by scipy's betaln function.
+ assert(feqrel(logBeta(-1.4, -0.4), 1.133_156_234_422_692_6) > double.mant_dig-3);
+ assert(feqrel(logBeta(-0.5, 1.0), 0.693_147_180_559_945_2) > double.mant_dig-3);
+ assert(feqrel(logBeta(1.0, 2.0), -0.693_147_180_559_945_3) > double.mant_dig-3);
+ assert(feqrel(logBeta(14.0, 3.0), -7.426_549_072_397_305) > double.mant_dig-3);
+ assert(feqrel(logBeta(20.0, 30.0), -33.968_820_791_977_386) > double.mant_dig-3);
+}
+
+
private {
/*
* These value can be calculated like this:
*/
real betaIncomplete(real aa, real bb, real xx )
{
- if ( !(aa>0 && bb>0) )
+ // If any parameters are NaN, return the NaN with the largest payload.
+ if (isNaN(aa) || isNaN(bb) || isNaN(xx))
{
- if ( isNaN(aa) ) return aa;
- if ( isNaN(bb) ) return bb;
- return real.nan; // domain error
+ // With cmp,
+ // -NaN(larger) < -NaN(smaller) < -inf < inf < NaN(smaller) < NaN(larger).
+ const largerParam = cmp(abs(aa), abs(bb)) >= 0 ? aa : bb;
+ return cmp(abs(xx), abs(largerParam)) >= 0 ? xx : largerParam;
}
- if (!(xx>0 && xx<1.0))
+
+ // domain errors
+ if (signbit(aa) == 1 || signbit(bb) == 1) return real.nan;
+ if (xx < 0.0L || xx > 1.0L) return real.nan;
+
+ // edge cases
+ if ( xx == 0.0L ) return 0.0L;
+ if ( xx == 1.0L ) return 1.0L;
+
+ // degenerate cases
+ if (aa is +0.0L || aa is real.infinity || bb is +0.0L || bb is real.infinity)
{
- if (isNaN(xx)) return xx;
- if ( xx == 0.0L ) return 0.0;
- if ( xx == 1.0L ) return 1.0;
- return real.nan; // domain error
+ if (aa is +0.0L && bb is +0.0L) return real.nan;
+ if (aa is real.infinity && bb is real.infinity) return real.nan;
+ if (aa is +0.0L || bb is real.infinity) return 1.0L;
+ if (aa is real.infinity || bb is +0.0L) return 0.0L;
}
+
+ // symmetry
+ if (aa == bb && xx == 0.5L) return 0.5L;
+
if ( (bb * xx) <= 1.0L && xx <= 0.95L)
{
return betaDistPowerSeries(aa, bb, xx);
b = aa;
xc = xx;
x = 1.0L - xx;
+ if (x == 1.0L) x = nextDown(x);
}
else
{
t *= pow(x,a);
t /= a;
t *= w;
- t *= gamma(a+b) / (gamma(a) * gamma(b));
+ t /= beta(a, b);
}
else
{
/* Resort to logarithms. */
- y += t + logGamma(a+b) - logGamma(a) - logGamma(b);
+ y += t - logBeta(a, b);
y += log(w/a);
t = exp(y);
assert(isIdentical(betaIncompleteInv(2,NaN(0xABC),8), NaN(0xABC)));
assert(isIdentical(betaIncompleteInv(2,3, NaN(0xABC)), NaN(0xABC)));
- assert(isNaN(betaIncomplete(-1, 2, 3)));
+ assert(isNaN(betaIncomplete(-0., 1, .5)));
+ assert(isNaN(betaIncomplete(1, -0., .5)));
+ assert(isNaN(betaIncomplete(1, 1, -1)));
+ assert(isNaN(betaIncomplete(1, 1, 2)));
+
+ assert(betaIncomplete(+0., +0., 0) == 0);
+ assert(isNaN(betaIncomplete(+0., +0., .5)));
+ assert(betaIncomplete(+0., +0., 1) == 1);
+ assert(betaIncomplete(+0., 1, .5) == 1);
+ assert(betaIncomplete(1, +0., 0) == 0);
+ assert(betaIncomplete(1, +0., .5) == 0);
+ assert(betaIncomplete(1, real.infinity, .5) == 1);
+ assert(betaIncomplete(real.infinity, real.infinity, 0) == 0);
+ assert(isNaN(betaIncomplete(real.infinity, real.infinity, .5)));
assert(betaIncomplete(1, 2, 0)==0);
assert(betaIncomplete(1, 2, 1)==1);
- assert(isNaN(betaIncomplete(1, 2, 3)));
+ assert(betaIncomplete(9.99999984824320730e+30, 9.99999984824320730e+30, 0.5) == 0.5L);
+ assert(betaIncomplete(1.17549435082228751e-38, 9.99999977819630836e+22, 9.99999968265522539e-22) == 1.0L);
+ assert(betaIncomplete(1.00000001954148138e-25, 1.00000001490116119e-01, 1.17549435082228751e-38) == 1.0L);
+ assert(isClose(betaIncomplete(9.99999983775159024e-18, 9.99999977819630836e+22, 1.00000001954148138e-25), 1.0L));
+ assert(isClose(
+ betaIncomplete(9.99999974737875164e-06, 9.99999998050644787e+18, 9.99999968265522539e-22),
+ 0.9999596214389047L));
+
assert(betaIncompleteInv(1, 1, 0)==0);
assert(betaIncompleteInv(1, 1, 1)==1);
assert(betaIncompleteInv(0.01L, 8e-48L, 9e-26L) == 1-real.epsilon);
// Beware: a one-bit change in pow() changes almost all digits in the result!
+ // scipy says that this is 0.99999_99995_89020_6 (0x1.ffff_fffc_783f_2a7ap-1)
+ // in double precision.
assert(feqrel(
betaIncompleteInv(0x1.b3d151fbba0eb18p+1L, 1.2265e-19L, 2.44859e-18L),
- 0x1.c0110c8531d0952cp-1L
+ 0x1.ffff_fffc_783f_2a7ap-1
) > 10);
// This next case uncovered a one-bit difference in the FYL2X instruction
// between Intel and AMD processors. This difference gets magnified by 2^^38.
- // WolframAlpha crashes attempting to calculate this.
- assert(feqrel(betaIncompleteInv(0x1.ff1275ae5b939bcap-41L, 4.6713e18L, 0.0813601L),
- 0x1.f97749d90c7adba8p-63L) >= real.mant_dig - 39);
+ // WolframAlpha fails to calculate this.
+ // scipy says that this is 2.225073858507201e-308 in double precision,
+ // essentially double.min-normal.
+ assert(isClose(
+ betaIncompleteInv(0x1.ff1275ae5b939bcap-41L, 4.6713e18L, 0.0813601L),
+ 2.225_073_858_507_201e-308L,
+ 0,
+ 1e-40));
+
+ // scipy says that this is 8.068764506083944e-20 to double precision. Since this is a
+ // regression test where the original value isn't a known good value, I' updating the
+ // test value to the current generated value, which is closer to the scipy value.
real a1 = 3.40483L;
- assert(betaIncompleteInv(a1, 4.0640301659679627772e19L, 0.545113L) == 0x1.ba8c08108aaf5d14p-109L);
+ assert(betaIncompleteInv(a1, 4.0640301659679627772e19L, 0.545113L) == 0x1.2a867b1e12b9bdf0p-64L);
+
real b1 = 2.82847e-25L;
assert(feqrel(betaIncompleteInv(0.01L, b1, 9e-26L), 0x1.549696104490aa9p-830L) >= real.mant_dig-10);
// --- Problematic cases ---
- // This is a situation where the series expansion fails to converge
- assert( isNaN(betaIncompleteInv(0.12167L, 4.0640301659679627772e19L, 0.0813601L)));
+ // In the past, this was a situation where the series expansion failed
+ // to converge.
+ assert(!isNaN(betaIncompleteInv(0.12167L, 4.0640301659679627772e19L, 0.0813601L)));
+ // Using scipy, the result should be 1.683301919972747e-29.
+
// This next result is almost certainly erroneous.
// Mathematica states: "(cannot be determined by current methods)"
assert(betaIncomplete(1.16251e20L, 2.18e39L, 5.45e-20L) == -real.infinity);
u = a * log(x);
if ( (a+b) < MAXGAMMA && fabs(u) < MAXLOG )
{
- t = gamma(a+b)/(gamma(a)*gamma(b));
- s = s * t * pow(x,a);
+ s = s * pow(x,a) / beta(a, b);
}
else
{
- t = logGamma(a+b) - logGamma(a) - logGamma(b) + u + log(s);
+ if (abs(a*s - 1.0L) < 0.01L)
+ {
+ // Compute logGamma(a+b) - logGamma(b)
+ real lnGamma_apb_m_lnGamma_b;
+
+ if (b >= LN_GAMMA_STIRLING_LB)
+ {
+ const gamDiffApprox = a - a*log(b) + (0.5L - a - b)*log1p(a/b);
+
+ const gamDiffCorr
+ = poly(1.0L/b^^2, logGammaStirlingCoeffs) / b
+ - poly(1.0L/(a+b)^^2, logGammaStirlingCoeffs) / (a+b);
+
+ lnGamma_apb_m_lnGamma_b = -gamDiffApprox - gamDiffCorr;
+ }
+ else
+ {
+ lnGamma_apb_m_lnGamma_b = logGamma(a+b) - logGamma(b);
+ }
+
+ // Compute log(s) - logGamma(a)
+ const ln_s_m_lnGamma_a = log1p(a*s - 1.0L) - log(a) - logGamma(a);
+
+ t = lnGamma_apb_m_lnGamma_b + u + ln_s_m_lnGamma_a;
+ }
+ else
+ {
+ t = u + log(s) - logBeta(a, b);
+ }
if ( t < MINLOG )
{
} else
s = exp(t);
}
+
+ if (s > 1.0L) return (s - 2*real.epsilon <= 1.0L) ? 1.0L : real.nan;
return s;
}
* For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF.
*/
ulong getNaNPayload(real x) @trusted pure nothrow @nogc
+in
+{
+ // Precondition: Input must be NaN
+ import std.math.traits : isNaN;
+ assert(isNaN(x), "getNaNPayload called on a non-NaN value");
+}
+do
{
import std.math.traits : floatTraits, RealFormat;
* SUB = $1<sub>$2</sub>
* BIGSUM = $(BIG Σ <sup>$2</sup><sub>$(SMALL $1)</sub>)
* CHOOSE = $(BIG () <sup>$(SMALL $1)</sup><sub>$(SMALL $2)</sub> $(BIG ))
+ * CEIL = ⌈$1⌉
* PLUSMN = ±
* MNPLUS = ∓
* INFIN = ∞
* MNPLUSINF = ∓∞
* PI = π
* LT = <
+ * LE = ≤
* GT = >
* SQRT = √
* HALF = ½
- *
+ * COMPLEX = ℂ
*
* Copyright: Based on the CEPHES math library, which is
* Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com).
/** The sign of $(GAMMA)(x).
*
- * Returns -1 if $(GAMMA)(x) < 0, +1 if $(GAMMA)(x) > 0,
- * $(NAN) if sign is indeterminate.
+ * Params:
+ * x = the argument of $(GAMMA)
+ *
+ * Returns:
+ * -1 if $(GAMMA)(x) < 0, +1 if $(GAMMA)(x) > 0, and $(NAN) if $(GAMMA)(x)
+ * does not exist.
*
- * Note that this function can be used in conjunction with logGamma(x) to
- * evaluate gamma for very large values of x.
+ * Note:
+ * This function can be used in conjunction with `logGamma` to evaluate
+ * $(GAMMA)(x) when `gamma(x)` is too large to be represented as a `real`.
*/
-real sgnGamma(real x)
+pragma(inline, true) real sgnGamma(real x)
{
- import core.math : rndtol;
- /* Author: Don Clugston. */
- if (isNaN(x)) return x;
- if (x > 0) return 1.0;
- if (x < -1/real.epsilon)
- {
- // Large negatives lose all precision
- return real.nan;
- }
- long n = rndtol(x);
- if (x == n)
- {
- return x == 0 ? copysign(1, x) : real.nan;
- }
- return n & 1 ? 1.0 : -1.0;
+ return std.internal.math.gammafunction.sgnGamma(x);
}
+///
@safe unittest
{
- assert(sgnGamma(5.0) == 1.0);
- assert(isNaN(sgnGamma(-3.0)));
- assert(sgnGamma(-0.1) == -1.0);
- assert(sgnGamma(-55.1) == 1.0);
- assert(isNaN(sgnGamma(-real.infinity)));
- assert(isIdentical(sgnGamma(NaN(0xABC)), NaN(0xABC)));
+ assert(sgnGamma(10_000) == 1);
}
-/** Beta function
+/** Beta function, B(x,y)
+ *
+ * Mathematically, if x $(GT) 0 and y $(GT) 0 then
+ * B(x,y) = $(INTEGRATE 0, 1)$(POWER t, x-1)$(POWER (l-t), y-1)dt. Through analytic continuation, it
+ * is extended to $(COMPLEX)$(SUP 2) where it can be expressed in terms of $(GAMMA)(z).
+ *
+ * B(x,y) = $(GAMMA)(x)$(GAMMA)(y) / $(GAMMA)(x+y).
+ *
+ * This implementation restricts x and y to the set of real numbers.
+ *
+ * Params:
+ * x = the first argument of B
+ * y = the second argument of B
+ *
+ * Returns:
+ * It returns B(x,y) if it can be computed, otherwise $(NAN).
*
- * The beta function is defined as
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH y) $(TH beta(x, y)) )
+ * $(TR $(TD $(NAN)) $(TD y) $(TD $(NAN)) )
+ * $(TR $(TD -$(INFIN)) $(TD y) $(TD $(NAN)) )
+ * $(TR $(TD integer $(LT) 0) $(TD y) $(TD $(NAN)) )
+ * $(TR $(TD noninteger and x+y even $(LE) 0) $(TD noninteger) $(TD -0) )
+ * $(TR $(TD noninteger and x+y odd $(LE) 0) $(TD noninteger) $(TD +0) )
+ * $(TR $(TD +0) $(TD positive finite) $(TD +$(INFIN)) )
+ * $(TR $(TD +0) $(TD +$(INFIN)) $(TD $(NAN)) )
+ * $(TR $(TD $(GT) 0) $(TD +$(INFIN)) $(TD +0) )
+ * $(TR $(TD -0) $(TD +0) $(TD $(NAN)) )
+ * $(TR $(TD -0) $(TD $(GT) 0) $(TD -$(INFIN)) )
+ * $(TR $(TD noninteger $(LT) 0, $(CEIL x) odd) $(TD +$(INFIN)) $(TD -$(INFIN)) )
+ * $(TR $(TD noninteger $(LT) 0, $(CEIL x) even) $(TD +$(INFIN)) $(TD +$(INFIN)) )
+ * $(TR $(TD noninteger $(LT) 0) $(TD $(PLUSMN)0) $(TD $(PLUSMNINF)) )
+ * )
*
- * beta(x, y) = ($(GAMMA)(x) * $(GAMMA)(y)) / $(GAMMA)(x + y)
+ * Since B(x,y) = B(y,x), if the table states that beta(x, y) is a special value, then beta(y, x) is
+ * one as well.
*/
-real beta(real x, real y)
+pragma(inline, true) real beta(real x, real y)
{
- if ((x+y)> MAXGAMMA)
- {
- return exp(logGamma(x) + logGamma(y) - logGamma(x+y));
- } else return gamma(x) * gamma(y) / gamma(x+y);
+ return std.internal.math.gammafunction.beta(x, y);
}
+///
@safe unittest
{
+ assert(beta(1, 2) == 0.5);
assert(isIdentical(beta(NaN(0xABC), 4), NaN(0xABC)));
- assert(isIdentical(beta(2, NaN(0xABC)), NaN(0xABC)));
+ assert(beta(3, 4) == beta(4, 3));
+ assert(isNaN(beta(-real.infinity, +0.)));
+ assert(isNaN(beta(-1, 2)));
+ assert(beta(-0.5, 0.5) is -0.0L);
+ assert(beta(-1.5, 0.5) is +0.0L);
+ assert(beta(+0., +0.) == +real.infinity);
+ assert(isNaN(beta(+0., +real.infinity)));
+ assert(beta(1, +real.infinity) is +0.0L);
+ assert(isNaN(beta(-0., +0.)));
+ assert(beta(-0., nextUp(+0.0L)) == -real.infinity);
+ assert(beta(-0.5, +real.infinity) == -real.infinity);
+ assert(beta(nextDown(-1.0L), real.infinity) == real.infinity);
+ assert(beta(nextDown(-0.0L), +0.) == +real.infinity);
+ assert(beta(-0.5, -0.) == -real.infinity);
}
/** Digamma function, $(PSI)(x)
return std.internal.math.gammafunction.logmdigammaInverse(x);
}
-/** Incomplete beta integral
+/** Regularized incomplete beta function $(SUB I, x)(a,b)
*
- * Returns regularized incomplete beta integral of the arguments, evaluated
- * from zero to x. The regularized incomplete beta function is defined as
+ * Mathematically, if a and b are positive real numbers, and 0 $(LE) x $(LE) 1, then
+ * $(SUB I, x)(a,b) = $(INTEGRATE 0, x)$(POWER t, a-1)$(POWER (1-t), b-1)dt/B(a,b) where B is the
+ * beta function. It is also the cumulative distribution function of the beta distribution.
*
- * betaIncomplete(a, b, x) = $(GAMMA)(a + b) / ( $(GAMMA)(a) $(GAMMA)(b) ) *
- * $(INTEGRATE 0, x) $(POWER t, a-1)$(POWER (1-t), b-1) dt
+ * `betaIncomplete(a, b, x)` evaluates $(SUB I, `x`)(`a`,`b`).
*
- * and is the same as the cumulative distribution function of the Beta
- * distribution.
+ * Params:
+ * a = the first argument of B, must be positive
+ * b = the second argument of B, must be positive
+ * x = the fraction of integration completion from below, 0 $(LE) x $(LE) 1
*
- * The domain of definition is 0 <= x <= 1. In this
- * implementation a and b are restricted to positive values.
- * The integral from x to 1 may be obtained by the symmetry
- * relation
+ * Returns:
+ * It returns $(SUB I, x)(a,b), an element of [0,1].
+ *
+ * $(TABLE_SV
+ * $(TR $(TH a) $(TH b) $(TH x) $(TH betaIncomplete(a, b, x)) )
+ * $(TR $(TD negative) $(TD b) $(TD x) $(TD $(NAN)) )
+ * $(TR $(TD a) $(TD negative) $(TD x) $(TD $(NAN)) )
+ * $(TR $(TD a) $(TD b) $(TD $(LT) 0) $(TD $(NAN)) )
+ * $(TR $(TD a) $(TD b) $(TD $(GT) 1) $(TD $(NAN)) )
+ * $(TR $(TD +0) $(TD +0) $(TD (0,1)) $(TD $(NAN)) )
+ * $(TR $(TD $(INFIN)) $(TD $(INFIN)) $(TD (0,1)) $(TD $(NAN)) )
+ * )
*
- * betaIncompleteCompl(a, b, x ) = betaIncomplete( b, a, 1-x )
+ * If one or more of the input parameters are $(NAN), the one with the largest payload is returned.
+ * For equal payloads but with possibly different signs, the order of preference is x, a, b.
*
- * The integral is evaluated by a continued fraction expansion
- * or, when b * x is small, by a power series.
+ * Note:
+ * The integral is evaluated by a continued fraction expansion or, when `b * x` is small, by a
+ * power series.
+ *
+ * See_Also: $(LREF beta) $(LREF betaIncompleteCompl)
*/
real betaIncomplete(real a, real b, real x )
+in
+{
+ if (!isNaN(a) && !isNaN(b) && !isNaN(x))
+ {
+ assert(signbit(a) == 0, "a must be positive");
+ assert(signbit(b) == 0, "b must be positive");
+ assert(x >= 0 && x <= 1, "x must be in [0,1]");
+ }
+}
+out(i; isNaN(i) || (i >=0 && i <= 1))
+do
{
return std.internal.math.gammafunction.betaIncomplete(a, b, x);
}
+///
+@safe unittest
+{
+ assert(betaIncomplete(1, 1, .5) == .5);
+ assert(betaIncomplete(+0., +0., 0) == 0);
+ assert(isNaN(betaIncomplete(+0., +0., .5)));
+ assert(isNaN(betaIncomplete(real.infinity, real.infinity, .5)));
+ assert(betaIncomplete(real.infinity, real.infinity, 1) == 1);
+ assert(betaIncomplete(NaN(0x1), 1, NaN(0x2)) is NaN(0x2));
+ assert(betaIncomplete(1, NaN(0x3), -NaN(0x3)) is -NaN(0x3));
+}
+
+/** Regularized incomplete beta function complement $(SUB I$(SUP C), x)(a,b)
+ *
+ * Mathematically, if a $(GT) 0, b $(GT) 0, and 0 $(LE) x $(LE) 1, then
+ * $(SUB I$(SUP C), x)(a,b) = $(INTEGRATE x, 1)$(POWER t, a-1)$(POWER (1-t), b-1)dt/B(a,b) where B
+ * is the beta function. It is also the complement of the cumulative distribution function of the
+ * beta distribution. It can be shown that $(SUB I$(SUP C), x)(a,b) = $(SUB I, 1-x)(b,a).
+ *
+ * `betaIncompleteCompl(a, b, x)` evaluates $(SUB I$(SUP C), `x`)(`a`,`b`).
+ *
+ * Params:
+ * a = the first argument of B, must be positive
+ * b = the second argument of B, must be positive
+ * x = the fraction of integration completion from above, 0 $(LE) x $(LE) 1
+ *
+ * Returns:
+ * It returns $(SUB I$(SUP C), x)(a,b), an element of [0,1].
+ *
+ * $(TABLE_SV
+ * $(TR $(TH a) $(TH b) $(TH x) $(TH betaIncompleteCompl(a, b, x)) )
+ * $(TR $(TD negative) $(TD b) $(TD x) $(TD $(NAN)) )
+ * $(TR $(TD a) $(TD negative) $(TD x) $(TD $(NAN)) )
+ * $(TR $(TD a) $(TD b) $(TD $(LT) 0) $(TD $(NAN)) )
+ * $(TR $(TD a) $(TD b) $(TD $(GT) 1) $(TD $(NAN)) )
+ * $(TR $(TD +0) $(TD +0) $(TD (0,1)) $(TD $(NAN)) )
+ * $(TR $(TD $(INFIN)) $(TD $(INFIN)) $(TD (0,1)) $(TD $(NAN)) )
+ * )
+ *
+ * If one or more of the input parameters are $(NAN), the one with the largest payload is returned.
+ * For equal payloads but with possibly different signs, the order of preference is x, a, b.
+ *
+ * See_Also: $(LREF beta) $(LREF betaIncomplete)
+ */
+real betaIncompleteCompl(real a, real b, real x)
+in
+{
+ // allow NaN input to pass through so that it can be addressed by the
+ // internal NaN payload propagation logic
+ if (!isNaN(a) && !isNaN(b) && !isNaN(x))
+ {
+ assert(signbit(a) == 0, "a must be positive");
+ assert(signbit(b) == 0, "b must be positive");
+ assert(x >= 0 && x <= 1, "x must be in [0, 1]");
+ }
+}
+out(i; isNaN(i) || (i >=0 && i <= 1))
+do
+{
+ return std.internal.math.gammafunction.betaIncomplete(b, a, 1-x);
+}
+
+///
+@safe unittest
+{
+ assert(betaIncompleteCompl(.1, .2, 0) == betaIncomplete(.2, .1, 1));
+}
+
/** Inverse of incomplete beta integral
*
* Given y, the function finds x such that
version (Windows)
{
FlushViewOfFile(data.ptr, data.length);
+ FlushFileBuffers(hFile);
}
else version (Posix)
{
else
{
closePipeWriteEnds();
+
+ T retryInterrupted(T)(scope T delegate() syscall)
+ {
+ import core.stdc.errno : errno, EINTR;
+ T result;
+ do
+ result = syscall();
+ while (result == -1 && .errno == EINTR);
+ return result;
+ }
+
auto status = InternalError.noerror;
- auto readExecResult = core.sys.posix.unistd.read(forkPipe[0], &status, status.sizeof);
+ auto readExecResult = retryInterrupted(() => core.sys.posix.unistd.read(forkPipe[0], &status, status.sizeof));
// Save error number just in case if subsequent "waitpid" fails and overrides errno
immutable lastError = .errno;
// Forked child exits right after creating second fork. So it should be safe to wait here.
import core.sys.posix.sys.wait : waitpid;
int waitResult;
- waitpid(id, &waitResult, 0);
+ retryInterrupted(() => waitpid(id, &waitResult, 0));
}
if (readExecResult == -1)
if (status != InternalError.noerror)
{
int error;
- readExecResult = read(forkPipe[0], &error, error.sizeof);
+ readExecResult = retryInterrupted(() => read(forkPipe[0], &error, error.sizeof));
string errorMsg;
final switch (status)
{
* $(LI it has a 'bool isUniformRandom' field readable in CTFE)
* )
*/
-template isUniformRNG(Rng, ElementType)
-{
- enum bool isUniformRNG = .isUniformRNG!Rng &&
- is(std.range.primitives.ElementType!Rng == ElementType);
-}
+enum isUniformRNG(Rng, ElementType) = .isUniformRNG!Rng &&
+ is(std.range.primitives.ElementType!Rng == ElementType);
/**
* ditto
*/
-template isUniformRNG(Rng)
-{
- enum bool isUniformRNG = isInputRange!Rng &&
- is(typeof(
- {
- static assert(Rng.isUniformRandom); //tag
- }));
-}
+enum isUniformRNG(Rng) = isInputRange!Rng &&
+ is(typeof(
+ {
+ static assert(Rng.isUniformRandom); //tag
+ }));
///
@safe unittest
* $(LI it has a 'seed(ElementType)' function)
* )
*/
-template isSeedable(Rng, SeedType)
-{
- enum bool isSeedable = isUniformRNG!(Rng) &&
- is(typeof(
- {
- Rng r = void; // can define a Rng object
- SeedType s = void;
- r.seed(s); // can seed a Rng
- }));
-}
+enum isSeedable(Rng, SeedType) = isUniformRNG!(Rng) &&
+ is(typeof(
+ {
+ Rng r = void; ///< can define a Rng object
+ SeedType s = void; ///< Dummy doc to silence D-scanner.
+ r.seed(s); // can seed a Rng
+ }));
///ditto
-template isSeedable(Rng)
-{
- enum bool isSeedable = isUniformRNG!Rng &&
- is(typeof(
- {
- Rng r = void; // can define a Rng object
- alias SeedType = typeof(r.front);
- SeedType s = void;
- r.seed(s); // can seed a Rng
- }));
-}
+enum isSeedable(Rng) = isUniformRNG!Rng &&
+ is(typeof(
+ {
+ Rng r = void; ///< can define a Rng object
+ alias SeedType = typeof(r.front);
+ SeedType s = void; ///< Dummy doc to silence D-scanner.
+ r.seed(s); // can seed a Rng
+ }));
///
@safe unittest
$(TD Creates the range that results from discarding
the first element from the given range.
))
- $(TR $(TD $(D $(LREF dropBackOne)))
+ $(TR $(TD $(LREF dropBackOne))
$(TD Creates the range that results from discarding
the last element from the given range.
))
$(TD Similar to `recurrence`, except that a random-access range is
created.
))
- $(TR $(TD $(D $(LREF slide)))
+ $(TR $(TD $(LREF slide))
$(TD Creates a range that returns a fixed-size sliding window
over the original range. Unlike chunks,
it advances a configurable number of items at a time,
return _original[_index];
}
- static if (is(typeof((cast(const R)_original)[_index])))
+ static if (__traits(compiles, (const R r) => r[0]))
{
/// ditto
@property auto ref front() const
return _original[(n + _index) % _original.length];
}
- static if (is(typeof((cast(const R)_original)[_index])) &&
- is(typeof((cast(const R)_original).length)))
+ static if (__traits(compiles, (const R r) => r[0]) &&
+ __traits(compiles, (const R r) => r.length))
{
/// ditto
auto ref opIndex(size_t n) const
return _current.front;
}
- static if (is(typeof((cast(const R)_current).front)))
+ static if (__traits(compiles, (const R r) => r.front))
{
/// ditto
@property auto ref front() const
}
}
+// https://github.com/dlang/phobos/issues/10852
+@safe unittest
+{
+ // forward range
+ struct R
+ {
+ int i;
+ int front() => i;
+ bool empty() => i == 0;
+ void popFront() {--i;}
+ R save() => this;
+ }
+
+ auto r = R(10).cycle.take(20);
+ assert(r.array == [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
+}
+
private alias lengthType(R) = typeof(R.init.length.init);
/**
user-defined types that support `++`, the range is an input
range.
- An integral iota also supports `in` operator from the right. It takes
- the stepping into account, the integral won't be considered
- contained if it falls between two consecutive values of the range.
- `contains` does the same as in, but from lefthand side.
+ $(DDOC_SECTION_H `in` operator and `contains`:)
+ `iota` over an integral/pointer type defines the `in` operator from the right.
+ `val in iota(...)` is true when `val` occurs in the range. When present, it takes
+ `step` into account - `val` won't be considered
+ contained if it falls between two consecutive elements of the range.
+ The `contains` method does the same as `in`, but from the left-hand side.
Example:
+ $(RUNNABLE_EXAMPLE
---
void main()
{
writeln();
}
---
+ )
*/
auto iota(B, E, S)(B begin, E end, S step)
if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))
// libraries shipped with DMD. Thus, we check for getnameinfo at
// runtime in the shared module constructor, and use it if it's
// available in the base class method. Classes for specific network
- // families (e.g. InternetHost) override this method and use a
+ // families (e.g. InternetAddress) override this method and use a
// deprecated, albeit commonly-available method when getnameinfo()
// is not available.
// http://technet.microsoft.com/en-us/library/aa450403.aspx
sin6 = addr;
}
+ version (Posix)
+ {
+ /// Human readable string representing the IPv6 address in RFC 2373 form.
+ override string toAddrString() @trusted const
+ {
+ char[INET6_ADDRSTRLEN] buf;
+ string addrString = to!string(
+ .inet_ntop(AddressFamily.INET6, &sin6.sin6_addr, buf.ptr, INET6_ADDRSTRLEN)
+ );
+ return addrString;
+ }
+ }
+
/**
* Parse an IPv6 host address string as described in RFC 2373, and return the
* address.
return this.match!hashOf;
}
}
+
+ /**
+ * Returns the index of the current value's type in the `SumType`'s
+ * $(LREF Types).
+ *
+ * Note that $(LREF Types) does not include type qualifiers that are
+ * applied to the `SumType` itself. To obtain the properly-qualified type
+ * of a qualified `SumType`'s value, use $(REF CopyTypeQualifiers, std,traits).
+ */
+ size_t typeIndex() const
+ {
+ return tag;
+ }
}
// Construction
static assert(SumType!int.sizeof == int.sizeof);
}
+// typeIndex
+@safe unittest
+{
+ alias MySum = SumType!(int, string);
+
+ MySum a = 42;
+ MySum b = "hello";
+
+ assert(a.typeIndex == IndexOf!(int, MySum.Types));
+ assert(b.typeIndex == IndexOf!(string, MySum.Types));
+}
+
+// typeIndex with qualified SumTypes
+@safe unittest
+{
+ alias MySum = SumType!(int[], const int[], immutable int[]);
+
+ int[] a;
+ const int[] ca;
+ immutable int[] ia;
+
+ MySum s1 = a;
+ MySum s2 = ca;
+ MySum s3 = ia;
+
+ const MySum cs1 = s1;
+ const MySum cs2 = s2;
+ const MySum cs3 = s3;
+
+ // Copying a SumType doesn't change its typeIndex
+ assert(cs1.typeIndex == s1.typeIndex);
+ assert(cs2.typeIndex == s2.typeIndex);
+ assert(cs3.typeIndex == s3.typeIndex);
+
+ static bool isIndexOf(Target, Types...)(size_t i)
+ {
+ switch (i)
+ {
+ static foreach (tid, T; Types)
+ case tid: return is(T == Target);
+ default: return false;
+ }
+ }
+
+ // const(int[]) appears twice in ConstTypes.
+ // Both indices are valid return values for typeIndex.
+ alias ConstTypes = Map!(ConstOf, MySum.Types);
+
+ assert(isIndexOf!(const int[], ConstTypes)(cs1.typeIndex));
+ assert(isIndexOf!(const int[], ConstTypes)(cs2.typeIndex));
+ assert(isIndexOf!(immutable int[], ConstTypes)(cs3.typeIndex));
+}
+
/// True if `T` is an instance of the `SumType` template, otherwise false.
private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...);
bool has(Self)(auto ref Self self)
if (isSumType!Self)
{
- return self.match!checkType;
- }
+ import std.meta : ApplyLeft;
+ import std.traits : CopyTypeQualifiers;
- // Helper to avoid redundant template instantiations
- private bool checkType(Value)(ref Value value)
- {
- return is(Value == T);
+ alias AddQualifiers = ApplyLeft!(CopyTypeQualifiers, Self);
+ alias ValueTypes = Map!(AddQualifiers, Self.Types);
+
+ final switch (self.typeIndex)
+ {
+ static foreach (valueTid, Value; ValueTypes)
+ case valueTid:
+ return is(Value == T);
+ }
}
}
import std.typecons : No;
static if (__traits(isRef, self))
- return self.match!(getLvalue!(No.try_, T));
+ return self.getLvalue!(No.try_, T);
else
- return self.match!(getRvalue!(No.try_, T));
+ return self.getRvalue!(No.try_, T);
}
}
import std.typecons : Yes;
static if (__traits(isRef, self))
- return self.match!(getLvalue!(Yes.try_, T));
+ return self.getLvalue!(Yes.try_, T);
else
- return self.match!(getRvalue!(Yes.try_, T));
+ return self.getRvalue!(Yes.try_, T);
}
}
private template getLvalue(Flag!"try_" try_, T)
{
- ref T getLvalue(Value)(ref Value value)
+ ref T getLvalue(Self)(ref Self self)
+ if (isSumType!Self)
{
- static if (is(Value == T))
- {
- return value;
- }
- else
+ import std.meta : ApplyLeft;
+ import std.traits : CopyTypeQualifiers;
+
+ alias AddQualifiers = ApplyLeft!(CopyTypeQualifiers, Self);
+ alias ValueTypes = Map!(AddQualifiers, Self.Types);
+
+ final switch (self.typeIndex)
{
- static if (try_)
- throw new MatchException(failedGetMessage!(T, Value));
- else
- assert(false, failedGetMessage!(T, Value));
+ static foreach (valueTid, Value; ValueTypes)
+ {
+ case valueTid:
+ static if (is(Value == T))
+ {
+ return self.getByIndex!valueTid;
+ }
+ else
+ {
+ static if (try_)
+ throw new MatchException(failedGetMessage!(T, Value));
+ else
+ assert(false, failedGetMessage!(T, Value));
+ }
+ }
}
}
}
private template getRvalue(Flag!"try_" try_, T)
{
- T getRvalue(Value)(ref Value value)
+ T getRvalue(Self)(ref Self self)
+ if (isSumType!Self)
{
- static if (is(Value == T))
- {
- import core.lifetime : move;
+ import std.meta : ApplyLeft;
+ import std.traits : CopyTypeQualifiers;
- // Move if possible; otherwise fall back to copy
- static if (is(typeof(move(value))))
+ alias AddQualifiers = ApplyLeft!(CopyTypeQualifiers, Self);
+ alias ValueTypes = Map!(AddQualifiers, Self.Types);
+
+ final switch (self.typeIndex)
+ {
+ static foreach (valueTid, Value; ValueTypes)
{
- static if (isCopyable!Value)
- // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
- return __ctfe ? value : move(value);
- else
- return move(value);
+ case valueTid:
+ static if (is(Value == T))
+ {
+ import core.lifetime : move;
+
+ // Move if possible; otherwise fall back to copy
+ static if (is(typeof(move(self.getByIndex!valueTid))))
+ {
+ static if (isCopyable!Value)
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
+ if (__ctfe)
+ return self.getByIndex!valueTid;
+ else
+ return move(self.getByIndex!valueTid);
+ }
+ else
+ return move(self.getByIndex!valueTid);
+ }
+ else
+ return self.getByIndex!valueTid;
+ }
+ else
+ {
+ static if (try_)
+ throw new MatchException(failedGetMessage!(T, Value));
+ else
+ assert(false, failedGetMessage!(T, Value));
+ }
}
- else
- return value;
- }
- else
- {
- static if (try_)
- throw new MatchException(failedGetMessage!(T, Value));
- else
- assert(false, failedGetMessage!(T, Value));
}
}
}
// Written in the D programming language.
+//
+// NOTE: This must be kept in sync with phobos/sys/system.d
/**
* Information about the target operating system, environment, and CPU.
openBSD, /// OpenBSD
dragonFlyBSD, /// DragonFlyBSD
solaris, /// Solaris
+ haiku, /// HaikuOS
android, /// Android
otherPosix, /// Other Posix Systems
unknown, /// Unknown
else version (NetBSD) OS os = OS.netBSD;
else version (OpenBSD) OS os = OS.openBSD;
else version (DragonFlyBSD) OS os = OS.dragonFlyBSD;
+ else version (Solaris) OS os = OS.solaris;
+ else version (Haiku) OS os = OS.haiku;
else version (Posix) OS os = OS.otherPosix;
else OS os = OS.unknown;
sh, /// The SuperH architecture, 32-bit
webAssembly, /// The WebAssembly virtual ISA (instruction set architecture), 32-bit
alpha, /// The Alpha architecture
+ loongArch32, /// The LoongAtch architecture, 32-bit
+ loongArch64, /// The LoongArch architecture, 64-bit
+ xtensa, /// The Xtensa architecture, 32-bit
unknown, /// Unknown
}
else version (SH) ISA instructionSetArchitecture = ISA.sh;
else version (WebAssembly) ISA instructionSetArchitecture = ISA.webAssembly;
else version (Alpha) ISA instructionSetArchitecture = ISA.alpha;
+ else version (LoongArch32) ISA instructionSetArchitecture = ISA.loongArch32;
+ else version (loongArch64) ISA instructionSetArchitecture = ISA.loongArch64;
+ else version (Xtensa) ISA instructionSetArchitecture = ISA.xtensa;
else ISA instructionSetArchitecture = ISA.unknown;
}
static assert(pstc.length == 4); // number of parameters
static assert(pstc[0] == STC.ref_);
static assert(pstc[1] == STC.out_);
- version (none)
- {
- // TODO: When the DMD PR (dlang/dmd#11474) gets merged,
- // remove the versioning and the second test
- static assert(pstc[2] == STC.in_);
- // This is the current behavior, before `in` is fixed to not be an alias
- static assert(pstc[2] == STC.scope_);
- }
+ static assert(pstc[2] == STC.in_);
static assert(pstc[3] == STC.none);
}
}
/**
-Detect whether `T` is a callable object, which can be called with the
+Detect whether `callable` is a callable object, which can be called with the
function call operator `$(LPAREN)...$(RPAREN)`.
$(NOTE Implicit Function Template Instantiation is *not* attempted - see below.)
template isCallable(alias callable)
{
static if (is(typeof(&callable.opCall) == delegate))
- // T is a object which has a member function opCall().
+ // callable is a object which has a member function opCall().
enum bool isCallable = true;
else static if (is(typeof(&callable.opCall) V : V*) && is(V == function))
- // T is a type which has a static member function opCall().
+ // callable is a type which has a static member function opCall().
enum bool isCallable = true;
else static if (is(typeof(&callable.opCall!()) TemplateInstanceType))
{
}
}
-/// Functions, function pointers, delegates, lambdas.
+/// Functions, function pointers, delegates, non-template function literals.
@safe unittest
{
void f() { }
int x;
static assert(!isCallable!x);
+ auto d = () => x;
+ static assert( isCallable!d);
}
/// Aggregate types with (static) opCall.
static assert( isCallable!(c.opCall));
static assert( isCallable!S);
static assert( isCallable!(I.value));
- static assert( isCallable!((int a) { return a; }));
static assert(!isCallable!I);
}
{
void f()() { }
T g(T = int)(T x) { return x; }
+ int h(T)();
struct S1 { static void opCall()() { } }
struct S2 { static T opCall(T = int)(T x) {return x; } }
static assert( isCallable!f);
static assert( isCallable!g);
+ static assert(!isCallable!h);
static assert( isCallable!S1);
static assert( isCallable!S2);
}
/**
- * Detect whether symbol or type `X` is a function. This is different that finding
- * if a symbol is callable or satisfying `is(X == function)`, it finds
- * specifically if the symbol represents a normal function declaration, i.e.
+ * Detect whether symbol or type `X` is a function.
+ * This is different from finding if a symbol is callable or satisfying `is(X == return)`.
+ * It finds specifically if the symbol represents a normal function (or method) declaration, i.e.
* not a delegate or a function pointer.
*
* Returns:
// x is a (nested) function symbol.
enum isFunction = true;
}
- else static if (is(X T))
+ else static if (is(X))
{
- // x is a type. Take the type of it and examine.
- enum isFunction = is(T == function);
+ // x is a type
+ enum isFunction = is(X == function);
}
else
enum isFunction = false;
{
static void func(){}
static assert(isFunction!func);
+ static assert(isFunction!(typeof(func)));
+
+ auto fp = &func; // function pointer
+ static assert(!isFunction!fp);
+
+ int i;
+ int f2() => i; // nested function
+ static assert(isFunction!f2);
struct S
{
$(LREF No)
$(LREF Yes)
))
-$(TR $(TD Memory allocation) $(TD
+$(TR $(TD Reference Counting) $(TD
+ $(LREF borrow)
+ $(LREF RefCountedAutoInitialize)
+ $(LREF RefCounted)
+ $(LREF refCounted)
$(LREF SafeRefCounted)
$(LREF safeRefCounted)
- $(LREF RefCountedAutoInitialize)
+))
+$(TR $(TD Memory allocation) $(TD
$(LREF scoped)
$(LREF Unique)
))
$(LREF BlackHole)
$(LREF generateAssertTrap)
$(LREF generateEmptyFunction)
+ $(LREF NotImplementedError)
$(LREF WhiteHole)
))
$(TR $(TD Nullable) $(TD
+ $(LREF apply)
$(LREF Nullable)
$(LREF nullable)
$(LREF NullableRef)
$(LREF Proxy)
$(LREF rebindable)
$(LREF Rebindable)
- $(LREF ReplaceType)
$(LREF unwrap)
$(LREF wrap)
))
$(TR $(TD Types) $(TD
$(LREF alignForSize)
+ $(LREF ReplaceType)
+ $(LREF ReplaceTypeUnless)
$(LREF Ternary)
$(LREF Typedef)
$(LREF TypedefType)
assert(e != 12);
}
- size_t toHash() const @safe nothrow
+ static if (!isAggregateType!T || hasMember!(T, "toHash"))
{
- static if (__traits(compiles, .hashOf(_value.payload)))
- return _isNull ? 0 : .hashOf(_value.payload);
- else
- // Workaround for when .hashOf is not both @safe and nothrow.
- return _isNull ? 0 : typeid(T).getHash(&_value.payload);
+ size_t toHash() const @safe nothrow
+ {
+ static if (__traits(compiles, .hashOf(_value.payload)))
+ return _isNull ? 0 : .hashOf(_value.payload);
+ else
+ // Workaround for when .hashOf is not both @safe and nothrow.
+ return _isNull ? 0 : typeid(T).getHash(&_value.payload);
+ }
}
/**
auto result = cast(immutable(Nullable!(int*))) a;
}
+// https://github.com/dlang/phobos/issues/10758
+@safe unittest
+{
+ struct F
+ {
+ bool opEquals(ref const F rhs) const { return true; }
+ }
+
+ static assert(!__traits(compiles, bool[F]));
+ static assert(!__traits(compiles, bool[Nullable!F]));
+}
+
/**
Just like `Nullable!T`, except that the null state is defined as a
particular value. For example, $(D Nullable!(uint, uint.max)) is an
// apply
/**
-Unpacks the content of a `Nullable`, performs an operation and packs it again. Does nothing if isNull.
+Unpacks the content of a $(LREF Nullable), performs an operation and packs it again. Does nothing if $(LREF isNull).
When called on a `Nullable`, `apply` will unpack the value contained in the `Nullable`,
pass it to the function you provide and wrap the result in another `Nullable` (if necessary).
{
import std.functional : unaryFun;
+ ///
auto apply(T)(auto ref T t)
if (isInstanceOf!(Nullable, T))
{
{
import std.functional : unaryFun;
+ ///
auto ref borrow(RC)(RC refCount)
if
(
$(TD $(MYREF sha1UUID)
$(MYREF randomUUID)
$(MYREF md5UUID)
+ $(MYREF timestampRandomUUID)
)
)
$(TR $(TDNW Using UUIDs)
assert(id.empty);
}
+import core.time : dur;
+import std.bitmanip : bigEndianToNative, nativeToBigEndian;
+import std.datetime.systime : SysTime;
+import std.datetime : Clock, DateTime, UTC;
import std.range.primitives;
import std.traits;
///Version 4 (Random)
randomNumberBased = 4,
///Version 5 (Name based + SHA-1)
- nameBasedSHA1 = 5
+ nameBasedSHA1 = 5,
+ ///Version 7 (milliseconds since unix epoch + random)
+ timestampRandom = 7
}
union
assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1))));
}
+ /**
+ * UUID V7 constructor
+ *
+ * This implementation is not guaranteed to use a cryptographically secure PRNG.
+ * For more information please see: std.random.unpredictableSeed
+ *
+ * Params:
+ * timestamp = the timestamp part of the UUID V7
+ * random = UUID V7 has 74 bits of random data, which rounds to 10 ubyte's.
+ * If no random data is given, random data is generated.
+ */
+ @safe pure this(SysTime timestamp, ubyte[10] random = generateRandomData!10)
+ {
+ ulong epoch = (timestamp - SysTime.fromUnixTime(0)).total!"msecs";
+ this(epoch, random);
+ }
+
+ /// ditto
+ @safe pure this(ulong epoch_msecs, ubyte[10] random = generateRandomData!10)
+ {
+ ubyte[8] epoch = epoch_msecs.nativeToBigEndian;
+
+ this.data[0 .. 6] = epoch[2 .. 8];
+ this.data[6 .. $] = random;
+
+ // version and variant
+ this.data[6] = (this.data[6] & 0x0F) | 0x70;
+ this.data[8] = (this.data[8] & 0x3F) | 0x80;
+ }
+
+ ///
+ @system unittest
+ {
+ import std.datetime : DateTime, SysTime;
+ SysTime st = DateTime(2025, 8, 19, 10, 38, 45);
+ UUID u = UUID(st);
+ SysTime o = u.v7Timestamp();
+ assert(o == st, st.toString() ~ " | " ~ o.toString());
+ }
+
/**
* <a name="UUID(string)"></a>
* Parse a UUID from its canonical string form. An UUID in its
enum res = ctfeTest();
}
+ /**
+ * If the UUID is of version 7 it has a timestamp that this function
+ * returns, otherwise an UUIDParsingException is thrown.
+ */
+ SysTime v7Timestamp() const {
+ if (this.uuidVersion != Version.timestampRandom)
+ {
+ throw new UUIDParsingException("The UUID is not of version" ~
+ " v7 therefore no timestamp exist", 0);
+ }
+
+ import std.bitmanip : bigEndianToNative;
+
+ ubyte[8] tmp = void;
+ tmp[0 .. 2] = 0;
+ tmp[2 .. 8] = data[0 .. 6];
+
+ ulong milli = tmp.bigEndianToNative!ulong;
+
+ return SysTime(DateTime(1970, 1, 1), UTC()) + dur!"msecs"(milli);
+ }
+
+ /**
+ * If the UUID is of version 7 it has a timestamp that this function
+ * returns as described in RFC 9562 (Method 3), otherwise an
+ * UUIDParsingException is thrown.
+ */
+ SysTime v7Timestamp_method3() const {
+ auto ret = v7Timestamp();
+
+ const ubyte[2] rand_a = [
+ data[6] & 0x0f, // masks version bits
+ data[7]
+ ];
+
+ const float hnsecs = rand_a.bigEndianToNative!ushort / MonotonicUUIDsFactory.subMsecsPart;
+ ret += dur!"hnsecs"(cast(ulong) hnsecs);
+
+ return ret;
+ }
+
/**
* RFC 4122 defines different internal data layouts for UUIDs.
* Returns the format used by this UUID.
return Version.randomNumberBased;
else if ((octet9 & 0xF0) == 0x50)
return Version.nameBasedSHA1;
+ else if ((octet9 & 0xF0) == 0x70)
+ return Version.timestampRandom;
else
return Version.unknown;
}
0x40 : UUID.Version.randomNumberBased,
0x50 : UUID.Version.nameBasedSHA1,
0x60 : UUID.Version.unknown,
- 0x70 : UUID.Version.unknown,
+ 0x70 : UUID.Version.timestampRandom,
0x80 : UUID.Version.unknown,
0x90 : UUID.Version.unknown,
0xa0 : UUID.Version.unknown,
assert(u1.uuidVersion == UUID.Version.randomNumberBased);
}
+///
+class MonotonicUUIDsFactory
+{
+ import core.sync.mutex : Mutex;
+ import core.time : Duration;
+ import std.datetime.stopwatch : StopWatch;
+
+ private shared Mutex mtx;
+ private StopWatch startTimePoint;
+
+ ///
+ this(in SysTime startTime = SysTime.fromUnixTime(0)) shared
+ {
+ this(Clock.currTime(UTC()) - startTime);
+ }
+
+ ///
+ this(in Duration timeElapsed, bool autostartDisabledForTesting = false) shared
+ {
+ mtx = new shared Mutex();
+
+ (cast() startTimePoint).setTimeElapsed = timeElapsed;
+
+ if (!autostartDisabledForTesting)
+ (cast() startTimePoint).start();
+ }
+
+ private auto peek() shared
+ {
+ mtx.lock();
+ scope(exit) mtx.unlock();
+
+ return (cast() startTimePoint).peek;
+ }
+
+ // hnsecs is 1/10_000 of millisecond
+ // rand_a size is 12 bits (4096 values)
+ private enum float subMsecsPart = 1.0f / 10_000 * 4096;
+
+ /**
+ * Returns a monotonic timestamp + random based UUIDv7
+ * as described in RFC 9562 (Method 3).
+ */
+ UUID createUUIDv7_method3(ubyte[8] externalRandom = generateRandomData!8) shared
+ {
+ const curr = peek.split!("msecs", "hnsecs");
+ const qhnsecs = cast(ushort) (curr.hnsecs * subMsecsPart);
+
+ ubyte[10] rand;
+
+ // Whole rand_a is 16 bit, but usable only 12 MSB.
+ // additional 4 less significant bits consumed
+ // by a version value
+ rand[0 .. 2] = qhnsecs.nativeToBigEndian;
+ rand[2 .. $] = externalRandom;
+
+ return UUID(curr.msecs, rand);
+ }
+}
+
+/// Generate monotone UUIDs
+@system unittest
+{
+ auto f = new shared MonotonicUUIDsFactory;
+
+ UUID[10] monotonic;
+
+ foreach (ref u; monotonic)
+ u = f.createUUIDv7_method3;
+}
+
+@system unittest
+{
+ import std.conv : to;
+ import std.datetime;
+
+ const currTime = SysTime(DateTime(2025, 9, 12, 21, 38, 45), UTC());
+ Duration d = currTime - SysTime.fromUnixTime(0) + dur!"msecs"(123);
+
+ auto f = new shared MonotonicUUIDsFactory(d, true);
+
+ const u1 = f.createUUIDv7_method3();
+ assert(u1.uuidVersion == UUID.Version.timestampRandom);
+
+ // sub-millisecond part zeroed
+ assert((u1.data[6] & 0b0000_1111) == 0);
+ assert(u1.data[7] == 0);
+
+ const uuidv7_milli_1 = u1.v7Timestamp;
+
+ {
+ const st = u1.v7Timestamp_method3;
+ assert(cast(DateTime) st == cast(DateTime) currTime, st.to!string);
+
+ const sp = st.fracSecs.split!("msecs", "usecs", "hnsecs");
+ assert(sp.msecs == 123, sp.to!string);
+ assert(sp.usecs == 0, sp.to!string);
+ }
+
+ // 0.3 usecs, but Method 3 precision is only 0.25 of usec,
+ // thus, expected value is 2
+ d += dur!"hnsecs"(3);
+ f = new shared MonotonicUUIDsFactory(d, true);
+
+ const u2 = f.createUUIDv7_method3();
+ const uuidv7_milli_2 = u2.v7Timestamp;
+ assert(uuidv7_milli_1 == uuidv7_milli_2);
+
+ {
+ const st = u2.v7Timestamp_method3;
+ assert(cast(DateTime) st == cast(DateTime) currTime, st.to!string);
+
+ const sp = st.fracSecs.split!("msecs", "usecs", "hnsecs");
+ assert(sp.msecs == 123, sp.to!string);
+ assert(sp.usecs == 0, sp.to!string);
+ assert(sp.hnsecs == 2, sp.to!string);
+ }
+}
+
+@system unittest
+{
+ import core.thread.osthread : Thread;
+ import std.datetime;
+
+ scope f = new shared MonotonicUUIDsFactory;
+
+ UUID[1000] uuids;
+
+ foreach (ref u; uuids)
+ {
+ // UUIDv7 Method 3 monotonicity is only guaranteed if UUIDs are
+ // generated slower than 2.5 microseconds
+ Thread.sleep(dur!("hnsecs")(25));
+ u = f.createUUIDv7_method3;
+ }
+
+ foreach (i; 1 .. uuids.length)
+ {
+ assert(uuids[i-1].v7Timestamp_method3 < uuids[i].v7Timestamp_method3);
+ assert(uuids[i-1].data[8 .. $] != uuids[i].data[8 .. $], "random parts are equal");
+ }
+}
+
+/**
+ * This function returns a timestamp + random based UUID aka. uuid v7.
+ */
+UUID timestampRandomUUID()
+{
+ return UUID(Clock.currTime(UTC()));
+}
+
+///
+@system unittest
+{
+ UUID u = timestampRandomUUID();
+ assert(u.uuidVersion == UUID.Version.timestampRandom);
+}
+
/**
* This is a less strict parser compared to the parser used in the
* UUID constructor. It enforces the following rules:
]);
}
+private ubyte[Size] generateRandomData(ubyte Size)() {
+ import std.random : Random, uniform, unpredictableSeed;
+
+ auto rnd = Random(unpredictableSeed);
+
+ ubyte[Size] bytes;
+ foreach (idx; 0 .. bytes.length)
+ {
+ bytes[idx] = uniform!(ubyte)(rnd);
+ rnd.popFront();
+ }
+ return bytes;
+}
+
/**
* This exception is thrown if an error occurs when parsing a UUID
* from a string.
assert(ex.position == 10);
assert(ex.reason == UUIDParsingException.Reason.tooMuch);
}
+
+/// uuidv7
+@system unittest
+{
+ import std.datetime : DateTime, SysTime;
+
+ SysTime st = DateTime(2025, 8, 19, 10, 38, 45);
+ UUID u = UUID(st);
+ assert(u.uuidVersion == UUID.Version.timestampRandom);
+ SysTime o = u.v7Timestamp();
+ assert(o == st, st.toString() ~ " | " ~ o.toString());
+ string s = u.toString();
+ UUID u2 = UUID(s);
+ SysTime o2 = u2.v7Timestamp();
+ assert(o2 == st, st.toString() ~ " | " ~ o2.toString());
+}
+
+@system unittest
+{
+ import std.datetime : SysTime;
+
+ UUID u = timestampRandomUUID();
+ assert(u.uuidVersion == UUID.Version.timestampRandom);
+
+ SysTime o = u.v7Timestamp();
+ assert(o.year > 2024);
+ assert(o.year < 3024);
+}
+
+/// uuid v7 generated by external tool
+@system unittest
+{
+ import std.datetime : DateTime, SysTime;
+ UUID u = UUID("0198c2b2-c5a8-7a0f-a1db-86aac7906c7b");
+ auto d = DateTime(2025,8,19);
+ assert((cast(DateTime) u.v7Timestamp()).year == d.year);
+}
testRequire1();
testRequire2();
testRequire3();
+ testRequire4();
testUpdate1();
testUpdate2();
+ testUpdate3();
testByKey1();
testByKey2();
testByKey3();
testTypeInfoCollect();
testNew();
testAliasThis();
+ testAliasThis2();
}
void testKeysValues1()
assert("foo" in aa);
}
+void testRequire4() pure
+{
+ int[int] aa;
+ try
+ aa.require(5, {
+ if (true)
+ throw new Exception("oops");
+ else
+ return 1;
+ }());
+ catch (Exception e) {}
+ assert(5 !in aa);
+}
+
void testUpdate1()
{
assert(updated);
}
+void testUpdate3() pure
+{
+ int[int] aa;
+ try
+ aa.update(5, {
+ if (true)
+ throw new Exception("oops");
+ else
+ return 1;
+ }, (ref int v) {
+ throw new Exception("unexpected update");
+ });
+ catch (Exception e) {}
+ assert(5 !in aa);
+}
+
+
void testByKey1() @safe
{
static struct BadValue
s.remove(1);
assert(S.numCopies == 0);
}
+
+void testAliasThis2()
+{
+ static struct A
+ {
+ bool extra;
+ uint id;
+ }
+
+ static struct B
+ {
+ uint id;
+
+ A toA() const pure nothrow
+ {
+ return A(false, id);
+ }
+
+ alias toA this;
+ }
+
+ bool[A] aa;
+ aa[B(5)] = true;
+ assert(B(5) in aa);
+ assert(A(false, 5) in aa);
+}
+++ /dev/null
-// { dg-skip-if "test hangs the testsuite PR103944" { *-*-darwin* } }
-import core.stdc.stdlib : exit;
-import core.sys.posix.sys.wait : waitpid;
-import core.sys.posix.unistd : fork;
-import core.thread : Thread;
-
-void main()
-{
- foreach (t; 0 .. 10)
- new Thread({
- foreach (n; 0 .. 100)
- {
- foreach (x; 0 .. 100)
- new ubyte[x];
- auto f = fork();
- assert(f >= 0);
- if (f == 0)
- exit(0);
- else
- waitpid(f, null, 0);
- }
- }).start();
-}
--- /dev/null
+import core.exception;
+import core.thread;
+
+__gshared bool caught;
+
+void main()
+{
+ filterThreadThrowableHandler = (ref Throwable t) {
+ if (auto t2 = cast(AssertError) t)
+ {
+ if (t2.message != "Hey!")
+ return;
+ }
+ else
+ return;
+
+ caught = true;
+ t = null;
+ };
+
+ Thread t = new Thread(&entry);
+ t.start();
+ t.join();
+
+ assert(caught);
+}
+
+void entry()
+{
+ throw new AssertError("Hey!");
+}
--- /dev/null
+import core.exception;
+import core.thread;
+
+__gshared bool caught;
+
+void main()
+{
+ Thread t = new MyThread(&entry);
+ t.start();
+ t.join();
+
+ assert(caught);
+}
+
+class MyThread : Thread
+{
+ this( void function() fn, size_t sz = 0 ) @safe pure nothrow @nogc
+ {
+ super(fn, sz);
+ }
+
+ override void filterCaughtThrowable(ref Throwable t) @system nothrow
+ {
+ if (auto t2 = cast(AssertError) t)
+ {
+ if (t2.message == "Hey!")
+ {
+ caught = true;
+ t = null;
+ return;
+ }
+ }
+
+ super.filterCaughtThrowable(t);
+ }
+}
+
+void entry()
+{
+ throw new AssertError("Hey!");
+}